精品欧美一区二区三区在线观看 _久久久久国色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)

成人影院中文字幕| 国产99在线| 国产乱子伦视频一区二区三区| 九九久久国产精品| 亚洲中文字幕一区| 国产精品第一国产精品| 一卡二卡三卡日韩欧美| 欧美精品一区二区三区四区五区| 这里只有精品9| 亚洲性视频h| 自拍偷拍亚洲一区| 在线观看av中文字幕| 黄色成人在线视频| 亚洲v日本v欧美v久久精品| 欧美激情第一页在线观看| 国产精品视频久久久久久| 国产一区二区三区的电影 | 日韩亚洲国产精品| 久久精品中文字幕| 亚洲午夜精品久久久久久高潮| 经典三级久久| 欧美三级一区二区| 免费黄色福利视频| 免费在线播放电影| 国产精品理伦片| 欧美人与性禽动交精品| 亚洲免费成人网| 国精产品一区一区三区mba桃花| 日韩免费观看在线观看| 久久精品性爱视频| 亚洲深深色噜噜狠狠爱网站| 国产亚洲欧美日韩美女| 国产夫妻性爱视频| 9l视频自拍九色9l视频成人| 欧美精品久久一区二区三区| 黄色国产小视频| 亚洲精品一区| 午夜免费久久看| 996这里只有精品| 亚洲欧洲国产伦综合| 欧类av怡春院| 久久精品凹凸全集| 欧美精品一区二区在线播放| 中文字幕55页| 成人在线视频国产| 制服丝袜一区二区三区| 岛国av在线免费| jizz久久久久久| 欧美日韩一级视频| 黄色手机在线视频| 久久精品国产福利| 欧美精品在线一区二区| 中文字幕22页| 国产精品xnxxcom| 日韩一级黄色片| 国产chinesehd精品露脸| 日韩三级av高清片| 精品福利二区三区| 污污污www精品国产网站| 国产精品宾馆| 日韩麻豆第一页| 实拍女处破www免费看| 国产精品欧美日韩一区| 国产亚洲精品91在线| 丰满的亚洲女人毛茸茸| 日本高清免费电影一区| 色偷偷av亚洲男人的天堂| a一级免费视频| 欧美精选一区| 26uuu亚洲伊人春色| 日韩精品一区二区亚洲av观看| 日韩高清国产一区在线| 国产噜噜噜噜噜久久久久久久久| 97人妻精品一区二区三区动漫| 国产在线国偷精品免费看| 91超碰在线免费观看| 欧美视频一二区| 久久久久久久久久久黄色| 亚洲欧美日韩国产yyy| 国产一二三区在线观看| 亚洲国产三级在线| 日韩视频在线免费看| 成人在线免费av| 日韩西西人体444www| 小毛片在线观看| 欧洲杯半决赛直播| xxx一区二区| 日韩欧美三级在线观看| 日本不卡免费在线视频| 99久久精品免费看国产四区| 深夜福利视频在线免费观看| 国产精品萝li| 日本a在线免费观看| 91伊人久久| 精品国偷自产国产一区| 日韩女同一区二区三区| 午夜精品久久| 国产精品欧美在线| 手机在线观看毛片| 18欧美乱大交hd1984| 亚洲熟妇国产熟妇肥婆| 久久天天久久| 日韩激情在线视频| 男女性高潮免费网站| 美女日韩在线中文字幕| 99re6在线| 91在线高清| 亚洲国产精品视频| 爱豆国产剧免费观看大全剧苏畅 | 亚洲成色777777在线观看影院| 日韩在线免费观看av| 欧美va亚洲va日韩∨a综合色| 日本高清不卡的在线| 亚洲精品视频专区| 中文字幕视频一区二区三区久| koreanbj精品视频一区| 久久伦理中文字幕| www国产精品com| 看黄色一级大片| 97久久超碰精品国产| 亚洲激情免费视频| 欧美成人aaa| 亚洲天堂av电影| 青青国产在线观看| 成人国产精品免费网站| 五月天在线免费视频| 色成人免费网站| 日韩高清不卡av| 精品无码一区二区三区电影桃花| 久久99精品久久久久久动态图| 欧美欧美一区二区| 麻豆蜜桃在线观看| 欧美精品一区视频| 久久精品免费在线| 国产精品888| 韩国无码av片在线观看网站| 亚洲视频资源| 日韩一区二区欧美| 亚洲视频一区二区三区四区| 欧美激情自拍偷拍| 少妇激情一区二区三区| 国产精品三级| 国产极品jizzhd欧美| 麻豆av电影在线观看| 欧美性猛交xxxx乱大交| 亚洲午夜福利在线观看| 天堂成人国产精品一区| 色乱码一区二区三在线看| 欧美xnxx| xvideos成人免费中文版| 国产理论片在线观看| 亚洲色欲色欲www| 色网站在线视频| 综合久久十次| 97中文在线观看| 男女羞羞视频在线观看| 亚洲电影免费观看高清完整版在线观看 | 亚欧精品在线视频| 欧美日韩国产高清| 精品999在线观看| 涩涩视频在线| 中文字幕在线国产精品| 国产精品久久久久久免费播放 | 国产a级片免费看| 精品国产18久久久久久二百| 欧美激情免费看| 五月激情六月婷婷| 欧洲亚洲国产日韩| 五月综合色婷婷| 成人性生交大片免费看视频在线| 三上悠亚久久精品| 国产尤物久久久| 国产热re99久久6国产精品| 中文av资源在线| 亚洲国产天堂久久国产91| 国产精品熟女视频| 亚洲人成亚洲人成在线观看图片 | 久久久久国色av免费观看性色| 人人妻人人玩人人澡人人爽| 日韩人在线观看| 免费在线观看黄色小视频| 粉嫩久久99精品久久久久久夜| 日韩免费视频播放| 国产精品成人av| 国产乱码精品一区二区三区卡 | 国产原创在线观看| 亚洲黄色www网站| 亚洲欧美综合另类| 中文字幕日韩精品一区| 美女黄色一级视频| 蜜桃精品视频在线| 九色自拍视频在线观看| 色男人天堂综合再现| 国产精品有限公司| 伊人亚洲精品| 欧美又大粗又爽又黄大片视频| 欧美jizzhd欧美| 亚洲欧美日韩在线高清直播| 国产免费高清av| 在线国产电影不卡| 国产无套粉嫩白浆内谢| 国产精品乱码妇女bbbb| 亚洲黄色在线网站| 国产成人免费视频网站| 另类小说色综合| 宅男噜噜噜66一区二区| 强开小嫩苞一区二区三区网站 | 久久久国产精品麻豆| 中国老熟女重囗味hdxx| 青草国产精品久久久久久| www.日本在线播放| 一区二区影视| 亚洲欧美精品在线观看| 女厕嘘嘘一区二区在线播放 | 欧美美乳在线| 精品久久人人做人人爰| 亚洲熟妇av乱码在线观看| 欧美性猛交xxxx富婆| 久久视频免费在线观看| 亚洲精品少妇30p| 成人午夜免费影院| 久久久国产一区二区三区四区小说 | 日本在线xxx| 午夜电影亚洲| 日本老太婆做爰视频| 91影院成人| 色一情一乱一伦一区二区三欧美| 欧美人妖视频| 好吊色欧美一区二区三区| 日本精品一区二区三区在线观看视频| 国产精品久久久久久久9999| 国产精品av一区二区三区| 97精品久久久| 麻豆av在线免费观看| 欧美人在线视频| 老司机精品视频在线观看6| 日韩一区二区福利| 精品国产白色丝袜高跟鞋| 日韩中文在线中文网三级| www.视频在线.com| 中文日韩电影网站| 99免在线观看免费视频高清| 亚洲视频在线免费观看| 国产区在线视频| 伊人伊成久久人综合网小说| 九九九伊在人线综合| 亚洲视频专区在线| 成a人片在线观看www视频| 中文字幕亚洲情99在线| 香港伦理在线| 日韩三级成人av网| 国产高清一区二区三区视频| 久久亚洲影音av资源网| 色网在线观看| 91av视频在线播放| 欧美男女交配| 国产精品视频精品视频| 热久久久久久| 91亚洲精品丁香在线观看| 国产精品x8x8一区二区| 久久av二区| 日韩激情图片| 一区二区三区四区免费观看| 欧美性色综合| 成人在线免费观看av| 日韩va亚洲va欧美va久久| 亚洲美女性囗交| 国产成人av一区二区三区在线 | 国产午夜精品久久久久久免费视| 自拍偷拍视频亚洲| 中文字幕亚洲视频| 麻豆视频在线观看| 欧美日韩精品在线播放| 无码久久精品国产亚洲av影片| 欧美色男人天堂| 国内老熟妇对白xxxxhd| 亚洲精品电影久久久| 国产天堂素人系列在线视频| 美女精品久久久| 国产高清视频色在线www| 国产精品成人v| 日韩中文字幕一区二区高清99| 久草精品电影| 91精品综合久久久久久久久久久| 日韩小视频网站| 日韩专区中文字幕一区二区| 伊人五月天婷婷| 久久久久亚洲综合| 免费人成视频在线| 91成人看片片| 懂色av蜜臀av粉嫩av分享吧| 在线观看亚洲视频| av电影院在线看| 国产在线精品成人一区二区三区| 成人黄色av网址| 亚洲狠狠婷婷综合久久久| 国内精品嫩模av私拍在线观看| 成年人在线观看视频免费| 高清国产一区二区| 国产精品久久免费观看| 亚洲国产成人91porn| 在线观看国产区| 欧美不卡一区二区三区| 日韩毛片久久久| 欧美中文字幕在线观看| 亚州一区二区| 一本一道久久久a久久久精品91| 国产日韩一区| 女同性αv亚洲女同志| 国产精品美女视频| 国产99久久久| 精品剧情v国产在线观看在线| 欧美a免费在线| 国产精品久久99久久| 久久激情av| 无码av天堂一区二区三区| 精品一区二区日韩| 久久日免费视频| 色综合天天综合给合国产| 日韩一区二区三区不卡| 欧美精品日韩三级| 国产精品99| 日本一区视频在线观看免费| 亚洲毛片一区| 欧美激情 亚洲| 亚洲精品老司机| 国产美女裸体无遮挡免费视频| 一区二区三区美女xx视频| 欧美日韩美女| 蜜桃视频在线观看91| 亚洲一区二区三区四区五区午夜| 成人一区二区三区仙踪林| 亚洲精品视频在线观看免费| 91tv国产成人福利| www.亚洲一区| 成人在线啊v| 国内自拍中文字幕| 国产精品一区二区你懂的| 日韩一级片大全| 欧美一级生活片| 午夜影院免费在线| 91视频免费在线观看| 欧美69wwwcom| 韩国黄色一级片| 亚洲国产精品久久久久婷婷884| 内射无码专区久久亚洲| 久久久影视精品| 欧美a大片欧美片| 1024精品视频| 久久精品人人做人人爽97| 精品成人无码久久久久久| 在线日韩第一页| 国产香蕉久久| a级网站在线观看| 国产成人av影院| 久久久久久久久久影院| 国产视频综合在线| 色成人免费网站| 手机看片日韩国产| 成人一区二区三区在线观看| 日产欧产va高清| 亚洲欧美日韩网| www一区二区三区| 人妻av无码专区| 26uuu成人网一区二区三区| 波多野结衣日韩| 久久伊人色综合| 黄色美女久久久| 国产精品免费成人| 国产精品伦一区| 人妻精品一区一区三区蜜桃91| 欧美一级视频免费在线观看| 欧美日韩激情在线一区二区三区| 成人综合久久网| 午夜精品久久久久久久99樱桃 | 久久久精品综合| 亚洲最大成人av| 97色伦亚洲国产| 成人影视亚洲图片在线| 95视频在线观看| 欧美怡红院视频| 黄页在线观看免费| 欧美一二三区| 国产精品一级在线| 亚洲GV成人无码久久精品| 久久精品99久久香蕉国产色戒| 丁香婷婷成人| 牛夜精品久久久久久久| 亚洲综合精品久久| 成人在线视频成人| 成人在线视频电影| 奇米精品一区二区三区在线观看 | 日本japanese极品少妇| 欧美色图一区二区三区| 成人影音在线| 亚洲一区二区三区精品动漫| k8久久久一区二区三区| 一区二区日韩在线观看| 欧美性视频网站| 欧美日本国产|