WPF: 在MVVM中使用Navigtaion

Navigation可以很方便的在页面间进行切换,但是在MVVM模式下,使用Naviation会有一个问题,切换的逻辑需要在ViewModel层完成,但是Navigation需要知道页面的实例或者Uri才能进行切换,那我们如何在ViewModel与UI分离的情况下,用Navigation完成页面的切换呢?

假如有一个程序如下所示,点击Switch之后会从Summary
Page切换到另一个页面Detail
Page:

在MVVM中,我们需要有三个ViewModel,一个是SummaryViewModel对应SummaryPage,
一个是DetailViewModel对应DetailPage,再加上一个ControlViewModel负责通过改变CurrentPageViewModel来实现逻辑数据的切换并将其反应到UI,如下所示:


public class SummaryViewModel
{
public ObservableCollection<SummaryModel> Summaries { get; set; }

public SummaryViewModel()
{
Summaries = new ObservableCollection<SummaryModel>();
}
}


public class DetailViewModel
{
public ObservableCollection<DetailModel> Details { get; set; }

public DetailViewModel()
{
Details = new ObservableCollection<DetailModel>();
}
}


public class NavigationControlViewModelBase : ViewModelBase
{
private object currentPageViewModel;
public object CurrentPageViewModel
{
get
{
return currentPageViewModel;
}
set
{
currentPageViewModel = value;
RaisePropertyChanged(() => CurrentPageViewModel);
}
}
}

public class ControlViewModel : NavigationControlViewModelBase
{
private SummaryViewModel summary;
public SummaryViewModel Summary { get { return summary; } set { summary = value; RaisePropertyChanged(() => Summary); } }

private DetailViewModel detail;
public DetailViewModel Detail { get { return detail; } set { detail = value; RaisePropertyChanged(() => Detail); } }

public ControlViewModel()
{
SwitchCommand = new RelayCommand(Switch);
Summary = new SummaryViewModel();
Detail = new DetailViewModel();
CurrentPageViewModel = Summary;

GenerateData();
}

public ICommand SwitchCommand {get;set;}

private void Switch()
{
if (CurrentPageViewModel == Summary)
{
CurrentPageViewModel = Detail;
}
else
{
CurrentPageViewModel = Summary;
}
}

private void GenerateData()
{
var ran = new Random();
Summary.Summaries.Clear();
Detail.Details.Clear();

for (int i = 0; i < 100; i++)
{
Summary.Summaries.Add(new SummaryModel() { ID = ran.Next(0, 100) });
}

for (int i = 0; i < 100; i++)
{
Detail.Details.Add(
new DetailModel()
{
ID = i,
Data1 = Guid.NewGuid().ToString().Substring(0, 4),
Data2 = Guid.NewGuid().ToString().Substring(0, 4),
Data3 = Guid.NewGuid().ToString().Substring(0, 4),
Data4 = Guid.NewGuid().ToString().Substring(0, 4),
Data5 = Guid.NewGuid().ToString().Substring(0, 4),
});
}
}
}

添加一个类NavigationControlFrame继承Frame负责控制Navigation,
在这个类里有一个依赖属性CurrentPageObject,在XAML中会将它与ControlViewModel的CurrentPageViewModel绑定,CurrentPageViewMdoel改变时,触发OnCurrentPageObjectChanged。通过XAML里定义的ViewModel类型和Page
Uri对应关系找到相应的页面进行切换:


class NavigationControlFrame : Frame
{
public NavigationControlFrame()
{
Navigated += navigationFrame_Navigated;
}

public static readonly DependencyProperty CurrentPageObjectProperty =
DependencyProperty.Register("CurrentPageObject", typeof(object), typeof(NavigationControlFrame), new PropertyMetadata(default(object), OnCurrentPageObjectChanged));

private static void OnCurrentPageObjectChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var navigationFrame = (NavigationControlFrame)dependencyObject;
var newValue = dependencyPropertyChangedEventArgs.NewValue;

if (newValue == null)
{
navigationFrame.Navigate(null);
return;
}

var pageUri = (string)navigationFrame.TryFindResource(newValue.GetType());
navigationFrame.Navigate(new Uri(pageUri, UriKind.Relative), newValue);
}

static void navigationFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
if (e.ExtraData != null)
{
var control = e.Content as Page;
control.DataContext = e.ExtraData;
}
}

public object CurrentPageObject
{
get { return GetValue(CurrentPageObjectProperty); }
set { SetValue(CurrentPageObjectProperty, value); }
}
}

在XAML中在Resource中定义ViewModel与Page
Uri的对应关系,添加NavigtaionControlFrame,并将CurrentPageObject绑定到CurrentPageViewModel,这样ControlViewModel中的CurrentPageViewModel变化时,对应的页面也会进行切换:


<Window x:Class="NavigationApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:navigationApp="clr-namespace:NavigationApp"
xmlns:viewModels="clr-namespace:ViewModels;assembly=ViewModels"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="600" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Resources>
<viewModels:ControlViewModel x:Key="Cvm1"></viewModels:ControlViewModel>
<sys:String x:Key="{x:Type viewModels:DetailViewModel}">/DetailPage.xaml</sys:String>
<sys:String x:Key="{x:Type viewModels:SummaryViewModel}">/SummaryPage.xaml</sys:String>
</Grid.Resources>

