Wpf 使用 Prism 实战开发Day18

数据加载动画实现

概要:

当打开功能页面时,在数据未加载完毕前,希望有一个友好的等待提示。那么,本章通过学习Prism 中事件聚合器(EventAggregator),并通过创建等待提示窗口,同时结合事件订阅发布来实现该功能点。


一.事件聚合器(EventAggregator)实现发布和订阅

1.定义事件消息模型

 定义一个 UpdateLoadingEvent (等待加载完成的事件)消息模型,并继承PubSubEvent。PubSubEvent 是一个泛型,可以传入字符串或其他。如下传入一个UpdateModel 实体类。表明了当前的消息模型用来传递一个对象实体类的消息或定义成一个string (字符串) 表示传递一个字符串的消息。

传递对象实体类消息

    public class UpdateModel{public bool IsOpen { get; set; }}public class UpdateLoadingEvent:PubSubEvent<UpdateModel>{}

传递字符串消息

public class UpdateLoadingEvent:PubSubEvent<string>
{}

2.发布事件 

如何使用:在构造函数中注入IEventAggregator,并通过IEventAggregatorGetEvent获取定义的UpdateLoadingEvent 消息,再通过Publish 方法进行发布消息。

当前发布事件主要用来,处理某个View 或ViewMode 之间传递消息,则使用 Publish 来进行发布一个消息

/// <summary>
/// 发布事件
/// </summary>
/// <param name="aggregator"></param>/// <param name="model"></param>
public UpdateLoading(IEventAggregator aggregator,UpdateModel model)
{aggregator.GetEvent<UpdateLoadingEvent>().Publish(model);
}

3.订阅事件

如何使用:在构造函数中注入IEventAggregator,并通过IEventAggregatorGetEvent获取定义的UpdateLoadingEvent 消息,再通过Subscribe方法注册,Subscribe是一个委托方法

/// <summary>
/// 订阅事件
/// </summary>
/// <param name="aggregator"></param>
/// <param name="model"></param>
public UpdateLoading(IEventAggregator aggregator,Action<UpdateModel> model)
{aggregator.GetEvent<UpdateLoadingEvent>().Subscribe(model);
}

一种写法

