应用模式-视图-视图模型设计模式时包括部分视图
假设我有一个仅处理消息
和用户
的应用程序,我希望我的窗口有一个通用的菜单
和一个区域,其中当前显示>查看
。
我只能使用消息或用户,因此无法同时使用这两个视图。因此,我有以下 Controls
- MessageView.xaml
- UserView.xaml
只是为了让它更容易一点,消息模型
和用户模型
看起来像这样:
- 名称
- 描述
现在,我有以下三个 ViewModel:
- MainWindowViewModel
- UsersViewModel
- MessagesViewModel
UsersViewModel
和 MessagesViewModel
都只获取 ObserverableCollection
> 其相关的 Model
绑定在相应的 View
中,如下所示:
MainWindowViewModel
连接了两个不同的 Commands
,它们实现了 ICommand
,如下所示:
public class ShowMessagesCommand : ICommand
{
private ViewModelBase ViewModel { get; set; }
public ShowMessagesCommand (ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public void Execute(object parameter)
{
var viewModel = new ProductsViewModel();
ViewModel.PartialViewModel = new MessageView { DataContext = viewModel };
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
还有另一个类似的命令将显示用户。现在引入了ViewModelBase
,它仅包含以下内容:
public UIElement PartialViewModel
{
get { return (UIElement)GetValue(PartialViewModelProperty); }
set { SetValue(PartialViewModelProperty, value); }
}
public static readonly DependencyProperty PartialViewModelProperty =
DependencyProperty.Register("PartialViewModel", typeof(UIElement), typeof(ViewModelBase), new UIPropertyMetadata(null));
此依赖项属性在MainWindow.xaml
中使用,以动态显示用户控件
,如下所示:
此 Window
上还有两个按钮可以触发命令:
- ShowMessagesCommand
- ShowUsersCommand
当这些按钮被触发时, UserControl 发生变化,因为 PartialViewModel
是一个依赖属性。
我想知道这是否是不好的做法?我不应该像这样注入用户控件吗?是否有另一种“更好”的替代方案更符合设计模式?或者这是包含部分视图的好方法?
Consider that I have an application that just handles Messages
and Users
I want my Window to have a common Menu
and an area where the current View
is displayed.
I can only work with either Messages or Users so I cannot work simultaniously with both Views. Therefore I have the following Controls
- MessageView.xaml
- UserView.xaml
Just to make it a bit easier, both the Message Model
and the User Model
looks like this:
- Name
- Description
Now, I have the following three ViewModels:
- MainWindowViewModel
- UsersViewModel
- MessagesViewModel
The UsersViewModel
and the MessagesViewModel
both just fetch an ObserverableCollection<T>
of its regarding Model
which is bound in the corresponding View
like this:
<DataGrid ItemSource="{Binding ModelCollection}" />
The MainWindowViewModel
hooks up two different Commands
that have implemented ICommand
that looks something like the following:
public class ShowMessagesCommand : ICommand
{
private ViewModelBase ViewModel { get; set; }
public ShowMessagesCommand (ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public void Execute(object parameter)
{
var viewModel = new ProductsViewModel();
ViewModel.PartialViewModel = new MessageView { DataContext = viewModel };
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
And there is another one a like it that will show Users. Now this introduced ViewModelBase
which only holds the following:
public UIElement PartialViewModel
{
get { return (UIElement)GetValue(PartialViewModelProperty); }
set { SetValue(PartialViewModelProperty, value); }
}
public static readonly DependencyProperty PartialViewModelProperty =
DependencyProperty.Register("PartialViewModel", typeof(UIElement), typeof(ViewModelBase), new UIPropertyMetadata(null));
This dependency property is used in the MainWindow.xaml
to display the User Control
dynamicly like this:
<UserControl Content="{Binding PartialViewModel}" />
There are also two buttons on this Window
that fires the Commands:
- ShowMessagesCommand
- ShowUsersCommand
And when these are fired, the UserControl changes because PartialViewModel
is a dependency property.
I want to know if this is bad practice? Should I not inject the User Control like this? Is there another "better" alternative that corresponds better with the design pattern? Or is this a nice way of including partial views?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
为什么不在主窗口中使用带有数据模板的 ContentPresenter/ContentControl ?
而不是 UserControl Content="{Binding PartialViewModel}" />,您可以使用:
您所要做的就是将您的 PartialViewmodel 设置为您的子视图模型并创建一个数据模板,这样 wpf 将知道如何渲染您的子视图
模型您在 MainViewmodel 中设置 PartialViewmodel,正确的 View 将在您的 ContenControl 中呈现。
编辑1
至少您必须在 ViewModel 中实现 INotifyPropertyChanged 并在设置 PartViewModel 属性时触发它。
编辑2
如果您在视图模型中使用命令,请查看一些 mvvm 框架实现,例如 DelegateCommand 或 RelayCommand。这样处理 ICommand 就变得容易多了。在你的主视图模型中,你可以创建像这样简单的命令
why not use a ContentPresenter/ContentControl with a datatemplate in your mainwindow?
instead of UserControl Content="{Binding PartialViewModel}" />, you can use a:
all you have to do: is set your PartialViewmodel to your child viewmodel and create a datatemplate, so wpf will know how to render your childviewmodel
when ever you set your PartialViewmodel in your MainViewmodel, the right View will render in your ContenControl.
Edit 1
at least you have to implement INotifyPropertyChanged in your ViewModel and fire it when ever the PartViewModel property is set.
Edit 2
if you use Commands in your viewmodels take a look at some mvvm framework implementations like DelegateCommand or RelayCommand. handling ICommand become much easier with this. within your mainviewmodel you can create commands simple like that
乍一看,这并不是一个坏方法,在小型应用程序中使用可能就很好。
但是,有一些事情不太好:
DependencyObject
才能拥有DependencyProperty
。在现实世界中,我发现必须以单线程方式处理 ViewModel 是非常烦人的(可能需要执行很多异步操作)。任何像样的 MVVM 框架都可以通过提供将子视图组合到主视图中的基础设施来简化 UI 组合。在 Prism(这是我个人的偏好)中,这种情况发生在
区域
。This isn't a bad approach at first sight, it might be just fine to use in a small app.
However, there are a couple of things that aren't that nice:
DependencyObject
to have aDependencyProperty
. In the real world I 've found that it's very annoying to have to treat ViewModels in a single-threaded manner (there are lots of async operations one might want to perform).Any decent MVVM framework makes UI composition easy by providing infrastructure to compose sub-Views into your main View. In Prism (which is my personal preference), this happens with
Regions
.我会考虑使用 MVVM 框架,例如 Caliburn.Micro,它使视图组合变得非常简单。如果您的视图模型上有一个属于视图模型类型的属性,并且视图上有一个与您的属性名称相同的
ContentControl
,那么 Caliburn.Micro 将通过以下方式找到该视图模型对应的视图:约定,自动为您进行绑定,并将视图注入到ContentControl
中。我还会避免在视图模型上使用依赖属性,而是实施 INotifyPropertyChanged。 Caliburn.Micro 附带了一个实现此接口的
PropertyChangedBase
类型,并且还提供了一个辅助方法,用于使用 lambda 表达式而不是魔术字符串调用PropertyChanged
事件(这要好得多)以便稍后重构)。编辑
http://msdn.microsoft.com/en -us/library/ms743695.aspx 显示了实现 INotifyPropertyChanged 的示例。
要在 Caliburn.Micro 中实现您想要做的事情,您可以执行如下操作(一个粗略的示例,但它向您展示了使用 MVVM 框架进行视图组合是多么容易):
请注意
UsersViewModel
和MessagesViewModel
将从Screen
派生。要使用 Caliburn.Micro 调用
ShowUsers
或ShowMessages
动词,您只需创建具有相同名称的视图控件。 Conductor 类型有一个ActiveItem
属性,它是当前执行的项目,因此您可以将一个ContentControl
添加到名为ActiveItem
的 MainView.xaml 中,Caliburn.Micro 将负责注入正确的视图。所以你的 MainView.xaml 可能看起来像:
I would look at using an MVVM framework such as Caliburn.Micro which makes view composition incredibly easy. If you have a property on your view model which is a view model type, and a
ContentControl
on your view which is named the same as your property, then Caliburn.Micro will locate that view models corresponding view via conventions, do the binding for you automatically, and inject the view into theContentControl
.I would also avoid using dependency properties on your view models, and instead implement INotifyPropertyChanged. Caliburn.Micro comes with a
PropertyChangedBase
type which implements this interface, and also provides a helper method for invoking thePropertyChanged
event using lambda expressions rather than magic strings (which is much better for refactoring later).EDIT
http://msdn.microsoft.com/en-us/library/ms743695.aspx shows an example of implementing INotifyPropertyChanged.
To achieve what you want to do in Caliburn.Micro, you would do something like the following (a crude example, but it shows you how easy it is doing view composition using an MVVM framework):
Note that
UsersViewModel
andMessagesViewModel
would derive fromScreen
.To invoke the
ShowUsers
orShowMessages
verbs with Caliburn.Micro, you just need to create view controls with the same name. The conductor type has anActiveItem
property which is the currently conducted item, so you can add aContentControl
to your MainView.xaml which is namedActiveItem
, and Caliburn.Micro will take care of injecting the correct view.So your MainView.xaml may look like:
你应该看看prism。它为您提供区域处理。
我还会查看 MEF 来导出视图,并以这种方式保持项目的可扩展性。
you should take a look at prism. It gives you region handling.
I would also take a look at MEF to Export Views and on this way maintain an extensibility for your project.