wpf mvvm 用行为以及依赖注入的方式实现导航功能

您所在的位置:网站首页 wpf分页实现 wpf mvvm 用行为以及依赖注入的方式实现导航功能

wpf mvvm 用行为以及依赖注入的方式实现导航功能

2023-03-22 21:38| 来源: 网络整理| 查看: 265

最近在学习MVVM模式,使用的框架是微软的Community toolkit mvvm,但是这个框架好像没有导航的功能,又不想用别的框架,只能自己试着搞一搞,就当作学习了。

简单的学习、研究了下Prism是怎么实现导航之后

Prism 源码解读1-Bootstrapper和Region的创建 - 阿杜聊编程 - 博客园 (cnblogs.com)

大概整理出一个思路,跟Prism有些区别。思路如下:

整体主要借助行为和依赖注入实现;实现过程分为3个部分:

1、往DI容器注册导航目的地Region(主要是ContentControl控件);

2、往DI容器注册被导航目标View(主要是UserControl);

3、实现导航;

对了,要下载3个Nuget包

 

 

 

 

一、往DI容器注册导航目的地Region

  (严格来说,我是先往DI容器里面注册了一个RegionManager区域管理类,这个类里面有一个字典,用于存放作为区域的ContentControl控件,对应的key就是下文中的RegionName)

  (但是这个RegionManager的注册动作是在第二步里面完成的,所以这第一步提到的注册,实际上是先通过DI容器拿到RegionManager之后,往它的字典里面Add成员)

  注册这个动作我是借助行为实现的,1、新建一个行为类 :public class RegionRegisterBehavior : Behavior

  2、添加一个依赖属性RegionName用来设置区域的名字,

  3、重写OnAttached(),为附加方控件增加Loaded事件 :AssociatedObject.Loaded += AssociatedObject_Loaded

  4、在AssociatedObject_Loaded中,通过DI容器拿到RegionManager之后,往它的字典里面Add成员,key就是依赖属性RegionName,Value则是Sender as ContentControl

完整代码如下:

