PrismMVVM功能实现(通知、命令)

news/2025/1/18 3:23:50/文章来源:https://www.cnblogs.com/ywtssydm/p/18339304

常见的MVVM框架,基本围绕ICommand、INotifyPropertyChanged的封装实现绑定、通知等功能;而对于不同框架,在实现相同功能上,只是表现的形式有所不同,下图列举几种常见框架的功能区别:

功能\框架 Prism MVVMLight Micorsoft.Tookit.Mvvm
通知 BindableBase ViewModelBase ObservableObject
命令 DelegateCommand RelayCommand Async/RelayCommand 
事件聚合器 IEventAggregator IMessenger IMessenger
模块化 × ×
容器 × ×
依赖注入 × ×
导航 × ×
对话服务 × ×

通知(BindableBase)

BindableBase 主要实现 INotifyPropertyChanged 接口,用于监听属性更改时提供通知,实现方式为在属性 SET 中添加 RaisePropertyChanged() 方法,具体过程如下:

基础的通知属性的三种方式

// 基础的通知属性
private string _value;public string Value
{get { return _value; }set{// 第一种方式SetProperty<string>(ref _value, value);// 第二种方式_value = value;this.RaisePropertyChanged(nameof(Value));// 第三种方式_value = value;this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs(nameof(Value)));}
}

运行效果如下:

单一命令(DelegateCommand)

ViewModel 可以通过将命令接口实现为命令对象 (实现 ICommand 接口的对象)。视图与命令的交互以声明的方式定义,而无需在视图的代码隐藏文件中使用复杂的事件处理代码。实现 ICommand 接口很简单,Prism 提供了 DelegateCommand 此接口的实现,您可以在应用程序中轻松使用。

创建委托命令 

Prism DelegateCommand 类封装了两个委托,每个委托都引用了在 ViewModel 类中实现的方法。它通过调用这些委托来实现 ICommand 接口Execute 和方法。 CanExecute 您在 DelegateCommand 类构造函数中指定 ViewModel 方法的委托。例如,以下代码示例显示了如何DelegateCommand 通过指定 OnSubmit 和 CanSubmit ViewModel 方法的委托来构造表示提交命令的实例。然后,该命令通过一个只读属性暴露给视图,该属性返回对 DelegateCommand . 

//无参、仅Excute构造方式
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit);}void Submit(){//执行方法}
}
//有参、Execute和CanExecut同时构造方式
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit);}void Submit(object parameter){//执行方法}bool CanSubmit(object parameter){return true;}
}
//无参、Execute和CanExecut同时构造方式
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit, CanSubmit);}void Submit(){//implement logic}bool CanSubmit(){return true;}
}

从视图调用命令

使用 CommandParameter 属性定义命令参数。定义的参数类型在 DelegateCommand 泛型声明中指定。当用户与该控件交互时,该控件将自 动调用目标命令,并且命令参数(如果提供)将作为参数传递给命令的 Execute 方法。 

<Button Command="{Binding SubmitCommand}" CommandParameter="OrderId"/>

事件转命令

侦听Grid的MouseLeftButtonDown事件,并使用Command绑定

<Window x:Class="Zhaoxi.MvvmBaseObject.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:Zhaoxi.MvvmBaseObject"xmlns:i="http://schemas.microsoft.com/xaml/behaviors"xmlns:p="http://prismlibrary.com/"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid Background="Transparent"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><p:InvokeCommandAction Command="{Binding ButtonCommand}"/></i:EventTrigger></i:Interaction.Triggers></Grid>
</Window>
public DelegateCommand<object> ButtonCommand { get; set; }public MainViewModel()
{ButtonCommand = new DelegateCommand<object>(arg =>{});
}

不设置p:InvokeCommandAction的TriggerParameterPath属性,传递参数是{System.Windows.Input.MouseButtonEventArgs}

设置TriggerParameterPath参数会传递所设置的参数,或者传递{System.Windows.Input.MouseButtonEventArgs}中具体的属性比如ButtonState

    <Grid Background="Transparent"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><p:InvokeCommandAction Command="{Binding ButtonCommand}" TriggerParameterPath="ButtonState"/></i:EventTrigger></i:Interaction.Triggers></Grid>

使用WPF原生的InvokeCommandAction默认不传递参数,需要设置PassEventArgsToCommand="True"可以传递{System.Windows.Input.MouseButtonEventArgs}

    <Grid Background="Transparent"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding ButtonCommand}" PassEventArgsToCommand="True"/></i:EventTrigger></i:Interaction.Triggers></Grid>

创建异步委托命令

在当今的 async / await 使用中,在委托中调用异步方法 Execute 是非常普遍的要求。每个人的第一直觉是他们需要一个 AsyncCommand ,但这种假设是错误的。 ICommand 本质上是同步的, Execute 并且 CanExecute 委托应该被视为事件。这意味着这 async void 是用于命令的完全有效的语法。有两种方法可以将异步方法与 DelegateCommand 结合使用:

