【我们一起写框架】MVVM的WPF框架(一)—序篇
原文:【我们一起写框架】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

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的【推荐】,非常感谢!
如果您觉得这篇文章对您有所帮助,那就不妨支付宝小小打赏一下吧。

posted on 2019-02-14 10:34 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10373222.html

【我们一起写框架】MVVM的WPF框架(一)—序篇相关推荐

  1. C# WPF MVVM模式Prism框架下事件发布与订阅

    01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...

  2. C# WPF MVVM模式Prism框架从零搭建(经典)

    01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...

  3. WPF框架教程 | 从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器

    之前时间一直在使用Caliburn.Micro这种应用了MVVM模式的WPF框架做开发,是时候总结一下了. Caliburn.Micro(https://blog.csdn.net/lzuacm/ar ...

  4. C#/.Net Core/WPF框架初建(国际化、主题色)

    English | 简体中文 作为 TerminalMACS 的一个子进程模块 - WPF管理端,目前搭建框架部分功能:本地化.国际化.主题色修改等. 导航目录 1.框架已添加功能说明 1.1. 国际 ...

  5. c# contains方法_C#/.Net Core/WPF框架初建(国际化、主题色)

    C#/.Net Core/WPF框架初建(国际化.主题色) English | 简体中文 作为 TerminalMACS 的一个子进程模块 - WPF管理端,目前搭建框架部分功能:本地化.国际化.主题 ...

  6. C# WPF框架下 Console.WriteLine()无法打印内容

    最近在做C#桌面应用的相关开发,用的是wPF框架. 今天本来想测试一下自己写的dll库 于是新建了一个项目,导入了自己写的库,然后像往常一样想打印下日志 发现不能打印.代码如下: public par ...

  7. WPF框架的内存泄漏BUG

    用户在使用GIX4某模块的过程中,内存只见加不见减.我们怀疑出现了内存泄漏,所以我花了相当一段时间来进行此问题的排查. 我使用Red Gate公司的产品ANTS Memory Profiler 5进行 ...

  8. 如何搭建python框架_从零开始:写一个简单的Python框架

    原标题:从零开始:写一个简单的Python框架 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 你为什么想搭建一个Web框架?我想有下面几个原因: 有一个 ...

  9. WPF 框架全构建环境虚拟机硬盘分享

    现在 WPF 完全开源了,咱可以构建自己私有的版本.我分享一个虚拟机硬盘给你,只要你下载下来,通过 VMWare 导入,即可无需任何配置,拿到一个能构建 WPF 官方源代码的全构建环境.可以用来只做你 ...

  10. 微软 WPF 框架源码现已托管至 GitHub

    WPF(Windows Presentation Foundation) 是微软推出的用于构建桌面客户端应用程序的 UI 框架,具有应用程序模型.控件.图形.布局.数据绑定和安全性等功能,属于 .NE ...

最新文章

  1. Netapp存储基础之WAFL, NVRAM, RAID, SnapShot
  2. idea maven打jar包_Dev 日志 | 如何将 jar 包发布到 Maven 中央仓库
  3. 断网与黑客无关 我来抖一抖暴风那点见不得人的猫腻
  4. graylog2 架构--转载
  5. php 类 静态调用 实例化 效率,php类的静态调用和实例化调用有哪些不同点?
  6. Opencv学习笔记——release和debug两个模式的运行问题
  7. 深度学习:卷积层的实现
  8. Struts里面的配置笔记
  9. azure web应用部署_使用Visual Studio Code将Python应用程序部署到Azure Functions
  10. 【狂人小白】MyBatis.001 学习巴提斯!
  11. 兄弟连视频教程下载地址汇总-2014
  12. java quartz 教程_Quartz 教程
  13. Oracle 常用SQL语句大全(精)
  14. css3中的@font-face你真的了解吗
  15. 电脑的ppt打不开计算机二级,ppt打不开怎么办?详细教您详细解决方法
  16. PageOffice——动态填充Word模板并在线编辑
  17. html怎么设置seo,简单说明一下html相关的seo设置!
  18. 时间序列及异常检测综述(资料)
  19. 2019最新微信墙微信上墙微信弹幕婚庆会议大屏幕3D签到抽奖摇一摇微信上墙
  20. MAC下 生成安卓签名证书.keystore文件【详细】

热门文章

  1. Ubuntu16.04+Cuda9.0+Cudnn7.0+python2.7+Caffe
  2. 电驴无法增加服务器怎么办,电驴连接不上服务器是什么原因?介绍原因及解决方法步骤...
  3. 聚合数据手机话费充值API,话费充值功能接入
  4. 计算机设计大赛感言,平面设计大赛获奖感言
  5. 64位Win10 2004正式版_MSDN我告诉你win10 2004镜像下载
  6. django for 前端_django 5. 前端页面设计 - 刘江的django教程
  7. 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)
  8. IP-Guard使用中63个常见问题
  9. 有限体积法求解二维方腔流(三)——代码以及与icoFoam结果对比
  10. Diligent将收购Steele Compliance Solutions