如何搭建新的WPF項目框架
下面就WPF項目框架搭建步驟一步一步的分享給大家。
在WPF項目開發(fā)中最常用的開發(fā)模式無疑是MVVM模式, MVVM模式開發(fā)的好處,在這里就不詳細討論, 還有 本文中所使用MVVMLight框架,為什么使用MVVM框架(1、框架較輕,2、學(xué)習(xí)成本低、3、適用大多數(shù)中小型項目,4、相對于微軟的prism框架更容易上手)
下面開始 一步一步 搭建框架
第一步: 利用反射創(chuàng)建VM構(gòu)造器
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模式開發(fā)大多數(shù)的開發(fā)者碰到的問題無疑是各個VM之間的數(shù)據(jù)通信問題,利用Dictionary緩存起來有兩個好處:
1、可以解決VM之間相互通信的問題(當(dāng)然你也可以用MvvmLight的 Message機制來通信,PS:個人認(rèn)為完全沒必要用MvvmLight中的 Messgae,如果我們框架搭的合理完全可以規(guī)避去用MvvmLight中 Message,Message比較難于管理,如果在我們的代碼中出現(xiàn)大量的Message無疑是一件痛苦的事情,所以筆者不推薦用MvvmLight中的Message)
2、如果我們的應(yīng)用程序要頻繁的與服務(wù)器做交互,我們完全可以用緩存,以避免每次都去請求服務(wù)器(可以緩存一些在應(yīng)用程序中一直使用的數(shù)據(jù),規(guī)避二次請求)
public static T GetViewModel<T>() where T : ViewModelBase 這個函數(shù)(將我們的VM完全限定名作為KEY緩存)適用于單例模式的VM,
public static T GetViewModel<T>(object[] data,string id) where T : ViewModelBase 這個函數(shù)(主要構(gòu)件帶參數(shù)的VM構(gòu)造函數(shù),id是唯一ID),為什么會用到它,舉個例子
例如我們的QQ聊天窗口,所有聊天窗口基本相同用到的VM類型也是相同,所以這時候就需要多個VM實例了,第一種方法就行不通了 所以會用到這種方法去構(gòu)建VM,并將id作為KEY值緩存起來
第二步:構(gòu)建我們的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,因為大多數(shù)的時候我們會在窗體或用戶控件Loaded的時候去加載數(shù)據(jù),有可能是異步加載,也有可能是同步加載,所以我們在CustomViewModel中
聲明省去了各個VM子類中去聲明LoadCommand的麻煩,使用時我們直接在XAML利用MvvmLight提供的EventToCommand 去綁定LoadCommand,然后在對應(yīng)的VM去重寫CustomViewModel基類中的OnLoad方法就可以了。
2、CloseEvent 故名思議是用來在VM中關(guān)閉窗體用的(詳細用法會在下文中討論)
3、我們也可以將一些公有的數(shù)據(jù)都提煉到VM中來。
第三步 管理窗口:
在開發(fā)程序的時候我們通常要去管理窗口的如果你沒用到MVVM模式 或者是傳統(tǒng)的Winform 你可以隨便的去new Window(),或者隨便的去改Window的構(gòu)造函數(shù),或者隨意的去構(gòu)造單例窗體,但是如果用到了MVVM模式似乎以上所說的一切都變得復(fù)雜了,剛開始的時候我也是挺傷腦筋的,后來在不斷的重構(gòu)代碼中找到了解決方法,(PS:本人也是一名菜鳥,只想把自己在開發(fā)中的問題及解決方法分享出來,未必就是好的解決方案,所以大神們勿噴)下面上代碼: 構(gòu)建我們的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) //如果構(gòu)造參數(shù)為null
{
key = controlType.FullName; //key = T 的完全限定名
}
else
{
// 如果不為空 并且 第二個構(gòu)造參數(shù)為string(第二個參數(shù)代表id -->有可能是GroupId 有可能是UserId);
if (constructors.Length == 2 && constructors[1] is string) //ps:這里本人寫死了可以根據(jù)需求自行修改
{
key = controlType.FullName + constructors[1].ToString(); //key = 控件 完全限定名+id;
}
else //不滿足條件
{
key = controlType.FullName; //key = 限定名
}
}
if (windowManager.ContainsKey(key)) //如果包含KEY
{
windowManager[key].Topmost = true; //設(shè)置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 注冊關(guān)閉事件
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中用來創(chuàng)建UserControl并顯示在Window中的。你可能會問為啥用windowManager 將窗口緩存起來(PS這里主要還是為了解決單例窗口的麻煩),
至于 下面這段代碼,我們可以回到創(chuàng)建的CustomerViewModel中,對這里需要注冊VM中CloseEvent事件,這樣我們在VM中就可以直接調(diào)用OnClose()方法就OK了
#region 注冊關(guān)閉事件
if (content.DataContext as CustomViewModel != null)
{
CustomViewModel vm = content.DataContext as CustomViewModel;
vm.CloseEvent += (obj) =>
{
if (content.DataContext.Equals(obj))
{
window.Close();
}
};
}
#region 注冊關(guān)閉事件
(2)開始剖析 public static void ShowDiaglogUc<T>(string title, object[] constructors = null, bool isDialog = false) where T : UserControl 函數(shù)中的 constructors 參數(shù)
在開始剖析 constructors 之前先讓我們 聯(lián)想一下應(yīng)用場景(可以先想下,QQ的聊天窗口,例如群聊天吧,所有的群聊天都是相同界面,也就是說他們所對應(yīng)的VM應(yīng)該是統(tǒng)一類型的 VM,如果我們雙擊群,則會彈出對應(yīng)相應(yīng)的聊天窗口,正常的思維是會給聊天窗口傳遞參數(shù)也就是組ID 這時候我們的VM就需要構(gòu)造參數(shù)了,還有一個問題就是每個群組聊天窗口只能有一個,總不能每次雙擊就new一個聊天窗口了吧 所以這時候我們就需要做緩存了,) 綜上constructors參數(shù)在配合ViewModelFactory中的 public static T GetViewModel<T>(object[] data,string id) where T : ViewModelBase 方法 可以解決我們VM中需要傳遞參數(shù)的問題,windowManager 可以解決窗口緩存問題(如果你現(xiàn)在還看不明白請 仔細看上面代碼(雖然代碼有點渣),如果實在看不明白可以在留言板吐槽)。
1、 開始 剖析 public static CustomDialogResult ShowOkCancleUC<T>(string title, MsgBoxBtn okCancle, out object data) where T : Control
?。?)開始剖析該函數(shù)前讓我們 新建一個自己的帶返回值的 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 設(shè)置控件
if (btnState == MsgBoxBtn.Ok) //如果為OK狀態(tài)
{
Grid.SetColumnSpan(okBtn, 2); //設(shè)置OK按鈕跨兩列
okBtn.HorizontalAlignment = System.Windows.HorizontalAlignment.Center; //設(shè)置OK按鈕居中對齊
canleBtn.Visibility = System.Windows.Visibility.Collapsed; //設(shè)置Cancel 按鈕隱藏;
if (uc != null)
{
control = uc;
Grid.SetRow(uc, 0); //設(shè)置控件所在Grid 的行
Grid.SetColumnSpan(uc, 2); //設(shè)置控件所在Grid 的列
this.Width = uc.Width; //設(shè)置窗體寬度
this.Height = uc.Height + grid.RowDefinitions[1].Height.Value + 35; //設(shè)置窗體寬度 高度
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); //設(shè)置控件所在Grid 的行
Grid.SetColumnSpan(uc, 2); //設(shè)置控件所在Grid 的列
this.Width = uc.Width; //設(shè)置窗體寬度
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 可以輸出一些數(shù)據(jù),另外我們要自定義一些枚舉
public static CustomDialogResult MessageBoxDialog(string title, string message, MsgBoxBtn okCancle) 主要用來顯示自定義MessageBoxUserControl;和上面得方法差不多,
以上分為三大步驟對WPF 項目框架搭建的介紹,并結(jié)合代碼做剖析,希望對大家有所幫助。
相關(guān)文章
C#采用HttpWebRequest實現(xiàn)保持會話上傳文件到HTTP的方法
這篇文章主要介紹了C#采用HttpWebRequest實現(xiàn)保持會話上傳文件到HTTP的方法,很實用的功能,需要的朋友可以參考下2014-08-08
c#不使用windows api函數(shù)打開我的電腦和獲取電腦驅(qū)動器信息
這篇文章主要介紹了c#不使用windows api函數(shù)打開我的電腦和電腦驅(qū)動器信息的方法,大家參考使用2013-12-12
C#利用性能計數(shù)器監(jiān)控網(wǎng)絡(luò)狀態(tài)
這篇文章主要為大家詳細介紹了C#利用性能計數(shù)器監(jiān)控網(wǎng)絡(luò)狀態(tài)的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01