方式1 

public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit);}async void Submit(){await SomeAsyncMethod();}
}

方式2

public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(async ()=> await Submit());}Task Submit(){return SomeAsyncMethod();}
}

复合命令(CompositeCommand) 

在许多情况下,视图模型定义的命令将绑定到关联视图中的控件,以便用户可以直接从视图中调用该命令。但是,在某些情况下,您可能希望能够从应用程序 UI 的父视图中的控件调用一个或多个视图模型上的命令。

Prism 通过 CompositeCommand 类支持这种情况 。

该类 CompositeCommand 表示由多个子命令组成的命令。当调用复合命令时,依次调用它的每个子命令。在您需要将一组命令表示为 UI 中的单个命令或您希望调用多个命令来实现逻辑命令的情况下,它很有用。

该类 CompositeCommand 维护一个子命令( DelegateCommand 实例)列表。类的 Execute 方法 CompositeCommand 简单地依次调用Execute 每个子命令的方法。该 CanExecute 方法类似地调用 CanExecute 每个子命令的方法,但如果任何子命令无法执行,该 CanExecute 方法将返回 false 。也就是说,默认情况下,a CompositeCommand 只有在所有子命令都可以执行时才能执行。

创建复合命令

复合命令绑定到视图

在视图中,将“CompositeButton”按钮与到 TestCompositeCommand 命令绑定。

运行结果如下:

不同页面绑定同一复合命令

跨页面绑定需要全局单例复合命令,可以注册单例复合命令,在注入到各模块中

MainWindow

 <Grid><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><StackPanel Orientation="Horizontal"><Button Content="显示页面" Command="{Binding ShowPageCommand}"/><Button Content="全部保存" Command="{Binding AllSaveCommand}"/></StackPanel><UniformGrid Columns="2" Grid.Row="1"><ContentControl p:RegionManager.RegionName="Region1"/><ContentControl p:RegionManager.RegionName="Region2"/></UniformGrid></Grid>

ViewA

    <Grid><Button Content="A保存" Command="{Binding SaveCommand}"/></Grid>

ViewB

    <Grid><Button Content="B保存" Command="{Binding SaveCommand}"/></Grid>

注册页面和单例复合命令

