【咱們一塊兒寫框架】MVVM的WPF框架(一)—序篇

前言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

----------------------------------------------------------------------------------------------------

注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的推薦】,很是感謝!

 

相關文章
相關標籤/搜索