使用MVVM DataTemplate在WPF XAML视图之间切换

简介: 原文 使用MVVM DataTemplate在WPF XAML视图之间切换 更新:这个技术的改进版本,一个不创建视图,可以在以下链接找到: http://www.technical-recipes.

原文 使用MVVM DataTemplate在WPF XAML视图之间切换

更新:这个技术的改进版本,一个不创建视图,可以在以下链接找到:

http://www.technical-recipes.com/2018/navigating-between-views-in-wpf-mvvm/

已经在许多博客/网站论坛上讨论过这种技术,包括:

https://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/ 
http://stackoverflow.com/questions/19654295/wpf-mvvm-navigate-views 
http:// stackoverflow .COM /问题/ 10993385 /改变-视图- buttonclick

我认为分享这种技术的工作版本的实现是有用的。

完整的Visual Studio项目可以从这里下载:

 

http://www.technical-recipes.com/Downloads/MvvmSwitchViews.zip

总而言之,您的应用程序至少应该实现以下内容:

一个ViewModel,它包含一个定义当前视图的属性,以便更改视图以切换ViewModel的属性。
ViewModel需要实现INotifyPropertyChanged,否则在属性更改时不会通知视图。
一个ContentControl,其内容绑定到当前视图。
您希望切换的每个视图的一些DataTemplates。

要开始,请在Visual Studio中创建一个新的WPF项目:

MvvmSwitch1

在我们的项目中创建2个新的WPF用户控件,View1.xaml和View2.xaml:

MvvmSwitch2

View1.xaml

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
< UserControl x:Class = "MvvmSwitchViews.View1"
              xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
              mc:Ignorable = "d"
              d:DesignHeight = "300" d:DesignWidth = "300" >
     < Grid >
         < Button
             Content = "Goto View 2"
             Command = "{Binding GotoView2Command}"          
             HorizontalAlignment = "Center"              
             Margin = "10,10,0,0"
             VerticalAlignment = "Center"
             Width = "75" >
         </ Button >
     </ Grid >
</ UserControl >

View2.xaml

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
< UserControl x:Class = "MvvmSwitchViews.View2"
              xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
              mc:Ignorable = "d"
              d:DesignHeight = "300" d:DesignWidth = "300" >
     < Grid >
         < Button
             Content = "Goto View 1"
             Command = "{Binding GotoView1Command}"          
             HorizontalAlignment = "Center"              
             Margin = "10,10,0,0"
             VerticalAlignment = "Center"
             Width = "75" >
         </ Button >
     </ Grid >
</ UserControl >

为每个视图创建ViewModel:View1ViewModel和View2ViewModel。这些只是我们极简主义实现的空类:

MvvmSwitch3

View1ViewModel.cs

1
2
3
4
6
namespace MvvmSwitchViews
{
    public class View1ViewModel
    {
    }
}

View2ViewModel.cs

1
2
3
4
6
namespace MvvmSwitchViews
{
    public class View2ViewModel
    {
    }
}

修改MainWindow.xaml以包含DataTemplate和CurrentView绑定:

MainWindow.xaml

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
< Window x:Class = "MvvmSwitchViews.MainWindow"
         xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local = "clr-namespace:MvvmSwitchViews" 
         Title = "MainWindow" Height = "350" Width = "525" >
     < Window.Resources >
         < DataTemplate DataType = "{x:Type local:View1ViewModel}" >
             < local:View1 />
         </ DataTemplate >
         < DataTemplate DataType = "{x:Type local:View2ViewModel}" >
             < local:View2 />
         </ DataTemplate >
     </ Window.Resources >
 
     < Window.DataContext >
         < local:MainWindowViewModel />
     </ Window.DataContext >
 
     < Grid >
         < ContentControl Content = "{Binding CurrentView}" />
     </ Grid >
</ Window >

然后,我们为MainWindow.xaml创建一个名为MainWindowViewModel的新ViewModel:

MainWindowViewModel.cs

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using System.Windows.Input;
 
