WPF使用MVVM模式浏览视图
我正在使用MVVM模式构建我的第一个WPF。 在这个社区的帮助下,我设法创建了我的模型,我的第一个ViewModel和视图。 现在我想为设计基本应用程序布局界面的应用程序增加一些复杂性。 我的想法是至少有两个子视图和一个主视图,并将它们分开放在几个XAML上:
Main将有一个菜单和一个空间来加载子视图(产品和客户端)。 现在,遵循MVVM模式,视图之间的所有导航逻辑应该写在ViewModel上。 所以mi想法是有4个ViewModels:
那么NavigationViewModel应该包含一系列子视图模型? 和一个活跃的viewmodel是对的?
所以我的问题是:
1)如何使用MVVM模式在主视图上加载不同的视图(产品,客户端)?
2)我如何实现导航视图模型?
3)如何控制打开或活动视图的最大数量?
4)如何在打开的视图之间切换?
我一直在做大量的搜索和阅读,并找不到任何WPF MVVM导航的简单工作示例,它在主视图中加载多个视图。 其中许多是:
1)使用外部工具包,我现在不想使用它。
2)将所有视图的所有代码放在一个单独的XAML文件中,这似乎不是一个好主意,因为我需要实现近80个视图!
我在这条正确的道路上? 任何帮助,特别是一些代码将不胜感激。
UPDATE
所以,我按照@LordTakkera的建议建立了一个测试项目,但是被卡住了。 这就是我的解决方案:
我创造:
两种模式(客户和产品)
一个MainWindow和两个wpf用户控件(客户端和产品)XAML。
三个ViewModel(客户端,产品和主ViewModel)
然后,我将每个视图上的dataContext设置为相应的viewModel。 之后,我使用ContentPresenter创建MainWindow,并将其绑定到viewmodel的属性。
MainWindow.XAML
<Window x:Class="PruevaMVVMNavNew.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="519" Width="890">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.ColumnSpan="2" Background="AntiqueWhite" ></Border>
<Border Grid.Row="1" Grid.RowSpan="2" Background="AliceBlue"></Border>
<Border Grid.Row="1" Grid.Column="1" Background="CadetBlue"></Border>
<ContentPresenter Grid.Row="1" Grid.Column="1" x:Name="ContentArea" Content="{Binding CurrentView}"/>
<StackPanel Margin="5" Grid.Column="0" Grid.Row="1">
<Button>Clients</Button>
<Button>Products</Button>
</StackPanel>
</Grid>
而且这也是MainWindow的视图模型:
class Main_ViewModel : BaseViewModel
{
public Main_ViewModel()
{
CurrentView = new Clients();
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
所以这个默认客户端的加载视图是这样的(这是正确的!):
所以我想我需要一种方法将左边的按钮与某个viemodel相关联,然后将它们与Main viewModel的CurrentView属性绑定。 我怎样才能做到这一点?
UPDATE2
根据@LordTakkera的建议,我这样修改我的main viewModel:
class Main_ViewModel : BaseViewModel
{
public ICommand SwitchViewsCommand { get; private set; }
public Main_ViewModel()
{
//CurrentView = new Clients();
SwitchViewsCommand = new RelayCommand((parameter) => CurrentView = (UserControl)Activator.CreateInstance(parameter as Type));
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
我使用RelayCommand而不是DelegateCommand,但我认为它以同样的方式工作。 当我点击按钮和类型参数字符串它的确定但我得到这个错误时执行该命令:
翻译: 值不能为空。 参数名称:类型。 建议使用New关键字来创建对象实例我不知道在哪里放置New关键字。 我尝试CommandParameter但它不会工作。 任何想法? 谢谢
更新3
在收到所有的建议和帮助后,以及大量的工作,这里是我的最终导航菜单和我的应用程序界面的基础。
我不确定你需要一个单独的“导航”视图模型,你可以很容易地把它放到主要。 无论哪种方式:
要分离你的“孩子”的意见,我会在你的“主”视图上使用一个简单的ContentPresenter:
<ContentPresenter Content="{Binding CurrentView}"/>
实现支持属性的最简单方法是将其设为UserControl
,尽管有人会认为这样做违反了MVVM(因为ViewModel现在依赖于“视图”类)。 你可以让它成为一个对象,但是你会失去一些类型的安全。 在这种情况下,每个视图都是一个UserControl。
要在它们之间切换,您将需要某种选择控制。 我之前用单选按钮做过这件事,就像这样绑定它们:
<RadioButton Content="View 1" IsChecked="{Binding Path=CurrentView, Converter={StaticResource InstanceEqualsConverter}, ConverterParameter={x:Type views:View1}"/>
转换器非常简单,在“转换”中它只是检查当前控件是否是参数的一种类型,在“ConvertBack”中它将返回参数的一个新实例。
public class InstanceEqualsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (parameter as Type).IsInstanceOfType(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing;
}
}
绑定到组合框或其他选择控件将遵循类似的模式。
当然你也可以使用DataTemplates(带有一个选择器,不幸的是我之前完成的工作),并使用合并字典(允许单独的XAML)将它们加载到资源中。 我个人更喜欢用户控制路线,选择哪个最适合你!
这种方法是“一次一个观点”。 将转换为多个视图相对容易(您的UserControl成为用户控件的集合,在转换器中使用.Contains等)。
要做到这一点与按钮,我会使用命令并利用CommandParameter。
按钮XAML将如下所示:
<Button ... Command={Binding SwitchViewsCommand} CommandParameter={x:Type local:ClientsView}/>
然后你有一个委托命令(教程在这里),它从转换器运行激活码:
public ICommand SwitchViewsCommand {get; private set;}
public MainViewModel()
{
SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type));
}
这是我的头顶,但应该非常接近。 让我知道事情的后续!
如果我提供更多信息,请告诉我!
更新:
回答你的担忧:
是的,每次按下按钮时,都会创建一个新的视图实例。 您可以通过持有预先创建了视图和索引的Dictionary<Type, UserControl>
来轻松解决此问题。 对于这个问题,你可以使用Dictonary<String, UserControl>
并使用简单的字符串作为转换器参数。 缺点是你的ViewModel与它可以呈现的视图类型紧密耦合(因为它必须填充所述字典)。
只要没有其他人持有对它的引用(认为它注册的事件处理程序),就应该抛弃该类。
正如你指出的那样,一次只创建一个视图,所以你不需要担心内存。 当然,你是在调用构造函数,但这并不昂贵,特别是在现代计算机上,我们倾向于有足够的CPU时间进行备份。 与往常一样,对性能问题的回答是“对其进行基准测试”,因为只有您可以访问预期的部署目标和整个来源,才能看到实际效果最佳的部分。
恕我直言,最好的选择是使用MVVM框架(PRISM,MMVM Light,Chinch等),因为导航已经实现。 如果你想创建你自己的导航 - 试试DataTemplate。
链接地址: http://www.djcxy.com/p/9603.html