考虑到我有一个只处理Messages
和Users
的应用程序我希望我的Window有一个公共Menu
和一个显示当前View
的区域。
我只能使用消息或用户,所以我无法同时使用两个视图。因此,我有以下控件
为了让它更容易一点,Message Model
和User Model
都是这样的:
现在,我有以下三个ViewModel:
UsersViewModel
和MessagesViewModel
两者都只获取其ObserverableCollection<T>
的约Model
,View
绑定在相应的<DataGrid ItemSource="{Binding ModelCollection}" />
中,如下所示:
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
在User Control
中使用此依赖项属性以动态显示<UserControl Content="{Binding PartialViewModel}" />
:
Window
此PartialViewModel
上还有两个按钮可以激活命令:
当这些被触发时,UserControl会发生变化,因为{{1}}是依赖属性。
我想知道如果这是不好的做法?我不应该像这样注入用户控件吗?是否有另一种“更好”的替代方案更符合设计模式?或者这是包含部分视图的好方法吗?
答案 0 :(得分:2)
初看起来这不是一个糟糕的方法,在小应用程序中使用可能会很好。
然而,有一些事情并不那么好:
DependencyObject
才能拥有DependencyProperty
。在现实世界中,我发现必须以单线程方式处理ViewModel是非常烦人的(人们可能想要执行许多异步操作)。任何体面的MVVM框架都可以通过提供基础结构将子视图组合到主视图中来简化UI组合。在Prism中(这是我个人的偏好),这发生在Regions
。
答案 1 :(得分:2)
为什么不在主窗口中使用带有datatemplate的ContentPresenter / ContentControl?
代替UserControl Content =“{Binding PartialViewModel}”/&gt;,您可以使用:
<ContentPresenter Content="{Binding Path=PartialViewModel}" />
所有你需要做的:将你的PartialViewmodel设置为你的子视图模型并创建一个datatemplate,所以wpf将知道如何渲染你的childviewmodel
<DataTemplate DataType={x:Type UserViewModel}>
<UserView/>
</DataTemplate>
<DataTemplate DataType={x:Type MessageViewModel}>
<MessageView/>
</DataTemplate>
当您在MainViewmodel中设置PartialViewmodel时,右侧视图将在ContenControl中呈现。
修改1 至少你必须在ViewModel中实现INotifyPropertyChanged,并在设置PartViewModel属性时触发它。
编辑2 如果在viewmodels中使用Commands,请查看一些mvvm框架实现,如DelegateCommand或RelayCommand。处理ICommand变得更加容易。在您的mainviewmodel中,您可以创建简单的命令
private DelegateCommand _showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
return this._showMessageCommand ?? (this._showMessageCommand = new DelegateCommand(this.ShowMessageExecute, this.CanShowMessageExecute));
}
}
答案 2 :(得分:1)
我会考虑使用像Caliburn.Micro这样的MVVM框架,这使得视图合成非常容易。如果视图模型上的属性是视图模型类型,并且视图上的ContentControl
名称与您的属性相同,那么Caliburn.Micro将通过约定找到该视图模型对应的视图,自动绑定,并将视图注入ContentControl
。
我还会避免在视图模型上使用依赖项属性,而是实现INotifyPropertyChanged。 Caliburn.Micro带有一个实现此接口的PropertyChangedBase
类型,并且还提供了一个帮助方法,用于使用lambda表达式而不是魔术字符串调用PropertyChanged
事件(这对于稍后的重构更好)。 / p>
修改强>
http://msdn.microsoft.com/en-us/library/ms743695.aspx显示了实现INotifyPropertyChanged的示例。
要在Caliburn.Micro中实现您想要做的事情,您可以执行以下操作(一个粗略的示例,但它向您展示了使用MVVM框架进行视图合成是多么容易):
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private UsersViewModel usersViewModel;
private MessagesViewModel messagesViewModel;
public UsersViewModel UsersViewModel
{
get { return this.usersViewModel; }
set { this.usersViewModel = value; this.NotifyOfPropertyChanged(() => this.UsersViewModel);
}
public MessagesViewModel MessagesViewModel
{
get { return this.messagesViewModel; }
set { this.messagesViewModel = value; this.NotifyOfPropertyChanged(() => this.MessagesViewModel);
}
public MainViewModel()
{
this.UsersViewModel = new UsersViewModel();
this.MessagesViewModel = new MessagesViewModel();
this.Items.Add(this.UsersViewModel);
this.Items.Add(this.MessagesViewModel);
// set default view
this.ActivateItem(this.UsersViewModel);
}
public ShowUsers()
{
this.ActivateItem(this.UsersViewModel);
}
public ShowMessages()
{
this.ActivateItem(this.MessagesViewModel);
}
}
请注意UsersViewModel
和MessagesViewModel
来自Screen
。
要使用Caliburn.Micro调用ShowUsers
或ShowMessages
谓词,您只需要创建具有相同名称的视图控件。导体类型具有ActiveItem
属性,这是当前执行的项目,因此您可以将ContentControl
添加到名为ActiveItem
的MainView.xaml中,并且Caliburn.Micro将负责注入正确的视图。
因此,您的MainView.xaml可能如下所示:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinition>
<!-- Menu in left hand column -->
<StackPanel Grid.Column="0">
<Button x:Name="ShowUsers">Show Users</Button>
<Button x:Name="ShowMessages">Show Messages</Button>
</StackPanel>
<!-- Currently active item -->
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
</Grid>
答案 3 :(得分:0)
你应该看看prism。它为您提供区域处理。 我还要看看MEF导出视图,并以这种方式保持项目的可扩展性。