C# 使用来自资源的控制

C# 使用来自资源的控制,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我尝试在我的应用程序中使用MVVM,因为它使事情变得更简单。因此,我的菜单项是一个数据类,绑定到一个具有自定义样式的按钮。该样式实际上绑定了底层数据 问题是,这些菜单项可以选择性地包含其他内容,如果不需要,可以将这些内容设置为折叠。到目前为止,这工作还不错,但是切换到MVVM会带来一些问题。我需要在某处定义附加内容,并以编程方式将其附加到菜单项 用代码进行设计是一种糟糕的方法。所以我想到了使用WPFs资源系统。我尝试将内容插入到有效的资源中。我可以按原样指定控件 但是当我尝试通过FindReso

我尝试在我的应用程序中使用MVVM,因为它使事情变得更简单。因此,我的菜单项是一个数据类,绑定到一个具有自定义样式的按钮。该样式实际上绑定了底层数据

问题是,这些菜单项可以选择性地包含其他内容,如果不需要,可以将这些内容设置为折叠。到目前为止,这工作还不错,但是切换到MVVM会带来一些问题。我需要在某处定义附加内容,并以编程方式将其附加到菜单项

用代码进行设计是一种糟糕的方法。所以我想到了使用WPFs资源系统。我尝试将内容插入到有效的资源中。我可以按原样指定控件

但是当我尝试通过FindResource方法加载它时,它什么也找不到。我把它放在不同的地方,在不同的状态调用代码窗口构造函数,加载事件。。。但它在任何地方都找不到这种控制

我尝试了Application.Current.Resources[…],Application.Current.FindResource。。。窗口类也是如此。它总是出现ResourceReferenceKeyNotFoundException

此外,当放入资源文件时,它会给我一个解析异常,表示我不能在资源对象上使用x:Name。所以我基本上不能使用整个系统,因为我需要名称来绑定控件子元素之间的内容

这可能吗?是否打算这样使用?有没有更好的方法来实现这一点?这也是我不了解WPF设计原则的地方

PS:我尝试使用页面和资源作为.xaml文件的编译选项

这就是控制:

<views:AnimatedStackPanel x:Key="LiftMenuAnalyzeContent" HorizontalAlignment="Stretch" Orientation="Vertical" >
<views:AnimatedStackPanel.Children>
    <StackPanel x:Name="PaneAnalysisStep" Orientation="Horizontal" HorizontalAlignment="Center">
        <shared:Triangle Width="20" Height="24" ApexSide="Left"
                                            Fill="#ff8000" />
        <Border HorizontalAlignment="Stretch" VerticalAlignment="Center" Background="#ff8000"
                                BorderBrush="#ff8000" BorderThickness="0" Padding="4">
            <TextBlock x:Name="TxtAnalysisStep" Text="Analyzing" />
        </Border>
        <shared:Triangle Width="20" Height="24" ApexSide="Right"
                                            Fill="#ff8000" />
    </StackPanel>

    <Grid x:Name="GridAnalysisInfo" HorizontalAlignment="Center" VerticalAlignment="Stretch">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30" />
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="50" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="27" />
            <RowDefinition Height="27" />
            <RowDefinition Height="27" />
        </Grid.RowDefinitions>

        <Label x:Name="LblObjectsFound" Grid.Row="1" Grid.Column="0"
                                Content="0" />
        <Label Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left"
                                Content="Targets" />
        <Label x:Name="LblIssuesFound" Grid.Row="2" Grid.Column="0"
                                Content="0" />
        <Label Grid.Row="2" Grid.Column="2" HorizontalAlignment="Left"
                                Content="Issues" />
        <Label x:Name="LblErrorsFound" Grid.Row="3" Grid.Column="0"
                                Content="0" />
        <Label Grid.Row="3" Grid.Column="2" HorizontalAlignment="Left"
                                Content="Errors" />
    </Grid>

    <TextBlock Margin="0,5,0,0" FontWeight="Bold"
                                Text="Not possible." TextAlignment="Center" TextWrapping="Wrap" />
