中文字幕日韩一区二区_国产一区二区av_国产毛片av_久久久久国产一区_色婷婷电影_国产一区二区精品

.NET控件Designer架構(gòu)設(shè)計(jì)

  總體結(jié)構(gòu)

Designer設(shè)計(jì)

  Designer總體上由三大部分組成:View,ViewModel和Model,這個(gè)結(jié)構(gòu)借鑒了流行的MVVM模式。這三部分的職責(zé)分工是:

  View

  負(fù)責(zé)把ViewModel以圖形的方式展現(xiàn)出來,它主要在處理畫法。View適合用xaml來表達(dá),對(duì)于某些復(fù)雜的layout,仍然會(huì)需要寫一些code,但這些code不涉及業(yè)務(wù)邏輯。和MVVM的區(qū)別是,我們只是在簡(jiǎn)單輸入的情況下,采用了Behavior模式,對(duì)于復(fù)雜的輸入,由于判斷用戶的意圖需要參考許多其它信息,可能要用到很多Service,或者查閱很多的狀態(tài)信息,這些代碼寫在View端不合適,我們就直接把事件發(fā)給了ViewModel,由ViewModel去處理。View和平臺(tái)相關(guān),不同平臺(tái)(WPF、SL,WP7)的xaml可能不同,代碼也不同。

  ViewModel

  主要負(fù)責(zé)邏輯的處理,接收Event和Command,判斷用戶意圖,改變數(shù)據(jù),并反饋給View。ViewModel既有數(shù)據(jù),又能響應(yīng)事件,而且是一棵樹,所以它本質(zhì)上就是一個(gè)view,只不過是一個(gè)抽象的View,它把瑣碎的畫法丟給了真正的View,只關(guān)心那些和邏輯有關(guān)的數(shù)據(jù)。ViewModel和View有一定的對(duì)應(yīng)關(guān)系,但它的結(jié)點(diǎn)比View要少得多,因此比直接在View上進(jìn)行邏輯處理要簡(jiǎn)單得多。由于ViewModel的數(shù)據(jù)和操作都是針對(duì)抽象的概念進(jìn)行的,因此它和平臺(tái)無關(guān)。為了方便對(duì)ViewModel中的邏輯操作進(jìn)行管理,我們引入了Service和Feature的概念,Service是向其它模塊提供支持的內(nèi)部模塊,是系統(tǒng)的基礎(chǔ),所有的Service構(gòu)成了系統(tǒng)的骨架。Feature是實(shí)現(xiàn)系統(tǒng)外部功能的模塊,F(xiàn)eature之間沒有依賴關(guān)系,它們只依賴于Service。

  系統(tǒng)中的Service不多,而且只關(guān)注最重要的邏輯,代碼量不大,所以Service都是經(jīng)過精心設(shè)計(jì)和良好的測(cè)試,具有很強(qiáng)的穩(wěn)定性。feature是系統(tǒng)的皮肉,它直接暴露給用戶,關(guān)注很多細(xì)節(jié),代碼量大,容易變化,由于feature和feature之間沒有依賴性,所以這種變化不會(huì)對(duì)其它模塊造成影響,利于漸進(jìn)式的開發(fā)。

  Model

  是數(shù)據(jù),按照業(yè)界大師的說法,Model是純粹的數(shù)據(jù)。但我很懷疑這個(gè)說法,如果Model是純粹的數(shù)據(jù),那它就沒有存在的必要,因?yàn)閂iewModel上也有數(shù)據(jù),何必要把數(shù)據(jù)存兩份呢,同步起來還挺麻煩。我的理解是,Model上是有邏輯的,只是這些邏輯是屬于另一個(gè)領(lǐng)域的范圍了。比如Designer的Model,就是Runtime的control,這些Control是有邏輯的,但它們的邏輯已經(jīng)和Designtime沒有任何關(guān)系。ViewModel和Model的關(guān)系是,ViewModel操縱Model,但同時(shí)要監(jiān)測(cè)Model的變化,和Model同步。如果我們清楚除了ViewModel外,不會(huì)有其它的模塊去修改Model(這種情況對(duì)于一些簡(jiǎn)單的Designer是正常的),那么ViewModel和Model的關(guān)系可以更簡(jiǎn)單一些,只有ViewModel改變Model,上圖中ViewModel和Model之間的箭頭就只需要保留左邊那一個(gè)(圖中的箭頭表示數(shù)據(jù)傳遞)。

  從上面的介紹,我們可以看出,View和Model在DesignTime下都是比較簡(jiǎn)單的,復(fù)雜度主要在ViewModel,我們需要進(jìn)一步對(duì)它闡述。

  ViewModel層的結(jié)構(gòu)

