封装:简要介绍自定义开发基于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" ...
随机推荐
- 【学习笔记】PYTHON语言程序设计(北理工 嵩天)
1 Python基本语法元素 1.1 程序设计基本方法 计算机发展历史上最重要的预测法则 摩尔定律:单位面积集成电路上可容纳晶体管数量约2年翻倍 cpu/gpu.内存.硬盘.电子产品价格等都遵 ...
- MySQL基础:DCL语句总结
SQL语言大致分为DCL.DDL.DML三种,本文主要介绍MySQL 5.7版本DCL语句. 概述 DCL(Data Control Language)语句:数据控制语句,用于控制不同数据段直接的许可 ...
- jmeter+jenkins配置过程
目录 前置条件 脚本规划 讲解非GUI方式运行脚本 在jenkins里面新建项目 在Jmeter的jtl结果文件转换成html页面 邮件.配置 前置条件 1.jmeter安装好并已配置好环境变量,可正 ...
- Zabbix 添加内存告警
1.在Template OS Linux模板创建item Configuration-->Templates-->Template OS Linux-->items-->cre ...
- Linux系统运行模式介绍
Linux运行模式 自由服务,即不需要用户独立去安装的软件服务,而是在系统安装好之后就可以直接使用的服务(内置服务). 运行模式也称为运行级别,属于linux的自有服务. 运行模式可以理解为一旦你开机 ...
- @TableId
描述:主键注解 属性 类型 必须指定 默认值 描述 value String 否 "" 主键字段名 type Enum 否 IdType.NONE 主键类型 #IdType 值 描 ...
- java.sql.SQLException: Access denied for user 'root'@'10.1.0.2' (using password: YES)
java.sql.SQLException: Access denied for user 'root'@'10.1.0.2' (using password: YES) at com.mysql.c ...
- CNN是怎样一步步工作的?
非常形象详细的博客:链接1 链接2 为了完成我们的卷积,我们不断地重复着上述过程,将feature和图中每一块进行卷积操作.最后通过每一个feature的卷积操作,我们会得到一个新的二维数组.这也可以 ...
- Python 模块B
包 包可以把一个模块分成多个文件同样的导入方式即可,用了包之后导入方式不变,使用者感觉不到变化.包其实是一个文件夹(必须得含有__init__.py 这个文件) 导包就是导入init 包的 ...
- Comet OJ 夏季欢乐赛 Gree的心房
Comet OJ 夏季欢乐赛 Gree的心房 题目传送门 题目描述 据说每一个走进Gree哥哥心房的小姑娘都没有能够再走出来-- 我们将Gree哥哥的心房抽象成一个n \times mn×m的地图,初 ...