精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

WPF中改進(jìn)自定義Command一些想法

開發(fā) 開發(fā)工具
自定義Command來(lái)源于Command命令模式,Command模式它封裝的是命令,把命令發(fā)出者的責(zé)任和命令執(zhí)行者的責(zé)任分開,直白的說(shuō)為了調(diào)用與具體實(shí)現(xiàn)解耦。

在WPF中定義的接口為ICommand,叫這個(gè)名字顯而易見,為什么不叫IXXXCommand,比如ICurryCommand,不好意思微軟的WPF控件不是我做的,否則我會(huì)考慮這一命名方案。當(dāng)然如果你感覺微軟定義的有缺陷準(zhǔn)備自己著手打造一套全新控件包括新的ICommand接口,那么您可以就此跳過(guò)了。

  1. publicinterfaceICommand  
  2. {  
  3. eventEventHandlerCanExecuteChanged;  
  4. boolCanExecute(objectparameter);  
  5. voidExecute(objectparameter);  

對(duì)于Execute方法不難理解,對(duì)于命令模式來(lái)說(shuō)有個(gè)統(tǒng)一的處理函數(shù)是必須的,自然包括可能的傳參;而對(duì)于CanExecute從字面意義上就可以了解到——方法能不能執(zhí)行,實(shí)際意義在于吃不到就不要讓人看到,執(zhí)行了函數(shù)然后告訴你由于啥啥狀況實(shí)際上不能運(yùn)行,還不如一開始就告訴別人這個(gè)函數(shù)執(zhí)行不了,至于為什么執(zhí)行不了,那就要自己想辦法通知咯;那為什么要有個(gè)事件呢?打個(gè)比方店里貨賣完了我不能買了,但進(jìn)貨后可以買,可什么時(shí)候能進(jìn)到貨我并不知道,需要店家通知。讓店家通知這個(gè)動(dòng)作在程序來(lái)說(shuō)就是注冊(cè)事件,告訴命令的發(fā)出者什么時(shí)候才能執(zhí)行,這個(gè)也就是CanExecuteChanged的由來(lái);在WPF中對(duì)于控件不能CanExecute的做法通常都是把控件的IsEnable設(shè)成False,當(dāng)注冊(cè)的CanExecuteChanged得到回應(yīng)時(shí)才設(shè)置成True。

  1. //Summary:  
  2. //Definesanobjectthatknowshowtoinvokeacommand.  
  3. publicinterfaceICommandSource  
  4. {  
  5. //Summary:  
  6. //Getsthecommandthatwillbeexecutedwhenthecommandsourceisinvoked.  
  7. ICommandCommand{get;}  
  8. //  
  9. //Summary:  
  10. //Representsauserdefineddatavaluethatcanbepassedtothecommandwhen  
  11. //itisexecuted.  
  12. //  
  13. //Returns:  
  14. //Thecommandspecificdata.  
  15. objectCommandParameter{get;}  
  16. //  
  17. //Summary:  
  18. //Theobjectthatthecommandisbeingexecutedon.  
  19. IInputElementCommandTarget{get;}  

作為命令的發(fā)出者,也就是調(diào)用者,微軟也給出了一個(gè)接口,自定義Command意義不必說(shuō)了,它通常都是在控件的Click中執(zhí)行,最常見的Button,CheckBox,RadioButton(注意這些控件實(shí)際都繼承于ButtonBase,所以你需要制作有Click動(dòng)作的控件不是有特別需求建議從他繼承)MenuItem,CommandParameter就是Command中Execute方法的參數(shù)。最后一個(gè)屬性CommandTarget是為了解決類似這種情況:右鍵菜單上有個(gè)粘貼命令,執(zhí)行命令后是把剪貼板的內(nèi)容復(fù)制到相對(duì)應(yīng)的文字框中,而不是把剪貼板的內(nèi)容拷貝到右鍵菜單上,這里的CommandTarget便是那個(gè)文字框,CommandTarget默認(rèn)為當(dāng)Command是RoutedCommand才能使用;當(dāng)CommandTarget為空時(shí),MSDN的說(shuō)法是找到當(dāng)前焦點(diǎn)所對(duì)應(yīng)的控件(KeyboardFoucs),如點(diǎn)擊Button,命令執(zhí)行后得到焦點(diǎn)的應(yīng)該是你點(diǎn)擊的那個(gè)Button,可我Reflector的結(jié)果貌似CommandTarget為空時(shí),直接用了Command發(fā)出的者,雖然都是同一個(gè)Button,但總感覺有點(diǎn)怪。

說(shuō)了這些你是不是覺得這三個(gè)屬性的值應(yīng)該都是外部給的,可微軟居然定義為get只讀,我也百思不得其解,這里還值得一提的是ICommandSource只是一種規(guī)范,和命令必須繼承ICommand不同(要不然至少微軟的控件不認(rèn)),不是必須的,可為了規(guī)范期間建議繼承該接口,方便他人閱讀理解也好為一些操作統(tǒng)一做法。

內(nèi)置Command

前面說(shuō)了ICommand只是一個(gè)接口,好處是你可以隨意實(shí)現(xiàn),壞處便是每次使用都需要建立一個(gè)實(shí)現(xiàn)它的具體類,那么微軟有沒有給個(gè)默認(rèn)的實(shí)現(xiàn)類,答案是肯定的,它叫做RoutedCommand,不用不知道,一用嚇一跳,默認(rèn)的這個(gè)RoutedCommand類居然不能傳委托,為什么說(shuō)不能穿委托很詫異,上面說(shuō)了Command的主要功能是有個(gè)函數(shù)讓人執(zhí)行,可函數(shù)不傳給他,你讓別人執(zhí)行啥?(派生于他的類幾乎啥也做不了——他沒有任何虛方法),微軟這里又用了一招——CommandBinding,他彌補(bǔ)了RoutedCommand在功能上的缺陷,可以為ICommand指定CanExecute委托和Execute委托,RoutedCommand是ICommand的具體實(shí)現(xiàn),自然可以舒舒服服的享用,不過(guò)CommandBinding的出現(xiàn)真的只為了RoutedCommand的亡羊補(bǔ)牢?

試想有這樣一種要求,在xaml中有個(gè)Grid,Grid中有個(gè)Button,點(diǎn)擊Button需要Grid背景變色??吹竭@個(gè)要求很多人可能笑了,很簡(jiǎn)單嘛,注冊(cè)Button的Click事件,為Grid取個(gè)名字,在Click的事件委托中為Grid的Background賦值,沒錯(cuò)。

假使把這個(gè)Button封裝到一個(gè)UserControl中,Grid中包含的只是UserControl,這個(gè)時(shí)候依舊需要點(diǎn)擊Button來(lái)修改Grid的顏色,有些人已經(jīng)破口而出了,在UserControl中定義一個(gè)事件,在Button的Click事件委托中調(diào)用這個(gè)事件,一切看起來(lái)都很輕松;

那么現(xiàn)在假設(shè)Button被裝到一個(gè)Style中,我繼承的不是UserControl而是Control,你可能會(huì)聳聳肩,說(shuō)道那只好注冊(cè)事件路由就可以了比如this.AddHandle(Button.ClickEvent,XXDelegate);可如果我現(xiàn)在里面放的按鈕不是一個(gè)而是一百個(gè)呢?我只需要其中的一個(gè)有改變Grid的功能。為Button取個(gè)名字然后判斷也是個(gè)辦法,用Button上的文字顯然會(huì)受到多語(yǔ)言的困擾。

最后這個(gè)為Grid改變背景的功能還被放到另外50個(gè)按鈕上以及一些MenuItem上,甚至需要Ctrl+K這樣的快捷鍵來(lái)實(shí)現(xiàn),您是否還有熱情為他們一一取名判斷?

那用CommandBinding怎么解決呢?綜觀這些按鈕,菜單,快捷鍵的作用只有一個(gè),就是為Grid改變背景,那么換句話說(shuō)他們執(zhí)行的是同一個(gè)命令,只要讓Grid知道有人執(zhí)行了這個(gè)命令,然后得到這個(gè)消息后自己改變背景就可以了,也可以理解為命令沿可視樹向上通知直到有人接收。

命令的向上傳遞,容易讓我們想到事件路由,事實(shí)也是如此,我們知道事件路由首先得定義一個(gè)RoutedEvent,事件發(fā)出者通過(guò)方法RaiseEvent傳遞RoutedEventArgs參數(shù)通知,當(dāng)RoutedEventArgs中的Handled屬性為True時(shí),會(huì)阻止之后的事件執(zhí)行,除非事件在開始的時(shí)候是通過(guò)AddHandle方法注冊(cè),且把第三個(gè)參數(shù)handledEventsToo設(shè)為了True,那么這個(gè)RoutedEvent在哪里?這個(gè)我們又要說(shuō)到CommandManager這個(gè)類,他在其中定義了PreviewExecutedEvent,ExecutedEvent,PreviewCanExecuteEvent等事件,通過(guò)Reflector可以看到UIElement的RegisterEvents方法中有這樣的定義(其中的type指的是typeof(UIElement)):

也就是說(shuō)凡是派生于UIElement的子類都可以受到這個(gè)路由傳遞。同理沒有繼承與UIElement的類只要注冊(cè)以上事件便可接受Command的響應(yīng)。大家具體實(shí)做后會(huì)發(fā)現(xiàn),CommandManager.ExecutedEvent的參數(shù)ExecutedRoutedEventArgs類它的構(gòu)造函數(shù)是internal,意思就是說(shuō)我們不能通過(guò)普通的new來(lái)創(chuàng)建,通常在我們習(xí)慣性的問(wèn)候了一些女性后,便開始接受這樣無(wú)奈的事實(shí)——使用RoutedCommand是官方唯一指定的具備引發(fā)CommandManager.ExecutedEvent條件的途徑(可以實(shí)例化ExecutedRoutedEventArgs,內(nèi)部關(guān)系到處存在,唉…)。

說(shuō)來(lái)這些或許有人開始點(diǎn)頭,之后又開始疑惑這和CommandBinding有啥關(guān)系,完全是CommandManager和RoutedCommand的那點(diǎn)事,他怎么進(jìn)行第三者插足來(lái)運(yùn)行那些委托方法?以UIElement.OnExecutedThunk來(lái)做說(shuō)明,它其實(shí)調(diào)用的是CommandManager.OnExecuted(objectsender,ExecutedRoutedEventArgse)sender就是當(dāng)前的UIElement,這個(gè)方法會(huì)瞧瞧UIElement上的CommandBindingCollection看其中的CommandBinding包含的Command有沒有和e中的Command相同的,因?yàn)槭鞘录酚?,他可根?jù)可視樹往上找,一個(gè)不成再看下一個(gè),如果有則執(zhí)行CommandBinding的OnExecuted,也就是運(yùn)行委托傳入的方法,之后把e.Handled設(shè)為True,這使得我們同一個(gè)Command的委托方法只能用CommandBinding一次,連續(xù)定義幾個(gè)相同委托的CommandBinding沒有任何意義,同理CommandManager.AddExecutedHandler加入的委托也不能引發(fā),除非顯示的用AddHanlde把第三個(gè)參數(shù)設(shè)為True——