Designer設(shè)計(jì)1
  我們前面提到,Designer主體結(jié)構(gòu)分成三大部分:View,ViewModel,Model,這里的概念是一個(gè)宏觀概念,代表它所在的那一層里的所有結(jié)構(gòu)。我們現(xiàn)在討論ViewModel這一層,它里面除了一個(gè)ViewModel樹,還有一些Service和許多的Feature。我們知道,圖形軟件的功能,不外乎就是處理用戶的鼠標(biāo)鍵盤輸入,然后改變數(shù)據(jù),最后以可視化的方式反饋給用戶,因此,我們只要分析清楚我們的軟件是如何來應(yīng)對(duì)這樣一個(gè)輸入輸出過程就可以了。

  我們看上圖,紅色虛線內(nèi)的結(jié)構(gòu)都是在處理輸入,紅色虛線外的部分在處理輸出(展現(xiàn)),可見對(duì)于Designer,輸入非常復(fù)雜,輸出比較簡(jiǎn)單。我們先分析簡(jiǎn)單的輸出:ViewModel時(shí)刻監(jiān)視著Model的變化,一旦發(fā)現(xiàn)Model發(fā)生了變化,就改變自己的Property以同步,注意這里的Model不一定實(shí)現(xiàn)了INotifyPropertyChanged接口,因此這種同步可能不能借用綁定。但ViewModel一定是DependencyObject,或者實(shí)現(xiàn)了INotifyPropertyChanged接口,所以當(dāng)ViewModel的屬性變化后,View通過綁定會(huì)讓展現(xiàn)和數(shù)據(jù)保持一致,輸出過程就完成了。

  對(duì)于輸入,我們需要針對(duì)不同的情況進(jìn)行考慮,基本上,我們可以把輸入分成兩大類:簡(jiǎn)單輸入和復(fù)雜輸入。

  什么是簡(jiǎn)單輸入?

  就是整個(gè)輸入處理過程很簡(jiǎn)單,牽涉的模塊很少,Command有明確的接受對(duì)象。常見的Adorner上的行為,大部分都是如此。舉一個(gè)具體的例子,有一個(gè)Button,當(dāng)它被選中的時(shí)候,會(huì)出現(xiàn)一個(gè)Adorner,上面有一個(gè)Slider,調(diào)整這個(gè)Slider,Button的透明度會(huì)隨著變化。要處理這個(gè)Slider對(duì)Model的改變,最簡(jiǎn)單的做法就是把Slider雙向綁定到對(duì)應(yīng)的Adorner ViewModel的某個(gè)屬性,即使不能用雙向綁定,也可以通過Behavior模式調(diào)用對(duì)應(yīng)ViewModel的Command。整個(gè)過程只涉及到一個(gè)Adorner View,一個(gè)Adorner ViewModel和一個(gè)Button Control,和系統(tǒng)的其它部分沒有什么關(guān)系,這類輸入行為用雙向綁定或者Behavior模式處理最合適。

  什么是復(fù)雜輸入呢?

  就是整個(gè)輸入處理過程涉及到的模塊比較多,受很多系統(tǒng)狀態(tài)的影響,輸入沒有明確的接收對(duì)象,充滿了變化。這類行為在Designer中也很多。舉一個(gè)Multirow Template Designer的例子,一個(gè)CellView上收到一個(gè)MouseLeftButtonDown事件,View應(yīng)該怎么處理呢?它會(huì)調(diào)用ViewModel的什么Command呢?CellView需要先判斷用戶的意圖,但這個(gè)判斷比較有難度。用戶有可能是想選中這個(gè)Cell,如果是這樣需要執(zhí)行Selection Command,但是如果這個(gè)時(shí)候Designer處于Tab Order模式,那就不允許選擇,可能是用戶想改變Tab order的值。也有可能是用戶剛才選擇了一個(gè)ToolboxItem,現(xiàn)在是想創(chuàng)建一個(gè)Cell,還有可能是用戶想移動(dòng)Cell,要進(jìn)行這些判斷,必須要借助其它Service和查詢系統(tǒng)中某些狀態(tài),如果判斷出來是選擇,還得檢查這個(gè)時(shí)候的鍵盤狀態(tài),檢查目前是否支持?jǐn)U展選擇,在擴(kuò)展選擇模式下,按Control鍵和Shift鍵的行為不一樣。我們還得檢查當(dāng)前Cell是否已經(jīng)被選中,如果已經(jīng)被選中,就需要反選,這需要我們查詢Selection Service。如果我們發(fā)現(xiàn)Cell是可以移動(dòng)的,那么MouseLeftButtonDown的處理又得注意了,如果Cell沒有被選擇,要先選中Cell,如果Cell已經(jīng)被選中了,不能立即反選,要看用戶是否后續(xù)有移動(dòng)的操作,反選必須放到MouseLeftButtonUp中進(jìn)行。

  還要考慮到,今后可能需要增加新的Feature,比如增加一個(gè)移動(dòng)畫布的功能,用戶先在Toolbar上單擊了一個(gè)手型Icon的Command,然后再在CellView上單擊了一下,這個(gè)時(shí)候以前的判斷都無效,因?yàn)橛脩衄F(xiàn)在是要移動(dòng)整個(gè)畫布,那么我們很可能得去修改以前的CellView的Code。總之,View在處理某些事件的時(shí)候,需要知道的東西太多,只靠ViewModel提供的Property遠(yuǎn)遠(yuǎn)不夠,ViewModel層必須把整個(gè)結(jié)構(gòu)(所有的Service和各種狀態(tài))完全暴露給View層,這樣顯然不符合我們模塊劃分的思路。因此,對(duì)于這類復(fù)雜輸入,我們讓View什么都不處理,而是把事件轉(zhuǎn)發(fā)給ViewModel層去處理。

