如何搭建新的WPF项目框架

下面就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 项目框架搭建的介绍,并结合代码做剖析,希望对大家有所帮助。

(0)

相关推荐

  • WPF MVVM示例讲解

    在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时,WPF会自动发出通知去更新UI. 我们使用模式,一般是想达到高内聚低耦合.在WPF开发中,经典的编程模式是MVVM,是为WPF量身定做的模式,该模式充分利用了WPF的数据绑定机制,最大限度地降低了Xmal文件和CS文件的耦合度,也就是UI显示和逻辑代码的耦合度,如需要更换界面时,逻辑代码修改很少,

  • WPF实现时钟特效

    WPF在样式定义和UI动画上面相对于以前的技术有了不少的提升,下面给出WPF技术实现钟表的效果: 1.Visual Studio新建一个WPF应用程序,命名为WpfClock,新建一个images文件夹,并准备一个钟表的背景图片和程序图标素材. 2.编辑MainWindow.xaml文件,对UI进行定制,代码如下(指针都是用Rectangle实现的,当然可以用图片代替): using System; using System.Collections.Generic; using System.L

  • VS中模仿WPF模板创建最简单的WPF程序

    如果不用VS的WPF项目模板,如何手工创建一个WPF程序呢?我们来模仿WPF模板,创建一个最简单的WPF程序. 第一步:文件--新建--项目--空项目,创建一个空项目. 第二步:添加引用,PresentationFramework,PresentationCore,WindowsBase,System,System.Xaml,这几个是WPF的核心dll. 第三步:在项目上右键添加新建项,添加两个"xml文件",分别命名为App.xaml和MainWindow.xaml.可以看出,xam

  • WPF实现渐变淡入淡出的登陆窗口效果

    本文实例讲述了WPF实现渐变淡入淡出的登陆窗口效果的方法.分享给大家供大家参考.具体实现方法如下: 1.实现原理 ① 利用UIElement.OpacityMask属性,用于改变对象区域的不透明度的画笔.可以使元素的特定区域透明或部分透明,从而实现比较新颖的效果. ② OpacityMask属性接受任何画刷,可利用LinearGradientBrush线性渐变画刷,通过对渐变画刷中各颜色点加以动画处理即可. 2.渐变淡入实现 渐变淡入效果,可通过事件触发器触发Loaded事件实现,所以可以仅用前

  • WPF气泡样式弹窗效果代码分享

    页面设计需求,做了一个气泡形状的弹出框,效果如下: 设计思路如下: 1. 使用Path绘制气泡的尖尖,将这个放到顶层: 2. 在用border绘制长方形框,将这个放到底层,并且设置Margin值,使得Path图层和border看起来衔接在一起. 代码如下: <Window x:Class="BubblePanelTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentatio

  • WPF中引入WindowsForms控件的方法

    本文实例讲述了WPF中引入WindowsForms控件的方法.分享给大家供大家参考,具体如下: 环境: [1]WindowsXP with SP3 [2]VS2008 with SP1 正文: Step1:在现有工程中引入Windows Forms 鼠标右键[References]->选择[Add Reference]->[.NET]标签页 加入[WindowsFormsIntegration]和[System.Windows.Forms]两项 Step2:在XAML文件里加入 [S2-1]加

  • WPF的ListView控件自定义布局用法实例

    本文实例讲述了WPF的ListView控件自定义布局用法.分享给大家供大家参考,具体如下: 概要: 以源码的形式贴出,免得忘记后,再到网上查资料.在VS2008+SP1环境下调试通过 引用的GrayscaleEffect模块,可根据参考资料<Grayscale Effect...>中的位置下载. 正文: 如何布局是在App.xaml中定义源码如下 <Application x:Class="CWebsSynAssistant.App" xmlns="http

  • WPF自动隐藏的消息框的实例代码

    (鼠标放上去将一直显示,移开动画继续),提供normal和error两种边框. 介绍:传统的确定,取消,OK,CANCAL之类的对话框太繁琐了,由于项目需要而诞生的仿手机式提示对话框.当然传统的对话框项目中也有,这里就不做介绍了. 出场和退场动画做得很简单,就用Blend随便鼓捣了一番,将就用吧. 预览效果如下: 思路其实很简单:将窗体透明化->布局和样式设计->后台传值调用. 准备工作:Microsoft.Expression.Interactions.dll和System.Windows.

  • PowerShell中调用WPF生成炫酷窗口实例

    怎样在PowerShell中调用WPF,你知道,我也知道:怎样在PowerShell中将很长的.NET类型名称缩短成别名,你知道,我也知道.但是怎样将这两个知识点融汇贯通,写出一个优雅的DEMO,并且让你一眼就能看出,这就是WPF,不是别的,也许你以前就知道,而我直到今天才知道,有种相见恨晚的感觉. 先看一下炫酷的效果吧! PowerShell之WPF炫酷 # Plik: 4_Demo_v3_Reflection.ps1 #requires -version 3 $Akceleratory =

  • WPF仿三星手机充电界面实现代码

    先上效果图 这个效果来自于三星S5的充电界面,当然有些细节差别,主要看思路.本文目的是技术交流,不要将效果直接运用于商业产品和项目. 电池背景 因为电池内部有好几个部分,所以本例用了一个Grid来做背景,用Clip属性剪切出一个电池的轮廓,这样不仅显示出一个电池的轮廓,还可以避免水波和气泡跑显示Grid的外面. Clip的内部,是一个Path形状.具体画法就不多说了,以前写过.有兴趣的同学看这里:http://www.cnblogs.com/tsliwei/p/5609035.html 表示电量

随机推荐