<StackPanel Grid.Row="0" Grid.Column="0" DataContext="{StaticResource Cvm1}" Background="LightGreen">
<navigationApp:NavigationControlFrame CurrentPageObject="{Binding CurrentPageViewModel}">
</navigationApp:NavigationControlFrame>
<Button Content="Switch" Command="{Binding SwitchCommand}"></Button>
</StackPanel>
</Grid>
</Window>

WPF: 在MVVM中使用Navigtaion

时间: 2024-10-11 06:46:21

WPF: 在MVVM中使用Navigtaion的相关文章

在WPF的MVVM框架中获取下拉选择列表中的选中项

文章概述: 本演示介绍如何在WPF的MVVM框架中,通过数据绑定的方式获取下拉列表中的选中项.程序运行后的效果如下图所示: 相关下载(代码.屏幕录像):http://pan.baidu.com/s/1sjwN357 在线播放:http://v.youku.com/v_show/id_XODA5OTYzMDU2.html 温馨提示:如果屏幕录像和代码不能正常下载,可站内留言,或发邮件到[email protected] XAML代码如下所示: <Window x:Class="Demo02E

WPF MVVM 中怎样在ViewModel总打开的对话框在窗体之前

今天在WPF的项目中,写打印插件,在ViewModel中对需要弹出打印对话框,而对话框如果没有Owner所属的时候经常会被当前应用程序遮住,导致我都不知道到底弹出来没有! 参照:http://www.codeproject.com/Articles/20347/WebControls/后,果断顺利通过. 第一步定义WindowWrapper类:实现System.Windows.Forms.IWin32Window接口 1 /// <summary> 2 /// 句柄转换IWin32Window

WPF 在事件中绑定命令(可以在模版中绑定命令)

其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实现将命令绑定到事件中. 上一篇中我们介绍了MVVMLight中的命令的用法,那么仅仅知道命令是如何构建使用的还不够,很多情况下我们都需要在某个事件触发的时候才去触发命令,所以将命令绑定到事件上是非常有效的做法,下面我们来接着实现将命令绑定到事件中. WPF实现命令绑定到事件 使用 System.Windows.Interactivity.dll 中的 Interaction 可以帮助我们实现

WPF之MVVM(Step4)&mdash;&mdash;使用Prism(2)

上一篇简单介绍使用Prism中的NotificationObject,以及DelegateCommand.这一篇更是简单,仅仅描述下DelegateCommand<T>如何使用. ICommand接口一开始提供的就是带参数的方法,而我们使用时经常会遇到那个参数毫无用处的情况,Prism也就帮了偶们一把啦.当然,Prism并没有忘记我们有时还是要参数滴.   在定义上,我们使用ICommand定义,DelegateCommand和DelegateCommand<T>一样.在实例化时有

WPF之MVVM(Step2)&mdash;&mdash;自己实现DelegateCommand:ICommand

在自己实现MVVM时,上一篇的实现方式基本是不用,因其对于命令的处理不够方便,没写一个命令都需要另加一个Command的类.此篇主要介绍DelegateCommand来解决上面所遇到的问题. 首先,我们创建自己的DelegateCommand. 代码如下: /// <summary> /// 实现DelegateCommand /// </summary> class MyDelegateCommand : ICommand { /// <summary> /// 命令

WPF 微信 MVVM 【续】发送部分QQ表情

今天主要记录的就是发送QQ表情, WPF 微信 MVVM里写了,后期为了发送QQ表情,需要把TextBox替换为RichTextBox,接下来就说说替换的过程. 一.支持Binding的RichTextBox RichTextBox虽然支持文字,图片,链接,但是,原生的它不支持Binding,这个对于MVVM是很不方便的,因此,需要给RichTextBox设置一个依赖属性Document,来让它支持绑定,这样才能继续下一步. public class BindableRichTextBox :

WPF框架MVVM简单例子

MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发.Model——可以理解为带有字段,属性的类.View——可以理解为我们所看到的UI.View Model在View和Model之间,起到连接的作用,并且使得View和Model层分离.View Model不仅仅是Model的包装,它还包含了程序逻辑,以及Model扩展,例如,如果Model中有一个公开属性不需要在UI上显示,此时我们可以不再View Model中去定义它. 在MVVM中,

WPF之MVVM(Step1)&mdash;&mdash;自己实现ICommand接口

开发WPF应用程序,就不得不提MVVM.下面偶将展示MVVM中简单的实现,其中主要在于ICommand的实现上,不过这种实现方式,应该不会有多少人在开发中使用,在此仅作学习使用. 准备: 界面绘制,简单的以一个输入框TextBox和一个按钮Button组成.   入手 接下来写ViewModel,注意其中ViewModel需要继承接口INotifyPropertyChanged,其主要功能是保证后台属性改变能够通知到前台改变. class TestViewModel : INotifyPrope

WPF 在事件中绑定命令

导航:MVVMLight系列文章目录:<关于 MVVMLight 设计模式系列> 其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实现将命令绑定到事件中. 上一篇中我们介绍了MVVMLight中的命令的用法,那么仅仅知道命令是如何构建使用的还不够,很多情况下我们都需要在某个事件触发的时候才去触发命令,所以将命令绑定到事件上是非常有效的做法,下面我们来接着实现将命令绑定到事件中. WPF实现命令绑定到事件 使用 System.Windows