原文:封装:简要介绍自定义开发基于WPF的MVC框架

一、目的:在使用Asp.net Core时,深感MVC框架作为页面跳转数据处理的方便,但WPF中似乎没有现成的MVC框架,由此自定义开发一套MVC的框架,在使用过程中也体会到框架的优势,下面简要介绍一下这套基于MVVM的MVC框架

二、项目结构:

主要有三部分组成:Controller、View、ViewModel

其中View和ViewModel就是传统WPF中的MVVM模式

不同地方在于页面的跳转应用到了Controller做控制,如下示例Controller的定义

二、Controller的结构和定义

1、定义LoyoutController


  1. [Route("Loyout")]
  2. class LoyoutController : Controller
  3. {
  4. public LoyoutController(ShareViewModel shareViewModel) : base(shareViewModel)
  5. {
  6. }
  7. public async Task<IActionResult> Center()
  8. {
  9. return View();
  10. }
  11. [Route("OverView/Button")]
  12. public async Task<IActionResult> Mdi()
  13. {
  14. return View();
  15. }
  16. public async Task<IActionResult> Left()
  17. {
  18. return View();
  19. }
  20. public async Task<IActionResult> Right()
  21. {
  22. return View();
  23. }
  24. public async Task<IActionResult> Top()
  25. {
  26. return View();
  27. }
  28. public async Task<IActionResult> Bottom()
  29. {
  30. return View();
  31. }
  32. [Route("OverView/Toggle")]
  33. public async Task<IActionResult> Toggle()
  34. {
  35. return View();
  36. }
  37. [Route("OverView/Carouse")]
  38. public async Task<IActionResult> Carouse()
  39. {
  40. return View();
  41. }
  42. [Route("OverView/Evaluate")]
  43. public async Task<IActionResult> Evaluate()
  44. {
  45. return View();
  46. }
  47. [Route("OverView/Expander")]
  48. public async Task<IActionResult> Expander()
  49. {
  50. return View();
  51. }
  52. [Route("OverView/Gif")]
  53. public async Task<IActionResult> Gif()
  54. {
  55. return View();
  56. }
  57. [Route("OverView/Message")]
  58. public async Task<IActionResult> Message()
  59. {
  60. return View();
  61. }
  62. [Route("OverView/Upgrade")]
  63. public async Task<IActionResult> Upgrade()
  64. {
  65. return View();
  66. }
  67. [Route("OverView/Property")]
  68. public async Task<IActionResult> Property()
  69. {
  70. return View();
  71. }
  72. [Route("OverView/ProgressBar")]
  73. public async Task<IActionResult> ProgressBar()
  74. {
  75. return View();
  76. }
  77. [Route("OverView/Slider")]
  78. public async Task<IActionResult> Slider()
  79. {
  80. return View();
  81. }
  82. [Route("OverView/Tab")]
  83. public async Task<IActionResult> Tab()
  84. {
  85. return View();
  86. }
  87. [Route("OverView/Tree")]
  88. public async Task<IActionResult> Tree()
  89. {
  90. return View();
  91. }
  92. [Route("OverView/Observable")]
  93. public async Task<IActionResult> Observable()
  94. {
  95. return View();
  96. }
  97. [Route("OverView/Brush")]
  98. public async Task<IActionResult> Brush()
  99. {
  100. return View();
  101. }
  102. [Route("OverView/Shadow")]
  103. public async Task<IActionResult> Shadow()
  104. {
  105. return View();
  106. }
  107. [Route("OverView/Button")]
  108. public async Task<IActionResult> Button()
  109. {
  110. await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));
  111. this.ViewModel.ButtonContentText = DateTime.Now.ToString();
  112. return View();
  113. }
  114. [Route("OverView/Grid")]
  115. public async Task<IActionResult> Grid()
  116. {
  117. return View();
  118. }
  119. [Route("OverView/Combobox")]
  120. public async Task<IActionResult> Combobox()
  121. {
  122. return View();
  123. }
  124. [Route("OverView")]
  125. public async Task<IActionResult> OverView()
  126. {
  127. await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));
  128. MessageService.ShowSnackMessageWithNotice("OverView");
  129. return View();
  130. }
  131. [Route("OverView/TextBox")]
  132. public async Task<IActionResult> TextBox()
  133. {
  134. return View();
  135. }
  136. [Route("OverView/Book")]
  137. public async Task<IActionResult> Book()
  138. {
  139. return View();
  140. }
  141. [Route("OverView/Xaml")]
  142. public async Task<IActionResult> Xaml()
  143. {
  144. return View();
  145. }
  146. [Route("OverView/Dimension")]
  147. public async Task<IActionResult> Dimension()
  148. {
  149. return View();
  150. }
  151. [Route("OverView/Geometry")]
  152. public async Task<IActionResult> Geometry()
  153. {
  154. return View();
  155. }
  156. [Route("OverView/Panel")]
  157. public async Task<IActionResult> Panel()
  158. {
  159. return View();
  160. }
  161. [Route("OverView/Transform3D")]
  162. public async Task<IActionResult> Transform3D()
  163. {
  164. return View();
  165. }
  166. [Route("OverView/Drawer")]
  167. public async Task<IActionResult> Drawer()
  168. {
  169. return View();
  170. }
  171. }

