【我们一起写框架】MVVM的WPF框架(一)—序篇
前言
我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数。
我是这部分人群中的一份子。
我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门。这类客户的特点是,资金有限,人力有限。
什么意思呢?就是你如果敢给他安一台Linux服务器,客户的信息员和测试员会把你堵在墙角问候你全家安好,他们Window都用不明白呢,你给安Linux,要疯啊。
所以,Core对我们而言,没有意义,因为大家都是Windows。
关于业务
在二三线城市的我们,立身之本不是写算法,也不是各种高级的、新出的技术,而是,写业务模块。
不要小看写业务模块,在二三线城市,一个不会写业务模块的程序员,即便知识面再广,也是个烂程序员。为什么?因为他不能干活呀。
其实把业务模块写好,并不是件容易的事。因为它涉及到对业务的理解,对社会的认知。
以我多年的经验,能写好业务模块的优秀开发人员,通常都需要三四年经验。普通一点,大约就需要五到十年。当然还有十年以上经验,还很没掌握写业务的。
这里面有个特例,那就是硕士和博士。因为他们的年龄较大,阅历较多,所以,通常两年就能把业务写的很好。此外就没有特例了,什么一年经验就能架构,刚毕业就是高级程序员的,那都是培训机构骗毕业生的。
但是,不得不说,高学历真的管用,硕士博士的成材率真的很高。大多数都能成为及格的程序员。
关于框架
回到写框架这件事。在我看来,写框架这件事是个程序员都能干。但写的好坏就另说了,所以写框架这件事还是与经验挂钩的。
在我的认知中,技术视野相对更高,技术范围更广的人写的框架会更好。所以,我认为,[实战]架构师和高级程序员,在本质上没有区别,都是程序员。
只是架构师技术更会好一点,并且接受过项目的洗礼。然而,一个项目只能洗礼一个人,所以能不能成为架构师,就不能只看技术了,要看老板给谁机会了。说白了,就是老板肯不肯花钱赌你能成事。
所以,当技术相差无几,沟通能力,文档能力,甚至生活状态,家境,毅力都是领导考察的依据。因此,机会不是留给有准备的人,而是留给各方面都更出色的人。
当然,如果老板认可你,一年经验做架构师也不是没可能。但在资金有限,人员有限的二三线城市,能遇到这样脑残的领导或老板的概率不高。
虽然架构师不是人人都能做,但框架是可以先学会编写的,毕竟这是个基础。有了基础,就算不能年轻有为,但起码有个机会。
也许,人家28岁拿到的机会,你在40岁也可以拿到,不是吗。有机会总比没有强,不是吗。
框架的前期准备
关于框架编写,我不想在Github上放一个源码,然后再写一篇介绍文档。我觉得,这种方式是高手之间的交流。
很多新手,会被这种海量的代码压垮,因为他们还不习惯阅读框架,会出现开始时事倍功半,到最后郁闷放弃的情况。
所以,我们一起从头开始,一起开始MVVM的WPF框架之旅吧。
框架的前期准备
框架是要一步一步编写的,首先,我们先定义框架包含的基本元素。基本元素如下:
WPFUI:就是WPF的Xaml页面。
ViewModel:每个WPF页面有唯一的ViewModel,用来处理页面业务逻辑。
Utility:存放一些常规处理类。
DTO:存放数据传输用的实体类。
Proxy:获取数据用的代理类。
先定义这五个元素,如果后期需要,我们再进行补充。定义了元素后,我们创建对应的应用程序集。项目结构如下:
做好了项目结构后,我们让ViewModel引用DTO,Proxy,Utility三个程序集,然后在让KibaFramework引用ViewModel,这样就实现了上图的结构逻辑。
然后,我们再让ViewModel引用PresentationCore,PresentationFramework,System.Windows,WindowsBase,Systm.Xaml这个五个DLL,它们是WPF的核心类库,为了后期反射前台控件用。
我怎么知道要引用这五个类库的?
这是经验,仅仅是经验,没有其他。
项目约定
创建完基础结构后,我们要做的是项目约定。(任何框架都有约定,而且约定要高于配置,这是约定优先原则。)
我们建立约定如下:
WPF项目窗体以Window作为前缀名创建,如WindowMain,WindowLogin。
WPF项目页面以Page作为前缀名创建,如PageMain,PageXXX。
WPF项目控件(UserControl)以UC作为前缀名创建,如UCTable,UCXXX。
WPF的窗体、页面、控件有且只有一个ViewModel。
ViewModel以VM_作为前缀名+对应的窗体名创建,如VM_WindowMain,VM_PageMain。
框架的实现
做完准备工作后,我们开始编写框架,先从系统的核心ViewModel开始,第一步,建立WPF页面与View的关系。
首先我们创建VM的基类BaseViewModel——之后再建立的VM都要引用这个基类。
在VM基类里,我们通过反射实现创建Xaml页面,并实现该页面的相关事件。代码如下:
namespace ViewModel
{public class BaseViewModel : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;public const string UINameSapce = "KibaFramework";public string UIElementName = "";public FrameworkElement UIElement { get; set; }public Window WindowMain { get; set; } //主窗体 public EventHandler CloseCallBack = null; //窗体/页面/控件 关闭委托 public BaseViewModel(){WindowMain = Application.Current.MainWindow; SetUIElement();}#region 通过反射创建对应的UI元素public void SetUIElement(){Type childType = this.GetType();//获取子类的类型 string name = this.GetType().Name;UIElementName = name.Replace("VM_", "");UIElementName = UIElementName.Replace("`1", "");//应对泛型实体if (name.Contains("Window")){UIElement = GetElement<Window>();(UIElement as Window).Closing += (s, e) =>{if (CloseCallBack != null){CloseCallBack(s, e);}};}else if (name.Contains("Page")){UIElement = GetElement<Page>();(UIElement as Page).Unloaded += (s, e) =>{if (CloseCallBack != null){CloseCallBack(s, e);}};}else if (name.Contains("UC")){UIElement = GetElement<UserControl>();(UIElement as UserControl).Unloaded += (s, e) =>{if (CloseCallBack != null){CloseCallBack(s, e);}};}else{throw new Exception("元素名不规范");}}public E GetElement<E>(){Type type = GetFormType(UINameSapce + "." + UIElementName);E element = (E)Activator.CreateInstance(type);return element;}public static Type GetFormType(string fullName){Assembly assembly = Assembly.Load(UINameSapce);Type type = assembly.GetType(fullName, true, false);return type;}#endregion#region 窗体操作public void Show(){if (UIElement is Window){(UIElement as Window).Show();}else{throw new Exception("元素类型不正确");}}public void ShowDialog(){if (UIElement is Window){(UIElement as Window).ShowDialog();}else{throw new Exception("元素类型不正确");}}public void Close(){if (UIElement is Window){(UIElement as Window).Close();}else{throw new Exception("元素类型不正确");}}public void Hide(){if (UIElement is Window){(UIElement as Window).Hide();}else{throw new Exception("元素类型不正确");}}#endregion#region Messagepublic void MessageBox(Window owner, string msg){DispatcherHelper.GetUIDispatcher().Invoke(new Action(() =>{if (owner != null){System.Windows.MessageBox.Show(owner, msg, "提示信息");}else{System.Windows.MessageBox.Show(WindowMain, msg, "提示信息");}}));}public void MessageBox(string msg){DispatcherHelper.GetUIDispatcher().Invoke(new Action(() =>{System.Windows.MessageBox.Show(WindowMain, msg, "提示信息");}));}public void MessageBox(string msg, string strTitle){DispatcherHelper.GetUIDispatcher().Invoke(new Action(() =>{System.Windows.MessageBox.Show(WindowMain, msg, "提示信息");}));}public void MessageBox(string msg, Action<bool> callback){MessageBox("系统提示", msg, callback);}public void MessageBox(string title, string msg, Action<bool> callback){DispatcherHelper.GetUIDispatcher().Invoke(new Action(() =>{if (System.Windows.MessageBox.Show(WindowMain, msg, title, MessageBoxButton.YesNo) == MessageBoxResult.Yes){callback(true);}else{callback(false);}}));}#endregion#region 异步线程public void AsyncLoad(Action action){IAsyncResult result = action.BeginInvoke((iar) =>{}, null);}public void AsyncLoad(Action action, Action callback){IAsyncResult result = action.BeginInvoke((iar) =>{this.DoMenthodByDispatcher(callback);}, null);}public void AsyncLoad<T>(Action<T> action, T para, Action callback){IAsyncResult result = action.BeginInvoke(para, (iar) =>{this.DoMenthodByDispatcher(callback);}, null);}public void AsyncLoad<T, R>(Func<T, R> action, T para, Action<R> callback){IAsyncResult result = action.BeginInvoke(para, (iar) =>{var res = action.EndInvoke(iar);this.DoMenthodByDispatcher<R>(callback, res);}, null);}public void AsyncLoad<R>(Func<R> action, Action<R> callback){IAsyncResult result = action.BeginInvoke((iar) =>{var res = action.EndInvoke(iar);this.DoMenthodByDispatcher<R>(callback, res);}, null);}public void DoMenthodByDispatcher<T>(Action<T> action, T obj){DispatcherHelper.GetUIDispatcher().BeginInvoke(new Action(() =>{action(obj);}), DispatcherPriority.Normal);}public void DoMenthodByDispatcher(Action action){DispatcherHelper.GetUIDispatcher().BeginInvoke(new Action(() =>{action();}), DispatcherPriority.Normal);}#endregion protected void OnPropertyChanged([CallerMemberName]string propertyName = ""){if (PropertyChanged != null){PropertyChanged(this, new PropertyChangedEventArgs(propertyName));}}}
}
BaseViewModel的代码如上所示,主要实现了以下功能:
1,UI元素Window,Page,UserControl的创建;
2,基础窗体方法,比如Show,Close,Message等等。
3,一系列线程切换的异步操作。
4,简洁化消息处理。(不理解的消息的可参看这篇文章C#语法——消息,MVVM的核心技术。)
--------------------------------------------------------------------------------------------------------------------------------
这样,BaseViewModel就编写完成了,之后我们一起修改WPF项目,让窗体的启动的时候,使用ViewModel启动。
在WPF项目中创建WindowMain窗体,并在VM中创建对应的ViewModel。
然后在App.Xaml.cs文件中重写启动函数,代码如下:
protected override void OnStartup(StartupEventArgs e)
{VM_WindowMain vm = new VM_WindowMain();Application.Current.MainWindow = vm.UIElement as Window;vm.Show();base.OnStartup(e);
}
在删除App.Xaml的StartupUri属性。
这样运行WPF就会启动我们的WindowMain窗体了。
ViewModel创建窗体
主窗体已经运行了,如果我们想运行其他窗体,该怎么做呢?
很简单,只要在主窗体的ViewModel中new那个想要运行的窗体的VM,然后Show一下就可以了。代码如下:
VM_WindowCreateUser vm = new VM_WindowCreateUser();
vm.Show();
到此,窗体相关的内容我们已经一起编写完成了。
接下来需要编写的是Page和UserControl的基础使用方式。
但Page和UserControl是被Window使用的,不能直接呈现,所以,在使用Page和UserControl之前,我们需要编写MVVM框架中,用于在WPF页面和ViewModel传递信息的Command(命令)。
本篇文章就先不介绍Command了,敬请期待下一篇文章,让我们一起继续完善我们的框架。
框架代码已经传到Github上了,并且会持续更新。
To be continued
Github地址:https://github.com/kiba518/KibaFramework
----------------------------------------------------------------------------------------------------
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的【推荐】,非常感谢!
如果您觉得这篇文章对您有所帮助,那就不妨支付宝小小打赏一下吧。
转载于:https://www.cnblogs.com/lonelyxmas/p/10373222.html
【我们一起写框架】MVVM的WPF框架(一)—序篇相关推荐
- C# WPF MVVM模式Prism框架下事件发布与订阅
01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...
- C# WPF MVVM模式Prism框架从零搭建(经典)
01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...
- WPF框架教程 | 从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器
之前时间一直在使用Caliburn.Micro这种应用了MVVM模式的WPF框架做开发,是时候总结一下了. Caliburn.Micro(https://blog.csdn.net/lzuacm/ar ...
- C#/.Net Core/WPF框架初建(国际化、主题色)
English | 简体中文 作为 TerminalMACS 的一个子进程模块 - WPF管理端,目前搭建框架部分功能:本地化.国际化.主题色修改等. 导航目录 1.框架已添加功能说明 1.1. 国际 ...
- c# contains方法_C#/.Net Core/WPF框架初建(国际化、主题色)
C#/.Net Core/WPF框架初建(国际化.主题色) English | 简体中文 作为 TerminalMACS 的一个子进程模块 - WPF管理端,目前搭建框架部分功能:本地化.国际化.主题 ...
- C# WPF框架下 Console.WriteLine()无法打印内容
最近在做C#桌面应用的相关开发,用的是wPF框架. 今天本来想测试一下自己写的dll库 于是新建了一个项目,导入了自己写的库,然后像往常一样想打印下日志 发现不能打印.代码如下: public par ...
- WPF框架的内存泄漏BUG
用户在使用GIX4某模块的过程中,内存只见加不见减.我们怀疑出现了内存泄漏,所以我花了相当一段时间来进行此问题的排查. 我使用Red Gate公司的产品ANTS Memory Profiler 5进行 ...
- 如何搭建python框架_从零开始:写一个简单的Python框架
原标题:从零开始:写一个简单的Python框架 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 你为什么想搭建一个Web框架?我想有下面几个原因: 有一个 ...
- WPF 框架全构建环境虚拟机硬盘分享
现在 WPF 完全开源了,咱可以构建自己私有的版本.我分享一个虚拟机硬盘给你,只要你下载下来,通过 VMWare 导入,即可无需任何配置,拿到一个能构建 WPF 官方源代码的全构建环境.可以用来只做你 ...
- 微软 WPF 框架源码现已托管至 GitHub
WPF(Windows Presentation Foundation) 是微软推出的用于构建桌面客户端应用程序的 UI 框架,具有应用程序模型.控件.图形.布局.数据绑定和安全性等功能,属于 .NE ...
最新文章
- Netapp存储基础之WAFL, NVRAM, RAID, SnapShot
- idea maven打jar包_Dev 日志 | 如何将 jar 包发布到 Maven 中央仓库
- 断网与黑客无关 我来抖一抖暴风那点见不得人的猫腻
- graylog2 架构--转载
- php 类 静态调用 实例化 效率,php类的静态调用和实例化调用有哪些不同点?
- Opencv学习笔记——release和debug两个模式的运行问题
- 深度学习:卷积层的实现
- Struts里面的配置笔记
- azure web应用部署_使用Visual Studio Code将Python应用程序部署到Azure Functions
- 【狂人小白】MyBatis.001 学习巴提斯!
- 兄弟连视频教程下载地址汇总-2014
- java quartz 教程_Quartz 教程
- Oracle 常用SQL语句大全(精)
- css3中的@font-face你真的了解吗
- 电脑的ppt打不开计算机二级,ppt打不开怎么办?详细教您详细解决方法
- PageOffice——动态填充Word模板并在线编辑
- html怎么设置seo,简单说明一下html相关的seo设置!
- 时间序列及异常检测综述(资料)
- 2019最新微信墙微信上墙微信弹幕婚庆会议大屏幕3D签到抽奖摇一摇微信上墙
- MAC下 生成安卓签名证书.keystore文件【详细】
热门文章
- Ubuntu16.04+Cuda9.0+Cudnn7.0+python2.7+Caffe
- 电驴无法增加服务器怎么办,电驴连接不上服务器是什么原因?介绍原因及解决方法步骤...
- 聚合数据手机话费充值API,话费充值功能接入
- 计算机设计大赛感言,平面设计大赛获奖感言
- 64位Win10 2004正式版_MSDN我告诉你win10 2004镜像下载
- django for 前端_django 5. 前端页面设计 - 刘江的django教程
- 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)
- IP-Guard使用中63个常见问题
- 有限体积法求解二维方腔流(三)——代码以及与icoFoam结果对比
- Diligent将收购Steele Compliance Solutions