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

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

    • 商品编号:
      #65452381_300
      • 原价:
        免费
      • 会员价:
        免费
    • 版本:
      • V1.0.0
    • 数量:
      库存(不限)

    购物车中已存在此商品,请在购物车中操作单击跳转购物车

    • 开发者:kiba518
    • 开发语言:wpf
    • 源码架构:B/S
    • 数据库:无
    • 版本工具管理:GitHub
    • 开源协议:BSD协议
    • 编码格式:UTF-8
    • 开发环境:VisualStudio

    前言

    我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数。

    我是这部分人群中的一份子。

    我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门。这类客户的特点是,资金有限,人力有限。

    什么意思呢?就是你如果敢给他安一台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 Message
            public 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文件中重写启动函数,代码如下:

    1
    2
    3
    4
    5
    6
    7
    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一下就可以了。代码如下:

    1
    2
    VM_WindowCreateUser vm = new VM_WindowCreateUser();
    vm.Show();

    到此,窗体相关的内容我们已经一起编写完成了。

    接下来需要编写的是Page和UserControl的基础使用方式。

    但Page和UserControl是被Window使用的,不能直接呈现,所以,在使用Page和UserControl之前,我们需要编写MVVM框架中,用于在WPF页面和ViewModel传递信息的Command(命令)。

    本篇文章就先不介绍Command了,敬请期待下一篇文章,让我们一起继续完善我们的框架。

    权利声明:本站所有商品信息、客户评价等信息是初心商城重要的数据资源,未经许可,禁止非法转载使用。 注:本站商品信息均来自初心商城,其真实性、准确性和合法性由初心商城负责。

                    初心源说明:初心商城主要为程序员提供开发基础的代码源以及成熟项目,网站中所有的商品有提供收费版本的, 也有提供免费版本的,按照大家各自不同的需求进行购买。实实在在的让程序员只用专注于自己的业务实现你的小梦想, 如果您对我们的成果表示认同并且觉得对你有所帮助我们愿意接受来自各方面的支持^_^。

                    支持:用手机扫描二维码支付

                    支付宝支持我们 微信支持我们

                    您的支持将被用于:
                    1、持续深入的上传更多更好的源代码
                    2、建立更加完善的技术社区
                    3、完善现在系统出现各种问题
                    4、购买域名和租赁服务器

                    1、交易规则

                    2、发货方式

                    1、自动:在上方保障服务中标有自动发货的商品,拍下后,将会自动收到来自卖家的商品获取(下载)链接

                    2、手动:在上方保障服务中标有手动发货的商品,拍下后,卖家会收到邮件,也可通过QQ或订单中的电话联系对方。

                    3、退款说明

                    1、描述:源码描述(含标题)与实际源码不一致的(例:描述PHP实际为ASP、描述的功能实际缺少、版本不符等)

                    2、演示:有演示站时,与实际源码小于95%一致的(但描述中有"不保证完全一样、有变化的可能性"类似显著声明的除外)

                    3、发货:手动发货源码,在卖家未发货前,已申请退款的

                    4、服务:卖家不提供安装服务或需额外收费的(但描述中有显著声明的除外)

                    5、其它:如质量方面的硬性常规问题等

                    备注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。交易中的商品,卖家无法对描述进行修改!

                    4、注意事项

                    1、客户买完之后未确认收货,将不会收到下载地址和下载码,确认收货之后才能收到下载地址和下载码。

                    2、在未拍下前,双方在QQ上所商定的内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准);

                    3、在商品同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外);

                    4、在没有"无任何正当退款依据"的前提下,写有"一旦售出,概不支持退款"等类似的声明,视为无效声明;

                    5、虽然交易产生纠纷的几率很小,但请尽量保留如聊天记录这样的重要信息,以防产生纠纷时出现问题不明确的情况。

                    5、交易声明

                    1、本站作为直卖平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益;

                    2、非平台线上交易的商品,出现任何后果均与本站无关;无论卖家以何理由要求线下交易的,请联系管理举报。

                    初心Logo

                    初心商城| 初心系列| 初心博客| 版本历史| 通知公告| 系统反馈

                    © 2016-2019 山西米立信息技术有限公司 保留所有权利 京ICP备16055626号
                    违法和不良信息举报电话:186-2950-9347,本网站所列数据,除特殊说明,所有数据均出自我工作室
                    本网站兼容所有主流浏览器,不支持手机自适应

                    返回顶部小火箭