之前的blog中讲了很多比较细化的知识点,但是在开发过程中如何保持一个比较好的开发模式是前期开发中比较困惑的地方。这里就讲解下MVVM开发模式,也是目前WPF开发中普遍采用的开发模式。
MVVM 模式就是 View ViewModel Model :将前后端开发分离,保持了较好的低耦合,便于后期的维护,也比较好的表现了数据驱动的运行模式。
我在自己的开发过程中总结了MVVM模式开发要遵循的几个要求
1:在xaml的后置文件cs中不编写业务代码。
2:xmal中是数据都通过Binding来进行UI渲染。
3:ViewModel 中编写业务处理的代码,并且不能有操作对view直接操作的代码。
4:控件的命令和事件,属性也要通过binding的模式来进行编写。
遵循上面几个要求就能编写出简洁高效的代码并从中体会到数据驱动的美妙之处,但是MVVM并没有严格的限制,具体的开发实现还要根据具体的业务需求来进行调整。
下面就分三个部分View ViewModel Model来进行讲解。
View:
<UserControl
x:Class="AlphaLog.UserControls.UserDelPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Metro="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:AlphaLog.UserControls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="250"
Height="150">
<!-- 事件的绑定 -->
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Loaded">
<interactivity:InvokeCommandAction Command="{Binding LoadedCommand}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="User:" />
<TextBox
x:Name="txtUserName"
Grid.Row="0"
Grid.Column="1"
Width="130"
Height="25"
HorizontalAlignment="Left"
Metro:TextBoxHelper.ClearTextButton="True"
Metro:TextBoxHelper.Watermark="用户名"
Background="Transparent"
IsEnabled="False"
Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Pass:" />
<TextBox
x:Name="txtUserPass"
Grid.Row="1"
Grid.Column="1"
Width="130"
Height="25"
HorizontalAlignment="Left"
Metro:TextBoxHelper.ClearTextButton="True"
Metro:TextBoxHelper.Watermark="用户密码"
Background="Transparent"
IsEnabled="False"
Text="{Binding UserPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Addr:" />
<TextBox
x:Name="txtUserAddr"
Grid.Row="2"
Grid.Column="1"
Width="130"
Height="25"
HorizontalAlignment="Left"
Metro:TextBoxHelper.ClearTextButton="True"
Metro:TextBoxHelper.Watermark="连接地址"
Background="Transparent"
IsEnabled="False"
Text="{Binding ConnectIP, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<StackPanel
Grid.Row="1"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Orientation="Vertical">
<TextBlock
x:Name="txtDelResult"
Foreground="#CD3333"
Text="{Binding DelResult, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="Visible" />
<StackPanel Orientation="Horizontal">
<ComboBox
x:Name="comBoxUserList"
Width="150"
Metro:TextBoxHelper.Watermark="请选择要删除的账户"
DisplayMemberPath="UserName"
ItemsSource="{Binding UserList, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding UserListSelectedItem}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged">
<interactivity:InvokeCommandAction Command="{Binding UserListSelectionChangedCommand}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</ComboBox>
<Button
x:Name="btnDelUser"
Width="40"
Margin="5,0"
Command="{Binding DelUserCommand}"
Content="Del" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
在上面的代码中所有的数据,command和事件都是通过Binding来实现的,保证了后置cs文件中不错在业务相关代码。
Binding中button的command绑定最为直接,处理也相对简洁。
CombBox中的selectionChange事件需要通过interactivity来进行操作。
下面是View中的后置cs代码,在控件初始完成之后添加语句DataContext = ViewModel即可。
using AlphaLog.Model;
using System;
using System.Windows.Controls;
namespace AlphaLog.UserControls
{
/// <summary>
/// UserDelPanel.xaml 的交互逻辑
/// </summary>
public partial class UserDelPanel : UserControl
{
public UserDelPanel()
{
InitializeComponent();
DataContext = new UserDelViewModel();
}
}
}
ViewModel中只需要添加好业务相关的代码,没有直接对View的操作,也就是说View视图中所有数据的变化和响应都是通过“数据驱动”来完成的。
using AlphaLog.DataCore;
using AlphaLog.Model;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
namespace AlphaLog
{
public class UserDelViewModel:INotifyPropertyChanged
{
public UserDelViewModel()
{
//注册删除命令委托
_delUserCommand = new CustomCommand
{
ExecuteDelegate = del =>
{
UserDelFunc();
}
};
_loadedCommand = new CustomCommand
{
ExecuteDelegate = loaded =>
{
LoadedFunc();
}
};
_userListSelectionChangedCommand = new CustomCommand
{
ExecuteDelegate = selectionChange =>
{
SelectionChangeFunc();
}
};
}
#region command
private readonly ICommand _delUserCommand;
private readonly ICommand _loadedCommand;
private readonly ICommand _userListSelectionChangedCommand;
public ICommand DelUserCommand
{
get
{
return _delUserCommand;
}
}
public ICommand LoadedCommand
{
get
{
return _loadedCommand;
}
}
public ICommand UserListSelectionChangedCommand
{
get
{
return _userListSelectionChangedCommand;
}
}
#endregion
#region property
private string _userName = "";
public string UserName
{
set
{
_userName = value;
OnPropertyChange();
}
get
{
return _userName;
}
}
private string _userPassword = "";
public string UserPassword
{
set
{
_userPassword = value;
OnPropertyChange();
}
get
{
return _userPassword;
}
}
private string _connectIP = "";
public string ConnectIP
{
set
{
_connectIP = value;
OnPropertyChange();
}
get
{
return _connectIP;
}
}
private string _delResult = "";
public string DelResult
{
set
{
_delResult = value;
OnPropertyChange();
}
get
{
return _delResult;
}
}
public ObservableCollection<UserModel> UserList { set; get; } = new ObservableCollection<UserModel>();
private UserModel _userListSelectedItem = null;
public UserModel UserListSelectedItem
{
set
{
_userListSelectedItem = value;
OnPropertyChange();
}
get
{
return _userListSelectedItem;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChange(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public void UserDelFunc()
{
if (DataCenter.DelUser(UserListSelectedItem))
{
DelResult = "删除成功";
UserList.Remove(UserListSelectedItem);
UserName = "";
UserPassword = "";
ConnectIP = "";
}
else
{
DelResult = "删除失败";
}
}
public void LoadedFunc()
{
DelResult = "";
UserList.Clear();
foreach (var item in DataCenter._userList)
{
UserList.Add(item);
}
}
public void SelectionChangeFunc()
{
if (UserListSelectedItem == null)
{
UserName = "";
UserPassword = "";
ConnectIP = "";
}
else
{
UserName = UserListSelectedItem.UserName;
UserPassword = UserListSelectedItem.Password;
ConnectIP = UserListSelectedItem.Address;
}
}
}
}
上面的代码基本上可以分为三个部分:1.命令的注册。2.属性的定义。3.业务逻辑的处理。其中业务逻辑的处理简洁明了,只用考虑业务数据的变化,不用再考虑界面该如何调控和刷新,极大的增强了代码的健壮性,降低了代码的耦合。
下面代码是Model的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace AlphaLog.Model
{
public class UserModel: INotifyPropertyChanged
{
private string _userName = "";
private string _password = "";
private string _address = "";
public string UserName
{
set
{
_userName = value;
OnPropertyChange();
}
get
{
return _userName;
}
}
public string Password
{
set
{
_password = value;
OnPropertyChange();
}
get
{
return _password;
}
}
public string Address
{
set
{
_address = value;
OnPropertyChange();
}
get
{
return _address;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChange([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
model中是对View中用到的数据集的定义,model对象中基本都是属性,可能会有少量的处理方法。不建议在model中添加方法,因为当model生成的集合数目过大时,会消耗很多内存,同时也会导致View视图刷新变慢。
总结:以上就是一个MVVM简单的实现,其中可以根据业务的不同来进行适当的调整。
在MVVM开发中需要遵循的几个规则。
1:在xaml的后置文件中不写业务处理代码。
2:在xaml文件中需要的数据都通过Binding的形式实现。
3:在ViewModel 文件中编写业务处理的代码,ViewModel 中不能有对View操作的代码。
4:控件的命令,事件和属性也通过binding来实现。