uiElementControl.AddHanlde(CommandManager.ExecutedEvent,xxxDelegate,true),設(shè)成True的后果是這個(gè)委托每次必執(zhí)行。

擴(kuò)展自定義Command

對(duì)于程序來(lái)說(shuō),我們希望把業(yè)務(wù)邏輯和呈現(xiàn)盡量分離,以期實(shí)現(xiàn)不同UI的相同調(diào)用,一個(gè)程序B/S架構(gòu)能用,C/S架構(gòu)也能用,或許有人說(shuō)了:這不就是要把業(yè)務(wù)封裝成個(gè)DLL或是WebService嘛,我們?cè)谟肳CF完全沒問(wèn)題。是的,這樣可以更方便的測(cè)試并增加代碼的重用性降低出錯(cuò)幾率。隨著人口的增長(zhǎng),剩余勞動(dòng)力的增加,各種分工愈趨細(xì)化…等等,先不要仍雞蛋,開個(gè)玩笑也不行?拿Web前端打比方,需要的技術(shù)可能有javascript、vbScript、css、html、圖片處理(如PS),在有些狀況下這事我們?nèi)噶?,但在?nèi)心深處或許有一個(gè)聲音:我需要美工;潛臺(tái)詞是沒有美術(shù)細(xì)胞。

