偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

對(duì)于Execute方法不難理解,對(duì)于命令模式來說有個(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)作在程序來說就是注冊(cè)事件,告訴命令的發(fā)出者什么時(shí)候才能執(zhí)行,這個(gè)也就是CanExecuteChanged的由來;在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意義不必說了,它通常都是在控件的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的說法是找到當(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)怪。

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

內(nèi)置Command

前面說了ICommand只是一個(gè)接口,好處是你可以隨意實(shí)現(xiàn),壞處便是每次使用都需要建立一個(gè)實(shí)現(xiàn)它的具體類,那么微軟有沒有給個(gè)默認(rèn)的實(shí)現(xiàn)類,答案是肯定的,它叫做RoutedCommand,不用不知道,一用嚇一跳,默認(rèn)的這個(gè)RoutedCommand類居然不能傳委托,為什么說不能穿委托很詫異,上面說了Command的主要功能是有個(gè)函數(shù)讓人執(zhí)行,可函數(shù)不傳給他,你讓別人執(zhí)行啥?(派生于他的類幾乎啥也做不了——他沒有任何虛方法),微軟這里又用了一招——CommandBinding,他彌補(bǔ)了RoutedCommand在功能上的缺陷,可以為ICommand指定CanExecute委托和Execute委托,RoutedCommand是ICommand的具體實(shí)現(xiàn),自然可以舒舒服服的享用,不過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來修改Grid的顏色,有些人已經(jīng)破口而出了,在UserControl中定義一個(gè)事件,在Button的Click事件委托中調(diào)用這個(gè)事件,一切看起來都很輕松;

那么現(xiàn)在假設(shè)Button被裝到一個(gè)Style中,我繼承的不是UserControl而是Control,你可能會(huì)聳聳肩,說道那只好注冊(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這樣的快捷鍵來實(shí)現(xiàn),您是否還有熱情為他們一一取名判斷?

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

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

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

說來這些或許有人開始點(diǎn)頭,之后又開始疑惑這和CommandBinding有啥關(guān)系,完全是CommandManager和RoutedCommand的那點(diǎn)事,他怎么進(jìn)行第三者插足來運(yùn)行那些委托方法?以UIElement.OnExecutedThunk來做說明,它其實(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ì)于程序來說,我們希望把業(yè)務(wù)邏輯和呈現(xiàn)盡量分離,以期實(shí)現(xiàn)不同UI的相同調(diào)用,一個(gè)程序B/S架構(gòu)能用,C/S架構(gòu)也能用,或許有人說了:這不就是要把業(yè)務(wù)封裝成個(gè)DLL或是WebService嘛,我們?cè)谟肳CF完全沒問題。是的,這樣可以更方便的測(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的局限我們通過字符串來模擬對(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è)行為。不過調(diào)用這個(gè)行為的方式我們成為Command。

既然是數(shù)據(jù)對(duì)象那么它可以完全不理會(huì)UI的呈現(xiàn)方式,在WPF你要把Name放到一個(gè)TextBlock上還是一個(gè)Label上,這個(gè)Label的顏色是紅是白可以由界面設(shè)計(jì)者說了算,這稱為MVVM模式。可對(duì)于行為WPF還不能完全綁定到對(duì)象上的方法,要把方法轉(zhuǎn)換到Command中去,也就是說要把方法轉(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()來統(tǒng)一引發(fā),當(dāng)在主線程外使用該方法注意需要這樣來調(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è)疑問,事件可是強(qiáng)引用,一旦加入這個(gè)全局的事件,是否會(huì)發(fā)生內(nèi)存泄露,這點(diǎn)你可以放心,全局事件只是看上去,實(shí)際上它是用WeakReference來存放加入的委托,執(zhí)行委托的時(shí)候判斷WeakReference的Target是否為空,為空則清除,你可以用工具看下CommandManager的源碼就完全清楚了,RoutedCommand也是用這個(gè)全局方式來處理。同理如果你認(rèn)為統(tǒng)一引發(fā)效能太差或沒有必要也可以自己手動(dòng)引發(fā),如Prism中的DelegateCommand就需要自己調(diào)用他的RaiseCanExecuteChanged函數(shù)來引發(fā),值得注意的是Prism中的事件沒有采用弱引用機(jī)制,你的Command和UI多次切換會(huì)有內(nèi)存泄漏,建議使用微軟在MVVMDEMO中的DelegateCommand,它在構(gòu)造函數(shù)中還有參數(shù)來開關(guān)是否要加入CommandManager.RequerySuggested,此Command已在在附錄中。

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

不能引發(fā)路由,就讓能引發(fā)的來做。已經(jīng)有人迫不及待了:不就new個(gè)RoutedCommand,然后把我們自定義的Command中的方法剝離出來賦給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. }  

大家可能問了用CommandBinding用就用了,那為什么還需要把他綁定到非命令父類,問題是綁定到他自己本身話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也變的望塵莫及。

思來想去無(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接口來承接對(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)做法

通常來說對(duì)自定義Command改進(jìn)的還有增加泛型,泛型有什么用呢?這個(gè)其實(shí)是給Execute里的參數(shù)用的,他的參數(shù)按照ICommand規(guī)定默認(rèn)是object,可有時(shí)候我們的參數(shù)是個(gè)Employee類,那么在執(zhí)行的時(shí)候我們需要做Employeeemployee=argasEmployee的操作,假如穿進(jìn)來的參數(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í)行的問題:一次增加了多了訂單,只要每個(gè)訂單都被允許保存,則不需要一個(gè)個(gè)點(diǎn)訂單的Save按鈕,來個(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屬性,我把他提了出來用ref來控制,這種做法似乎有點(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)比如需要用附加屬性,這樣用起來就比較不統(tǒng)一,還有就是反射用的較多效率不說,也破壞了原有的對(duì)象封裝,并需要在Command中放入了UI元素(IElement),希望本文是拋磚引玉,當(dāng)然被拍磚引來的玉,我也同樣歡迎。

【編輯推薦】

  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é)任編輯:彭凡 來源: 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面板

2009-12-24 15:22:10

WPF繼承自定義窗口

2021-09-12 07:33:23

python管理編程

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

2022-06-06 09:01:16

SwiftUI自定義導(dǎo)航

2010-06-09 17:13:12

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

2011-06-15 09:24:36

Qt Widget Model

2012-09-17 10:46:06

設(shè)計(jì)LogoIcon

2015-02-12 15:33:43

微信SDK

2021-06-18 07:35:46

Java接口應(yīng)用

2009-12-23 17:01:09

WPF Command

2020-11-09 16:00:26

LinuxLinux內(nèi)核

2011-06-20 16:54:40

Qt Widget model

2015-02-12 15:38:26

微信SDK

2010-05-11 13:16:21

Unix awk
點(diǎn)贊
收藏

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