前言 我想,有一部分程式員應該是在二三線城市的,雖然不知道占比,但想來應該不在少數。 我是這部分人群中的一份子。 我們這群人,面對的客戶,大多是國內中小企業,或者政府的小部門。這類客戶的特點是,資金有限,人力有限。 什麼意思呢?就是你如果敢給他安一臺Linux伺服器,客戶的信息員和測試員會把你堵在牆 ...
前言
我想,有一部分程式員應該是在二三線城市的,雖然不知道占比,但想來應該不在少數。
我是這部分人群中的一份子。
我們這群人,面對的客戶,大多是國內中小企業,或者政府的小部門。這類客戶的特點是,資金有限,人力有限。
什麼意思呢?就是你如果敢給他安一臺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文件中重寫啟動函數,代碼如下:
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一下就可以了。代碼如下:
VM_WindowCreateUser vm = new VM_WindowCreateUser(); vm.Show();
到此,窗體相關的內容我們已經一起編寫完成了。
接下來需要編寫的是Page和UserControl的基礎使用方式。
但Page和UserControl是被Window使用的,不能直接呈現,所以,在使用Page和UserControl之前,我們需要編寫MVVM框架中,用於在WPF頁面和ViewModel傳遞信息的Command(命令)。
本篇文章就先不介紹Command了,敬請期待下一篇文章,讓我們一起繼續完善我們的框架。
框架代碼已經傳到Github上了,並且會持續更新。
To be continued
Github地址:https://github.com/kiba518/KibaFramework
----------------------------------------------------------------------------------------------------
註:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的【推薦】,非常感謝!
如果您覺得這篇文章對您有所幫助,那就不妨支付寶小小打賞一下吧。