public class Startup : PrismBootstrapper
{protected override DependencyObject CreateShell(){return Container.Resolve<PrismRegion.Composite.MainWindow>();}protected override void RegisterTypes(IContainerRegistry containerRegistry){containerRegistry.RegisterForNavigation<PrismRegion.Composite.ViewA>();containerRegistry.RegisterForNavigation<PrismRegion.Composite.ViewB>();//注册单例命令containerRegistry.RegisterSingleton<CompositeCommand>();}
}

MainWindowViewModel

注入CompositeCommand拿到全局复合命令,并关联实例AllSaveCommand

public class MainWindowViewModel
{public CompositeCommand AllSaveCommand { get; set; }public DelegateCommand ShowPageCommand { get; set; }public MainWindowViewModel(IRegionManager regionManager,CompositeCommand composite){ShowPageCommand = new DelegateCommand(() =>{regionManager.RequestNavigate("Region1", "ViewA");regionManager.RequestNavigate("Region2", "ViewB");});AllSaveCommand = composite;//AllSaveCommand.RegisterCommand(ShowPageCommand);}
}

ViewAViewModel

注入CompositeCommand拿到全局复合命令,将SaveCommand加入到复合命令

public class ViewAViewModel
{public DelegateCommand SaveCommand { get; set; }public ViewAViewModel(CompositeCommand composite){SaveCommand = new DelegateCommand(() =>{});composite.RegisterCommand(SaveCommand);}
}

ViewBViewModel

注入CompositeCommand拿到全局复合命令,将SaveCommand加入到复合命令

public class ViewBViewModel
{public DelegateCommand SaveCommand { get; set; }public ViewBViewModel(CompositeCommand composite){SaveCommand = new DelegateCommand(() =>{});// 这里有前后关系,很重要composite.RegisterCommand(SaveCommand);//composite.UnregisterCommand(SaveCommand);}
}

运行后按钮先为黑色,因为还没ViewA和ViewB没有初始化,复合命令为空

点击显示页面后,ViewA和ViewB的命令加入到复合命令后按钮可以点击

取消注册命令

如前面的示例所示,子命令是使用该 TestCompositeCommand.RegisterCommand 方法注册的。但是,当您不再希望响应 TestCompositeCommand或者您正在销毁 View/ViewModel 以进行垃圾回收时,您应该使用该 TestCompositeCommand.UnregisterCommand 方法取消注册子命令。

/// 取消命令注册例子
public void Destroy()
{TestCompositeCommand.UnregisterCommand(TestCommand1);
}

通过依赖注入、静态类来实现 CompositeCommand 全局可用/绑定等用法,请查阅Prism官方文档。

来源:https://www.cnblogs.com/ZHIZRL/p/17675034.html

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/776726.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SemanticKernel/C#:使用Ollama中的对话模型与嵌入模型用于本地离线场景

本文介绍了在SemanticKernel/C#中如何使用Ollama中的对话模型与嵌入模型用于本地离线场景。前言 上一篇文章介绍了使用SemanticKernel/C#的RAG简易实践,在上篇文章中我使用的是兼容OpenAI格式的在线API,但实际上会有很多本地离线的场景。今天跟大家介绍一下在SemanticKernel/…

Prism视图模型定位器(ViewModelLocator)

视图模型定位器(ViewModelLocator) 依照“标准命名约定”将 视图(View) 中的数据上下文链接到 视图模型(ViewModel) 的实例。 自动绑定视图模型 Prism 视图模型定位器 (ViewModelLocator) 有一个 AutoWireViewModel 属性:当设置为 true 时, AutoWireViewModelChanged…

无法将为“Microsoft.Office.Interop.Word.ApplicationClass”的 COM 对象强制转换为接口类型

原文链接:https://blog.csdn.net/Castlehe/article/details/124380648 1.错误原因安装了多版本的Office安装过WPS后没正常卸载2. 解决方式2.1 office多版本问题导致的以下四个操作基本覆盖常见原因了,可以从2.1.1尝试,每尝试一种,就去试一下看问题解决了没有,如果已经解决…

MOS管栅极电阻和泄放电阻的作用

目录: 一、栅极电阻 二、泄放电阻一、栅极电阻我们知道,mos管是电压控制器件,与双极性三极管不同的是,mos管的导通只需要控制栅极的电压超过其开启阈值电压即可,不需要栅极电流。所以本质上,MOS管栅极上无需串联任何电阻。 对于普通的双极性三极管,它是电流控制器件。它的…

Win11不在C盘安装WSL2(Linux环境),安装Nvidia驱动和默认使用Win11的网络代理服务

众所周知,WSL 2 为 Windows 用户提供了一个强大、高效且灵活的 Linux 环境,特别适合开发者使用。它结合了 Windows 和 Linux 的优点,为用户提供了更加全面和高效的工作环境。但缺点也很明显,那就是默认安装在本来空间就不富裕的C盘。本次我们在非C盘的盘符快速安装基于wsl2…

【闲话】08.02.24

SPFA死了0802 闲话 头图:今日推歌: 《レディメイド feat.Ado》 すりぃ1 2 3で弾け飛んだ 一、二、三 绽破而飞 固定観念バットで打って 固定概念 用球棒击碎 どうだい?どうだい? 如何 如何 楽ならまっいっか 觉得快乐的话就无所谓啦我还是现充的时候就喜欢上这首歌了,,,…

JavaSE基础编程十题(数组和方法部分)

接着前面的数组和方法的习题,这是十道编程题!写在前面 继续昨天Java中的数组和方法部分的习题,今天写十题编程题,来看看你能写出来几题。答案也是仅供参考,如果有更好的解法欢迎在下面留言! 题目展示 1.数组查找操作:定义一个长度为10 的一维字符串数组,在每一个元素存…

.net项目使用Docker部署(包括解决后台验证码,部署后不显示的问题)

Vue部署到Docker 参考文档:手把手教你用 Docker 部署 Vue3 项目_docker部署vue3项目-CSDN博客 参考文档:dockerfile 部署前端vue项目_vue dockerfile-CSDN博客 nginx文档:使用docker安装nginx - 静以修身俭以养德 - 博客园 (cnblogs.com) 结合使用了两个文档的方法和DockerFI…

NewStarCTF WEEK5|WEB Yes Pickle

下载附件 # -*- coding: utf-8 -*- import base64 import string import random from flask import * import jwcrypto.jwk as jwk import pickle from python_jwt import *app = Flask(__name__) # 创建 Flask 应用实例def generate_random_string(length=16):""&q…

数仓sql场景:迭代求结果问题

1.需求2.sql实现 这道题先需要去分析结果集,本质上是一个迭代累加的过程,先要得到如下结果如果在面试数仓中实现了以上结果,基本上面试官会很通过,也在短时间内可以实现,实现sql如下with tb as ( select 1 as s,a as pv union all select 2 as s,b as pv union all select…

Vue 使用 vue-drag-resize 实现拖拽和随意缩放大小及安装报错处理

一、vue-drag-resize的安装yarn add vue-drag-resize 下面是错误解决方案: TypeError: Cannot read properties of undefined (reading ‘_c’) 解决方案: 在引入时加上“/src”:import VueDragResize from "vue-drag-resize"; 改成 import VueDragResize from …