我們希望美工干什么?界面美化?廢話?界面美化包括頁(yè)面布局、色調(diào)搭配、圖片修改等,那么之上的這些技術(shù)中留下的可能只剩javascript和vbscript了,那javascript能干什么?在ajax沒有誕生的歲月,有段時(shí)間他已經(jīng)淪落到做些簡(jiǎn)單的動(dòng)畫效果和動(dòng)態(tài)增加表單元素之類的地步,頁(yè)面回調(diào)刷新,太復(fù)雜的也沒有必要,甚至于在那段時(shí)間我都有聽到一些少用javascript的言論,現(xiàn)在反觀自然是毛骨悚然,如同回望50年前的生活,也是不可想象的,時(shí)代在進(jìn)步,思想也在變化。

Ajax中數(shù)據(jù)一般是傳遞json,由于http的局限我們通過(guò)字符串來(lái)模擬對(duì)象,一個(gè)對(duì)象通常對(duì)應(yīng)固定的UI,當(dāng)對(duì)象數(shù)據(jù)發(fā)生變化時(shí)UI也能夠發(fā)現(xiàn)變化,我們希望有份模板可以留給美工修改,假設(shè)對(duì)象為Employee上面有個(gè)屬性為Name,那么UI上會(huì)有個(gè)div它的innerHTML為其對(duì)應(yīng)呈現(xiàn),Name為王五,innerHTML也為王五,Name為張三時(shí),innerHTML自動(dòng)的也更改為張三,這種在Web上近乎的天方夜譚,但在WPF中卻成為了可能,甚至于Employee上有個(gè)行為Walk(),在UI上操作按鈕執(zhí)行的可以是Employee這個(gè)行為。不過(guò)調(diào)用這個(gè)行為的方式我們成為Command。

