C# 如何管理从实用程序类加载动态视图

C# 如何管理从实用程序类加载动态视图,c#,wpf,C#,Wpf,我正在开发一个wpf dll,其中包含许多视图以及附带的视图模型,以满足MVVM的要求 在我的项目中,我有一个类,它充当我的“视图管理器”,负责将每个视图绑定到它们正确的视图模型 namespace ControlsAndResources { public class View { private static readonly ViewModelLocator s_viewModelLocator = new ViewModelLocator();

我正在开发一个wpf dll,其中包含许多视图以及附带的视图模型,以满足MVVM的要求

在我的项目中,我有一个类,它充当我的“视图管理器”,负责将每个视图绑定到它们正确的视图模型

namespace ControlsAndResources
{
    public class View
    {
        private static readonly ViewModelLocator s_viewModelLocator = new ViewModelLocator();

        public static readonly DependencyProperty ViewModelProperty = DependencyProperty.RegisterAttached("ViewModel", typeof(string), 
            typeof(ViewModelLocator), new PropertyMetadata(new PropertyChangedCallback(OnChanged)));

        public static void SetViewModel(UserControl view, string value) => view.SetValue(ViewModelProperty, value);

        public static string GetViewModel(UserControl view) => (string)view.GetValue(ViewModelProperty);

        private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UserControl view = (UserControl)d;
            string viewModel = e.NewValue as string;
            switch (viewModel)
            {
                case "TestViewModel":
                    view.DataContext = s_viewModelLocator.TestViewModel;
                    break;
                case "FooViewModel":
                    view.DataContext = s_viewModelLocator.FooViewModel;
                    break;
                default:
                    view.DataContext = null;
                    break;
            }
        }
    }
}
然后对每个xaml声明进行绑定(这里是一个示例)


...
...

这是完美的。但现在我想做的是,在MainView.xaml中,包含ContentControl或ItemControl,并使用绑定从视图类更新视图。我该怎么做呢?

下面是我要做的:我将编写一个实现INotifyPropertyChanged的
MainViewModel
。我会给它一个SelectedChild属性。我将对您得到的INotifyPropertyChanged实现进行一些假设。如果这些假设是错误的,请告诉我,我们可以让它与你所拥有的一起工作

MainViewModel.cs

private Object _selectedChild;
public Object SelectedChild
{
    get => _selectedChild;
    set => SetProperty(ref _selectedChild, value);
}
MainViewModel将是我们的主视图的DataContext,可能是MainWindow

MainWindow.xaml.cs

public MainWindow()
{
    InitializeComponent();

    //  More about this guy later
    DynamicDataTemplateCreator creator = this.FindResource("DynamicDataTemplateCreator") 
        as DynamicDataTemplateCreator;

    //  This gibberish is a stand in for whatever information the template creator 
    //  may need to figure out what type of view belongs to what type of viewmodel. 
    creator.ViewLookupInformation = 
        "My expatriate aunt Sally ate nine autumnal polar bears in Zanzibar.";

    DataContext = new MainViewModel();
}
在MainWindow的XAML中,我们将把ContentControl绑定到SelectedChild,并创建一个模板选择器实例(见下文),用于创建显示SelectedChild的模板:

MainWindow.xaml

<Window.Resources>
    <local:DynamicDataTemplateCreator x:Key="DynamicDataTemplateCreator" />
</Window.Resources>
<Grid>
    <ContentControl 
        Content="{Binding SelectedChild}" 
        ContentTemplateSelector="{StaticResource DynamicDataTemplateCreator}" 
        />

您应该有一个主viewmodel,其子viewmodel属性
公共对象SelectedChild{/*INPC stuff*/}
。主视图模型应该是主视图的DataContext。将SelectedChild绑定到主视图中ContentControl的Content属性。为每个viewmodel类型编写一个。隐式datatemplate只创建适当的视图。您的viewmodel定位器类不是必需的。啊,您似乎正在从某些外部源加载视图。在这种情况下,您可以编写一个只包含所需视图实例的新DataTemplate,而不是编写隐式datatemplates。您可以将其与上面的ContentControl一起使用。您希望尽可能少地重新发明我第一次评论中描述的轮子。@EdPlunkett好的,我将尝试这种方法。谢谢。我也看不出这一票有什么意义。不过,抱怨是没有意义的。你应该对这里的落选保持坚忍。不管怎么说,当时没有留下评论的人现在留下评论的可能性几乎为零。否决权似乎有点苛刻。也许上下文可以解释得更清楚一点。耸肩请投我一票,非常感谢。我喜欢的是,我可以告别ViewModelLocator。我只是觉得没什么意义。
<Window.Resources>
    <local:DynamicDataTemplateCreator x:Key="DynamicDataTemplateCreator" />
</Window.Resources>
<Grid>
    <ContentControl 
        Content="{Binding SelectedChild}" 
        ContentTemplateSelector="{StaticResource DynamicDataTemplateCreator}" 
        />
public class DynamicDataTemplateCreator : DataTemplateSelector
{
    //  If mainwindow or the main viewmodel has information that we need about the 
    //  dynamically loaded views, pass that information via this property. It can be any 
    //  type you want, preferably the exact type of the information you are passing.
    public object ViewLookupInformation { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //  item is the viewmodel.
        Type viewType = null;

        //  A quickie UserControl I wrote for testing. 
        //viewType = typeof(VMView);

        /* 
         * logic here to determine the type of view we want. Assign that Type to viewType
         *  If you need extra information from the main program, smuggle it in to here via 
         *  ViewLookupInformation
         */

        return new DataTemplate
        {
            VisualTree = new FrameworkElementFactory(viewType)
        };
    }
}