Wpf 具有统一性的嵌套数据上下文
去年我在大学上了一门关于VB.Net+WPF的课程。对于最后一个项目,我决定试一试MVVM(我们在课程中根本没有讨论它,我只是研究了一下,认为这是一个有用的练习)。这是一次很好的经历,但我相当肯定,在设计方面,我可能会做出一些糟糕的选择 我已经毕业了,我的工作与WPF或Windows开发无关,但是我在自己的时间里开发了一个小应用程序,我认为使用C#和WPF会很有趣(C#是一种我非常喜欢使用的语言,我喜欢使用WPF,所以这是一个非常合乎逻辑的选择) 无论如何,我将利用这个机会来学习更多关于MVVM的知识,并尝试以比以前更好的方式实现它。我读了更多的书,我发现它比我在学习WPF的同时尝试实现它时要容易得多 我已经使用了盒式MVVM培训作为指导,并将在此时使用Unity进行依赖注入 现在,在指南中开发的示例应用程序中,有一个单一视图模型(MainWindowViewModel)。MainWindow实际上是一个包含3个或4个用户控件的容器,这些控件都共享MainWindow的DataContext 在我的应用程序中,我希望有一个基于选项卡的界面。因此,主窗口主要涉及显示按钮列表以切换当前视图(即从“添加”视图移动到“列表视图”)。每个视图都是一个自包含的UserControl,它将实现自己的DataContext 应用程序中的相同代码如下所示:Wpf 具有统一性的嵌套数据上下文,wpf,mvvm,dependency-injection,unity-container,Wpf,Mvvm,Dependency Injection,Unity Container,去年我在大学上了一门关于VB.Net+WPF的课程。对于最后一个项目,我决定试一试MVVM(我们在课程中根本没有讨论它,我只是研究了一下,认为这是一个有用的练习)。这是一次很好的经历,但我相当肯定,在设计方面,我可能会做出一些糟糕的选择 我已经毕业了,我的工作与WPF或Windows开发无关,但是我在自己的时间里开发了一个小应用程序,我认为使用C#和WPF会很有趣(C#是一种我非常喜欢使用的语言,我喜欢使用WPF,所以这是一个非常合乎逻辑的选择) 无论如何,我将利用这个机会来学习更多关于MVVM
MainWindow window = container.Resolve<MainWindow>();
window.DataContext = container.Resolve<MainWindowViewModel>();
window.Show();
MainWindow=container.Resolve();
window.DataContext=container.Resolve();
window.Show();
这对于设置主窗口的数据上下文很好,但是如何处理将每个用户上下文分配给它自己的ViewModel作为DataContext
编辑:更具体地说,当我说基于选项卡的界面时,我不是指文本编辑器或web浏览器中的选项卡。相反,每个“选项卡”都是应用程序的不同屏幕-一次只有一个活动屏幕
此外,虽然Slauma的文章有些帮助,但它并没有真正解释我将如何向这些选项卡注入依赖项。例如,如果NewStatementView需要输出其数据,我将如何注入实现“IStatementWriter”接口的类的实例
编辑:为了简化我的问题,我基本上是想弄清楚如何在不通过构造函数传递每个依赖项的情况下向类注入依赖项。作为一个人为的例子:
A类有B类。
类B作为构造函数参数,需要接口I1的实现。
B类使用C类。
类C作为构造函数参数,需要接口I2的实现
我如何使用DI(和Unity)处理这个场景?我不想做的是:
公共类A(I1I1,I2I2){……}
我可以使用Unity注册所有内容(即创建I2,然后创建C,然后创建I1和B,最后将它们插入到A中),但如果我想使用A,那么我必须实例化所有内容,即使我甚至可能不需要B的实例(如果我有一大堆其他类与B处于相同的情况,该怎么办?)。(它还提供了一个可下载的示例应用程序。) 将每个选项卡与UserControl连接的基本思想如下(只是一个粗略的草图,详细信息在本文中): 主窗口视图有一个ContentControl
<ContentControl Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}" />
对于每个特定选项卡,您都有一个用户控件,其中包含一个从WorkspaceViewModel派生的ViewModel
public class MySpecialViewModel : WorkspaceViewModel
。。。通过DataTemplate与UserControl相关:
<DataTemplate DataType="{x:Type vm:MySpecialViewModel}" >
<v:MySpecialUserControl />
</DataTemplate>
其余部分由WPF绑定引擎完成。TabControl自动识别集合中此特殊工作区项的类型为
MySpecialViewModel
,并通过我们定义的数据模板选择正确的视图/用户控件,以连接ViewModel和View,并将其显示在新选项卡中。MVVM有很多好处,但根据我的经验,连接视图模型和视图是最大的复杂性之一
有两种主要方法可以做到这一点:
1:
将视图模型关联到视图
在此场景中,main窗口的XAML包含子控件。在您的情况下,其中一些视图可能会被隐藏(因为您一次只显示一个屏幕)
视图模型通常通过以下两种方式之一连接到视图:
在代码隐藏中,在调用InitializeComponents()
之后,或者在this.Loaded
事件处理程序中,让this.DataContext=container.Resolve()代码>
注意,在这种情况下,容器需要全局可用。这在使用Unity的应用程序中是典型的。您询问孩子们将如何解析像IStatementWriter
这样的接口。如果容器是全局的,子视图模型可以简单地调用container.Resolve()代码>
将视图模型关联到视图的另一种方法是在XAML中创建视图模型的实例,如下所示:
<UserControl ...>
<UserControl.DataContext>
<local:MyViewModelType/>
</UserControl.DataContext>
...
</UserControl>
...
这种方法与Unity不兼容。有一些MVVM框架允许您在XAML中解析类型(我相信是的)。这些框架通过以下方式实现这一点
2:
将视图关联到视图模型
public class MainViewModel
{
public MyViewModelType Model1 { get; private set; }
public ViewModelType2 Model2 { get; private set; }
public ViewModelType3 Model3 { get; private set; }
public MainViewModel()
{
// This allows us to use Unity to resolve the view models!
// We can use a global container or pass it into the constructor of the main view model
// The dependencies for the child view models could then be resolved in their
// constructors if you don't want to make the container global.
Model1 = container.Resolve<MyViewModelType>();
Model2 = container.Resolve<ViewModelType2>();
Model3 = container.Resolve<ViewModelType3>();
CurrentViewModel = Model1;
}
// You will need to fire property changed notifications here!
public object CurrentViewModel { get; set; }
}
这通常是我首选的方法,尽管它使XAML树更加复杂。当需要在主视图模型中执行导航时,此方法非常有效
在主视图模型中创建子视图模型对象
public class MainViewModel
{
public MyViewModelType Model1 { get; private set; }
public ViewModelType2 Model2 { get; private set; }
public ViewModelType3 Model3 { get; private set; }
public MainViewModel()
{
// This allows us to use Unity to resolve the view models!
// We can use a global container or pass it into the constructor of the main view model
// The dependencies for the child view models could then be resolved in their
// constructors if you don't want to make the container global.
Model1 = container.Resolve<MyViewModelType>();
Model2 = container.Resolve<ViewModelType2>();
Model3 = container.Resolve<ViewModelType3>();
CurrentViewModel = Model1;
}
// You will need to fire property changed notifications here!
public object CurrentViewModel { get; set; }
}
public类主视图模型
{
公共MyViewModelType Model1{get;priva
<UserControl ...>
<UserControl.DataContext>
<local:MyViewModelType/>
</UserControl.DataContext>
...
</UserControl>
public class MainViewModel
{
public MyViewModelType Model1 { get; private set; }
public ViewModelType2 Model2 { get; private set; }
public ViewModelType3 Model3 { get; private set; }
public MainViewModel()
{
// This allows us to use Unity to resolve the view models!
// We can use a global container or pass it into the constructor of the main view model
// The dependencies for the child view models could then be resolved in their
// constructors if you don't want to make the container global.
Model1 = container.Resolve<MyViewModelType>();
Model2 = container.Resolve<ViewModelType2>();
Model3 = container.Resolve<ViewModelType3>();
CurrentViewModel = Model1;
}
// You will need to fire property changed notifications here!
public object CurrentViewModel { get; set; }
}
<Window ...>
...
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelType}">
<local:MyViewType/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType2}">
<local:ViewType2/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType3}">
<local:ViewType3/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
...
</Window>