既然是數(shù)據(jù)對(duì)象那么它可以完全不理會(huì)UI的呈現(xiàn)方式,在WPF你要把Name放到一個(gè)TextBlock上還是一個(gè)Label上,這個(gè)Label的顏色是紅是白可以由界面設(shè)計(jì)者說(shuō)了算,這稱為MVVM模式??蓪?duì)于行為WPF還不能完全綁定到對(duì)象上的方法,要把方法轉(zhuǎn)換到Command中去,也就是說(shuō)要把方法轉(zhuǎn)換成ICommand的Execute的形式——void,且只能傳一個(gè)參數(shù)。而且這樣的話RoutedCommand也就失去了功效,他不能傳委托,對(duì)象又不知道具體的前端控件不能使用CommandBinding,這時(shí)我們需要自定一個(gè)Command

  1. ///Acommandwhosesolepurposeisto  
  2. ///relayitsfunctionalitytoother  
  3. ///objectsbyinvokingdelegates.The  
  4. ///defaultreturnvaluefortheCanExecute  
  5. ///methodis'true'.  
  6. ///</summary>  
  7. publicclassDelegateCommand:ICommand  
  8. {  
  9. #regionFields  
  10. readonlyAction<object>_execute;  
  11. readonlyPredicate<object>_canExecute;  
  12. #endregion//Fields  
  13. #regionConstructors  
  14. ///<summary>  
  15. ///Createsanewcommandthatcanalwaysexecute.  
  16. ///</summary>  
  17. ///<paramname="execute">Theexecutionlogic.</param>  
  18. publicDelegateCommand(Action<object>execute)  
  19. :this(execute,null)  
  20. {  
  21. }  
  22. ///<summary>  
  23. ///Createsanewcommand.  
  24. ///</summary>  
  25. ///<paramname="execute">Theexecutionlogic.</param>  
  26. ///<paramname="canExecute">Theexecutionstatuslogic.</param>  
  27. publicDelegateCommand(Action<object>execute,Predicate<object>canExecute)  
  28. {  
  29. if(execute==null)  
  30. thrownewArgumentNullException("execute");  
  31. _execute=execute;  
  32. _canExecute=canExecute;  
  33. }  
  34. #endregion//Constructors  
  35. #regionICommandMembers  
  36. [DebuggerStepThrough]  
  37. publicboolCanExecute(objectparameter)  
  38. {  
  39. return_canExecute==null?true:_canExecute(parameter);  
  40. }  
  41. publiceventEventHandlerCanExecuteChanged  
  42. {  
  43. add{CommandManager.RequerySuggested+=value;}  
  44. remove{CommandManager.RequerySuggested-=value;}  
  45. }  
  46. publicvoidExecute(objectparameter)  
  47. {  
  48. _execute(parameter);  
  49. }  
  50. #endregion//ICommandMembers對(duì)于其中的  
  51.  
  52. publiceventEventHandlerCanExecuteChanged  
  53. {  
  54. add{CommandManager.RequerySuggested+=value;}  
  55. remove{CommandManager.RequerySuggested-=value;}  

您可能有點(diǎn)疑惑,我們知道CanExecuteChanged是給命令執(zhí)行體通知是否可執(zhí)行命令用的(譬如控件的IsEnable屬性是否更改),也就是上面比方中店里貨到了,店家通知我可以買貨了,可通知必須要對(duì)應(yīng)的Command去發(fā)出,且一個(gè)個(gè)發(fā)出這便有些麻煩,這個(gè)時(shí)候我們需要把事件注冊(cè)到全局統(tǒng)一發(fā)出,CommandManager.RequerySuggested就給我們提供了這樣方便,注冊(cè)后可用CommandManager.InvalidateRequerySuggested()來(lái)統(tǒng)一引發(fā),當(dāng)在主線程外使用該方法注意需要這樣來(lái)調(diào)用

  1. Application.Current.Dispatcher.BeginInvoke((Action)delegate()  
  2. {  
  3. CommandManager.InvalidateRequerySuggested();  

System.Windows.Threading.DispatcherPriority.Normal);繼承于UIElement的類,當(dāng)鼠標(biāo)點(diǎn)擊、鍵盤按下或鼠標(biāo)滾輪也會(huì)觸發(fā)該方法。
你可能有個(gè)疑問(wèn),事件可是強(qiáng)引用,一旦加入這個(gè)全局的事件,是否會(huì)發(fā)生內(nèi)存泄露,這點(diǎn)你可以放心,全局事件只是看上去,實(shí)際上它是用WeakReference來(lái)存放加入的委托,執(zhí)行委托的時(shí)候判斷WeakReference的Target是否為空,為空則清除,你可以用工具看下CommandManager的源碼就完全清楚了,RoutedCommand也是用這個(gè)全局方式來(lái)處理。同理如果你認(rèn)為統(tǒng)一引發(fā)效能太差或沒有必要也可以自己手動(dòng)引發(fā),如Prism中的DelegateCommand就需要自己調(diào)用他的RaiseCanExecuteChanged函數(shù)來(lái)引發(fā),值得注意的是Prism中的事件沒有采用弱引用機(jī)制,你的Command和UI多次切換會(huì)有內(nèi)存泄漏,建議使用微軟在MVVMDEMO中的DelegateCommand,它在構(gòu)造函數(shù)中還有參數(shù)來(lái)開關(guān)是否要加入CommandManager.RequerySuggested,此Command已在在附錄中。

到這里大家似乎已經(jīng)很滿意了,差不多自己也就是這么做的,可有沒有想過(guò),這樣的話CommandBinding是用不了的,畢竟有時(shí)候需要用它做些UI層的攔截,如命令執(zhí)行完之后可以把當(dāng)前對(duì)話框關(guān)閉這也屬于UI層面的,那CommandBinding為什么用不了?我們沒有引發(fā)CommandManager上的事件像CommandManager.ExecutedEvent。沒有引發(fā)也就沒有路由事件,沒有糧食怎么吃肉?通過(guò)CommandManager.AddExecutedHandler加入的委托也是用不的了,都是用的CommandManager.ExecutedEvent事件。

不能引發(fā)路由,就讓能引發(fā)的來(lái)做。已經(jīng)有人迫不及待了:不就new個(gè)RoutedCommand,然后把我們自定義的Command中的方法剝離出來(lái)賦給CommandBinding。這里需要用到附加屬性,前端需要這樣定義,而不能直接為Command賦值:

  1. <Buttonlocal:CommandAttachBehavior.Command="{BindingSave}">Save</Button>CommandAttachBehavior類如下:  
  2. publicstaticclassVisualExtension  
  3. {  
  4. publicstaticTFindAncestor<T>(thisVisualvisual,Predicate<T>predicate)whereT:Visual  
  5. {  
  6. while(visual!=null&&!predicate(visualasT))  
  7. {  
  8. visual=(Visual)VisualTreeHelper.GetParent(visual);  
  9. }  
  10. return(T)visual;  
  11. }  
  12. }  
  13. ///<summary>  
  14. ///AttachedpropertythatcanbeusedtocreateabindingforaCommandModel.Setthe  
  15. ///CommandAttachBehavior.CommandpropertytoaCommandModel.  
  16. ///</summary>  
  17. publicstaticclassCommandAttachBehavior  
  18. {  
  19. publicstaticreadonlyDependencyPropertyCommandProperty  
  20. =DependencyProperty.RegisterAttached("Command",typeof(ICommand),typeof(CommandAttachBehavior),  
  21. newPropertyMetadata(newPropertyChangedCallback(OnCommandInvalidated)));  
  22. publicstaticICommandGetCommand(DependencyObjectsender)  
  23. {  
  24. return(ICommand)sender.GetValue(CommandProperty);  
  25. }  
  26. publicstaticvoidSetCommand(DependencyObjectsender,ICommandcommand)  
  27. {  
  28. sender.SetValue(CommandProperty,command);  
  29. }  
  30. ///<summary>  
  31. ///CallbackwhentheCommandpropertyissetorchanged.  
  32. ///</summary>  
  33. privatestaticvoidOnCommandInvalidated(DependencyObjectsender,DependencyPropertyChangedEventArgse)  
  34. {  
  35. varcommand=e.NewValueasICommand;  
  36. if(command==null)  
  37. return;  
  38. varel=senderasUIElement;  
  39. if(el==null)  
  40. thrownewArgumentNullException();  
  41. if(elisICommandSource)  
  42. {  
  43. varroutedCommand=newRoutedCommand();  
  44. vartype=el.GetType();  
  45. varpropInfo=type.GetProperty("Command");  
  46. propInfo.SetValue(el,command,null);  
  47. el.Dispatcher.BeginInvoke((Action)delegate  
  48. {  
  49. varelParent=el.FindAncestor<UIElement>(u=>!(uisICommandSource));  
  50. if(elParent==null)  
  51. return;  
  52. elParent.CommandBindings.Add(newCommandBinding(routedCommand,  
  53. (target,arg)=>  
  54. {  
  55. command.Execute(arg.Parameter);  
  56. },  
  57. (target,arg)=>  
  58. {  
  59. arg.CanExecute=command.CanExecute(arg.Parameter);  
  60. }));  
  61. },DispatcherPriority.Render);  
  62. }  
  63. }  

大家可能問(wèn)了用CommandBinding用就用了,那為什么還需要把他綁定到非命令父類,問(wèn)題是綁定到他自己本身話CommandManager.AddExecutedHandler還是不能用,會(huì)被CommandBinding給攔截掉,這里要注意下CommandManager.AddExecutedHandler的用法,由于它注冊(cè)的是CommandManager.ExecutedEvent事件,如果你把它注冊(cè)給容器,而這個(gè)容器包含很多Button,各個(gè)Button命令不同,路由事件的特性會(huì)使得任一命令發(fā)出時(shí)都會(huì)響應(yīng)注冊(cè)的委托,原因是這些命令都引發(fā)了CommandManager.ExecutedEvent事件,所以僅對(duì)當(dāng)前控件的命令攔截的話最好只注冊(cè)到命令發(fā)出者本身(Button)。

這種方法雖然可以攔截了,但CommandBinding已經(jīng)被用了,外部無(wú)法再使用,況且循環(huán)找父類效率也差,為什么要在Render之后才找呢?如果你用了類似Prism框架中Region的延遲加載一開始會(huì)找不到父類。我們自定義的Command淪為了中間的代理對(duì)象,想手動(dòng)控制CanExecuteChanged也變的望塵莫及。

思來(lái)想去無(wú)奈為了實(shí)例化ExecutedRoutedEventArgs我只好用了反射的方法:

varargsConstructo=typeof(ExecutedRoutedEventArgs).GetConstructors(BindingFlags.NonPublic|BindingFlags.Instance);
ExecutedRoutedEventArgsargs=(ExecutedRoutedEventArgs)argsConstructo[0].Invoke(newobject[]{this,parameter});
args.RoutedEvent=CommandManager.PreviewExecutedEvent;由于引發(fā)這個(gè)事件需要實(shí)際UIElement、UIElement3D或ContentElement對(duì)象,只有這些類才擁有RaiseEvent方法,所以我為DelegateCommand又定義了一個(gè)IElement接口來(lái)承接對(duì)象,為了讓CommandTarget也能使用,Render之后我才對(duì)IElement賦值,因?yàn)槲也恢繡ommandTarget屬性是否會(huì)定義在Command之后。CommandAttachBehavior類上的OnCommandInvalidated改寫為如下:(我改進(jìn)的DelegateCommand也在附件)

  1. privatestaticvoidOnCommandInvalidated(DependencyObjectsender,DependencyPropertyChangedEventArgse)  
  2. {  
  3. varcommand=e.NewValueasICommand;  
  4. if(command==null)  
  5. return;  
  6. sender.Dispatcher.BeginInvoke((Action)delegate  
  7. {  
  8. ICommandSourcecommandSource=senderasICommandSource;  
  9. if(commandSource!=null)  
  10. {  
  11. vardelegateCommand=commandasIElement;  
  12. if(delegateCommand!=null)  
  13. delegateCommand.Target=commandSource.CommandTarget??(IInputElement)sender;  
  14. vartype=sender.GetType();  
  15. varpropInfo=type.GetProperty("Command");  
  16. propInfo.SetValue(sender,command,null);  
  17. }  
  18. },DispatcherPriority.Render);  

自定義Command的其他一些改進(jìn)做法

通常來(lái)說(shuō)對(duì)自定義Command改進(jìn)的還有增加泛型,泛型有什么用呢?這個(gè)其實(shí)是給Execute里的參數(shù)用的,他的參數(shù)按照ICommand規(guī)定默認(rèn)是object,可有時(shí)候我們的參數(shù)是個(gè)Employee類,那么在執(zhí)行的時(shí)候我們需要做Employeeemployee=argasEmployee的操作,假如穿進(jìn)來(lái)的參數(shù)直接是Employee自然不需要這么做了,而轉(zhuǎn)成Employee對(duì)象的操作在Command中已經(jīng)被做掉——CanExecute((T)parameter)。

自定義Command雖好,可一個(gè)控件限定一個(gè)Command有時(shí)候就會(huì)顯的不夠用,或者那個(gè)控件壓根沒有Command那不完了,MVVM沒法混了?沒有命令事件總該有吧,什么,沒有事件?單純顯示用的?那他憑什么有行為?有事件的話,我們可以注冊(cè)事件在委托中執(zhí)行Command,具體做法請(qǐng)參考Prism中的ButtonBaseClickCommandBehavior、CommandBehaviorBase、Click這三個(gè)類。

Prism中還有個(gè)關(guān)于Command的類叫做CompositeCommand,他主要為了解決幾個(gè)自定義Command一起能執(zhí)行的問(wèn)題:一次增加了多了訂單,只要每個(gè)訂單都被允許保存,則不需要一個(gè)個(gè)點(diǎn)訂單的Save按鈕,來(lái)個(gè)SaveAll一起保存,要是里面有個(gè)訂單不能保存,那么SaveAll是不能用的。實(shí)現(xiàn)原理也比較直觀,就是把幾個(gè)自定義Command放到一個(gè)列表并注冊(cè)他們的CanExecuteChanged,看是不是都能被執(zhí)行,如果不能執(zhí)行則CompositeCommand的CanExecute為false,能執(zhí)行則用CanExecuteChanged通知前端控件,執(zhí)行時(shí)只要循環(huán)執(zhí)行列表中Command的Execute方法即可。

一般定義的Command不能控制ExecutedRoutedEventArgs中的Handled屬性,我把他提了出來(lái)用ref來(lái)控制,這種做法似乎有點(diǎn)讓ViewModel知曉UI的味道,可有時(shí)候還是必要的,如我的SaveCommand結(jié)束后本該會(huì)有個(gè)關(guān)閉窗口的CommandBinding相隨,可執(zhí)行SaveCommand時(shí)發(fā)生了錯(cuò)誤,這時(shí)就要把Handled設(shè)為True不能讓之后的CommandBinding進(jìn)行。

PS:我自己改進(jìn)的這個(gè)DelegateCommand也有些缺點(diǎn)比如需要用附加屬性,這樣用起來(lái)就比較不統(tǒng)一,還有就是反射用的較多效率不說(shuō),也破壞了原有的對(duì)象封裝,并需要在Command中放入了UI元素(IElement),希望本文是拋磚引玉,當(dāng)然被拍磚引來(lái)的玉,我也同樣歡迎。

【編輯推薦】

  1. Visual Studio 2010截圖曝光 以WPF開發(fā)UI
  2. 為WPF項(xiàng)目創(chuàng)建單元測(cè)試
  3. 詳解Silverlight和WPF互相擴(kuò)展
  4. 教你如何理解WPF中的Template類
  5. 詳談WPF開發(fā)中的數(shù)據(jù)虛擬化
責(zé)任編輯:彭凡 來(lái)源: cnblogs
相關(guān)推薦

2018-12-27 09:30:04

Windows 10自定義配置

2022-03-07 07:33:24

Spring自定義機(jī)制線程池

2009-12-23 14:49:46

WPF面板

2021-09-12 07:33:23

python管理編程

2009-12-24 15:22:10

WPF繼承自定義窗口

2015-12-01 15:26:36

.net轉(zhuǎn)型面試薪資

2021-11-23 15:06:42

Kubernetes 運(yùn)維開源

2021-09-07 10:12:25

分布式數(shù)據(jù)庫(kù)集群

2015-01-13 09:20:08

DockerCoreOSRocket

2012-09-17 10:46:06

設(shè)計(jì)LogoIcon

2010-06-09 17:13:12

IPv6協(xié)議路由協(xié)議

2011-06-15 09:24:36

Qt Widget Model

2022-06-06 09:01:16

SwiftUI自定義導(dǎo)航

2021-06-18 07:35:46

Java接口應(yīng)用

2015-02-12 15:33:43

微信SDK

2009-12-23 17:01:09

WPF Command

2020-11-09 16:00:26

LinuxLinux內(nèi)核

2015-02-12 15:38:26

微信SDK

2010-05-11 13:16:21

Unix awk

2011-06-20 16:54:40

Qt Widget model
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

久久一区91| 色呦呦呦在线观看| 麻豆91在线播放免费| 精品国产美女在线| 日韩少妇一区二区| 春暖花开亚洲一区二区三区| 国产精品美女久久久久久2018 | 国产精品18久久久久| 国内精品久久久久影院优| 在线观看国产精品一区| 久久久91麻豆精品国产一区| 狠狠躁夜夜躁人人爽天天天天97| 伊人久久青草| 午夜小视频在线播放| 久久99热99| 欧美壮男野外gaytube| 黄色录像二级片| 国产成人影院| 日韩av在线精品| www.亚洲自拍| 日韩天堂在线| 亚洲大尺度视频在线观看| 亚洲一区二区四区| 久久天堂电影| 99天天综合性| 亚洲影院色无极综合| 国产免费a视频| 亚洲国内自拍| 欧美贵妇videos办公室| 国产视频精品免费| 欧美丝袜丝交足nylons172| 精品sm在线观看| 中国男女全黄大片| 国产精品1区在线| 在线精品视频小说1| 国产毛片视频网站| 黄色在线看片| 一区二区免费在线播放| 波多野结衣激情| 自拍视频在线| 国产欧美一区二区三区网站| 欧美精品欧美精品| 亚洲欧美丝袜中文综合| 99在线精品观看| 国产在线观看一区| 日本高清视频免费观看| 成人av影院在线| 国产精品三区在线| 人人妻人人澡人人爽精品日本 | 成人高清在线观看| 国产wwwwwww| 国产成人精品免费| 成人免费在线看片| 亚洲美女性生活| 成人免费视频一区| 精品国产一区二区三区日日嗨| 成人午夜免费在线观看| 成人午夜激情在线| 精品一区在线播放| 日韩在线无毛| 日本一区二区三区视频视频| 亚洲啪啪av| 麻豆传媒视频在线观看| 一区二区三区在线视频观看58 | 欧美一级网站| 国产97人人超碰caoprom| 日本久久综合网| 美国三级日本三级久久99| 成人激情春色网| 亚洲av无码国产精品久久不卡 | 九九热免费在线| 成人久久综合| 欧美精品一区三区| 国产亚洲精品女人久久久久久| 最新国产拍偷乱拍精品 | 高清在线一区二区| 精品国产一区二区三区忘忧草 | 不卡av一区二区| 裸体女人亚洲精品一区| 国产一区视频在线播放| 免费黄色在线视频| 凹凸成人精品亚洲精品密奴| 日韩在线高清视频| 久久久国产成人| 国产精品普通话对白| 国产精品久久久久久久久久东京| 一级特黄aaaaaa大片| 高清不卡在线观看| 日韩av大全| 性网站在线观看| 韩曰欧美视频免费观看| 欧美第一页浮力影院| 91久久精品无嫩草影院| 亚洲另类激情图| 麻豆网址在线观看| 一本久久综合| 成人黄色网免费| 视频在线观看你懂的| 欧美国产激情二区三区| 国产尤物av一区二区三区| 唐人社导航福利精品| 欧美一区二区久久| 人妻aⅴ无码一区二区三区| 欧美另类综合| 国产伦精品免费视频| 日本韩国免费观看| 中文字幕在线不卡国产视频| 无码粉嫩虎白一线天在线观看| 51一区二区三区| 亚洲国产精品999| 婷婷伊人五月天| 麻豆成人在线| 99国精产品一二二线| 高清性色生活片在线观看| 亚洲一级片在线观看| 香蕉视频999| 精品久久久久久久久久久下田| 欧美激情videos| 91肉色超薄丝袜脚交一区二区| 97aⅴ精品视频一二三区| 午夜久久久久久久久久久| 欧美高清视频在线播放| 国产一区二区三区视频免费观看 | 日韩一区二区三区观看| 免费看裸体网站| 国产一区二区三区的电影| 99视频日韩| 国产在线观看av| 欧美日韩色一区| 欧美18—19性高清hd4k| 西西人体一区二区| 国产91免费视频| 中文字幕中文字幕在线中高清免费版 | 亚洲精品一区中文| 国产精品99精品| 国产成a人亚洲精| 视色,视色影院,视色影库,视色网| 国产成人精品一区二区三区视频| 亚洲欧美综合精品久久成人| 毛片视频网站在线观看| aa级大片欧美| 国精产品一区一区三区视频| 欧美电影在线观看完整版| 欧美激情手机在线视频| 亚洲AV无码一区二区三区少妇| 中文字幕亚洲电影| 精品综合久久久久| 999久久久91| 91丨九色丨国产在线| 国产黄色小视频在线| 91精品国产欧美一区二区成人| 人人干在线观看| 国产精品一区不卡| 欧美狂野激情性xxxx在线观| 国产亚洲亚洲国产一二区| 久热精品在线视频| 99久久久国产精品无码网爆| 亚洲狠狠丁香婷婷综合久久久| 成年人性生活视频| 午夜亚洲福利| 国产一级二级三级精品| 69久成人做爰电影| 一区二区三区日韩在线| 国产又大又粗又硬| 一区二区三区在线免费视频| 中文字幕乱视频| 性高湖久久久久久久久| 色涩成人影视在线播放| 自拍偷拍欧美日韩| 欧美大片网站在线观看| 亚洲 美腿 欧美 偷拍| 在线观看亚洲精品| 污污的视频在线免费观看| 国产成人综合在线观看| 精品视频免费在线播放| 国产最新精品| 成人激情av| 国产精品粉嫩| 精品精品国产国产自在线| 丰满人妻熟女aⅴ一区| 色狠狠一区二区| 精品国产精品国产精品| k8久久久一区二区三区| 天天干天天干天天干天天干天天干| 91超碰国产精品| 极品尤物一区二区三区| 国产福利亚洲| 国内精品久久久久久久久| 国产有码在线| 欧美videofree性高清杂交| 精品国产乱子伦| 亚洲精品乱码久久久久久黑人| 欧亚乱熟女一区二区在线| 日韩激情一区二区| 国产精品国三级国产av| 极品美女一区二区三区| 成人9ⅰ免费影视网站| 日本在线中文字幕一区二区三区| 久久久精品一区二区三区| 天堂av在线资源| 欧美一区二区三区视频在线| 国产成人免费看| 亚洲女厕所小便bbb| 亚洲精品成人无码| 成人免费毛片嘿嘿连载视频| 国产精品区在线| 香蕉成人久久| 欧洲精品在线播放| 99tv成人| 日本精品一区二区| 欧美一性一交| 99re视频在线播放| 日韩精品第二页| 欧洲一区二区视频| av色在线观看| 久久的精品视频| 成人福利在线| 亚洲精品影视在线观看| 你懂的网站在线| 欧美一区二区三区免费观看视频| 日本成人一级片| 欧美午夜精品伦理| 天海翼一区二区| 一区二区不卡在线播放 | 午夜精品国产| 亚洲图色在线| 狠狠色狠狠色综合婷婷tag| 精品国产免费人成电影在线观... 精品国产免费久久久久久尖叫 | 日本少妇激三级做爰在线| 日日摸夜夜添夜夜添精品视频| 欧美视频免费看欧美视频| 欧美精品一级| 天天想你在线观看完整版电影免费 | 欧美成人精品1314www| 国产精品自拍电影| 欧美电影一区二区| 亚洲天堂网在线视频| 色婷婷av一区二区| 日韩在线播放中文字幕| 欧美日韩在线影院| 日韩欧美成人一区二区三区| 午夜电影久久久| 国产手机在线视频| 婷婷夜色潮精品综合在线| 久久精品无码人妻| 亚洲mv在线观看| 日韩激情在线播放| 午夜视频一区二区三区| 天天操天天干视频| 欧美午夜性色大片在线观看| 天天操天天爽天天干| 天天做天天摸天天爽国产一区 | 日韩影院免费视频| 久久精品免费网站| 老司机免费视频一区二区三区| 国产喷水theporn| 久久国产精品99精品国产| 天天操狠狠操夜夜操| 国产一区激情在线| 亚洲午夜久久久久久久久| av不卡一区二区三区| 高潮毛片无遮挡| 国产精品高潮久久久久无| 成人高潮免费视频| 性做久久久久久免费观看| 成人午夜淫片100集| 欧美性一级生活| 国产美女主播在线观看| 精品国产免费视频| 男人天堂资源在线| 日韩中文字幕免费视频| 天天色天天射天天综合网| 欧美在线视频网站| 国产一区高清| 国产精品福利视频| 精品中文一区| 日韩精品福利片午夜免费观看| 99在线|亚洲一区二区| 免费黄色一级网站| 国产精品一区不卡| 中文字幕av网址| 综合激情成人伊人| 日韩特黄一级片| 欧美三级视频在线| 乱精品一区字幕二区| 国产香蕉一区二区三区在线视频| 黄网页免费在线观看| 97国产真实伦对白精彩视频8| 国精产品一区一区三区四川| 91av免费看| 精品高清久久| 男人天堂a在线| 久久成人免费网站| 日本少妇xxxx| 中文字幕综合网| 中文字幕亚洲乱码熟女1区2区| 7878成人国产在线观看| 午夜在线观看视频18| www.日本久久久久com.| 性感女国产在线| 51成人做爰www免费看网站| 国产一区二区欧美| 国产精品专区在线| 国产精品白丝jk白祙喷水网站| 无码国产69精品久久久久同性| 亚洲精品国产a| 在线观看免费观看在线| 亚洲精品suv精品一区二区| 日本www在线| 国产成人拍精品视频午夜网站| 日韩精品亚洲专区在线观看| 日韩福利影院| 麻豆久久精品| av av在线| 亚洲精品成人天堂一二三| 在线免费观看av片| 亚洲欧美另类人妖| 草草在线视频| 国产成人精品福利一区二区三区| 99久久精品网| 国产成人黄色网址| 久久精品一区八戒影视| 日韩伦理在线视频| 精品少妇一区二区三区视频免付费| 午夜看片在线免费| 国产精品免费观看在线| 久久99国产精品视频| 每日在线更新av| 成人av在线一区二区三区| 精品爆乳一区二区三区无码av| 欧美精品aⅴ在线视频| 自拍视频在线网| 国产精品一区二区三区毛片淫片| 国产精品视频一区二区三区四蜜臂| 国产黄视频在线| 成人av电影免费观看| 久久久久成人网站| 精品国精品自拍自在线| 性欧美video高清bbw| 99精品国产一区二区| 中文字幕午夜精品一区二区三区| 五月天激情播播| 亚洲男同性视频| 国产成人精品亚洲精品色欲| 精品中文字幕在线2019| 美女久久精品| 成人在线视频一区二区三区| 国产精品456| 妺妺窝人体色www聚色窝仙踪| 欧美成人艳星乳罩| heyzo中文字幕在线| 精品无人区一区二区三区| 国产精品亚洲欧美| 亚洲码无人客一区二区三区| 91福利国产成人精品照片| av色图一区| 91亚洲国产精品| 伊人激情综合| 蜜桃精品成人影片| 欧美亚洲自拍偷拍| 国产秀色在线www免费观看| 99高清视频有精品视频| 亚洲三级网站| 无码 人妻 在线 视频| 欧美日韩国产美女| 97超碰资源站在线观看| 国产伦一区二区三区色一情| 亚洲欧美视频| 日本女人性生活视频| 日韩一级片网址| 亚洲天堂电影| 亚洲午夜精品久久| 丁香婷婷综合色啪| 国产精品久免费的黄网站| 色黄久久久久久| 天堂精品在线视频| 成年人黄色片视频| 亚洲同性gay激情无套| 日韩中文字幕免费在线观看| 国产91色在线播放| 午夜国产欧美理论在线播放| 国产熟妇搡bbbb搡bbbb| 91精品久久久久久蜜臀| 成人三级高清视频在线看| 五月婷婷综合色| 国产成人av一区二区三区在线观看| 欧美特黄aaaaaa| 久久视频免费观看| 先锋影音国产精品| 色网站在线视频| 色综合久久天天| 黄网站在线观| 亚洲欧美丝袜| 99在线视频精品| 国产欧美日韩成人| 欧美一性一乱一交一视频| 91精品国产自产在线观看永久∴| 精品人妻一区二区三区日产| 在线不卡欧美精品一区二区三区| 涩涩视频在线|