Hello,Behavior
引言????????
???? 在看PDC-09大會的視頻時,其中一篇講利用Blend來擴(kuò)展Silverlight元素的行 為,當(dāng)時感覺很酷:在Blend中,將MouseDragElementBehavior拖到任意一個元素上,這個元 素就可以被隨意拖動。
???? 因為之前在Silverlight SDK中好像沒有看到相關(guān)的介紹,事實如此, Microsoft.Expression.Interactions和System.Windows.Interactivity程序集在Blend工具中才有。但是從來沒有去看過Blend的幫助文檔,因為我以為那不過是告訴你怎樣使用工具,事實上 我錯了,Microsoft Expression Blend 軟件開發(fā)工具包(SDK) 沒有包含怎樣使用Blend的信息, 而只有介紹以上提到的兩個程序集。
???? 無知的人第一件會做的事就是上Google,我也是這樣的人。所以趕緊Google一下,原來 Behavior是一種Silverlight元素行為的重用方式,并且每一篇介紹Behavior的作者,無不流露出對Behavior的贊賞,甚至對有些人來講,那是Silverlight最令人激動的特性,因為那使他們可能很方便并且簡單的為自己的界面添加只有少數(shù)具有美術(shù)創(chuàng)意的人才能做出的元素效果。????????
???? 于是我決定整理一下Behavior相關(guān)的知識。
???? 然而Blend SDK一看卻感到很吃驚:這不是微軟史上最簡單的SDK么!如果排除類庫介紹 這個SDK只有3個頁面,而里面每個例子的代碼加起來還不到50行。更驚訝的是微軟認(rèn)為這么簡單的東西我卻怎么看也不會做,因為我有一種先天缺陷:在不明白為什么的情況下再簡單的事情我都不會做
???? Behavior真這么簡單么?事實上不能這么說,從使用的角度,實現(xiàn)一個簡單的Behavior不需要多少代碼,需要做的事情也不多,然而在這背后的思路呢。比如ASP.NET,一般人看幾個小時說就能Hello,World,但是真正做起來卻會遇到很多問題。尤其現(xiàn)在開發(fā)工具越來越強大,許多東西不需要我過多理解就能實現(xiàn),但是我相信,只有真正理解一些東西,我們的技術(shù)才會真正提高。
???? 本文從自己的學(xué)習(xí)角度更深入點的分析這種特性。網(wǎng)上介紹的一般只是教怎么做,很多差不多就是SDK里面的簡單的例子。Behavior特性的背后其實是有比較好的思想的。這些思想有助于我們更好的理解這種特性。
???? 本文不打算寫太多例子,因為例子實在很簡單,可以參考Blend SDK。
??
Behavior 的設(shè)計模式是一種行為模式
我們應(yīng)該怎樣認(rèn)識設(shè)計模式??????????
???? 設(shè)計模式到底是一種什么樣的東西,每個開發(fā)人員有自己的理解。有的人認(rèn)為那是具有很高技術(shù)經(jīng)驗的人才應(yīng)該掌握的東西;有的人也認(rèn)為我們一般的開發(fā)中不會用到什么設(shè)計模式……. 這些觀點好像想說明這樣一件事情:設(shè)計模式是一種可有可無的東西,你有那個興致就可以去看 下,看了記不住也沒有關(guān)系。
???? 當(dāng)然每個人有自己的個人經(jīng)驗,也許不懂設(shè)計模式不會影響你做某些事情,但是明白一種設(shè)計模式卻能幫助你更好的做某件事情。其實在我們的開發(fā)中,很多技術(shù)都從設(shè)計模式中抽象了出來,使我們不用去實現(xiàn)這些高度抽象的技術(shù)思維。從語言級別,比如C#中的迭代器模式,有一次我面試沒有答上來的委托對應(yīng)的Observer模式;到框架級別,比如MVC,MVP以及MVVM模 式。Ruby On Rails從2004年出現(xiàn)到現(xiàn)在,一直受到不少開發(fā)者的追捧,其實ROR的開發(fā)者只不過是用Ruby來實現(xiàn)了MVC的模式,這個框架使其他開發(fā)者可以直接利用MVC的模式進(jìn)行開發(fā),而不必關(guān)心怎樣自己去實現(xiàn)這個模式。但是與對照教程做練習(xí)相比,理解MVC的思路絕對有助于真正的掌握Ruby On Rails技術(shù)。到我們的WPF也是一樣,XAML不僅僅是一個可以編寫界面的方式,它包含的是一種MVP的設(shè)計模式,當(dāng)我們明白這種設(shè)計模式的思路時,我相信我們會覺得 XAML就是那么一回事,不然,跟著技術(shù)細(xì)節(jié)跑,你總會有許多的為什么并且不知道為什么。比如WPF有事件模式,為什么又要來個命令模式(Command),其實命令模式也是23種經(jīng)典模式之一,在WPF中得到支持,看看命令模式再來看WPF中的Command,相信會有不同的感覺。
???? 所以,在我們的學(xué)習(xí)中,可以將對技術(shù)的理解和設(shè)計模式一起思考,那樣將有助于我們更深刻的理解技術(shù)。我們本可以用更底層的技術(shù)來實現(xiàn)很多東西,正是因為一些高級語言和開發(fā)工具將一些復(fù)雜的設(shè)計模式融入到各種設(shè)計框架和語言中,才使得我們的開發(fā)更加簡單有效。既然 是基于這些抽象的模式之上,那么理解這些不同的模式將有助于我們真正掌握技術(shù)的內(nèi)涵。
?
重用行為????????
???? 語言發(fā)展到今天,很重要的一個進(jìn)步就是重用。無論是從語言級別的類,還是COM里的dll,這些技術(shù)為技術(shù)的發(fā)展做出了很大的貢獻(xiàn)。?????
???? Behavior也是可重用的一種特性。Expression的網(wǎng)站上有許許多多的Behavior可供下載。Blend本身也提供了幾個,如下圖:
????
???? 我們只要將一個Behavior拖到一個元素上,就能使這個元素具有某種行為,而實現(xiàn)這種行為的程序集和你的項目是獨立的,你創(chuàng)建的Behavior也可以被其他人使用。我們后面將會討論這是怎樣 實現(xiàn)的。
???? 在行為設(shè)計模式中。往往有一下幾個參與者:
?????? ??Element :要被附加行為的對象元素(這里也可以是一種邏輯功能的抽象,比如排序)
?????? ??IBehavior:行為接口
?????? ??Behavior:不同的行為實現(xiàn)
???? 通常,Element所在的程序集和Behavior所在的程序集是不同的,Behavior有多個實現(xiàn)版本,可以將不同的版本附加到Element,就會使Element具有不同的行為。比如這里Element是 一種排序的功能,那么你可以定義一個Behavior是歸并排序,而另一個Behavior是快速排序;所 不同的是行為模式中通常有一種選擇器來決定選取那個Behavior,而WPF采用一種附加的技術(shù)將 Behavior附加到一個Element,并且很獨特的是可以附加多個Behavior,后面我們將看到, Behavior中通常用于給元素附加事件,而事件的Add方法類似于多播委托,即使都添加相同的事 件仍然不會影響其他Behavior,事件和委托的一個重要區(qū)別就是事件沒有賦值方法(另一個是事 件確保只有事件所在的類才能觸發(fā)一個事件通知),這避免不小心取消其他訂閱。所以在 Silverlight中,一個元素可以添加多個Behavior。
???? 不管怎樣,我們看到的是這樣一種思路:你可以為某個元素添加某種獨特的行為,而這種行 為不是直接通過為元素添加事件來實現(xiàn),因為那樣在其他地方將達(dá)不到重用而不得不拷代碼或者 重寫。在漢語中很多相同字開頭的詞往往是近義詞,但我認(rèn)為重寫和重用是反義詞。而是通過一種附加的技術(shù),將其他程序集的Behavior附加到一個元素,這個意義非常重大,使得我們可無限 擴(kuò)展并且重用行為。這樣你可以享受別人的成果。那么Silverlight怎樣實現(xiàn)這種附加呢?下一節(jié)將會討論。
?怎樣將一種行為附加到一個元素呢?
附加,非常可怕的一種技術(shù)???????
????? 當(dāng)你在自定義一個Behavior的時候,你將決定千千萬萬以后會引用你的Behavior的元素的行為,你可以讓他放大,你可以讓縮小,旋轉(zhuǎn),任何你想得出來的創(chuàng)意。他人的添加你的程序集 并將你自定義的附加到一個元素時,這個元素就具有了你定義的行為,想想要是世界上有一種附加技術(shù)將一種殺人的行為附加到你身上真是非常可怕。
背后的英雄 : IAttachedObject?
???? Interface這種技術(shù)隱含的意義真是無法估量。.Net Framework中就有大量的接口,以下是 IAttachedObject 定義:????????
???? An interface for an object that can be attached to another object.???
???? public interfaceIAttachedObject???
?? ?{
? ?????? DependencyObject AssociatedObject { get; }?????
????? ?? void Attach(DependencyObject dependencyObject);???
??? ???? void Detach();
??? }
???? 實現(xiàn)這個接口的類的實例可以被附加到另一個對象,如:TriggerBase,TriggerAction, Behavior等。在程序中我們可以調(diào)用void Attach(DependencyObject dependencyObject)方 法將繼承該接口的對象附加到另一對象,這里是dependencyObject對象。????????
???? 通常我們在自定義行為的時候不是直接繼承自IAttachedObject接口,而是繼承自從 IAttachedObject繼承的一些類,這些類都有可重載的OnAttached(),OnDetached()方法,比如 Behavior類,TriggerBase類,TriggerAction類等。通常在Attach()方法中會觸發(fā) OnAttached()方法,在Detach()方法中會觸發(fā)OnDetached()方法。????????
???? 所以Blend SDK中自定義Action,Behavior和Trigger的例子都非常簡單,你只需要重寫 Ontached()方法和OnDetached()方法即可。你可以為AssociatedObject 添加各種事件從 而改變其行為。???????
???? 以上分析的就是Silverlight中Behavior的機(jī)制。所以,本質(zhì)上,Behavior仍然是利用事件機(jī)制來實現(xiàn)的。但是我們通過引入一種不同的設(shè)計模式而使得相同的功能更具有擴(kuò)展性。這進(jìn)一步說明設(shè)計模式的重要性。 后面我們進(jìn)一步來分析實現(xiàn)方法。
回顧附加屬性:????????
???? 這里有必要回顧以下附加屬性的概念,這將有助于理解Behavior的附加方式。附加屬性其 實是一種普通的依賴項屬性。并且它由WPF屬性系統(tǒng)管理,不同的是附加屬性被應(yīng)用到一個非定 義到該屬性的類,附加屬性通過DependencyProperty.RegisterAttached()方法來定義。并 \且該屬性不需要.NET屬性包裝器,因為附加屬性可以被應(yīng)用與任何附加的對象。看一下Grid的定義:
??? ?public class Grid: Panel???
? ? {
??????? public static readonly DependencyProperty ColumnProperty;??????
????? ? public static readonly DependencyProperty ColumnSpanProperty;
??????? public static readonly DependencyProperty RowProperty;
??????? public static readonly DependencyProperty RowSpanProperty;
??????? public static readonly DependencyProperty ShowGridLinesProperty;
??????? public Grid();
??????? public ColumnDefinitionCollection ColumnDefinitions { get; }
??????? public RowDefinitionCollection RowDefinitions { get; }
??????? public bool ShowGridLines { get; set; }
??????? protected override sealed Size ArrangeOverride(Size arrangeSize);
??????? public static int GetColumn(FrameworkElement element);
??????? public static int GetColumnSpan(FrameworkElement element);
??????? public static intGetRow(FrameworkElement element);?
??????? public static int GetRowSpan(FrameworkElement element);
??????? protected override sealed Size MeasureOverride(Size constraint);
??????? public static void SetColumn(FrameworkElement element, int value);
??????? public static void SetColumnSpan(FrameworkElement element, int value);
??????? public static void SetRow(FrameworkElement element, int value);
??????? public static void SetRowSpan(FrameworkElement element, int value);
??? }
???? 可以看到上面標(biāo)記處的Grid的幾個附加屬性,它們沒有.NET屬性包裝器,而通過 GetPropertyName()和SetPropertyName()靜態(tài)方法來取值和賦值。????????
???? 在被附加屬性的元素內(nèi)部看不到屬性的值,所以也根本不知道它的存在---但是在自己定義 的代碼中可以根據(jù)相應(yīng)的值進(jìn)行操作。比如設(shè)置了Grid.Row屬性后,Grid在計算和排列內(nèi)部元素 時知道每個元素的這個屬性的值,可以根據(jù)此值安排此元素在Grid中的位置,Canvac的 Top,Left,Bottom,Right附加屬性也是如此,只不過Canvas的附加屬性更精確,所以內(nèi)部計算 更少,因此Canvas的執(zhí)行效率通常更高。????????
???? 你甚至可以在不包含Grid的地方使用Grid.Row等附加屬性,但是這沒有絲毫意義,除非你 想在程序中使用這個值。所以你也可以自定義這種附加屬性,比如你想在每個元素上保存一個狀 態(tài)值,你在程序中需要用到這個狀態(tài)值。我們接下來將看到Silverlight正是通過這種附加屬性的 特性實現(xiàn)了行為的附加
?
Silverlight 通過Interaction的附加屬性來實現(xiàn)這種附加??????????
???? 實現(xiàn)IAttachedObject的TriggerBase,TriggerAction以及Behavior通過Interaction的附 加屬性Triggers和Behaviors來實現(xiàn)這種附加。
???? namespace System.Windows.Interactivity
??? {
?? ???? public static class Interaction
?? ??? {
???? ??????? public static readonly DependencyProperty BehaviorsProperty;
???? ??????? public static readonly DependencyProperty TriggersProperty;
???? ??????? public static BehaviorCollection GetBehaviors(DependencyObject obj);
???????????? public static TriggerCollection GetTriggers(DependencyObject obj);
? ???? }
?? ?}
???? 通過Reflectot工具我們可以看到BehaviorsProperty和TriggersProperty的聲明:???
???? public static readonly DependencyProperty BehaviorsProperty =
????????????? DependencyProperty.RegisterAttached("Behaviors",
????????????????????????????????????????????????????????????????????? typeof(BehaviorCollection),
????????????????????????????????????????????????????????????????????? typeof(Interaction),
????????????????????????????????????????????????????????????????????? new PropertyMetadata( new PropertyChangedCallback(Interaction.OnBehaviorsChanged) )) ;
???? TriggersProperty的聲明方式類似,不再貼代碼。我們看到其數(shù)據(jù)類型為BehaviorCollection,并且注冊為附加屬性,在屬性發(fā)生改變時會觸發(fā) Interaction.OnBehaviorsChanged方法:
???? private static void OnBehaviorsChanged(DependencyObjectobj, DependencyPropertyChangedEventArgs args)
??? {
????? ?? BehaviorCollection oldValue = (BehaviorCollection) args.OldValue;
???? ??? BehaviorCollection newValue = (BehaviorCollection) args.NewValue;
???? ??? if (oldValue != newValue)
??? ??? {?
????? ?????? if ((oldValue != null) && (oldValue.AssociatedObject != null))?
???? ?????? {
???? ??????????? oldValue.Detach();?
????? ?????? }
??? ??????? if ((newValue != null) && (obj != null))??
????? ????? {
???? ??????????? if (newValue.AssociatedObject != null)
??? ??????????? {
????? ??????????????? throw new InvalidOperationException(ExceptionStringTable.CannotHostBehaviorCollectionMultiple TimesExceptionMessage);
??? ??????????? }?
??????????????? newValue.Attach(obj);
???????????? }
??? ??? }
???? }
?????分析此方法,此方法最主要的功勞就是調(diào)用BehaviorCollection.Attach()方法。而 BehaviorCollection集合會調(diào)用里面每一個子項的Attach()方法。前面我們講過在調(diào)用Behavior類的Attach()方法的時候,該方法會調(diào)用OnAttached()方法,所以在OnAttached方法里面,我們就可以為元素添加各種事件代碼。而因為事件的Add操作使得其中 一個Behavior添加的相同的事件處理程序不會覆蓋另一個Behavior添加的事件處理程序,從而我 們可以為一個元素添加很多的Behavior。????????
???? 這里值得注意的是,上面標(biāo)出的紅色背景的參數(shù)obj,在依賴項屬性中 PropertyChangedCallback函數(shù)中的第一個參數(shù)是定義這個屬性的類本身的實例對象。而對于以 來屬性,這個參數(shù)是指被附加了附加屬性的那個對象。這里不明白的話就看不懂上面的代碼。
?
Behaviors VS Triggers???????
???? 下面來看下所謂簡單的實例代碼:
????
???? 看得出這都是Blend提供的程序集才有的命名空間。在實例中我們?yōu)镽ectangle和Canvas 元素添加了Interaction的兩個附加屬性,這兩個附加屬性是集合類型的,里面添加了一些子項, 比如MouseDragElementBehavior,EventTrigger。?????????
???? Trigger和Behavior是類似的,而且用法也如此,Triggers的子項也都是繼承自 IAttachedObject接口的類的實例,所不同的是Triggers子項包含Actions屬性,這個屬性是一個 Action集合,每個Action也都是繼承自IAttachedObject接口。都有Attach()方法和 DeTach()方法,并且相應(yīng)的會觸發(fā)OnAttached()和OnDetached()方法。而Action繼承 自Action的子類還包含InVoke()方法。????????
???? 所以,對于Interaction.Triggers中的每個子項,會制定特定的事件,當(dāng)該事件觸發(fā)會會自動調(diào)用Actions屬性中每個子項的InVoke()方法;而對于Interaction.Behaviors中的每個子 項,通常具體對什么事件做處理封裝在每個Behavior中,這種方式更靈活。????????
???? 對照下面的代碼相信很容易理解兩種的區(qū)別:
????
???? 上面的類ListBoxItemSendToTop是Blend示例中的ColorSwatchSL中定義的一個Action。 該行為將ListBoxItem中的每個元素添加一種行為:當(dāng)鼠標(biāo)經(jīng)過每個Item時將該元素放大,并且 浮到其他元素的上面。可以參考Blend的實例效果。我們可以參考下面的基礎(chǔ)層次結(jié)構(gòu),事實上 TriggerAction還繼承自IAttachedObject接口。
????
???? 這幾行代碼將元素添加附加屬性Interaction.Triggers,并添加一個子項,指明當(dāng)Loaded事 件發(fā)生時觸發(fā)ListBoxItemSendToTop的Invoke()方法。不過奇怪的是Blend的實例并沒有按 正確的思路使用,下面是截圖:
????
???? 從上面的代碼可以看出,對元素行為的附加發(fā)生在OnAttached()方法中,而此方法不是 被EventTrigger制定的Loaded事件觸發(fā),而是發(fā)生在程序初始化時TriggerCollection.Attach()觸發(fā) EventTrigger.Attach(),一次觸發(fā)ListBoxItemSendToTop.Attach()而附加行為的。當(dāng)這種觸發(fā)Loaded事 件的時候什么都沒有做。雖然這些寫程序沒有問題,但是和Triggers的思路卻是不一致的。以上例 子我們可以看出Triggers的執(zhí)行過程。???????
???? 為了比較Triggers和Behaviors,我將上面的Triggers實現(xiàn)改為Behaviors實現(xiàn),代碼如下:
????
???? 我將SendToTopBehavior聲明為一種Behavior,繼承自Behavior<T>,Behavior<T>的基 礎(chǔ)層次結(jié)構(gòu)和Action類似,同樣它也繼承自IAttachedObject接口:
????
???? 通過改寫為Behavior,這才是真正通過OnAttached()方法來添加行為,這和前面分析的過程一致。?????
???? 通過以上比較應(yīng)該能明白Triggers和Behaviors的區(qū)別。Behaviors提供了更靈活的方式,行為響應(yīng)什么方式將完全有行為自己決定。而Action只提供行為,行為受什么事件觸發(fā)則由元素定義中 自己決定。
?
結(jié)論:???????
???? Behavior簡單嗎?現(xiàn)在看來它的思路不算復(fù)雜。然而從上面的分析過程來看也不算簡單。最主 要的問題,里面涉及到的東西比較多,比如要理解附加屬性。還得對XAML有一定理解,對于 XAML ,它里面的每一個標(biāo)記都是對象或者對象的屬性。那么既然是對象,我們就可以利用對象初 始化的時候去干一些事情,比如為元素添加事件。這樣我們就能理解上面發(fā)生的一切,并且會明 白為什么我們感覺XAML又在做一些我們在后臺代碼才能做的事情,認(rèn)為XAML就是一堆死標(biāo)記, 這就不能真正理解 XAML 背后的設(shè)計模式和意義。??????
???? 另外,我也從設(shè)計模式的角度思考這個問題。這樣更有助于理解。???????
???? Blend SDK上兩分鐘就能看完的例子,我花兩天才勉強理解。我在想微軟是不是太高估開發(fā)者?還是我太笨了?
?
System.Windows.EventTrigger VS
System.Windows.Interactivity.EventTrigger
???? 正當(dāng)準(zhǔn)備收筆的時候,才想起Silverlight本身也有觸發(fā)器的啊。那里的用法好像可不一樣。???????
???? 這可吃驚不小,因為很多天沒看Silverlight了,加上一直沒做過項目,概念理解也不是很深。于是趕緊Go To Definition。才發(fā)現(xiàn)兩種是有區(qū)別的,這里做一下比較:
1 ,命名空間區(qū)別:?????????
???? Silverlight本身的Triggers位于System.Windows命名空間,而Blend提供的Triggers位于 System.Windows.Interactivity命名空間,并且隨Blend一起附帶,與Silverlight SDK是獨立的。
2 ,定義區(qū)別:
???? namespace System.Windows.Interactivity
??? {
?? ??? public static class Interaction???
????? {
? ??????? public static readonly DependencyProperty BehaviorsProperty;
? ??????? public static readonly DependencyProperty TriggersProperty;
?? ?????? public static BehaviorCollection GetBehaviors(DependencyObject obj);?
????????? public static TriggerCollection GetTriggers(DependencyObject obj);
????? }
????}
???? namespace System.Windows
??? {
?? ??? public abstract class FrameworkElement : UIElement
?? ??? {
????????????…...???????
??????????? public TriggerCollectionTriggers{ get; }???
??????????? …..
??? ?? }
??? ?}
?????從定義上看,兩種是有區(qū)別的,這決定了使用方式會有區(qū)別。???????
???? ?Silverlight本身的Triggers是一個普通的.NET屬性,而Blend提供的Triggers是一個附加屬性。而我 們轉(zhuǎn)到TriggerCollection的定義,前者是一個普通的.NET集合類,繼承自 DependencyObject,IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable ,而后者 繼承自 DependencyObjectCollection<T> , IAttachedObject ,具有 Attach(),DeTatch(),OnAttached(),OnDeTached()方法。
3 ,使用方式區(qū)別????????
???? 前者的子項是StoryBoard,通過響應(yīng)事件來激發(fā)動畫,而后者正如前面分析,子項是繼承自 IAttachedObject接口的Action對象,通過響應(yīng)事件來激發(fā)Action的Invoke()方法。
???? 以上簡單的分析可以看出兩者雖然名稱相同,但要功能是不一樣的,Silverlight的Trigger是用 來觸發(fā)動畫,動畫是在一段時間內(nèi)持續(xù)的改變元素屬性。而Blend的Trigger更多用來響應(yīng)鼠標(biāo)和鍵 盤這種用戶行為,當(dāng)然你也可以在OnAttached里面來啟動一個動畫,這也是可以的。???????
???? 總之,Blend提供的Trigger更靈活,功能更豐富,可以這么說,你能做出一切你想得到的用戶交互行為。正如WPF的外觀可以無限定制。雖然SDK是那么的簡單!
轉(zhuǎn)載于:https://www.cnblogs.com/hielvis/archive/2010/10/06/1806813.html
總結(jié)
以上是生活随笔為你收集整理的Hello,Behavior的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JME3中级手册一API特征映射1
- 下一篇: 无光驱不支持USB设备启动的笔记本,如何