namespace MvvmSwitchViews
{
    public class MainWindowViewModel : ViewModelBase
    {
       private ICommand _gotoView1Command;
       private ICommand _gotoView2Command;
       private object _currentView;
       private object _view1;
       private object _view2;
 
       public MainWindowViewModel()
       {
          _view1 = new View1();
          _view2 = new View2();
 
          CurrentView = _view2;
       }
 
       public object GotoView1Command
       {
          get
          {
             return _gotoView1Command ?? (_gotoView1Command = new RelayCommand(
                x =>
                {
                   GotoView1();
                }));
          }
       }
 
       public ICommand GotoView2Command
       {
          get
          {
             return _gotoView2Command ?? (_gotoView2Command = new RelayCommand(
                x =>
                {
                   GotoView2();
                }));
          }
       }
 
       public object CurrentView
       {
          get { return _currentView; }
          set
          {
             _currentView = value;
             OnPropertyChanged( "CurrentView" );
          }
       }
 
       private void GotoView1()
       {
          CurrentView = _view1;
       }
 
       private void GotoView2()
       {
          CurrentView =  _view2;
       }
    }
}

我们还需要实现INotifyPropertyChanged或创建一个实现INotifyPropertyChanged的类。为此,我们创建了一个名为ViewModelBase的新类:

MvvmSwitch4

ViewModelBase.cs

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.ComponentModel;
using System.Windows.Input;
 
namespace MvvmSwitchViews
{
    public class ViewModelBase : INotifyPropertyChanged
    {     
       public event PropertyChangedEventHandler PropertyChanged;    
       protected void OnPropertyChanged( string propertyName)
       {
         
          var handler = PropertyChanged;
          if (handler != null )
          {
             handler( this , new PropertyChangedEventArgs(propertyName));
          }
       }
    }
}

为我们的事件处理创建另外三个类来实现RelayCommand,EventArgs和EventRaiser:

RelayCommand.cs

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System;
using System.Windows.Input;
 
namespace MvvmSwitchViews
{
    public class RelayCommand<T> : ICommand
    {
       private readonly Predicate<T> _canExecute;
       private readonly Action<T> _execute;
 
       public RelayCommand(Action<T> execute)
          : this (execute, null )
       {
          _execute = execute;
       }
 
       public RelayCommand(Action<T> execute, Predicate<T> canExecute)
       {
          if (execute == null )
          {
             throw new ArgumentNullException( "execute" );
          }
          _execute = execute;
          _canExecute = canExecute;
       }
 
       public bool CanExecute( object parameter)
       {
          return _canExecute == null || _canExecute((T) parameter);
       }
 
       public void Execute( object parameter)
       {
          _execute((T) parameter);
       }
 
       public event EventHandler CanExecuteChanged
       {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
       }
    }
 
    public class RelayCommand : ICommand
    {
       private readonly Predicate< object > _canExecute;
       private readonly Action< object > _execute;
 
       public RelayCommand(Action< object > execute)
          : this (execute, null )
       {
          _execute = execute;
       }
 
       public RelayCommand(Action< object > execute, Predicate< object > canExecute)
       {
          if (execute == null )
          {
             throw new ArgumentNullException( "execute" );
          }
          _execute = execute;
          _canExecute = canExecute;
       }
 
       public bool CanExecute( object parameter)
       {
          return _canExecute == null || _canExecute(parameter);
       }
 
       public void Execute( object parameter)
       {
          _execute(parameter);
       }
 
       // Ensures WPF commanding infrastructure asks all RelayCommand objects whether their
       // associated views should be enabled whenever a command is invoked
       public event EventHandler CanExecuteChanged
       {
          add
          {
             CommandManager.RequerySuggested += value;
             CanExecuteChangedInternal += value;
          }
          remove
          {
             CommandManager.RequerySuggested -= value;
             CanExecuteChangedInternal -= value;
          }
       }
 
       private event EventHandler CanExecuteChangedInternal;
 
       public void RaiseCanExecuteChanged()
       {
          CanExecuteChangedInternal.Raise( this );
       }
    }
}

