news2025/7/18 14:03:40
MVC Model View Control
MVP
MVVM即Model-View-ViewModel,MVVM模式与MVP(Model-View-Presenter)模式相似,主要目的是分离视图(View)和模型(Model),具有低耦合、可重用性、独立开发、可测试性等优点。
MVVM框架有很多,开源的主要有:
闭源框架主要有:
Models 存放的是数据模型Service存放的是业务逻辑
ViewModels存放的便是视图模型
Views存放WPF窗口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wpfmvvm_demo.Models
{
public class User
{
/// <summary>
/// 用户名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Password { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpfmvvm_demo.Models;
namespace Wpfmvvm_demo.Services
{
public class UserService
{
/// <summary>
/// 获取所有用户方法
/// </summary>
/// <returns></returns>
public List<User> GetAllUser()
{
List<User> users = new ();
for (int i = 0; i < 3; i++)
{
var user = new User();
user.Name = "用户" + i;
user.Password = "密码" + i;
users.Add(user);
}
return users;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wpfmvvm_demo.ViewModels
{
public class NotificationObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Wpfmvvm_demo.ViewModels
{
public class DelegateCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
public Func<object?,bool>? DoCanExecute { get; set; }
public Action<object?>? DoExecute { get; set; }
public bool CanExecute(object? parameter)
{
if (DoCanExecute != null)
{
return DoCanExecute(parameter);
}
return true;
}
public void Execute(object? parameter)
{
if (DoExecute != null)
{
DoExecute.Invoke(parameter);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpfmvvm_demo.Models;
using Wpfmvvm_demo.Services;
namespace Wpfmvvm_demo.ViewModels
{
public class MainWindowViewModel : NotificationObject
{
/// <summary>
/// 用户List
/// </summary>
private List<User>? users;
public List<User>? Users
{
get { return users; }
set
{
users = value;
RaisePropertyChanged("Users");
}
}
/// <summary>
/// 程序名
/// </summary>
public string AppName { get; set; }
/// <summary>
/// 电话
/// </summary>
private string? phone;
public string? Phone
{
get { return phone; }
set
{
phone = value;
RaisePropertyChanged("Phone");
}
}
/// <summary>
/// 获取所有用户命令
/// </summary>
public DelegateCommand? GetAllUsersCommand { get; set; }
/// <summary>
/// 构造初始化
/// </summary>
public MainWindowViewModel()
{
AppName = "WPF MVVM 模式测试";
Phone = "123456";
GetAllUsersCommand = new DelegateCommand
{
DoExecute = new Action<object?>(GetAllUsersCommandExecute)
};
}
/// <summary>
/// 获取所有用户命令执行方法
/// </summary>
private void GetAllUsersCommandExecute(object? paramater)
{
Phone = Phone!.Equals("123456") ? "1234567" : "123456";
UserService userService = new UserService();
Users = userService.GetAllUser();
}
}
}
<Window x:Class="Wpfmvvm_demo.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:Wpfmvvm_demo"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="550">
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="程序名"></Label>
<Label Content="{Binding AppName}"></Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="电话"></Label>
<Label Content="{Binding Phone}"></Label>
</StackPanel>
<StackPanel>
<Button Content="获取所有用户" Command="{Binding GetAllUsersCommand}"></Button>
</StackPanel>
<DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False" GridLinesVisibility="All" CanUserDeleteRows="False" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="用户名" Binding="{Binding Name}"></DataGridTextColumn>
<DataGridTextColumn Header="密码" Binding="{Binding Password}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using Wpfmvvm_demo.ViewModels;
namespace Wpfmvvm_demo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
微软虽然提出了 MVVM,但又没有提供一个官方的 MVVM 库(多年前有过 Prism,但已经离家出走了)。每次有人提起 MVVM 库,有些人会推荐 Prism(例如我),有些人会推荐 MVVMLight。可是现在 Prism 已经决定不再支持 UWP , 而 MVVMLight 又不再更新,在这左右为难的时候 Windows Community Toolkit 挺身而出发布了 MVVM Toolkit。 MVVM Toolkit 延续了 MVVMLight 的风格,是一个轻量级的组件,而且它基于 .NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno 等多个平台。相比它的前身 MVVMLight,它有以下特点:
- 更高:版本号更高,一出手就是 7.0。
- 更快:速度更快,MVVM Toolkit 从一开始就以高性能为实现目标。
- 更强:后台更强,MVVM Toolkit 的全称是 'Microsoft.Toolkit.Mvvm',根正苗红。
这个包主要提供了如下的
Microsoft.Toolkit.Mvvm.ComponentModel
- ObservableObject
- ObservableRecipient
- ObservableValidator
Microsoft.Toolkit.Mvvm.DependencyInjection
- Ioc
Microsoft.Toolkit.Mvvm.Input
- RelayCommand
- AsyncRelayCommand
- IRelayCommand
- IAsyncRelayCommand
Microsoft.Toolkit.Mvvm.Messaging
- IMessenger
- WeakReferenceMessenger
- StrongReferenceMessenger
- IRecipient
- MessageHandler
Microsoft.Toolkit.Mvvm.Messaging.Messages
- PropertyChangedMessage
- RequestMessage
- AsyncRequestMessage
- CollectionRequestMessage
- AsyncCollectionRequestMessage
- ValueChangedMessage
public class UserVM : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
private int age;
public int Age
{
get => age;
set => SetProperty(ref age, value);
}
}
页面的类中添加
public partial class MainWindow : Window
{
private UserVM userVM = new UserVM();
public MainWindow()
{
InitializeComponent();
DataContext = userVM;
Task.Run(() =>
{
while (true)
{
userVM.Age += 1;
Thread.Sleep(1000);
}
});
}
}
页面中
<Grid>
<StackPanel>
<TextBlock FontSize="30" Text="{Binding Age}" />
</StackPanel>
</Grid>
我们就可以看到数字就会一直递增了。
页面中我们添加命令
<StackPanel Height="40" Orientation="Horizontal">
<TextBlock FontSize="30" Text="{Binding Name}" />
<TextBlock FontSize="30" Text="{Binding Age}" />
<Button Command="{Binding IncrementAgeCommand}" Content="年龄递增" />
</StackPanel>
对应的ViewModel中添加命令及响应的事件
public class UserVM : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
private int age;
public int Age
{
get => age;
set => SetProperty(ref age, value);
}
public ICommand IncrementAgeCommand { get; }
public UserVM()
{
IncrementAgeCommand = new RelayCommand(IncrementAge);
}
private void IncrementAge() => Age++;
}
这样只要我们点击按钮,年龄就会递增1。
添加传递消息的类
public class ZMessage
{
public int Code { get; set; }
public string Message { get; set; }
public ZMessage(int code, string msg)
{
Code = code;
Message = msg;
}
}
消息接收与发送
public MainWindow()
{
InitializeComponent();
initMessage();
}
private void initMessage()
{
WeakReferenceMessenger.Default.Register<ZMessage>(
this,
(r, m) =>
{
Console.WriteLine("接收到信息:" + m.Message);
}
);
WeakReferenceMessenger.Default.Send(new ZMessage(100, "Hello"));
}
当然我们也可以让我们的ViewModel接收消息
public class UserVM : ObservableRecipient, IRecipient<ZMessage>
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
private int age;
public int Age
{
get => age;
set => SetProperty(ref age, value);
}
public UserVM()
{
IsActive = true;
}
public void Receive(ZMessage message)
{
Name = message.Message;
Age = message.Code;
}
}
这里一定要注意
ObservableRecipient
类,实现IRecipient<>
接口。IsActive = true;
,才能接收消息。我们还是在页面的类中发送消息
WeakReferenceMessenger.Default.Send(new ZMessage(18, "XiaoMing"));
页面也稍做修改
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="30" Text="{Binding Name}" />
<TextBlock FontSize="30" Text="{Binding Age}" />
</StackPanel>
我们会发现页面上已经变更为我们发送消息的数据了。
添加依赖库Microsoft.Extensions.DependencyInjection
Install-Package Microsoft.Extensions.DependencyInjection -Version 6.0.0
创建我们要自动注入的类
加入如下是我们用户相关的服务
public interface IUserService
{
string getUserName();
}
和
public class UserService : IUserService
{
public string getUserName()
{
return "XiaoMing";
}
}
添加注入的控制
public partial class App : Application
{
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
}
public new static App Current => (App)Application.Current;
public IServiceProvider Services { get; }
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IUserService, UserService>();
// ViewModels
services.AddTransient<UserVM>();
return services.BuildServiceProvider();
}
}
其中
权重:
AddSingleton`→`AddTransient`→`AddScoped
生命周期:
注意:
由于AddScoped对象是在请求的时候创建的 所以不能在AddSingleton对象中使用 甚至也不能在AddTransient对象中使用
使用
private UserVM userVM = (UserVM)App.Current.Services.GetService(typeof(UserVM));
private IUserService userService = (IUserService)App.Current.Services.GetService(typeof(IUserService));
这样是不是感觉还麻烦了
但是如果我们的ViewModel是这样的
public class UserVM : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
private int age;
public int Age
{
get => age;
set => SetProperty(ref age, value);
}
public ICommand IncrementAgeCommand { get; }
public IUserService userService { get; }
public UserVM(IUserService _userService)
{
userService = _userService;
IncrementAgeCommand = new RelayCommand(IncrementAge);
}
private void IncrementAge() => Age++;
}
这里的IUserService
的实例并没有传入但是就可以用了,因为IOC框架已经自动注入了。
众所周知, 如果你了解WPF当中的ICommand, INotifyPropertyChanged的作用, 就会发现众多框架都是基于这些进行扩展, 实现其通知、绑定、命令等功能。
对于不同的MVVM框架而言, 大体使用上会在声明方式上的差异, 以及特定功能上的差别。
下面列举了常用的3个MVVM框架,他们的一些差异。如下所示:
功能↓ / →框架名
Prism
Mvvmlight
Micorosoft.Toolkit.Mvvm
通知
BindableBase
ViewModelBase
ObservableObject
命令
DelegateCommand
RelayCommand
Async/RelayCommand
聚合器
IEventAggregator
IMessenger
IMessenger
模块化
√
×
×
容器
√
×
×
依赖注入
√
×
×
导航
√
×
×
对话
√
×
×
正如你所见, 各个框架之间都有各自的通知、绑定、事件聚合器等基础的功能, 而Prsim自带的依赖注入、容器、以及导航会话等功能, 可以为你提供更加强大的功能。
当然,在实际的开发过程当中, 可以根据实际的功能需求, 对不同的框架选型, 同时这也需要你对各个框架之间的优缺点进行判断。
那么, 下面将主要介绍Prism
在Prism当中, 你需要继承于BindableBase
public class TestViewModel : BindableBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; RaisePropertyChanged(); }
}
}
在Prism当中, 你可以使用DelegateCommand及带参数的Command
public class TestViewModel : ViewModelBase
{
public DelegateCommand SendCommand { get; set; }
public DelegateCommand<string> SendMessageCommand { get; set; }
}
对于单个Command而言, 只是触发单个对应的功能, 而复合命令是Prism当中非常强大的功能, CompositeCommand简单来说是一个父命令, 它可以注册N个子命令
当父命令被激活, 它将触发对所有的子命令, 如果任意一个命令CanExecute=false,它将无法被激活,如下所示:
public class MyViewModel : NotificationObject
{
private readonly CompositeCommand saveAllCommand;
public ArticleViewModel(INewsFeedService newsFeedService,
IRegionManager regionManager,
IEventAggregator eventAggregator)
{
this.saveAllCommand = new CompositeCommand();
this.saveAllCommand.RegisterCommand(new SaveProductsCommand());
this.saveAllCommand.RegisterCommand(new SaveOrdersCommand());
}
public ICommand SaveAllCommand
{
get { return this.saveAllCommand; }
}
}
- 松耦合基于事件通讯
- 多个发布者和订阅者
- 微弱的事件
- 过滤事件
- 传递参数
- 取消订阅
该功能主要作用为, 事件聚合器负责接收订阅以及发布消息。订阅者可以接收到发布者发送的内容。
//创建事件
public class SavedEvent : PubSubEvent<string> { }
//发布
IEventAggregator.GetEvent<SavedEvent>().Publish("some value");
//订阅
IEventAggregator.GetEvent<SavedEvent>().Subscribe(.Subscribe(message=>
{
//do something
});
在实际的开发过程当中,我们往往会在多个位置订阅一个事件, 但是对于订阅者而言, 他并不需要接收任何消息, 如下所示:
在Prism当中, 我们可以指定为事件指定过滤条件, 如下所示:
eventAggregator.GetEvent<MessageSentEvent>()
.Subscribe(arg =>
{
//do something
},
ThreadOption.PublisherThread,
false,
//设置条件为token等于“MessageListViewModel” 则接收消息
message => message.token.Equals(nameof(MessageListViewModel)));
关于Subscribe当中的4个参数, 详解:
为注册的消息取消订阅, Prism提供二种方式取消订阅,如下:
1.通过委托的方式取消订阅
var event = IEventAggregator.GetEvent<MessageSentEvent>();
event.Subscribe(OnMessageReceived);
event.Unsubscribe(OnMessageReceived);
2.通过获取订阅者token取消订阅
var _event = eventAggregator.GetEvent<MessageSentEvent>();
var token = _event.Subscribe(OnMessageReceived);
_event.Unsubscribe(token);
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243339.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!
原网址: 访问
创建于: 2025-07-18 22:15:39
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论