2、前端的页面

如下

其中红色部分对应Controller里面的要跳转的Route

如:选择了红色部分的Button,首先会调用Button()方法,跳转到当前Controller对应的View文件加下的ButtonControl.xaml页面

[Route("OverView/Button")]

        public async Task<IActionResult> Button()

        {

            await MessageService.ShowWaittingMessge(() => Thread.Sleep(500)

;

this.ViewModel.ButtonContentText = DateTime.Now.ToString();

return View();

}

可以在Button()方法中,写一些业务逻辑,如对当前ViewModel的增删改查等常规操作,其中当前Controller成员ViewModel是内部封装好的ViewModel,对应ViewModel文件下面的当前Controller的ViewModel

3、示例:

4、左侧的Xaml列表可以定义成如下形式:


  1. <Grid>
  2. <wpfcontrollib:LinkGroupExpander ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="selectloyout"
  3. SelectedLink="{Binding SelectLink,Mode=TwoWay}"
  4. Command="{x:Static wpfcontrollib:DrawerHost.CloseDrawerCommand}"
  5. CommandParameter="{x:Static Dock.Left}">
  6. <wpfcontrollib:LinkActionGroup DisplayName="基础控件" Logo="">
  7. <wpfcontrollib:LinkActionGroup.Links>
  8. <wpfcontrollib:LinkAction DisplayName="Button" Logo="" Controller="Loyout" Action="Button" />
  9. <wpfcontrollib:LinkAction DisplayName="TextBox" Logo="" Controller="Loyout" Action="TextBox"/>
  10. <wpfcontrollib:LinkAction DisplayName="Combobox" Logo="" Controller="Loyout" Action="Combobox" />
  11. <wpfcontrollib:LinkAction DisplayName="Toggle" Logo="" Controller="Loyout" Action="Toggle"/>
  12. <wpfcontrollib:LinkAction DisplayName="Evaluate" Logo="" Controller="Loyout" Action="Evaluate"/>
  13. <wpfcontrollib:LinkAction DisplayName="Expander" Logo="" Controller="Loyout" Action="Expander"/>
  14. <wpfcontrollib:LinkAction DisplayName="Gif" Logo="" Controller="Loyout" Action="Gif"/>
  15. <wpfcontrollib:LinkAction DisplayName="ProgressBar" Logo="" Controller="Loyout" Action="ProgressBar"/>
  16. <wpfcontrollib:LinkAction DisplayName="Slider" Logo="" Controller="Loyout" Action="Slider"/>
  17. </wpfcontrollib:LinkActionGroup.Links>
  18. </wpfcontrollib:LinkActionGroup>
  19. <wpfcontrollib:LinkActionGroup DisplayName="布局控件" Logo="">
  20. <wpfcontrollib:LinkActionGroup.Links>
  21. <wpfcontrollib:LinkAction DisplayName="MdiControl" Logo="" Controller="Loyout" Action="Mdi"/>
  22. <wpfcontrollib:LinkAction DisplayName="Carouse" Logo="" Controller="Loyout" Action="Carouse"/>
  23. <wpfcontrollib:LinkAction DisplayName="Tab" Logo="" Controller="Loyout" Action="Tab"/>
  24. <wpfcontrollib:LinkAction DisplayName="Tree" Logo="" Controller="Loyout" Action="Tree"/>
  25. <wpfcontrollib:LinkAction DisplayName="ObservableSource" Logo="" Controller="Loyout" Action="Observable"/>
  26. <wpfcontrollib:LinkAction DisplayName="Property" Logo="" Controller="Loyout" Action="Property"/>
  27. <wpfcontrollib:LinkAction DisplayName="Panel" Logo="" Controller="Loyout" Action="Panel"/>
  28. </wpfcontrollib:LinkActionGroup.Links>
  29. </wpfcontrollib:LinkActionGroup>
  30. <wpfcontrollib:LinkActionGroup DisplayName="全局控件" Logo="">
  31. <wpfcontrollib:LinkActionGroup.Links>
  32. <wpfcontrollib:LinkAction DisplayName="Message" Logo="" Controller="Loyout" Action="Message"/>
  33. <wpfcontrollib:LinkAction DisplayName="Upgrade" Logo="" Controller="Loyout" Action="Upgrade"/>
  34. <wpfcontrollib:LinkAction DisplayName="Drawer" Logo="" Controller="Loyout" Action="Drawer"/>
  35. </wpfcontrollib:LinkActionGroup.Links>
  36. </wpfcontrollib:LinkActionGroup>
  37. <wpfcontrollib:LinkActionGroup DisplayName="全局样式" Logo="">
  38. <wpfcontrollib:LinkActionGroup.Links>
  39. <wpfcontrollib:LinkAction DisplayName="Brush" Logo="" Controller="Loyout" Action="Brush"/>
  40. <wpfcontrollib:LinkAction DisplayName="Shadow" Logo="" Controller="Loyout" Action="Shadow"/>
  41. </wpfcontrollib:LinkActionGroup.Links>
  42. </wpfcontrollib:LinkActionGroup>
  43. </wpfcontrollib:LinkGroupExpander>
  44. </Grid>

通过LinkGroupExpander控件,封装LinkAction去实现页面的跳转,其中只需要定义LinkAction的几个属性即可达到跳转到指定页面的效果,如:

Controller属性:用来指示要跳转到哪个Controller

Action属性:用来指示跳转到哪个方法

DisplayName属性:在UI中显示的名称

Logo属性:在UI中显示的图标

如下,Controller中的Button()方法对应的跳转配置如下

[Route("OverView/Button")]

public async Task<IActionResult> Button()

<wpfcontrollib:LinkAction DisplayName="Button" Logo=""  Controller="Loyout" Action="Button" />

4、Controller基类的定义ControllerBase

主要方法是IActionResult View([CallerMemberName] string name = ""),这个方法是MVC实现的核心功能,主要通过反射去动态加载程序集,加载项目结构中的View、ViewModel去生成IActionResult返回给主页面进行页面跳转,代码如下:


  1. public abstract class ControllerBase : IController
  2. {
  3. protected virtual IActionResult View([CallerMemberName] string name = "")
  4. {
  5. var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
  6. string controlName = null;
  7. if (route.FirstOrDefault() == null)
  8. {
  9. controlName = this.GetType().Name;
  10. }
  11. else
  12. {
  13. controlName = route.FirstOrDefault().Name;
  14. }
  15. var ass = Assembly.GetEntryAssembly().GetName();
  16. string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";
  17. Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
  18. var content = Application.Current.Dispatcher.Invoke(() =>
  19. {
  20. return Application.LoadComponent(uri);
  21. });
  22. ActionResult result = new ActionResult();
  23. result.Uri = uri;
  24. result.View = content as ContentControl;
  25. Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");
  26. result.ViewModel = ServiceRegistry.Instance.GetInstance(type);
  27. Application.Current.Dispatcher.Invoke(() =>
  28. {
  29. (result.View as FrameworkElement).DataContext = result.ViewModel;
  30. });
  31. return result;
  32. }
  33. protected virtual IActionResult LinkAction([CallerMemberName] string name = "")
  34. {
  35. var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
  36. string controlName = null;
  37. if (route.FirstOrDefault() == null)
  38. {
  39. controlName = this.GetType().Name;
  40. }
  41. else
  42. {
  43. controlName = route.FirstOrDefault().Name;
  44. }
  45. var ass = Assembly.GetEntryAssembly().GetName();
  46. string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";
  47. Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
  48. var content = Application.Current.Dispatcher.Invoke(() =>
  49. {
  50. return Application.LoadComponent(uri);
  51. });
  52. ActionResult result = new ActionResult();
  53. result.Uri = uri;
  54. result.View = content;
  55. Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");
  56. result.ViewModel = ServiceRegistry.Instance.GetInstance(type);
  57. Application.Current.Dispatcher.Invoke(() =>
  58. {
  59. (result.View as FrameworkElement).DataContext = result.ViewModel;
  60. });
  61. return result;
  62. }
  63. }

说明:

1、通过Application.LoadComponent(uri);来加载生成Control

2、通过反射ViewModel基类NotifyPropertyChanged去找到对应ViewModel,绑定到View中

3、将View和ViewModel封装到IActionResult中返回给主页面进行加载

其中Controller中的方法返回类型是async Task<IActionResult>,也就是整个页面跳转都是在异步中进行的,可以有效的避免页面切换中的卡死效果

三、View中的结构和定义

其中View在项目中的定义就是根据Controller中的方法对应,在MVC中要严格按照结构定义[View/Loyout],好处是可以减少代码量,同时使格式统一代码整齐,结构如下:



其中红色ButtonControl.xaml即是Controller中Button()方法要跳转的页面,其他页面同理

四、ViewModel的结构和定义

其中LoyoutViewModel即是LoyoutController和整个View/Loyout下所有页面对应的ViewModel

五、整体MVC结构实现的效果如下

以上就是MVC应用在WPF中的简要示例,具体内容和示例可从如下链接中下载代码查看

代码地址:https://github.com/HeBianGu/WPF-ControlBase.git

另一个应用Sqlite数据库的示例如下

代码地址:https://github.com/HeBianGu/WPF-ExplorerManager.git

封装:简要介绍自定义开发基于WPF的MVC框架的更多相关文章

  1. .NET6: 开发基于WPF的摩登三维工业软件 (2)

    在<.NET6: 开发基于WPF的摩登三维工业软件 (1)>我们创建了一个"毛坯"界面,距离摩登还差一段距离.本文将对上一阶段的成果进行深化,实现当下流行的暗黑风格UI ...

  2. .NET6: 开发基于WPF的摩登三维工业软件 (7)

    做为一个摩登的工业软件,提供可编程的脚本能力是必不可少的能力.脚本既可以方便用户进行二次开发,也对方便对程序进行自动化测试.本文将结合AnyCAD对Python脚本支持的能力和WPF快速开发带脚本编辑 ...

  3. .NET6: 开发基于WPF的摩登三维工业软件 (8) - MVVM

    基于WPF开发界面的一个很大优势是可以方便地基于MVVM设计模式开发应用.本文从应用的角度基于MVVM实现参数化管材的创建界面. 1 MVVM MVVM是Model-View-ViewModel的简写 ...

  4. Unity容器的简单AOP与DI的应用Demo(基于asp.net mvc框架)

    转发请注明出处:https://home.cnblogs.com/u/zhiyong-ITNote/ 整个Demo是基于Controller-Service-Repository架构设计的,每一层之间 ...

  5. 开发自己的PHP MVC框架(一)

    这个教程能够使大家掌握用mvc模式开发php应用的基本概念.此教程分为三个部分.如今这篇是第一部分. 如今市面上有非常多流行的框架供大家使用.可是我们也能够自己动手开发一个mvc框架.採用mvc模式能 ...

  6. .NET6: 开发基于WPF的摩登三维工业软件 (10) - 机器人

    基于前文介绍的Ribbon界面.插件化.MVVM模式等内容,我们搭建了一个软件雏形.本文将综合之前的内容在RapidCAX框架中集成Robot组件,实现一个简单的机器人正向模拟模块. 1 目标 基于M ...

  7. .NET6: 开发基于WPF的摩登三维工业软件

    MS Office和VisualStudio一直引领着桌面应用的时尚潮流,大型的工业软件一般都会紧跟潮流,搭配着Ribbon和DockPanel风格的界面.本文将介绍WPF下两个轻量级的Ribbon和 ...

  8. 接口开发-基于SpringBoot创建基础框架

    说到接口开发,能想到的开发语言有很多种,像什么Java啊..NET啊.PHP啊.NodeJS啊,太多可以用.为什么选择Java,究其原因,最后只有一个解释,那就是“学Java的人多,人员招聘范围大,有 ...

  9. 基于C#语言MVC框架NPOI控件导出Excel表数据

    控件bin文件下载地址:https://download.csdn.net/download/u012949335/10610726@{ ViewBag.Title = "dcxx" ...

随机推荐

  1. 3.redis 都有哪些数据类型?分别在哪些场景下使用比较合适?

    作者:中华石杉 面试题 redis 都有哪些数据类型?分别在哪些场景下使用比较合适? 面试官心理分析 除非是面试官感觉看你简历,是工作 3 年以内的比较初级的同学,可能对技术没有很深入的研究,面试官才 ...

  2. (原+修改)ubuntu上离线安装pytorch

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/12000809.html 参考网址: https://blog.csdn.net/qq_4193655 ...

  3. HLOJ1366 Candy Box 动态规划(0-1背包改)

    题目描述: 给出N个盒子(N<=100),每个盒子有一定数量的糖果(每个盒子的糖果数<=100),现在有q次查询,每次查询给出两个数k,m,问的是,如果从N个盒子中最多打开k个盒子(意思是 ...

  4. Gym - 102056C(2018EC final) -Heretical … Möbius ——CRT

    题意 给出一个长为200的01序列,判断是否在前1e9个莫比乌斯*值中.(这里的莫比乌斯值加了绝对值) 分析 意到因为4的倍数一定是0,9的倍数一定是0……169的倍数一定是0.那么我们可以对4,9, ...

  5. Mongo 服务器的安装

    MongoDB的安装 CentOS 中使用yum安装: touch /etc/yum.repos.d/mongodb-org-4.2.repo vim /etc/yum.repos.d/mongodb ...

  6. ZROI 暑期高端峰会 A班 Day3 图论

    最短路 NOI2019 D2T1 弹跳 KD 树 线段树套set -> 线段树套并查集? POI2014/2015 ??? \(n\) 个点 \(m\) 条边 DAG.求删掉每个点后 \(1\) ...

  7. K8s集群认证之RBAC

    kubernetes认证,授权概括总结: RBAC简明总结摘要:API Server认证授权过程: subject(主体)----->认证----->授权[action(可做什么)]--- ...

  8. IDEA Gradle配置与使用

    1.安装Gradle,并添加环境变量. https://www.cnblogs.com/NyanKoSenSei/p/11458953.html 2.在IDEA中设置Gradle: 3.选中项目中的. ...

  9. [技术博客] JS正则活学活用

    正则基本语法 正则表达式(Regular Expression)是用单字符串来匹配一系列复合条件字符串的模式,对于乔姆斯基3型语法. 数学定义: 串行AB表示集合 {αβ | α ∈ A ,β ∈ B ...

  10. Hyperledger Fabric 1.4 快速环境搭建

    自己的硕士研究方向和区块链有关,工程上一直以IBM的Hyperledger Fabric为基础进行开发,对该项目关注也有两年了.目前迎来了Hyperledger Fabric v1.4,这也是Fabr ...