前言html
我想,有一部分程序員應該是在二三線城市的,雖然不知道佔比,但想來應該不在少數。git
我是這部分人羣中的一份子。程序員
咱們這羣人,面對的客戶,大可能是國內中小企業,或者政府的小部門。這類客戶的特色是,資金有限,人力有限。github
什麼意思呢?就是你若是敢給他安一臺Linux服務器,客戶的信息員和測試員會把你堵在牆角問候你全家安好,他們Window都用不明白呢,你給安Linux,要瘋啊。算法
因此,Core對咱們而言,沒有意義,由於你們都是Windows。服務器
關於業務架構
在二三線城市的咱們,立身之本不是寫算法,也不是各類高級的、新出的技術,而是,寫業務模塊。框架
不要小看寫業務模塊,在二三線城市,一個不會寫業務模塊的程序員,即使知識面再廣,也是個爛程序員。爲何?由於他不能幹活呀。異步
其實把業務模塊寫好,並非件容易的事。由於它涉及到對業務的理解,對社會的認知。ide
以我多年的經驗,能寫好業務模塊的優秀開發人員,一般都須要三四年經驗。普通一點,大約就須要五到十年。固然還有十年以上經驗,還很沒掌握寫業務的。
這裏面有個特例,那就是碩士和博士。由於他們的年齡較大,閱歷較多,因此,一般兩年就能把業務寫的很好。此外就沒有特例了,什麼一年經驗就能架構,剛畢業就是高級程序員的,那都是培訓機構騙畢業生的。
可是,不得不說,高學歷真的管用,碩士博士的成材率真的很高。大多數都能成爲及格的程序員。
關於框架
回到寫框架這件事。在我看來,寫框架這件事是個程序員都能幹。但寫的好壞就另說了,因此寫框架這件事仍是與經驗掛鉤的。
在個人認知中,技術視野相對更高,技術範圍更廣的人寫的框架會更好。因此,我認爲,[實戰]架構師和高級程序員,在本質上沒有區別,都是程序員。
只是架構師技術更會好一點,而且接受過項目的洗禮。然而,一個項目只能洗禮一我的,因此能不能成爲架構師,就不能只看技術了,要看老闆給誰機會了。說白了,就是老闆肯不願花錢賭你能成事。
因此,當技術相差無幾,溝通能力,文檔能力,甚至生活狀態,家境,毅力都是領導考察的依據。所以,機會不是留給有準備的人,而是留給各方面都更出色的人。
固然,若是老闆承認你,一年經驗作架構師也不是沒可能。但在資金有限,人員有限的二三線城市,能遇到這樣腦殘的領導或老闆的機率不高。
雖然架構師不是人人都能作,但框架是能夠先學會編寫的,畢竟這是個基礎。有了基礎,就算不能年輕有爲,但起碼有個機會。
也許,人家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
----------------------------------------------------------------------------------------------------
注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的【推薦】,很是感謝!