除了某些事件處理很復(fù)雜,某些Command的處理也比較麻煩,比如菜單上的cut,copy,paste,delete等,這些Command沒有明確的接收對(duì)象,最終由誰來處理需要根據(jù)系統(tǒng)當(dāng)時(shí)的各種狀態(tài)決定。為了解決這類Command,我們必須設(shè)計(jì)一個(gè)Command的路由機(jī)制,讓那些關(guān)心這個(gè)Command的feature能夠按照一定的優(yōu)先級(jí)來處理這個(gè)Command.

  為了處理上述的復(fù)雜輸入,我們學(xué)習(xí)wpf designer,設(shè)計(jì)了一個(gè)比較復(fù)雜的機(jī)制。我們?cè)O(shè)計(jì)了一個(gè)叫Tool的類,它有一個(gè)Task集合,按照一定的優(yōu)先級(jí)把Command交給每個(gè)Task處理。Task從哪兒來呢?Task屬于Feature,當(dāng)一個(gè)Feature認(rèn)為它需要監(jiān)聽某些Command時(shí),它會(huì)把自己的Task添加到Tool的Tasks中。事實(shí)上Task并沒有直接處理Command,Task內(nèi)部有一個(gè)CommandBinding集合,它負(fù)責(zé)處理Command。Task的Commandbinding在執(zhí)行代碼時(shí),修改ViewModel的屬性,或者執(zhí)行一個(gè)ViewModel的Command。

  對(duì)于View層轉(zhuǎn)發(fā)給ViewModel層的Event,在處理中會(huì)被先翻譯成Command,然后按照前面的Command流程處理。在這個(gè)過程中,需要經(jīng)歷下面兩個(gè)步驟:

  第一步

  View接到鼠標(biāo)鍵盤事件后,會(huì)調(diào)用InputService的一個(gè)函數(shù)PerformInput,把事件轉(zhuǎn)發(fā)給InputService。InputService會(huì)對(duì)這個(gè)事件進(jìn)行預(yù)處理,然后再轉(zhuǎn)發(fā)出去。預(yù)處理解決兩個(gè)問題:1.把針對(duì)View的事件,轉(zhuǎn)換成針對(duì)ViewModel的事件。因?yàn)閂iewModel就是一個(gè)抽象的View,如果把事件轉(zhuǎn)換成了針對(duì)ViewModel的事件(就是把事件的參數(shù)Sender轉(zhuǎn)換成對(duì)應(yīng)的ViewModel,EventArgs轉(zhuǎn)變成適合于ViewModel的EventArgs),我們就可以按照以前熟悉的windows事件處理思路來處理ViewModel的事件,把View徹底屏蔽掉。2.添加或改變一些事件,以方便后續(xù)的處理。Designer有一些頻率特別高的操作,比如Drag,系統(tǒng)的默認(rèn)事件比較弱,或者沒有對(duì)應(yīng)的事件,如果我們?cè)谶@兒進(jìn)行一些強(qiáng)化,后面的處理就會(huì)減少很多麻煩。

  第二步

  InputService對(duì)事件進(jìn)行完預(yù)處理后,會(huì)把事件交給Tool。Tool不但可以對(duì)Command派發(fā),還能對(duì)Event進(jìn)行派發(fā),因?yàn)門ask中除了有CommandBinding,還有InputBinding,InputBinding用于處理事件。事件被處理完后,會(huì)生成一個(gè)Command,這個(gè)過程就是把事件翻譯成Command的過程。翻譯成的Command,會(huì)發(fā)給Tool處理,繞這個(gè)圈是為了和前面的Command處理流程保持一致。

  在和大家的討論中,覺得輸入處理的流程太復(fù)雜,尤其是我開始的時(shí)候,為了減少ViewModel層的信息入口,不建議View去直接改變ViewModel,所有事件都轉(zhuǎn)發(fā)給ViewModel層來處理。大家發(fā)現(xiàn),如果那樣做,即使做一個(gè)很簡(jiǎn)單的輸入,都要繞很大一個(gè)圈子,非常麻煩。因此,對(duì)于簡(jiǎn)單的輸入處理,我們認(rèn)為應(yīng)該用雙向綁定或者Behavior模式,直接修改ViewModel,只對(duì)于那種比較復(fù)雜的輸入,才把事件轉(zhuǎn)發(fā)給ViewModel。這樣一來,這個(gè)圖變得似乎更復(fù)雜了,但我經(jīng)過仔細(xì)考慮,覺得不能刪減,因?yàn)镈esigner有些輸入處理的流程確實(shí)非常復(fù)雜,過于簡(jiǎn)單的結(jié)構(gòu)會(huì)導(dǎo)致后面寫feature的時(shí)候需要考慮更多的問題。

  另外說一下Tool,大家不大適應(yīng)這個(gè)結(jié)構(gòu)。因?yàn)榘凑瘴覀円郧暗乃悸罚词故录唤oViewModel層處理,經(jīng)過預(yù)處理后,InputService也應(yīng)該直接把事件派發(fā)給對(duì)應(yīng)的ViewModel,即使要路由,也可以學(xué)Wpf的路由機(jī)制,那樣大家都比較熟悉。但我認(rèn)為,那樣的設(shè)計(jì)會(huì)讓大量的邏輯寫到ViewModel中,和ViewModel綁得比較死,這樣會(huì)有兩個(gè)大的缺點(diǎn):

  復(fù)雜輸入處理

  邏輯往往跨越多個(gè)ViewModel,本來是一個(gè)完整的邏輯,不得不分片寫在不同的ViewModel中,依靠全局變量或者Service來協(xié)調(diào)。比如我們?cè)赪inform Designer中,就設(shè)計(jì)了一個(gè)DragService,用得非常頻繁,原因就是在Drag中,不同的View需要協(xié)作來完成一些任務(wù),它們只能通過DragService來協(xié)調(diào)。但現(xiàn)在這種機(jī)制下,就不需要DragService了。

  對(duì)原有的行為進(jìn)行修改很困難

  一個(gè)典型場(chǎng)景就是,在某種狀態(tài)下,需要禁止掉某些原有的行為。在Winform Designer下,我們只能有兩種處理方式:一,修改原來的Code,增加判斷條件,這種方式很容易搞出來新的Bug。二,在原來的View上蓋上一個(gè)透明的View,把事件劫持掉,這種方式屬于比較變態(tài)的方式,系統(tǒng)中如果用多了,會(huì)讓后面的人很難理解原有的設(shè)計(jì)。微軟的Winform Designer在處于這種情況時(shí)有一個(gè)經(jīng)典的變態(tài)處理,它需要放一個(gè)Runtime的Control在Designer上,但不想讓它的行為在Designer中起作用,或者在某些情況下有選擇的讓它起作用,它用了hook技術(shù),劫持windows消息,如果有需要,可以選擇性的放過去一些消息。Visual Studio中這類東西用得比較多,導(dǎo)致即使你按正常的方式放一個(gè)Control在Visual sdudio中,它有時(shí)工作也不正常,因?yàn)樗哪承┫⒈籬ook劫持掉了。wpf中提供了Preview message,在某些情況下能夠簡(jiǎn)化這類問題的處理,但我相信它的靈活性還是遠(yuǎn)遠(yuǎn)不如Tool這種把消息集中起來處理的方式,因?yàn)檫@種機(jī)制把邏輯徹底從ViewModel中剝離出來了,談不上需要改變哪一個(gè)ViewModel的行為,因?yàn)閂iewModel沒有控制行為的代碼,所有行為都屬于外面的Feature,只要Feature發(fā)生變化,對(duì)應(yīng)的ViewModel的“行為”自然就發(fā)生變化。

  當(dāng)然,Tool這種把所有消息集中處理的方式也有缺點(diǎn),就是模塊間的干擾非常嚴(yán)重,就相當(dāng)于編程語言中的全局變量,方便了使用,但帶來了干擾。因此我們推薦復(fù)雜的輸入用這種方式,簡(jiǎn)單的輸入用Behavior模式,直接修改ViewModel,或者通過DelegateCommand,把View的事件直接轉(zhuǎn)發(fā)給ViewModel處理,繞過這個(gè)機(jī)制。但是,我們一定要意識(shí)到,繞過這個(gè)機(jī)制會(huì)帶來的問題,就是后面要改變?cè)械男袨槭遣恍械模驗(yàn)橄⒌膫鬏斶^程中沒有留下改變的控制點(diǎn),只能去修改原有的View和ViewModel的代碼。在designer中,這類簡(jiǎn)單輸入方式主要應(yīng)該用于Adorner,因?yàn)锳dorner一般都是臨時(shí)使用一下,輸入簡(jiǎn)單,即使后面發(fā)現(xiàn)需要改變它的行為,不得已可以換一個(gè)AdornerModel和AdornerView,也不會(huì)對(duì)系統(tǒng)造成多大影響。但如果你要把Multirow Template designer中SectionViewModel和SectionView,或者CellViewModel和CellView換了,那影響就大了去了。所以我們今后在選擇哪種輸入處理方式時(shí),一定要充分考慮到后面變化的需要。

  View和ViewModel的對(duì)應(yīng)關(guān)系