/// <summary>
/// 订阅事件
/// </summary>
/// <param name="aggregator"></param>
/// <param name="model"></param>
public UpdateLoading(IEventAggregator aggregator)
{aggregator.GetEvent<UpdateLoadingEvent>().Subscribe(arg=>{// arg 就是订阅到的消息,进行逻辑处理});
}

 一种写法

/// <summary>
/// 订阅事件
/// </summary>
/// <param name="aggregator"></param>
/// <param name="model"></param>
public UpdateLoading(IEventAggregator aggregator)
{aggregator.GetEvent<UpdateLoadingEvent>().Subscribe(SubMessage);
}
private void SubMessage(UpdateModel model)
{//逻辑处理
}

 无论是那种写法,订阅事件都是需要传入一个委托。并且只有订阅了相关消息模型后,从其他View 或ViewMode 发布的消息,才能成功接收到。一般,订阅消息,也称注册消息,都是干同一件事件。

4.取消订阅

Unsubscribe 同样也是一个委托方法。事件订阅被取消后,无论再怎么发布该事件,都无法接收到该事件消息了。

/// <summary>
/// 发布事件
/// </summary>
/// <param name="aggregator"></param>
/// <param name="model"></param>
public UpdateLoading(IEventAggregator aggregator)
{
aggregator.GetEvent<UpdateLoadingEvent>().Unsubscribe(SubMessage);
}

这样,一个发布/订阅/取消事件的例子就完成了。上面只是说明Prism 事件聚合器的使用例子。表示怎么定义事件模型,以及使用事件模型。有了上面的基础后,接着实现根据事件聚合器实现数据加载等待窗口功能。


二.实现数据加载等待窗口功能

当打开待办事项或备忘录数据窗口时,在数据未准备完成前,显示一个加载中的提示消息窗口。

首先是要在MainView 中,定义一个名字:DialogHost

1.然后要在Views 文件夹中定义加载中视图窗口 (ProgressView.xaml)

<UserControl x:Class="MyToDo.Views.ProgressView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MyToDo.Views"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Grid><StackPanel Width="80"><ProgressBar Width="40" Height="40" IsIndeterminate="True" Margin=" 20"Style="{StaticResource MaterialDesignCircularProgressBar}"/></StackPanel></Grid>
</UserControl>

2.定义打开窗口的消息模型

创建一个 Events 文件夹,定义一个 UpdateLoadingEvent (等待加载完成的事件)消息模型。并且定义一个实体类 UpdateModel ,实体类有一个IsOpen属性,表示窗口是否打开状态。把该实体类传入泛型对象PubSubEvent 中。

namespace MyToDo.Common.Events
{public class UpdateModel{public bool IsOpen { get; set; }}public class UpdateLoadingEvent:PubSubEvent<UpdateModel>{}
}

3.创建一个基类 NavigationViewModel

封装一个基类(共用类) NavigationViewModel,该类的主要作用是可以设置窗口的等待,就是它需要等待我们数据加载完成,才能展示出对应的数据窗口。它同时具备导航功能,需要转到对应的数据页面。且需要继承INavigationAware。该基类的方法,增加 virtual,表示它是一个虚方法,表明其他继承基类的子类也能修改基类方法。

创建一个基类(NavigationViewModel)这里,有点绕。就是例如:待办事项页面,需要等待数据加载完成。那么我们就需要把这个待办事项页面设置成可等待窗口,同时显示加载中的效果。所以就需要这么去创建一个基类,同时其他页面也能沿用该基类。

然后在基类构造函数中通过 IOC 容器(IContainerProvider) ,注册事件聚合器,并拿到IEventAggregator 的实例。再通过IEventAggregator 去发布消息,设置页面要展示的加载中的效果。

NavigationViewModel 类

namespace MyToDo.ViewModels
{public class NavigationViewModel : BindableBase, INavigationAware{private readonly IContainerProvider containerProvider;public readonly IEventAggregator aggregator;public NavigationViewModel(IContainerProvider containerProvider){this.containerProvider = containerProvider;aggregator=containerProvider.Resolve<IEventAggregator>();}/// <summary>/// 重用以前窗口/// </summary>/// <param name="navigationContext"></param>/// <returns></returns>public virtual bool IsNavigationTarget(NavigationContext navigationContext){return true;}public virtual void OnNavigatedFrom(NavigationContext navigationContext){}public virtual void OnNavigatedTo(NavigationContext navigationContext){}/// <summary>/// 发布消息/// </summary>/// <param name="IsOpen">是否打开窗口</param>public void UpdateLoading(bool IsOpen){aggregator.UpdateLoading(new Common.Events.UpdateModel(){IsOpen = IsOpen});}}
}

然后接着实现IEventAggregator 事件的发布/订阅 。目前考滤是把发布和订阅,做成一个扩展类,可供NavigationViewModel 或MainView 使用。或其他view中需要使用到发布订阅都可以用到该扩展。

namespace MyToDo.Extensions
{public static class DialogExtensions{/// <summary>/// 发布事件/// </summary>/// <param name="aggregator"></param>/// <param name="model"></param>public static void UpdateLoading(this IEventAggregator aggregator,UpdateModel model){aggregator.GetEvent<UpdateLoadingEvent>().Publish(model);}/// <summary>/// 订阅事件/// </summary>/// <param name="aggregator"></param>/// <param name="model"></param>public static void Resgiter(this IEventAggregator aggregator,Action<UpdateModel> model){aggregator.GetEvent<UpdateLoadingEvent>().Subscribe(model);}}
}

4.发布消息

首先:当前待办事项页面,或备忘录页面,当打开页面时,需要加载数据。那么待办事项类就需要在数据未准备好前,发布一个打开加载中的窗口的一个消息。数据渲染完成后,发布一个关闭加载中的窗口的一个消息。 

待办事项类 (ToDoViewModel)修改。

namespace MyToDo.ViewModels
{public class ToDoViewModel: NavigationViewModel{//由于NavigationViewModel 类构造中传入了 IOC容器,所以当前类继承的时候,需要把对应的参数传通过Base传过去就不会报错了public ToDoViewModel(IToDoService toDoService, IContainerProvider provider):base(provider){ToDoDtos = new ObservableCollection<ToDoDto>();AddCommand = new DelegateCommand(Add);this.toDoService = toDoService;}private bool isRightDrawerOpen;/// <summary>/// 右侧编辑窗口是否展开/// </summary>public bool IsRightDrawerOpen{get { return isRightDrawerOpen; }set { isRightDrawerOpen = value; RaisePropertyChanged(); }}public DelegateCommand AddCommand{ get; private set; }private ObservableCollection<ToDoDto> toDoDtos;private readonly IToDoService toDoService;/// <summary>/// 创建数据的动态集合/// </summary>public ObservableCollection<ToDoDto> ToDoDtos{get { return toDoDtos; }set { toDoDtos = value;RaisePropertyChanged(); }}/// <summary>/// 获取数据/// </summary>async void GetDataAsync(){UpdateLoading(true); //发布消息,设置加载中的窗口//添加查询条件var todoResult=await toDoService.GetAllAsync(new Shared.Parameters.QueryParameter(){PageIndex = 0,PageSize = 100,});if (todoResult.Status){toDoDtos.Clear();foreach (var item in todoResult.Result.Items){toDoDtos.Add(item);}}UpdateLoading(false); //发布消息,关闭加载中的窗口}/// <summary>/// 添加待办/// </summary>/// <exception cref="NotImplementedException"></exception>private void Add(){IsRightDrawerOpen=true;}//重写导航加载数据的方法public override void OnNavigatedTo(NavigationContext navigationContext){base.OnNavigatedTo(navigationContext);GetDataAsync();}}
}

5.MainView 消息订阅

最后:当消息发布后,谁要订阅到这个消息?并进行对应的打开或关闭加载中的窗口逻辑处理?那就是要在 MainView中订阅,传递过来的消息是否要打开加载中的窗口(ProgressView.xaml)或关闭加载中的窗口(ProgressView.xaml)。所以需要在MainView中去实现该功能逻辑

namespace MyToDo.Views
{/// <summary>/// MainView.xaml 的交互逻辑/// </summary>public partial class MainView : Window{public MainView(IEventAggregator aggregator){InitializeComponent();//订阅是否打开或关闭加载中的窗口aggregator.Resgiter(arg =>{DialogHost.IsOpen = arg.IsOpen;//设置打开窗口if (DialogHost.IsOpen){DialogHost.DialogContent = new ProgressView();}});}}
}

三.最后项目结构

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

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

相关文章

【问题】解决1130-Host‘ ‘is not allowed to connect to this MySQL 本地无法连接服务器的数据库

【问题】解决1130-Host‘ ‘is not allowed to connect to this MySQL 本地无法连接服务器的数据库 原因: 默认mysql只允许 localhost 本地访问数据库, 解决方法 将 localhost 改为 % 所有 第一步 回车 输入密码 mysql -u root -p 第二步 切换数据库 use mysql 第三步 更新所…

NL2SQL基础系列(2):主流大模型与微调方法精选集,Text2SQL经典算法技术回顾七年发展脉络梳理

NL2SQL基础系列(2)&#xff1a;主流大模型与微调方法精选集&#xff0c;Text2SQL经典算法技术回顾七年发展脉络梳理 Text-to-SQL&#xff08;或者Text2SQL&#xff09;&#xff0c;顾名思义就是把文本转化为SQL语言&#xff0c;更学术一点的定义是&#xff1a;把数据库领域下的…

国外站群服务器有哪几种?

国外站群服务器种类繁多&#xff0c;它们各具特色&#xff0c;适用于不同的业务需求和场景。以下将为您科普几种常见的国外站群服务器及其特点。 首先&#xff0c;美国站群服务器以其丰富的IP资源和强大的网络技术著称。作为全球网络技术和数据中心发展的领先者&#xff0c;美国…

AI python

AI python 软件方面程序上的人工智能&#xff0c;和物理那种能跑机器人没关系

AI术语大全:AGI、LLM、GenAI、GPT、ChatGPT和AIGC是什么意思?

讲动人的故事,写懂人的代码 自2022年底ChatGPT在全球AI界闪亮登场以后,你是不是经常听到AGI、LLM、GenAI、GPT和AIGC这几个词,但总是分不清它们到底是什么意思? 今天,我就用简单的话来给你讲讲这些词到底是什么意思。 AI,人工智能(Artificial Intelligence),就是让机…

SQLI-labs-第九关和第十关

知识点&#xff1a;时间盲注 思路&#xff1a; 1、判断注入点 首先&#xff0c;我们先看一下注入点&#xff0c;输入?id1 接着输入?id1 # 结果还是没有变化 接着再输入 ?id1" # 结果还是没变 再输入?id-1 结果还是一样 通过这几种测试方法&#xff0c;结果回显都…

24. 【Android教程】适配器 Adapter

本节将会引入一个全新的概念——适配器&#xff0c;这个名字很形象&#xff0c;和电源适配器的功能类似&#xff0c;从程序设计的角度出发&#xff0c;它可以将不同类型、不同结构的数据适配到一起。 在 Android 中&#xff0c;适配器是 UI 组件和数据之间的桥梁&#xff0c;它…

C/C++基础----指针

指针的定义 在c/c中&#xff0c;有一个特殊的变量指向我们电脑中某个内存地址&#xff0c;进而可以让我们操作这段内存&#xff0c;指的就是指针类型 语法&#xff1a; int a 10; int* p &a;&符号是取出某个变量的内存地址 把这个内存地址赋值给一个变量p&#xff…

vscode和pycharm等idea编写protobuf文件格式化

想在pycharm或者goland等idea中开发protobuf文件的话&#xff0c;可以安装一个插件&#xff1a;protocol-buffers 安装之后&#xff0c;proto文件就会支持高亮和格式化了。 如果是vscode想要编写proto文件&#xff0c;可以安装另外一个插件&#xff1a;vscode-proto3 安装后&a…

自定义vue-cli 实现预设模板项目

模板结构 主要包括四个部分&#xff1a; preset.jsonprompts.jsgenerator/index.jstemplate/ 项目最终结构 preset.json preset.json 中是一个包含创建新项目所需预定义选项和插件的 JSON 对象&#xff0c;让用户无需在命令提示中选择它们&#xff0c;简称预设&#xff1b;…

【C 数据结构】栈

文章目录 【 1. 基本原理 】栈的分类 【 2. 动态链表栈 】2.1 双结构体实现2.1.0 栈的节点设计2.1.1 入栈2.1.2 出栈2.1.3 遍历2.1.4 实例 2.2 单结构体实现2.2.0 栈的节点设计2.2.1 入栈2.2.2 出栈2.2.3 实例 【 3. 顺序栈 】3.1 入栈3.2 出栈3.3 实例 【 1. 基本原理 】 栈&…

手机数据恢复工具

下载地址&#xff1a;手机数据恢复工具.zip Android/HarmonyOS 文件误删是日常使用电子设备时经常遇到的问题&#xff0c;也许一不小心就就可能会误删。 俗话说&#xff1a;数据无价&#xff0c;一但想要找回一些被删除的文件&#xff0c;就需要耗费大量的精力和财力来恢复文…