【发布时间】:2017-10-25 11:27:25
【问题描述】:
我刚开始学习来自 Java Swing 和 WinForms 的 WPF。我决定尝试一些新的东西来学习其他开发程序的概念和技术。上次介绍了 MVC Pattern 的概念。据我所知,这是一种分离 UI 逻辑、业务逻辑和数据的方法。我发现 WPF 的关键概念之一是绑定和 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 中操作它们。我不确定这是否是实现 MVVM 的正确方法,或者它甚至是 MVVM,因为我将其视为 MVC 模式。
在维基百科上,它说:
组件是 Model、View、ViewModel 和 Binder。
我的这部分代码显示了一个带有横幅的窗口,横幅的最右侧是显示日期和时间的标签。它有效,但我担心的是我制作它的方式是否实际上遵循 MVVM 模式。
【问题讨论】:
-
模型应该只是一个具有数据属性的类(如果需要,可以使用方法)。视图模型应该是实现 INotifyPropertyChanged 的模型。视图后面的代码最多只能说 this.DataContext =
; -
这意味着我每次在 ViewModel 中更新数据时都必须调用 PropertyChanged?我需要将模型的声明移动到视图?
-
@Mr.Leeds 没有实际理由(除了个人喜好)不在模型类中实现 INotifyPropertyChanged。 Svcutil 之类的工具甚至可以通过这种方式生成您的模型。
-
我有点困惑在哪里实现这些接口以及模型视图视图模型应该是什么样子。在我观看的一个教程中,程序员在每个属性的 set{} 上调用了 PropertyChanged。
-
请注意,在现实生活中通常没有模型(我的意思是作为一个单独的类)。 Viewmodel 结合了自身和模型(即同时具有命令、数据等)。旁注:
pce = new PropertyChangedEventArgs("");然后到处使用它是很奇怪的。您应该传递更改属性的名称,而不是空字符串。