Designer設(shè)計(jì)2
  討論中大家覺得View和ViewModel的對(duì)應(yīng)關(guān)系比較復(fù)雜,所以這兒?jiǎn)为?dú)花一節(jié)來談?wù)勊鼈兊年P(guān)系。舉一個(gè)大家熟悉的MultiRow的例子,現(xiàn)在假設(shè)有一個(gè)SectionViewModel,它的Chilren中有兩個(gè)Cell,分別是CellViewModel1和CellViewModel2,現(xiàn)在我們看這個(gè)ViewModel Tree如何展現(xiàn),事件如何傳遞,HitTest是如何實(shí)現(xiàn)的。

  先看一下我們會(huì)怎樣來設(shè)計(jì)View,為了便于用Xaml表達(dá),我們一般會(huì)用UserControl來表達(dá)View,雖然CustomControl也能用Xaml,但它的xaml一般要寫到Resource中,所以我們一般不用。我們現(xiàn)在有兩個(gè)類:SectionViewModel和CellViewModel,因此,對(duì)應(yīng)的我們會(huì)設(shè)計(jì)兩個(gè)UserControl,分別叫做SectionView和CellView。這兒我要說明的是,由于CellView很簡(jiǎn)單,做產(chǎn)品的時(shí)候也許不會(huì)單獨(dú)為它用一個(gè)UserControl,而是在Section的Xaml里直接表達(dá)了,甚至MultiRow的整個(gè)Template都用一個(gè)UserControl描述。在這里為了方便闡述概念,我們把兩個(gè)View看成是兩個(gè)獨(dú)立的UserControl。

  怎樣來設(shè)計(jì)SectionView呢?我們會(huì)在UserControl中放一個(gè)ItemsControl,把它的ItemsSource邦定到datacontext的Chilren屬性上,然后把ItemsPanel設(shè)置成Canvas,在ItemTemplate中指定用CellView來展現(xiàn)CellViewModel,當(dāng)然,我們也可以用隱式DataTemplate來表達(dá)。

  CellView呢?我們就在UserControl中放一個(gè)Border,把Border的Background綁定到DataContext的Background就可以了。

  當(dāng)外部某個(gè)對(duì)象把SectionView加載到VisualTree上時(shí),它會(huì)負(fù)責(zé)把SectionView的DataContext指向SectionViewModel(這個(gè)對(duì)象很可能也是一個(gè)DataTemplate),通過綁定,所有的CellViewModel都會(huì)有對(duì)應(yīng)的CellView,最后的VisualTree會(huì)如圖中所示。

  我們看到,VisualTree的Visual結(jié)點(diǎn)明顯多于ViewModel的結(jié)點(diǎn),那么它們的對(duì)應(yīng)關(guān)系是如何的呢?有兩條原則:
  1.一個(gè)ViewModel有且只有一個(gè)Visual和它對(duì)應(yīng),我們可以把這個(gè)Visual叫做這個(gè)ViewModel的View。一個(gè)Visual對(duì)應(yīng)一個(gè)或零個(gè)ViewModel。
  2.如果ViewModel A是ViewModel B的祖先,那么對(duì)應(yīng)的Visual A也應(yīng)該是Visual B的祖先,如果ViewModel A不是ViewModel B的祖先,那么對(duì)應(yīng)的Visual A也不應(yīng)該是Visual B的祖先。

  按照我們的設(shè)計(jì),ViewModel和Visual對(duì)應(yīng)關(guān)系如上圖,紅色結(jié)點(diǎn)的Visual就是ViewModel對(duì)應(yīng)的View。那么這個(gè)對(duì)應(yīng)關(guān)系是怎么記錄的,因?yàn)榻窈蟮暮芏噙壿嫊?huì)依賴這個(gè)數(shù)據(jù)。

  首先,我們會(huì)在設(shè)計(jì)的時(shí)候認(rèn)定ViewModel和Visual的對(duì)應(yīng)關(guān)系,如上圖,我們認(rèn)為SectionViewModel對(duì)應(yīng)SectionView(UsrControl),CellViewModel對(duì)應(yīng)CellView(UserControl),所以我們會(huì)在這兩個(gè)UserControl的Xaml中設(shè)置一個(gè)附加屬性ViewProperties.ViewModel,把它綁定到DataContext上,這樣就讓View指向了ViewModel,在附加屬性ViewProperties.ViewModel的PropertyChanged事件里,我們會(huì)創(chuàng)建一個(gè)IViewModel對(duì)象賦給對(duì)應(yīng)的ViewModel的View屬性,IViewModel會(huì)抓著真正的Visual。這樣ViewModel和View的雙向?qū)?yīng)關(guān)系就建立起來了。

  如何解決HitTest?

  View層會(huì)實(shí)現(xiàn)一個(gè)IViewService,里面有一個(gè)函數(shù):IEnumerable<IView> FindViews(Point p),其它對(duì)象可以調(diào)用這個(gè)函數(shù)來拿到HitTest的IView,再通過IView拿到ViewModel(我想這一步可以簡(jiǎn)化到直接返回ViewModel,目前是返回的IView)。View層是如何查找View的呢?它會(huì)調(diào)用VisualTreeHelper的HitTest,找到Hit的Visual,然后遍歷父Visual,找到某個(gè)有對(duì)應(yīng)ViewModel的Visual,那么這個(gè)Visual就是Hit的View了。

  解決了HitTest,InputService對(duì)事件的預(yù)處理就簡(jiǎn)單了,它拿到一個(gè)Mouse事件參數(shù)后,會(huì)得到Mouse的坐標(biāo),然后調(diào)用IViewService的FindView函數(shù),就可以知道這個(gè)Mouse事件是針對(duì)哪一個(gè)ViewModel,然后把Sender和EventArgs都轉(zhuǎn)換成適合于ViewModel的,再轉(zhuǎn)發(fā)出去就可以了。

  與PropertyGrid交互

  會(huì)有一個(gè)專門的Service來負(fù)責(zé)與PropertyGrid交互,展現(xiàn)在PropertyGrid上的對(duì)象是ViewModel創(chuàng)建的一個(gè)對(duì)象,因此受ViewModel控制,ViewModel可以決定是把自己交給PropertyGrid,或者設(shè)計(jì)另一個(gè)類型,融合Model的Property和Design Time的Property。

  序列化

  序列化由專門的Service來完成,Service中登記有不同類型的ViewModel的Serializer,默認(rèn)的Serializer會(huì)調(diào)用Runtime的序列化方法直接把RuntimeControl序列化成文本,這個(gè)序列化設(shè)計(jì)學(xué)習(xí)Winform designer的序列化架構(gòu)。

  Undo/Redo

  從我們上面的設(shè)計(jì)看,所有的輸入都要經(jīng)過ViewModel,所以在ViewModel上做Undo/Redo。系統(tǒng)中有一個(gè)UndoService,當(dāng)一個(gè)ViewModel的Property被改變時(shí),會(huì)通知UndoService,UndoService會(huì)把改變前的值記錄下來。值得注意的是,不是所有的ViewModel的屬性都需要Undo,這點(diǎn)具體設(shè)計(jì)時(shí)根據(jù)需要判斷,一般來說,Runtime的Property都需要,非Runtime的Property可能有部分需要。我考慮過根據(jù)Undo和序列化的要求,在ViewModel和runtime control之間再隔離出來一個(gè)層次,比如叫ModelItem,這樣結(jié)構(gòu)上更清楚一些。但多一個(gè)層次開發(fā)的時(shí)候會(huì)多不少工作,覺得不劃算,目前暫定由ViewModel兼任這個(gè)職責(zé)。

  架構(gòu)如何應(yīng)對(duì)未來的變化

  目前的架構(gòu)是針對(duì)復(fù)雜Designer設(shè)計(jì)的架構(gòu),如果未來的Designer比較簡(jiǎn)單,這個(gè)架構(gòu)是不是有點(diǎn)高射炮打蚊子呢?我的想法萬一未來的Designer比較簡(jiǎn)單,這個(gè)架構(gòu)可以從下面三個(gè)地方去簡(jiǎn)化:
  1.砍掉輸入的無關(guān)事件和無關(guān)Feature.目前的架構(gòu)添加了一些事件,如Drag,實(shí)現(xiàn)了一些和這些事件有關(guān)的核心Feature,如果未來不需要,可以砍掉。因?yàn)榘船F(xiàn)在的架構(gòu),F(xiàn)eature是獨(dú)立的,彼此互不影響,把這類Feature刪掉即可。事件也一樣,刪減事件不影響整體流程。
  2.如果仍然覺得復(fù)雜,可以把Tool,Task等概念刪掉,增加Tool,task的概念,是為了讓輸入集中處理,擁有更強(qiáng)的靈活性。如果把這些概念刪除掉,InputService直接把事件派發(fā)給對(duì)應(yīng)的ViewModel就可以了,這就相當(dāng)于winform的事件機(jī)制,由ViewModel直接處理事件。這一步把輸入大大的簡(jiǎn)化了。
  3.如果上面簡(jiǎn)化還不夠,把InputService也干掉,由View利用Behavior處理輸入,然后調(diào)用ViewModel的Command,這就變成了經(jīng)典的MVVM模式,到這一步應(yīng)該化到最簡(jiǎn)了。

