封装:简要介绍自定义开发基于WPF的MVC框架
一、目的:在使用Asp.net Core时,深感MVC框架作为页面跳转数据处理的方便,但WPF中似乎没有现成的MVC框架,由此自定义开发一套MVC的框架,在使用过程中也体会到框架的优势,下面简要介绍一下这套基于MVVM的MVC框架
二、项目结构:
主要有三部分组成:Controller、View、ViewModel
其中View和ViewModel就是传统WPF中的MVVM模式
不同地方在于页面的跳转应用到了Controller做控制,如下示例Controller的定义
二、Controller的结构和定义
1、定义LoyoutController
-
[Route("Loyout")]
-
class LoyoutController : Controller
-
{
-
-
public LoyoutController(ShareViewModel shareViewModel) : base(shareViewModel)
-
{
-
-
}
-
-
public async Task<IActionResult> Center()
-
{
-
return View();
-
}
-
-
[Route("OverView/Button")]
-
public async Task<IActionResult> Mdi()
-
{
-
return View();
-
}
-
-
public async Task<IActionResult> Left()
-
{
-
return View();
-
}
-
-
public async Task<IActionResult> Right()
-
{
-
return View();
-
}
-
-
public async Task<IActionResult> Top()
-
{
-
return View();
-
}
-
-
public async Task<IActionResult> Bottom()
-
{
-
return View();
-
}
-
-
[Route("OverView/Toggle")]
-
public async Task<IActionResult> Toggle()
-
{
-
return View();
-
}
-
-
[Route("OverView/Carouse")]
-
public async Task<IActionResult> Carouse()
-
{
-
return View();
-
}
-
-
[Route("OverView/Evaluate")]
-
public async Task<IActionResult> Evaluate()
-
{
-
return View();
-
}
-
-
[Route("OverView/Expander")]
-
public async Task<IActionResult> Expander()
-
{
-
return View();
-
}
-
-
[Route("OverView/Gif")]
-
public async Task<IActionResult> Gif()
-
{
-
return View();
-
}
-
-
[Route("OverView/Message")]
-
public async Task<IActionResult> Message()
-
{
-
return View();
-
}
-
-
[Route("OverView/Upgrade")]
-
public async Task<IActionResult> Upgrade()
-
{
-
return View();
-
}
-
-
[Route("OverView/Property")]
-
public async Task<IActionResult> Property()
-
{
-
return View();
-
}
-
-
[Route("OverView/ProgressBar")]
-
public async Task<IActionResult> ProgressBar()
-
{
-
return View();
-
}
-
-
[Route("OverView/Slider")]
-
public async Task<IActionResult> Slider()
-
{
-
return View();
-
}
-
-
[Route("OverView/Tab")]
-
public async Task<IActionResult> Tab()
-
{
-
return View();
-
}
-
-
[Route("OverView/Tree")]
-
public async Task<IActionResult> Tree()
-
{
-
return View();
-
}
-
-
[Route("OverView/Observable")]
-
public async Task<IActionResult> Observable()
-
{
-
return View();
-
}
-
-
[Route("OverView/Brush")]
-
public async Task<IActionResult> Brush()
-
{
-
return View();
-
}
-
-
[Route("OverView/Shadow")]
-
public async Task<IActionResult> Shadow()
-
{
-
return View();
-
}
-
-
[Route("OverView/Button")]
-
public async Task<IActionResult> Button()
-
{
-
await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));
-
-
this.ViewModel.ButtonContentText = DateTime.Now.ToString();
-
-
return View();
-
-
}
-
-
-
-
[Route("OverView/Grid")]
-
public async Task<IActionResult> Grid()
-
{
-
return View();
-
}
-
-
[Route("OverView/Combobox")]
-
public async Task<IActionResult> Combobox()
-
{
-
return View();
-
}
-
-
[Route("OverView")]
-
public async Task<IActionResult> OverView()
-
{
-
await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));
-
-
MessageService.ShowSnackMessageWithNotice("OverView");
-
-
return View();
-
}
-
-
[Route("OverView/TextBox")]
-
public async Task<IActionResult> TextBox()
-
{
-
return View();
-
}
-
-
[Route("OverView/Book")]
-
public async Task<IActionResult> Book()
-
{
-
return View();
-
}
-
-
[Route("OverView/Xaml")]
-
public async Task<IActionResult> Xaml()
-
{
-
return View();
-
}
-
-
[Route("OverView/Dimension")]
-
public async Task<IActionResult> Dimension()
-
{
-
return View();
-
}
-
-
[Route("OverView/Geometry")]
-
public async Task<IActionResult> Geometry()
-
{
-
return View();
-
}
-
-
[Route("OverView/Panel")]
-
public async Task<IActionResult> Panel()
-
{
-
return View();
-
}
-
[Route("OverView/Transform3D")]
-
public async Task<IActionResult> Transform3D()
-
{
-
return View();
-
}
-
-
[Route("OverView/Drawer")]
-
public async Task<IActionResult> Drawer()
-
{
-
return View();
-
}
-
}
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列表可以定义成如下形式:
-
<Grid>
-
<wpfcontrollib:LinkGroupExpander ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="selectloyout"
-
SelectedLink="{Binding SelectLink,Mode=TwoWay}"
-
Command="{x:Static wpfcontrollib:DrawerHost.CloseDrawerCommand}"
-
CommandParameter="{x:Static Dock.Left}">
-
<wpfcontrollib:LinkActionGroup DisplayName="基础控件" Logo="">
-
<wpfcontrollib:LinkActionGroup.Links>
-
<wpfcontrollib:LinkAction DisplayName="Button" Logo="" Controller="Loyout" Action="Button" />
-
<wpfcontrollib:LinkAction DisplayName="TextBox" Logo="" Controller="Loyout" Action="TextBox"/>
-
<wpfcontrollib:LinkAction DisplayName="Combobox" Logo="" Controller="Loyout" Action="Combobox" />
-
<wpfcontrollib:LinkAction DisplayName="Toggle" Logo="" Controller="Loyout" Action="Toggle"/>
-
<wpfcontrollib:LinkAction DisplayName="Evaluate" Logo="" Controller="Loyout" Action="Evaluate"/>
-
<wpfcontrollib:LinkAction DisplayName="Expander" Logo="" Controller="Loyout" Action="Expander"/>
-
<wpfcontrollib:LinkAction DisplayName="Gif" Logo="" Controller="Loyout" Action="Gif"/>
-
<wpfcontrollib:LinkAction DisplayName="ProgressBar" Logo="" Controller="Loyout" Action="ProgressBar"/>
-
<wpfcontrollib:LinkAction DisplayName="Slider" Logo="" Controller="Loyout" Action="Slider"/>
-
</wpfcontrollib:LinkActionGroup.Links>
-
</wpfcontrollib:LinkActionGroup>
-
-
<wpfcontrollib:LinkActionGroup DisplayName="布局控件" Logo="">
-
<wpfcontrollib:LinkActionGroup.Links>
-
<wpfcontrollib:LinkAction DisplayName="MdiControl" Logo="" Controller="Loyout" Action="Mdi"/>
-
<wpfcontrollib:LinkAction DisplayName="Carouse" Logo="" Controller="Loyout" Action="Carouse"/>
-
<wpfcontrollib:LinkAction DisplayName="Tab" Logo="" Controller="Loyout" Action="Tab"/>
-
<wpfcontrollib:LinkAction DisplayName="Tree" Logo="" Controller="Loyout" Action="Tree"/>
-
<wpfcontrollib:LinkAction DisplayName="ObservableSource" Logo="" Controller="Loyout" Action="Observable"/>
-
<wpfcontrollib:LinkAction DisplayName="Property" Logo="" Controller="Loyout" Action="Property"/>
-
<wpfcontrollib:LinkAction DisplayName="Panel" Logo="" Controller="Loyout" Action="Panel"/>
-
</wpfcontrollib:LinkActionGroup.Links>
-
-
</wpfcontrollib:LinkActionGroup>
-
-
<wpfcontrollib:LinkActionGroup DisplayName="全局控件" Logo="">
-
<wpfcontrollib:LinkActionGroup.Links>
-
<wpfcontrollib:LinkAction DisplayName="Message" Logo="" Controller="Loyout" Action="Message"/>
-
<wpfcontrollib:LinkAction DisplayName="Upgrade" Logo="" Controller="Loyout" Action="Upgrade"/>
-
<wpfcontrollib:LinkAction DisplayName="Drawer" Logo="" Controller="Loyout" Action="Drawer"/>
-
</wpfcontrollib:LinkActionGroup.Links>
-
</wpfcontrollib:LinkActionGroup>
-
-
<wpfcontrollib:LinkActionGroup DisplayName="全局样式" Logo="">
-
<wpfcontrollib:LinkActionGroup.Links>
-
<wpfcontrollib:LinkAction DisplayName="Brush" Logo="" Controller="Loyout" Action="Brush"/>
-
<wpfcontrollib:LinkAction DisplayName="Shadow" Logo="" Controller="Loyout" Action="Shadow"/>
-
-
</wpfcontrollib:LinkActionGroup.Links>
-
</wpfcontrollib:LinkActionGroup>
-
-
</wpfcontrollib:LinkGroupExpander>
-
</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返回给主页面进行页面跳转,代码如下:
-
public abstract class ControllerBase : IController
-
{
-
protected virtual IActionResult View([CallerMemberName] string name = "")
-
{
-
var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
-
-
string controlName = null;
-
-
if (route.FirstOrDefault() == null)
-
{
-
controlName = this.GetType().Name;
-
}
-
else
-
{
-
controlName = route.FirstOrDefault().Name;
-
}
-
-
var ass = Assembly.GetEntryAssembly().GetName();
-
-
string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";
-
-
Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
-
-
var content = Application.Current.Dispatcher.Invoke(() =>
-
{
-
return Application.LoadComponent(uri);
-
});
-
-
ActionResult result = new ActionResult();
-
-
result.Uri = uri;
-
result.View = content as ContentControl;
-
-
Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");
-
-
result.ViewModel = ServiceRegistry.Instance.GetInstance(type);
-
-
Application.Current.Dispatcher.Invoke(() =>
-
{
-
(result.View as FrameworkElement).DataContext = result.ViewModel;
-
-
});
-
-
return result;
-
}
-
-
-
protected virtual IActionResult LinkAction([CallerMemberName] string name = "")
-
{
-
var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
-
-
string controlName = null;
-
-
if (route.FirstOrDefault() == null)
-
{
-
controlName = this.GetType().Name;
-
}
-
else
-
{
-
controlName = route.FirstOrDefault().Name;
-
}
-
-
var ass = Assembly.GetEntryAssembly().GetName();
-
-
string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";
-
-
Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
-
-
var content = Application.Current.Dispatcher.Invoke(() =>
-
{
-
return Application.LoadComponent(uri);
-
});
-
-
ActionResult result = new ActionResult();
-
-
result.Uri = uri;
-
result.View = content;
-
-
Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");
-
-
result.ViewModel = ServiceRegistry.Instance.GetInstance(type);
-
-
Application.Current.Dispatcher.Invoke(() =>
-
{
-
(result.View as FrameworkElement).DataContext = result.ViewModel;
-
});
-
-
return result;
-
}
-
-
}
说明:
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框架的更多相关文章
- .NET6: 开发基于WPF的摩登三维工业软件 (2)
在<.NET6: 开发基于WPF的摩登三维工业软件 (1)>我们创建了一个"毛坯"界面,距离摩登还差一段距离.本文将对上一阶段的成果进行深化,实现当下流行的暗黑风格UI ...
- .NET6: 开发基于WPF的摩登三维工业软件 (7)
做为一个摩登的工业软件,提供可编程的脚本能力是必不可少的能力.脚本既可以方便用户进行二次开发,也对方便对程序进行自动化测试.本文将结合AnyCAD对Python脚本支持的能力和WPF快速开发带脚本编辑 ...
- .NET6: 开发基于WPF的摩登三维工业软件 (8) - MVVM
基于WPF开发界面的一个很大优势是可以方便地基于MVVM设计模式开发应用.本文从应用的角度基于MVVM实现参数化管材的创建界面. 1 MVVM MVVM是Model-View-ViewModel的简写 ...
- Unity容器的简单AOP与DI的应用Demo(基于asp.net mvc框架)
转发请注明出处:https://home.cnblogs.com/u/zhiyong-ITNote/ 整个Demo是基于Controller-Service-Repository架构设计的,每一层之间 ...
- 开发自己的PHP MVC框架(一)
这个教程能够使大家掌握用mvc模式开发php应用的基本概念.此教程分为三个部分.如今这篇是第一部分. 如今市面上有非常多流行的框架供大家使用.可是我们也能够自己动手开发一个mvc框架.採用mvc模式能 ...
- .NET6: 开发基于WPF的摩登三维工业软件 (10) - 机器人
基于前文介绍的Ribbon界面.插件化.MVVM模式等内容,我们搭建了一个软件雏形.本文将综合之前的内容在RapidCAX框架中集成Robot组件,实现一个简单的机器人正向模拟模块. 1 目标 基于M ...
- .NET6: 开发基于WPF的摩登三维工业软件
MS Office和VisualStudio一直引领着桌面应用的时尚潮流,大型的工业软件一般都会紧跟潮流,搭配着Ribbon和DockPanel风格的界面.本文将介绍WPF下两个轻量级的Ribbon和 ...
- 接口开发-基于SpringBoot创建基础框架
说到接口开发,能想到的开发语言有很多种,像什么Java啊..NET啊.PHP啊.NodeJS啊,太多可以用.为什么选择Java,究其原因,最后只有一个解释,那就是“学Java的人多,人员招聘范围大,有 ...
- 基于C#语言MVC框架NPOI控件导出Excel表数据
控件bin文件下载地址:https://download.csdn.net/download/u012949335/10610726@{ ViewBag.Title = "dcxx" ...
随机推荐
- 3.redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
作者:中华石杉 面试题 redis 都有哪些数据类型?分别在哪些场景下使用比较合适? 面试官心理分析 除非是面试官感觉看你简历,是工作 3 年以内的比较初级的同学,可能对技术没有很深入的研究,面试官才 ...
- (原+修改)ubuntu上离线安装pytorch
转载请注明出处: https://www.cnblogs.com/darkknightzh/p/12000809.html 参考网址: https://blog.csdn.net/qq_4193655 ...
- HLOJ1366 Candy Box 动态规划(0-1背包改)
题目描述: 给出N个盒子(N<=100),每个盒子有一定数量的糖果(每个盒子的糖果数<=100),现在有q次查询,每次查询给出两个数k,m,问的是,如果从N个盒子中最多打开k个盒子(意思是 ...
- Gym - 102056C(2018EC final) -Heretical … Möbius ——CRT
题意 给出一个长为200的01序列,判断是否在前1e9个莫比乌斯*值中.(这里的莫比乌斯值加了绝对值) 分析 意到因为4的倍数一定是0,9的倍数一定是0……169的倍数一定是0.那么我们可以对4,9, ...
- Mongo 服务器的安装
MongoDB的安装 CentOS 中使用yum安装: touch /etc/yum.repos.d/mongodb-org-4.2.repo vim /etc/yum.repos.d/mongodb ...
- ZROI 暑期高端峰会 A班 Day3 图论
最短路 NOI2019 D2T1 弹跳 KD 树 线段树套set -> 线段树套并查集? POI2014/2015 ??? \(n\) 个点 \(m\) 条边 DAG.求删掉每个点后 \(1\) ...
- K8s集群认证之RBAC
kubernetes认证,授权概括总结: RBAC简明总结摘要:API Server认证授权过程: subject(主体)----->认证----->授权[action(可做什么)]--- ...
- IDEA Gradle配置与使用
1.安装Gradle,并添加环境变量. https://www.cnblogs.com/NyanKoSenSei/p/11458953.html 2.在IDEA中设置Gradle: 3.选中项目中的. ...
- [技术博客] JS正则活学活用
正则基本语法 正则表达式(Regular Expression)是用单字符串来匹配一系列复合条件字符串的模式,对于乔姆斯基3型语法. 数学定义: 串行AB表示集合 {αβ | α ∈ A ,β ∈ B ...
- Hyperledger Fabric 1.4 快速环境搭建
自己的硕士研究方向和区块链有关,工程上一直以IBM的Hyperledger Fabric为基础进行开发,对该项目关注也有两年了.目前迎来了Hyperledger Fabric v1.4,这也是Fabr ...