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

原文 使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值