using Microsoft.Xaml.Behaviors; using System.Windows.Controls; using System.Windows; using CommunityToolkit.Mvvm.DependencyInjection; using MyBehavior.BaseClass; using System; namespace MyBehavior.AllBehaviors { /// /// 区域导航功能-注册区域行为:将附加该行为的ContenControl控件添加到RegionManager容器中 /// public class RegionRegisterBehavior : Behavior { /// /// 设置区域名字 /// public string RegionName { get { return (string)GetValue(RegionNameProperty); } set { SetValue(RegionNameProperty, value); } } public static readonly DependencyProperty RegionNameProperty = DependencyProperty.Register("RegionName", typeof(string), typeof(RegionRegisterBehavior)); protected override void OnAttached() { base.OnAttached(); AssociatedObject.Loaded += AssociatedObject_Loaded; } private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { if (AssociatedObject.GetType().Name != "ContentControl") { return; } var a = (sender as ContentControl); if (string.IsNullOrEmpty(RegionName)) { throw new Exception($"区域注册时未设置RegionName"); } Ioc.Default.GetService()?.RegionMaps.TryAdd(RegionName, a); } } }

 

二、往DI容器注册被导航目标View(主要是UserControl)

这一步也是用行为实现,1、新建行为类 NavigateInitializeBehavior : Behavior

2、增加依赖属性 NameOfRegisterView  ===> View的类名(字符串)

3、重写OnAttached(),直接往DI容器里面添加View类单例,这里需要被添加View的Type。主要是通过反射的方法,从程序集里面拿到类名(字符串)对应的Type。

这里的NameOfRegisterView属性,可以同时写多个View类名,用逗号隔开,方便分割;

using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Xaml.Behaviors; using MyBehavior.BaseClass; using System; using System.Windows; namespace MyBehavior.AllBehaviors { /// /// 区域导航功能-初始化行为:往DI容器注册一个或多个视图View,以及注册用于存放Region的RegionManager容器 /// public class NavigateInitializeBehavior : Behavior { /// /// 需导航的视图View的类名,用于注入DI容器 /// 可以同时注册多个View,只需在各自类名中间加上逗号 /// public string NameOfRegisterView { get { return (string)GetValue(NameOfRegisterViewProperty); } set { SetValue(NameOfRegisterViewProperty, value); } } public static readonly DependencyProperty NameOfRegisterViewProperty = DependencyProperty.Register("NameOfRegisterView", typeof(string), typeof(NavigateInitializeBehavior)); protected override void OnAttached() { base.OnAttached(); ServiceCollection services = new ServiceCollection(); services.AddSingleton();//注册用于存放Region的RegionManager容器 if (!string.IsNullOrEmpty(NameOfRegisterView)) { var splitArray = NameOfRegisterView.Split(','); foreach (string item in splitArray) { Type type = CommonTool.GetTypeBaseonName(item); if (type != null) { services.AddSingleton(type); } } } Ioc.Default.ConfigureServices(services.BuildServiceProvider()); } } }

 

三、实现导航

这一步也是用行为实现。新建行为类,添加依赖属性,重写OnAttached,在OnAttached中为控件的Click事件附加函数,在函数中实现导航。

using Microsoft.Xaml.Behaviors; using MyBehavior.BaseClass; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace MyBehavior.AllBehaviors { public class ClickToNavigateBehavior : Behavior { public string TargetRegionName { get { return (string)GetValue(TargetRegionNameProperty); } set { SetValue(TargetRegionNameProperty, value); } } public static readonly DependencyProperty TargetRegionNameProperty = DependencyProperty.Register("TargetRegionName", typeof(string), typeof(ClickToNavigateBehavior)); public string TargetViewName { get { return (string)GetValue(TargetViewNameProperty); } set { SetValue(TargetViewNameProperty, value); } } public static readonly DependencyProperty TargetViewNameProperty = DependencyProperty.Register("TargetViewName", typeof(string), typeof(ClickToNavigateBehavior)); protected override void OnAttached() { base.OnAttached(); AssociatedObject.Click += AssociatedObject_Click; } private void AssociatedObject_Click(object sender, RoutedEventArgs e) { CommonTool.Navigate(TargetRegionName, TargetViewName); } } } 四、工具类:通过类名获取Type、导航步骤实现 using CommunityToolkit.Mvvm.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace MyBehavior.BaseClass { internal static class CommonTool { internal static Type GetTypeBaseonName(string name) { var types = Assembly.GetEntryAssembly()?.GetTypes(); Type? type = null; foreach (var temp in types) { if (temp.Name.Equals(name)) { type = temp; } } return type; } /// /// /// /// /// /// internal static void Navigate(string regionName, string targetViewName) { if (string.IsNullOrEmpty(regionName)) { throw new Exception($"导航动作未设置目标Region"); } if (string.IsNullOrEmpty(targetViewName)) { throw new Exception($"导航动作未设置目标View"); } RegionManager regionCollection = Ioc.Default.GetService() ?? throw new Exception($"DI容器中找不到区域容器RegionManager"); if (regionCollection.RegionMaps.ContainsKey(regionName)) { Type type = GetTypeBaseonName(targetViewName) ?? throw new Exception($"在程序集{Assembly.GetEntryAssembly()}中找不到名为{targetViewName}的类型"); var targetView = Ioc.Default.GetService(type)?? throw new Exception($"DI容器中找不到类型{type.FullName}"); regionCollection.RegionMaps[regionName].Content = targetView; return; } else { throw new Exception($"区域容器中找不到名为{regionName}的区域"); } } } }

还有RegionManager类

using System.Collections.Generic; using System.Windows.Controls; namespace MyBehavior.BaseClass { public class RegionManager : IRegionService { public Dictionary RegionMaps { get; set; } = new(); } }  五、一个简单的例子 1、项目结构

 

 

 2、MainWindow.xaml

加粗部分就是附加行为,从上往下依次是:为window附加 NavigateInitializeBehavior ,注册RegionManager以及UserControl1;

为Button附加ClickToNavigateBehavior,指定导航的区域和视图之外,为该button的click事件附加导航功能;

为区域ContentControl附加RegionRegisterBehavior,往RegionManager的字典中添加名为Region的ContentControl;

3、随便新建一个UserControl1。 4、直接启动

按钮点击前:

 

 按钮点击后:

 

 

 

完成!

可以看出,只需要在xaml中添加一些行为代码,即可实现区域导航,相对来说还是比较方便的,后续再研究如何用更简洁的代码实现。

感谢观看,互相学习,一起进步!

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3