封装:简要介绍自定义开发基于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" ...
随机推荐
- XGBoost 引入 - 提升树
认识提升树 这个boosting 跟 Adaboost 不同. Adaboost 是通过上一轮的误差率来动态给定一下轮样本不同的权重来学习不同的模型. 现在的方式, 更多是基于残差 的方式来训练. 一 ...
- uwsgi no python application found错误的解决(python3+centos6)
近期在努力把自己的项目从python2转到python3上,因为生产环境无法抛弃centos7,所以只好在centos7上安装了python3.装好了python3,将python命令软连接改成pyt ...
- VS2015秘钥激活
点击软件菜单栏的"帮助",点击"注册产品",点击"使用产品密钥解锁",附送产品密钥: 专业版:HMGNV-WCYXV-X7G9W-YCX63 ...
- 海思3519A 移植ffmpeg
文件下载 下载x264 git clone git://git.videolan.org/x264.git 下载ffmpeg git clone git://source.ffmpeg.org/ffm ...
- Windows 2016 & Windows 10 中IIS安装和配置PHP的步骤
Windows 2016 和 Windows 10 内核是相同的,我们首先需要安装 Internet Information Services (IIS),当然 Win2016 跟 Win10 安装 ...
- openpyxl模块
openpyxl模块 可以对Excel表格进行操作的模块 第三方模块需要下载 pip install openpyxl 配置永久第三方源: D:\Python36\Lib\site-packages\ ...
- C# 不包含 AsEnumerable 的定义
引用 System.Data.DataSetExtensions 引用右键 ->添加引用 ->搜索 DataSetExtensions ->添加 注意版本
- Quay: Introducing an Application Registry for Kubernetes
转自: https://coreos.com/blog/quay-application-registry-for-kubernetes.html When we started Quay, we w ...
- ELK原理
为什么要使用Elasticsearch? 因为在我们中的数据,会随着时间变的非常多,若采用以往的模糊查询,模糊查询前置通配符,如:"%aa%",会放弃索引,导致数据表查询将变成全 ...
- 第08组 Beta冲刺(2/5)
队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述 ?按照时间进度的安排进行相应的检查 展示GitHub当日代码/文档签入记 ...