我刚刚开始学习来自Java Swing和WinForms的WPF。我决定尝试新的东西来学习开发程序的其他概念和技术。上次,我介绍了MVC Pattern的概念。根据我所学到的,它是一种分离UI逻辑,业务逻辑和数据的方法。我发现WPF的一个关键概念是Binding和MVVM模式。
这是我尝试实现MVVM的代码的一部分。
MainWindowModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Controls;
namespace DocNanzDCMS.Model
{
public class MainWindowModel : INotifyPropertyChanged
{
private PropertyChangedEventArgs pce;
public MainWindowModel()
{
pce = new PropertyChangedEventArgs("");
}
private UserControl userControl;
#region ControlProperty
public UserControl ContentProperty {
get
{
return userControl;
}
set
{
userControl = value;
PropertyChanged(this, pce);
}
}
#endregion
private DateTime dateTime;
#region DateProperty
public String DateProperty
{
get
{
return dateTime.ToLongDateString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
public String TimeProperty
#region TimeProperty
{
get
{
return dateTime.ToLongTimeString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
private String title;
public String TitleProperty
#region TitleProperty
{
get
{
return title;
}
set
{
title = value;
PropertyChanged(this, pce);
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
}
MainWindowViewModel.cs
using DocNanzDCMS.Model;
using DocNanzDCMS.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DocNanzDCMS.ViewModel
{
public class MainWindowViewModel
{
private MainWindow mainWindow;
private MainWindowModel mainWindowModel;
private Thread mainWindowThread;
private LoginModel loginModel;
private LoginViewModel loginViewModel;
private LoginView loginView;
private String title;
public MainWindowViewModel(MainWindowModel mainWindowModel, MainWindow mainWindow)
{
this.mainWindowModel = mainWindowModel;
this.mainWindow = mainWindow;
initialize();
}
private void initialize()
{
loginModel = new LoginModel();
loginView = new LoginView();
loginViewModel = new LoginViewModel(loginModel, loginView);
mainWindow.DataContext = mainWindowModel;
mainWindowThread = new Thread(BackgroundProcess);
mainWindowThread.IsBackground = true;
mainWindowThread.Start();
gotoLogin();
}
private void BackgroundProcess()
{
while(true)
{
updateTitle();
updateTime();
try
{
Thread.Sleep(100);
}
catch(ThreadInterruptedException e)
{
}
}
}
public void gotoLogin()
{
mainWindowModel.ContentProperty = loginView;
title = "Login";
}
private void updateTime()
{
mainWindowModel.DateProperty = DateTime.Now.ToString();
mainWindowModel.TimeProperty = DateTime.Now.ToString();
}
public void updateTitle()
{
mainWindowModel.TitleProperty = "Doc Nanz Dental | "+title;
}
}
}
MainWindow.cs
using DocNanzDCMS.Model;
using DocNanzDCMS.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DocNanzDCMS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainWindowModel mainWindowModel;
private MainWindowViewModel mainWindowViewModel;
public MainWindow()
{
InitializeComponent();
initializeApp();
}
private void initializeApp()
{
mainWindowModel = new MainWindowModel();
mainWindowViewModel = new MainWindowViewModel(mainWindowModel, this);
}
}
}
MainWindow.xaml
<Window x:Class="DocNanzDCMS.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DocNanzDCMS"
mc:Ignorable="d"
Title="{Binding TitleProperty}" Height="600" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<!--Banner-->
<Grid Grid.Row="0" Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="225"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<!--Date and Time Panel-->
<Grid Grid.Column="2" Background="Aquamarine">
<Grid.RowDefinitions>
<RowDefinition Height="1.5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--Date Background-->
<StackPanel Grid.Row="0" Background="BurlyWood"/>
<!--Date-->
<Label Grid.Row="0" Content="{Binding DateProperty}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<!--Time Background-->
<StackPanel Grid.Row="1" Background="BlanchedAlmond"/>
<!--Time-->
<Label Grid.Row="1" Content="{Binding TimeProperty}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
<!--Content-->
<ScrollViewer Grid.Row="1" Content="{Binding ContentProperty}"/>
<!--Status Bar-->
<Grid Grid.Row="2">
</Grid>
</Grid>
</Window>
我在ViewModel中创建了一个Model和View并对其进行了操作。我不确定这是否是实现MVVM的正确方法,或者它甚至是MVVM,因为我将其视为MVC模式。
在维基百科上,它说:
组件包括Model,View,ViewModel和Binder。
我的代码的这一部分显示了一个带横幅的窗口,横幅的最右边部分是显示日期和时间的标签。它可以工作,但我担心的是,我实现它的方式实际上是遵循MVVM模式。
答案 0 :(得分:4)
为了MVVM,View Model不应该包含对View的引用(被认为是不好的做法)
View是否知道ViewModel而不是相反。 View知道ViewModel,而ViewModel又知道模型(或模型)
应该在ViewModel中实现INotifyPropertyChanged接口,以允许视图通过绑定更新自身(在某些情况下,实现模型上的接口也是完全合法的。)
请记住,ViewModel可以看作是一个适合View需要的Model,所以考虑到这一点,我更喜欢将Model类保留为简单的POCO对象,并在其上编写INotifyPropertyChanged实现。视图模型
ViewModel成为View的DataContext(您可以在View后面或xaml中的代码中的View构造函数中分配DataContext。)
要浏览视图,您可以使用(至少)2种方法
您应该决定是使用View-first方法还是ViewModel优先方法。
在View-First方法中,当您想要导航到新页面时,您创建一个View,并且某个机制(binder)将创建相应的ViewModel(进而创建或获取模型)
在ViewModel第一种方法中,您创建一个新的ViewModel(然后创建或获取模型),绑定器将创建相应的视图。
根据我所说的,这是一个例子:
查看(MainWindowView.cs),我们分配DataContext:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel()
}
}
ViewModel(MainWindowViewModel.cs):
namespace DocNanzDCMS.ViewModel
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private MainWindowModel mainWindowModel;
public Model {get {return mainWindowModel;}}
public MainWindowViewModel()
{
this.mainWindowModel = new mainWindowModel();
}
}
}
Model(MainWindowModel.cs):
public class MainWindowModel
{
private PropertyChangedEventArgs pce;
public MainWindowModel()
{
pce = new PropertyChangedEventArgs("");
}
private UserControl userControl;
#region ControlProperty
public UserControl ContentProperty {
get
{
return userControl;
}
set
{
userControl = value;
PropertyChanged(this, pce);
}
}
#endregion
private DateTime dateTime;
#region DateProperty
public String DateProperty
{
get
{
return dateTime.ToLongDateString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
public String TimeProperty
#region TimeProperty
{
get
{
return dateTime.ToLongTimeString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
private String title;
public String TitleProperty
#region TitleProperty
{
get
{
return title;
}
set
{
title = value;
PropertyChanged(this, pce);
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
另外,我认为你应该看看像prism或caliburn micro这样的框架(我更喜欢第一个)来帮助你正确实现MVVM模式而不是重新发明轮子(如加上你还会得到一个导航系统,在视图之间导航。
答案 1 :(得分:3)
你的问题很广泛,但这里有一些想法。
视图模型不应该知道有关视图的任何信息。您应该只将MainWindowViewModel
的{{1}}设置为视图模型的实例,而不是将MainWindow
注入DataContext
,而不是MainWindow
。
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
当视图绑定到视图模型时,MainWindowViewModel
可以初始化和/或与模型通信。
此外,视图模型不应公开任何UIElements
,例如UserControl
。 UIElements
在视图中定义。
答案 2 :(得分:0)
经过几天的研究,我终于“吸收”了MVVM的概念及其与MVC的区别。
模型-大多数情况下,它们只是具有属性的类。像User
,Product
等...
视图-用户界面。 RegisterUserView
,LoginView
,AddProductView
。
ViewModel-这是执行操作的地方。 ViewModel
根据需求/规则操作模型,并为View
公开模型。但是ViewModel
不知道View
的存在。
绑定-这是View
和ViewModel
之间的粘合剂。
与MVC相比(我认为),
View
中的MVC
是被动的,而View
中的MVVM
是主动的。 Controller
中的MVC
决定应在View
中显示哪些内容,而在MVVM
中,View
执行绑定,使其对应该显示的内容负责。 WPF非常痛苦,但确实功能强大。