NET技術(shù).NET控件Designer架構(gòu)設(shè)計(jì),轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 一区二区三区高清不卡 | 伊人精品在线视频 | 久久久久久久久久久丰满 | 久久久久国产精品免费免费搜索 | 国产精品亚洲精品 | 欧美日韩在线观看一区 | 国产精品 欧美精品 | 国产精品无码专区在线观看 | 亚洲日韩中文字幕一区 | 欧美日韩精品一区二区三区四区 | 日韩欧美中文字幕在线观看 | 午夜精品久久久 | 国产伦一区二区三区 | 日韩影院在线观看 | 亚洲欧洲国产视频 | 天堂一区二区三区 | 久久av影院 | 国产电影一区二区在线观看 | 日本精品一区二区三区视频 | 成人午夜免费福利视频 | 免费精品久久久久久中文字幕 | 日韩在线不卡 | 一级二级三级黄色 | 国产精品观看 | 神马久久久久久久久久 | 亚洲欧美视频一区二区 | 久久精品亚洲精品 | 日韩精品不卡 | 成人三级网址 | 中文字幕第十页 | 97伦理电影网 | 日韩手机在线看片 | 欧美性猛交一区二区三区精品 | 狠狠撸在线视频 | 亚洲高清在线观看 | 91精品国产91久久久久福利 | 欧美日一区二区 | 日韩欧美国产一区二区三区 | 亚洲精品久久久久久久久久久 | 毛片.com | 日韩国产三区 |