浅谈 WPF 项目框架搭建

在WPF项目开发中最常用的开发模式无疑是MVVM模式,  MVVM模式开发的好处,在这里就不详细讨论, 还有 本文中所使用MVVMLight框架,为什么使用MVVM框架(1、框架较轻,2、学习成本低、3、适用大多数中小型项目,4、相对于微软的prism框架更容易上手)    下面开始 一步一步 搭建框架

第一步: 利用反射创建VM构造器

 public class ViewModelFactory
    {
        private static Dictionary<string, object> vmMap = new Dictionary<string, object>();
        public static T GetViewModel<T>() where T : ViewModelBase
        {
            Type vmType = typeof(T);
            if (vmMap.ContainsKey(vmType.FullName))
            {
                return (T)vmMap[vmType.FullName];
            }
            else
            {
                object vm = Activator.CreateInstance(vmType);
                vmMap.Add(vmType.FullName, vm);
                return (T)vm;
            }
        }

        public static T GetViewModel<T>(object[] data,string id) where T : ViewModelBase
        {
            Type vmType = typeof(T);
            if (vmMap.ContainsKey(id))
            {
                return (T)vmMap[id];
            }
            else
            {
                object vm = Activator.CreateInstance(vmType, data);
                vmMap.Add(id, vm);
                return (T)vm;
            }
        }
    }

为什么用一个Dictionary  将ViewModel  缓存起来,相信利用MVVM模式开发大多数的开发者碰到的问题无疑是各个VM之间的数据通信问题,利用Dictionary缓存起来有两个好处:

1、可以解决VM之间相互通信的问题(当然你也可以用MvvmLight的 Message机制来通信,PS:个人认为完全没必要用MvvmLight中的 Messgae,如果我们框架搭的合理完全可以规避去用MvvmLight中 Message,Message比较难于管理,如果在我们的代码中出现大量的Message无疑是一件痛苦的事情,所以笔者不推荐用MvvmLight中的Message)

2、如果我们的应用程序要频繁的与服务器做交互,我们完全可以用缓存,以避免每次都去请求服务器(可以缓存一些在应用程序中一直使用的数据,规避二次请求)

public static T GetViewModel<T>() where T : ViewModelBase  这个函数(将我们的VM完全限定名作为KEY缓存)适用于单例模式的VM,

public static T GetViewModel<T>(object[] data,string id) where T : ViewModelBase 这个函数(主要构件带参数的VM构造函数,id是唯一ID),为什么会用到它,举个例子

例如我们的QQ聊天窗口,所有聊天窗口基本相同用到的VM类型也是相同,所以这时候就需要多个VM实例了,第一种方法就行不通了 所以会用到这种方法去构建VM,并将id作为KEY值缓存起来

第二步:构建我们的ViewModel 基类:

  public delegate void CloseEventHandle(object sender);
    public class CustomViewModel : ViewModelBase
    {

        public event CloseEventHandle CloseEvent;
      protected bool hasData;

        public CustomViewModel()
        {
           LoadCommand = new RelayCommand(() =>
            {
                if (!hasData)
                {

                    ThreadPool.QueueUserWorkItem((obj) =>
                    {
                        lock (this)
                        {
                            OnLoad();
                            hasData = true;
                        }
                    });
                }
            });
        }public RelayCommand LoadCommand { private set; get; }

        protected virtual void OnLoad()
        {

        }

        protected void OnClose(object sender)
        {
            if (sender != null && CloseEvent != null)
            {
                CloseEvent(sender);
            }
        }
    }

上面CustomViewModel 继承的ViewModelBase 是MvvmLight中的ViewModelBase,至于MvvmLight用法不在本文中讨论,

1、为什么要声明LoadCommand,因为大多数的时候我们会在窗体或用户控件Loaded的时候去加载数据,有可能是异步加载,也有可能是同步加载,所以我们在CustomViewModel中

声明省去了各个VM子类中去声明LoadCommand的麻烦,使用时我们直接在XAML利用MvvmLight提供的EventToCommand 去绑定LoadCommand,然后在对应的VM去重写CustomViewModel基类中的OnLoad方法就可以了。

2、CloseEvent 故名思议是用来在VM中关闭窗体用的(详细用法会在下文中讨论)

3、我们也可以将一些公有的数据都提炼到VM中来。

