使用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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       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 : ICommand

  {

     private readonly Predicate _canExecute;

     private readonly Action _execute;

 

     public RelayCommand(Action execute)

        : this(execute, null)

     {

        _execute = execute;

     }

 

     public RelayCommand(Action execute, Predicate 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 : 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(this EventHandler> handler, object sender, T value)

     {

        if (handler != null)

        {

           handler(sender, new EventArgs(value));

        }

     }

 

     public static void Raise(this EventHandler handler, object sender, T value) where T : EventArgs

     {

        if (handler != null)

        {

           handler(sender, value);

        }

     }

 

     public static void Raise(this EventHandler> handler, object sender, EventArgs value)

     {

        if (handler != null)

        {

           handler(sender, value);

        }

     }

  }

}

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

MvvmSwitch5

然后切换到下一个视图:

MvvmSwitch6

目录
相关文章
|
8月前
|
XML 开发框架 .NET
|
4月前
|
设计模式 前端开发 C#
WPF 项目中 MVVM模式 的简单例子说明
本文通过WPF项目中的加法操作示例,讲解了MVVM模式的结构和实现方法,包括数据模型、视图、视图模型的创建和数据绑定,以及命令的实现和事件通知机制。
|
5月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
308 1
|
5月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
138 0
|
5月前
|
容器 C# 开发者
XAML语言大揭秘:WPF标记的魅力所在,让你轻松实现界面与逻辑分离,告别复杂代码!
【8月更文挑战第31天】XAML提供了一种直观且易于维护的界面设计方式,使得开发者可以专注于逻辑和业务代码的编写,而无需关心界面细节。通过数据绑定、布局管理和动画效果等特性,XAML可以实现丰富的界面交互和视觉效果。在实际开发过程中,开发者应根据具体需求选择合适的技术方案,以确保应用程序能够满足用户的需求。希望本文的内容能够帮助您在WPF应用程序开发中更好地利用XAML语言。
55 0
|
5月前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
261 0
|
5月前
|
设计模式 前端开发 C#
WPF/C#:理解与实现WPF中的MVVM模式
WPF/C#:理解与实现WPF中的MVVM模式
317 0
|
IDE C# 开发工具
2000条你应知的WPF小姿势 基础篇<40-44 启动关闭,Xaml,逻辑树>
2000条你应知的WPF小姿势 基础篇<40-44 启动关闭,Xaml,逻辑树>
65 0
|
C#
WPF技术之Xaml Window
WPF Window 是一个 WPF 窗口类,它具有许多属性枚举可以控制窗口的外观和行为。
137 0
WPF技术之Xaml Window
|
设计模式 开发框架 前端开发
深入理解WPF中MVVM的设计思想
近些年来,随着WPF在生产,制造,工业控制等领域应用越来越广发,很多企业对WPF开发的需求也逐渐增多,使得很多人看到潜在机会,不断从Web,WinForm开发转向了WPF开发,但是WPF开发也有很多新的概念及设计思想,如:数据驱动,数据绑定,依赖属性,命令,控件模板,数据模板,MVVM等,与传统WinForm,ASP.NET WebForm开发,有很大的差异,今天就以一个简单的小例子,简述WPF开发中MVVM设计思想及应用。
118 0