当前位置: 移动技术网 > IT编程>开发语言>c# > 如何搭建新的WPF项目框架

如何搭建新的WPF项目框架

2019年07月18日  | 移动技术网IT编程  | 我要评论

下面就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>();<br>
 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)开始剖析 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 可以解决窗口缓存问题(如果你现在还看不明白请 仔细看上面代码(虽然代码有点渣),如果实在看不明白可以在留言板吐槽)。

1、 开始 剖析 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;和上面得方法差不多,

以上分为三大步骤对wpf 项目框架搭建的介绍,并结合代码做剖析,希望对大家有所帮助。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网