第三步  管理窗口:

  在开发程序的时候我们通常要去管理窗口的如果你没用到MVVM模式 或者是传统的Winform 你可以随便的去new Window(),或者随便的去改Window的构造函数,或者随意的去构造单例窗体,但是如果用到了MVVM模式似乎以上所说的一切都变得复杂了,刚开始的时候我也是挺伤脑筋的,后来在不断的重构代码中找到了解决方法,(PS:本人也是一名菜鸟,只想把自己在开发中的问题及解决方法分享出来,未必就是好的解决方案,所以大神们勿喷)下面上代码: 构建我们的ShowHelper类:

  public class ShowHelper
    {
        private static Dictionary<string, Window> windowManager = new Dictionary<string, Window>();
        public static void ShowDiaglogUc<T>(string title, object[] constructors = null, bool isDialog = false) where T : UserControl
        {
            Type controlType = typeof(T);
            string key;

            if (constructors == null)   //如果构造参数为null
            {
                key = controlType.FullName;   //key = T 的完全限定名
            }
            else
            {
                // 如果不为空 并且 第二个构造参数为string(第二个参数代表id -->有可能是GroupId  有可能是UserId);
                if (constructors.Length == 2 && constructors[1] is string)  //ps:这里本人写死了可以根据需求自行修改
                {
                    key = controlType.FullName + constructors[1].ToString();  //key = 控件 完全限定名+id;
                }
                else  //不满足条件
                {
                    key = controlType.FullName;   //key = 限定名
                }

            }

            if (windowManager.ContainsKey(key))  //如果包含KEY
            {
                windowManager[key].Topmost = true;  //设置TopMost
                return;
            }

            UserControl content;
            if (constructors == null)
            {
                content = Activator.CreateInstance(controlType) as UserControl;
            }
            else
            {
                content = Activator.CreateInstance(controlType, constructors) as UserControl;
            }

            BaseWindow window = new BaseWindow();  //PS这是自己封装 的Window,(可以用直接用原始的Wpf Widnow)
            window.Title = title;
            windowManager.Add(key, window);
            window.Closed += (sen, cloE) =>
            {
                windowManager.Remove(key);
            };
            if (isDialog)
            {
                window.ShowDialog();
            }
            else
            {
                window.Show();
            }
            #region 注册关闭事件
            if (content.DataContext as CustomViewModel != null)
            {
                CustomViewModel vm = content.DataContext as CustomViewModel;
                vm.CloseEvent += (obj) =>
                {
                    if (content.DataContext.Equals(obj))
                    {
                        window.Close();
                    }
                };
            }
            #endregion
        }

        public static CustomDialogResult ShowOkCancleUC<T>(string title, MsgBoxBtn okCancle, out object data) where T : Control
        {
            Type vmType = typeof(T);
            Control content = Activator.CreateInstance(vmType) as Control;
            OkCanleWindow window = new OkCanleWindow();
            window.ShowInTaskbar = false;
            return window.ShowDialog(title, okCancle, content, out data);
        }

        public static CustomDialogResult MessageBoxDialog(string title, string message, MsgBoxBtn okCancle)
        {
            OkCanleWindow window = new OkCanleWindow();
            window.ShowInTaskbar = false;
            object none;
            return window.ShowDialog(title, okCancle, new MessageUC() { Message = message }, out none);
        }

1、(1)开始剖析 public static void ShowDiaglogUc<T>(string title, object[] constructors = null, bool isDialog = false) where T : UserControl
  ShowDialogUc  是用来在VM中用来创建UserControl并显示在Window中的。你可能会问为啥用windowManager 将窗口缓存起来(PS这里主要还是为了解决单例窗口的麻烦),

  至于 下面这段代码,我们可以回到创建的CustomerViewModel中,对这里需要注册VM中CloseEvent事件,这样我们在VM中就可以直接调用OnClose()方法就OK了

  #region 注册关闭事件
            if (content.DataContext as CustomViewModel != null)
            {
                CustomViewModel vm = content.DataContext as CustomViewModel;
                vm.CloseEvent += (obj) =>
                {
                    if (content.DataContext.Equals(obj))
                    {
                        window.Close();
                    }
                };
            }
  #region 注册关闭事件

  (2)开始剖析 public static void ShowDiaglogUc<T>(string title, object[] constructors = null, bool isDialog = false) where T : UserControl 函数中的 constructors 参数

  在开始剖析 constructors 之前先让我们 联想一下应用场景(可以先想下,QQ的聊天窗口,例如群聊天吧,所有的群聊天都是相同界面,也就是说他们所对应的VM应该是统一类型的      VM,如果我们双击群,则会弹出对应相应的聊天窗口,正常的思维是会给聊天窗口传递参数也就是组ID 这时候我们的VM就需要构造参数了,还有一个问题就是每个群组聊天窗口只能有一个,总不能每次双击就new一个聊天窗口了吧 所以这时候我们就需要做缓存了,) 综上constructors参数在配合ViewModelFactory中的 public static T GetViewModel<T>(object[] data,string id) where T : ViewModelBase 方法  可以解决我们VM中需要传递参数的问题,windowManager 可以解决窗口缓存问题(如果你现在还看不明白请 仔细看上面代码(虽然代码有点渣),如果实在看不明白可以在留言板吐槽)。

2、 开始 剖析 public static CustomDialogResult ShowOkCancleUC<T>(string title, MsgBoxBtn okCancle, out object data) where T : Control

  (1)开始剖析该函数前让我们 新建一个自己的带返回值的 ShowDialog 窗口

     新建xaml窗口

    

<controls:BaseWindow x:Class="Common.OkCanleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:controls="clr-namespace:Controls;assembly=Controls"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MessageBoxWindow">
    <Grid x:Name="grid">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Content="确   定" x:Name="okBtn" Click="okBtn_Click" Grid.Row="1" Height="30" Width="120" HorizontalAlignment="Right" Margin="0 0 10 0"/>
        <Button Content="取   消" x:Name="canleBtn" Click="canleBtn_Click" Grid.Row="1" Grid.Column="1" Height="30" Width="120" HorizontalAlignment="Left" Margin="10 0 0 0"/>

    </Grid>
</controls:BaseWindow>

后台代码:
    

 public partial class OkCanleWindow : BaseWindow
    {
        public OkCanleWindow()
        {
            InitializeComponent();
this.Closed += (s, e) =>
                {
                    if (result == CustomDialogResult.None)
                    {
                        result = CustomDialogResult.Cancel;
                    }

                };
        }
        private System.Windows.Controls.Control control;

        CustomDialogResult result;
        public CustomDialogResult ShowDialog(string title, MsgBoxBtn btnState, Control uc, out object dataContext)
        {
            #region  设置控件
            if (btnState == MsgBoxBtn.Ok) //如果为OK状态
            {
                Grid.SetColumnSpan(okBtn, 2);  //设置OK按钮跨两列
                okBtn.HorizontalAlignment = System.Windows.HorizontalAlignment.Center; //设置OK按钮居中对齐
                canleBtn.Visibility = System.Windows.Visibility.Collapsed; //设置Cancel 按钮隐藏;
                if (uc != null)
                {
                    control = uc;
                    Grid.SetRow(uc, 0);    //设置控件所在Grid 的行
                    Grid.SetColumnSpan(uc, 2); //设置控件所在Grid 的列
                    this.Width = uc.Width;    //设置窗体宽度
                    this.Height = uc.Height + grid.RowDefinitions[1].Height.Value + 35; //设置窗体宽度 高度
                    grid.Children.Add(uc);  //加入控件
                }
            }
            if (btnState == MsgBoxBtn.None)  //如果为None  既没有OK 也没有 Cancle
            {
                grid.RowDefinitions.RemoveAt(1);
                okBtn.Visibility = System.Windows.Visibility.Collapsed;
                canleBtn.Visibility = System.Windows.Visibility.Hidden;
                if(uc !=null)
                {
                    control = uc;
                    Grid.SetRow(uc, 0);    //设置控件所在Grid 的行
                    Grid.SetColumnSpan(uc, 2); //设置控件所在Grid 的列
                    this.Width = uc.Width;    //设置窗体宽度
                    this.Height = uc.Height + 35;
                    grid.Children.Add(uc);  //加入控件
                }
            }

            this.Title = title;
            dataContext = uc.DataContext;
            #endregion
            this.ShowDialog();return result;
        }

        private void okBtn_Click(object sender, RoutedEventArgs e)
        {
            result = CustomDialogResult.OK;
            this.Close();
        }

        private void canleBtn_Click(object sender, RoutedEventArgs e)
        {
            result = CustomDialogResult.Cancel;
            this.Close();
        }
    }

    public enum CustomDialogResult
    {
        None,OK,Cancel
    }

    public enum MsgBoxBtn
    {
        None,Ok,OkCancel
    }

剖析 ShowDialog(string title, MsgBoxBtn btnState, Control uc, out object dataContext) 方法

在Control uc 代表我们要ShowDialog的UC,dataContext 可以输出一些数据,另外我们要自定义一些枚举

public static CustomDialogResult MessageBoxDialog(string title, string message, MsgBoxBtn okCancle)  主要用来显示自定义MessageBoxUserControl;和上面得方法差不多,

本文目前先跟新到如果项目后期碰到什么问题会跟进本文;(转载请注明出处)

时间: 2024-10-05 14:00:49

浅谈 WPF 项目框架搭建的相关文章

浅谈Androidclient项目框架

写Android也有些时间了,一边工作,一边学习,一边积累.仅仅有遇到问题了,花时间去研究,自己的能力才干提升.刀假设不用.慢慢的就会生锈应该也是这个道理吧!上个月公司项目server框架进行的一些调整.可是当时自己的项目没有移植框架.还是前人的代码,一下子差点没把我搞死,真是筋疲力尽.一个周末两天所有加班赶,结果赶出来的质量还很差,等改完了之后大概稳定下来.自己赶紧抽闲余时间把自己的框架移植进去,我的框架是自己慢慢琢磨积累的,拿出来给大家分享一下.有不正确的地方.欢迎大家批评指正.谢谢. 首先

(三) Angular2项目框架搭建心得

前言: 在哪看到过angular程序员被React程序员鄙视,略显尴尬,确实Angular挺值得被调侃的,在1.*版本存在的几个性能问题,性能优化的"潜规则"贼多,以及从1.*到2.*版本的面目全非,不过宽容点来看这个强大的框架,升级到ng2肯定是一件好事情,虽然截至目前ng2还存在或多或少需要完善的地方,但是ng2做到了留下并强化ng1好的部分,移除或改善其不好的部分,并且基于许多较新Web技术来开发,不去看从ng1迁移到ng2的门槛和工作量的话,ng2的编程体验是很酷炫的. 目前n

浅谈软件项目的需求管理

软件项目区别于其它项目的最显著的特征是其不可见性,它不像硬件购销.建筑工程,都是实实在在可见的东西.而软件项目在系统交付之前很长一段时间,客户是无法感知自己想要的系统究竟是什么样子.因此,需求管理就显得十分重要,据相关统计数据分析,软件项目90%以上失败的原因都在于没有重视需求或者需求管理方面做的不到位导致的. 需求管理作为软件项目管理的一个重要内容,贯穿项目实施的全生命周期.俗话说:万事开头难.需求作为软件开发的第一个环节,其重要性不言而喻.市面上关于需求管理的相关理论和书籍很多,但多数停留在

1、Android项目框架搭建 (分析需求、整理资料)

闲来无事.想搭个框架试试 分析一般应用 将资料整理整理 粗略统计 需要以下资料 1.android-pulltorefresh 一个强大的拉动刷新开源项目,支持各种控件下拉刷新 ListView.ViewPager.WevView.ExpandableListView.GridView.(Horizontal )ScrollView.Fragment上下左右拉动刷新,比下面johannilsson那个只支持ListView的强大的多.并且他实现的下拉刷新ListView在item不足一屏情况下也

[AngularJS]项目框架搭建-MyFirst Skeleton

前文有提到过 做一个简单的订餐系统,最近花了点时间,了解了一下AngularJS框架的使用.因此本文的目的根据了解的知识重新搭建基于 AngularJS框架. 该框架是基于对于AngularJS的学习而制定的,这其中肯定有很多不足,在以后的学习中在加以改进. 一.系统准备 安装Node.js version>=0.10.0版本 Git  Shell 并添加该 Shell脚本到Path环境变量中  Path=:,"$git_home/bin"   二.手动搭建框架 2.1 创建开发

第一节项目框架搭建

动软代码生成器的使用 创建三个类库项目DAL.BLL.Model,创建两个asp.net应用程序Web:Front(前台).Admin(后台管理).DAL引用Model,BLL引用Model和DAL,Web引用BLL和Model. 如果报错“添加服务器配置失败”,则以管理员身份运行“动软代码生成器”. (*)根据我的表命名规范,配置“动软”的“选项”→“代码生成器设置”,命名规则中“替换表中的字符”填“T_”(大小写敏感),“类命名规则”中,除了Model最后一个文本框留空之外,其他两个填BLL

ASP.NET MVC企业级项目框架搭建实战

MVC项目搭建笔记---- 项目框架采用ASP.NET MVC+Entity Framwork+Spring.Net等技术搭建,搭建过程内容比较多,结合了抽象工厂的思想降低了三层之间的耦合,可以使用此套框架进行可扩展性要求高的企业级MVC项目开发.本框架的架构图如下: 第一步(创建分类文件夹): 创建5个文件夹.分别为UI,Model,BLL,DAL,Common,以便于将各模块分类整理. 第二步(项目类库的创建): 在UI文件夹创建ASP.NET MVC4项目模板选择基本. 在Model文件夹

记录-项目java项目框架搭建的一些问题(maven+spring+springmvc+mybatis)

伴随着项目框架的落成后,本以为启动就能成功的,but.... 项目启动开始报错误1:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener 这个错百度到说是缺少这个包,但实际在项目中看到maven里面是有这个包的.于是继续百度到[可能包是找到了,但没有依赖在项目中] 项目右击-----project-----deployment assembly , add ,java bui

2.0项目框架搭建

2.1.科学减肥网: http://www.kxjf1.com 项目架构如下 解决方案分层 01UI KXJF.Helper(web.wap帮助类库) KXJF.Logic(web控制器逻辑) KXJF.Logic.SysAdmin(web后台控制器逻辑) KXJF.Logic.Wap(wap控制器逻辑) KXJF.UrlProvider(URL优化,提供良好的URL) 02Service KXJF.IBLL(业务逻辑接口层) KXJF.BLL(业务逻辑实现层) 03Repository KXJ