wpf mvvm 用行为以及依赖注入的方式实现导航功能 |
您所在的位置:网站首页 › wpf分页实现 › wpf mvvm 用行为以及依赖注入的方式实现导航功能 |
最近在学习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 |