EventArgs.cs

1
2
3
4
6
7
8
9
10
11
12
13
14
using System;
 
namespace MvvmSwitchViews
{
    public class EventArgs<T> : EventArgs
    {
       public EventArgs(T value)
       {
          Value = value;
       }
 
       public T Value { get ; private set ; }
    }
}

EventRaiser.cs

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
using System;
 
namespace MvvmSwitchViews
{
    public static class EventRaiser
    {
       public static void Raise( this EventHandler handler, object sender)
       {
          if (handler != null )
          {
             handler(sender, EventArgs.Empty);
          }
       }
 
       public static void Raise<T>( this EventHandler<EventArgs<T>> handler, object sender, T value)
       {
          if (handler != null )
          {
             handler(sender, new EventArgs<T>(value));
          }
       }
 
       public static void Raise<T>( this EventHandler<T> handler, object sender, T value) where T : EventArgs
       {
          if (handler != null )
          {
             handler(sender, value);
          }
       }
 
       public static void Raise<T>( this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value)
       {
          if (handler != null )
          {
             handler(sender, value);
          }
       }
    }
}

我们现在可以运行该应用程序来演示如何通过按下按钮来实现视图切换:

MvvmSwitch5

然后切换到下一个视图:

MvvmSwitch6

目录
相关文章
|
5月前
|
XML 开发框架 .NET
|
7月前
|
IDE C# 开发工具
2000条你应知的WPF小姿势 基础篇<40-44 启动关闭,Xaml,逻辑树>
2000条你应知的WPF小姿势 基础篇<40-44 启动关闭,Xaml,逻辑树>
33 0
|
8月前
|
设计模式 开发框架 前端开发
深入理解WPF中MVVM的设计思想
近些年来,随着WPF在生产,制造,工业控制等领域应用越来越广发,很多企业对WPF开发的需求也逐渐增多,使得很多人看到潜在机会,不断从Web,WinForm开发转向了WPF开发,但是WPF开发也有很多新的概念及设计思想,如:数据驱动,数据绑定,依赖属性,命令,控件模板,数据模板,MVVM等,与传统WinForm,ASP.NET WebForm开发,有很大的差异,今天就以一个简单的小例子,简述WPF开发中MVVM设计思想及应用。
62 0
|
9月前
|
C#
WPF技术之Xaml Window
WPF Window 是一个 WPF 窗口类,它具有许多属性枚举可以控制窗口的外观和行为。
80 0
WPF技术之Xaml Window
|
9月前
|
前端开发
WPF-Binding问题-MVVM中IsChecked属性CommandParameter转换值类型空异常
WPF-Binding问题-MVVM中IsChecked属性CommandParameter转换值类型空异常
88 0
|
9月前
|
XML 数据格式 C++
WPF-疑难问题-xaml编码导致中文字符编译无效
WPF-疑难问题-xaml编码导致中文字符编译无效
113 0
|
10月前
|
前端开发 算法 JavaScript
走进WPF之MVVM完整案例
走进WPF之MVVM完整案例
156 0
|
12月前
|
C#
4.使用代码和未经编译的XAML创建WPF应用程序
4.使用代码和未经编译的XAML创建WPF应用程序
58 0
|
数据采集 人工智能 监控
【WPF】CAD工程图纸转WPF可直接使用的xaml代码技巧
随着工业化的进一步发展,制造业、工业自动化等多领域,都可能用到上位监控系统。而WPF在上位监控系统方面,应该算是当下最流行的前端框架之一了。而随着监控体系的不断完善与更新迭代,监控画面会变得越来越复杂、多样化和全面化。
245 0
【WPF】CAD工程图纸转WPF可直接使用的xaml代码技巧
|
前端开发 C# 图形学
【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入
前言:在C/S架构上,WPF无疑已经是“桌面一霸”了。在.NET生态环境中,很多小伙伴还在使用Winform开发C/S架构的桌面应用。但是WPF也有很多年的历史了,并且基于MVVM的开发模式,受到了很多开发者的喜爱。
567 0
【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入