</views:AnimatedStackPanel.Children>

如果要在MVVM中创建动态菜单,则首先需要为每个菜单项声明一个视图模型,该菜单项包含一个标题、一个动态内容字段(稍后将对此进行详细介绍)以及一个所有子菜单项的列表:

public class MenuItemViewModel : ViewModelBase
{
    public string Header {get; set;}
    public object Content {get; set;}
    public MenuItemViewModel[] Children { get; set; }
}
然后,视图模型需要创建以下结构的树:

public class DemoViewModel : ViewModelBase
{
    public MenuItemViewModel[] MenuItems {get; set;}

    public DemoViewModel()
    {
        this.MenuItems = new MenuItemViewModel[]
        {
            new MenuItemViewModel{
                Header = "File", Content="icon.png",
                Children = new MenuItemViewModel[]
                {
                    new MenuItemViewModel{Header="Open", Content="icon.png"},
                    new MenuItemViewModel{Header="Save", Content="icon.png"},
                    new MenuItemViewModel{Header="Close", Content="icon.png"},
                }
            },
            new MenuItemViewModel{
                Header = "Edit", Content="icon.png",
                Children = new MenuItemViewModel[]
                {
                    new MenuItemViewModel{Header="Cut", Content="icon.png"},
                    new MenuItemViewModel{Header="Copy", Content="icon.png"},
                    new MenuItemViewModel{Header="Paste", Content="icon.png"},
                }
            }
        };
    }
}
显示此菜单只需将菜单绑定到菜单项并指定一个HierarchicalDataTemplate,以便它知道如何绘制它们:

    <Menu ItemsSource="{Binding MenuItems}" DockPanel.Dock="Top">
        <Menu.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding Children}">
                <ContentPresenter Content="{Binding Header}" />
            </HierarchicalDataTemplate>
        </Menu.Resources>
    </Menu>
结果:


在这个特定的示例中,我将其硬编码为一个图像,并假设内容始终是一个字符串,但使用DataTemplate,您可以将内容设置为您想要的任何类,还可以指定每个类的显示方式。

不,这不是正确的方法。您尝试使用FindResource和Application.Current.Resources的事实表明您仍在使用代码隐藏,而这不是MVVM。回答这个问题几乎是不可能的,因为您发布的代码似乎与您的描述不匹配。您谈论的是一个具有自定义内容的菜单,而您提供的XAML是一个在网格上带有标签的StackPanel。你到底想做什么?问题不在于菜单。菜单不错,风格也不错。如果我使用INotifyChangedProperty添加此菜单项的仅属性实例,它将正确显示该项。但是MVVM不是让您绑定一组项,然后自动从模型中创建视图吗?在本例中,菜单项就是模型。所以我用代码创建这些菜单项,因为它应该是动态的。我想添加到这个控件中的内容应该在某个地方声明,因为如果用代码创建这个控件的话,需要做很多工作。所以我必须加载它。谢谢你的详细回答。我想到目前为止我是对的。毕竟它确实是这样工作的。我在这里的特殊问题是,我没有一个图标作为内容,而是一个控件的整个层次结构。我在原始帖子中发布的内容——这是一种内容。但是,在代码中声明这一点是一件非常麻烦的工作,而且极不灵活。我想像XAML中的任何视图一样声明它,将其加载到DemoViewModel中,向该控件层次结构的特定元素添加绑定,然后将其设置为一个菜单项的内容。但正如前面提到的:我不能从参考资料中加载它。那很好,只需将所有内容放在DataTemplate中,并用自定义类在视图模型中表示它。或者,将其放在ControlTemplate中,并使用DataTrigger将其设置为MenuItem的模板。无论哪种方式,您都不必直接在代码隐藏或视图模型中创建视图控件或访问资源。
            <HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Header}" />
                    <Image Width="16" Height="16">
                        <Image.Source>
                            <BitmapImage UriSource="{Binding Content}" />
                        </Image.Source>
                    </Image>
                </StackPanel>
            </HierarchicalDataTemplate>