Silverlight 里如何实现隐式样式,ImplicitStyleManager 的实现思想
在 WPF 中,我們可以方便的在全局范圍定義一個(gè)樣式,就可以應(yīng)用到所有這種類型的對(duì)象,這就是所謂的隱式樣式(implicit Style),比如:
<Window?x:Class="WpfImplicitStyle.Window1"????xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
????xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
????Title="Window1"?Height="300"?Width="300">
????<Grid>
????????<Grid.Resources>
????????????<!--?針對(duì)一種類型設(shè)置全局樣式?-->
????????????<Style?TargetType="Button">
????????????????<Setter?Property="Background"?Value="AliceBlue"?/>?
????????????</Style>
????????</Grid.Resources>
????????<StackPanel>
????????????<Button>Button?a</Button>
????????????<Button>Button?b</Button>
????????</StackPanel>
????</Grid>
</Window>
這樣之后,兩個(gè)按鈕就都變成了淺藍(lán)色的背景。
但是在 Silverlight 里沒(méi)有辦法這樣做。我們必須手工對(duì)每一個(gè)需要設(shè)置樣式的控件添加 Style="{StaticResource someStyle}" 這樣的語(yǔ)句,挨個(gè)設(shè)置,非常麻煩。
好在 Silverlight Toolkit 里提供了一個(gè)類似的實(shí)現(xiàn),叫做 ImplicitStyleManager (隱式樣式管理器,可以簡(jiǎn)稱 ISM)。
該類的使用方法,是在某個(gè)根元素上設(shè)置一個(gè)附加屬性(Attached Property),然后,該元素下屬的視覺(jué)樹(shù)里符合特定類型的子元素的樣式,就可以被自動(dòng)應(yīng)用隱式樣式了。
例子如下:
????xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"?
????xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"?
????Width="400"?Height="300"
????????xmlns:theming="clr-namespace:Microsoft.Windows.Controls.Theming;assembly=Microsoft.Windows.Controls.Theming">
????<Grid?x:Name="LayoutRoot"?Background="White">
????????<Grid.Resources>
????????????<Style?TargetType="Button">
????????????????<Setter?Property="Background"?Value="AliceBlue"?/>
????????????</Style>
????????</Grid.Resources>
????????<!--?在根元素上設(shè)置一次就可以了?-->
????????<StackPanel?theming:ImplicitStyleManager.ApplyMode="Auto">
????????????<Button?Content="Button?a"></Button>
????????????<Button?Content="Button?b"></Button>
????????</StackPanel>
????</Grid>
</UserControl>
運(yùn)行一下例子試試就會(huì)發(fā)現(xiàn),兩個(gè)按鈕的樣式都被設(shè)置了,這樣就實(shí)現(xiàn)了類似 WPF 里的隱式樣式行為。
在這個(gè)例子里可以看到,ApplyMode 屬性被設(shè)置成了 Auto. 其實(shí)它一共有3個(gè)可選值,分別代表如下含義:
1. Auto
每當(dāng) layout updated 的時(shí)候,ISM 會(huì)重新應(yīng)用隱式樣式。在這種模式下如果元素在以后被動(dòng)態(tài)的加入到視覺(jué)樹(shù)中, 它們將被應(yīng)用隱式樣式。
需要注意的是,LayoutUpdated 事件發(fā)生的非常頻繁,并且不光是當(dāng)你添加元素后才發(fā)生。而我們又沒(méi)有類似 ItemAddedToTree 的事件,如果視覺(jué)樹(shù)比較大的話,ISM 遍歷它的時(shí)候就會(huì)花費(fèi)比較多的時(shí)間,這可能給性能帶來(lái)一定的影響。但是為了方便,這里只好做一些折中的權(quán)衡,犧牲一點(diǎn)性能。如果視覺(jué)樹(shù)很大的時(shí)候,可以考慮改用 OneTime 模式。
2. OneTime
僅在第一次加載時(shí)起作用,對(duì)后面動(dòng)態(tài)加到 visual tree 里的元素不起作用。
有時(shí)候,你的視覺(jué)樹(shù)很大,所以你不考慮用 Auto 模式。這時(shí)候你可以用 OneTime 模式一次性應(yīng)用樣式;同時(shí),需要在添加新節(jié)點(diǎn)之后,在代碼里手工調(diào)用 ISM 的 Apply 方法,這樣可以重新應(yīng)用一次樣式。這樣的辦法可以避免 Auto 模式的一些性能損失。
3. None
效果跟沒(méi)設(shè)置 ApplyMode 屬性一樣。
了解了 ISM 如何使用,我們來(lái)看看它是怎么實(shí)現(xiàn)的。
我們知道,Silverlight 元素里面的 Style 在運(yùn)行時(shí)只能被設(shè)置一次,否則就會(huì)出錯(cuò),ISM 也不例外,也要受這個(gè)制約。
ISM 的實(shí)現(xiàn)原理大致如下:
1. 定義一個(gè)叫做 ApplyMode 的附加屬性(Attached Property),提供給需要設(shè)置樣式的“根”元素使用。
而我們知道,附加屬性可以在 xaml 里被設(shè)置,這就像上面的例子里所寫(xiě)的那樣;同時(shí),它有一個(gè)最大的好處,就是可以定義屬性改變時(shí)觸發(fā)的回調(diào)函數(shù)(注冊(cè)時(shí)定義在 PropertyMetadata 里面)。這樣,當(dāng)我們?cè)诖a里設(shè)置了 ApplyMode 后,ISM 就能觸發(fā)這個(gè)回調(diào)函數(shù)進(jìn)行處理了。
2. 在這個(gè)回調(diào)函數(shù)中,注冊(cè)元素的 LayoutUpdated 事件處理函數(shù),這樣,在該元素不管因?yàn)槭裁丛蚋缕?layout 的時(shí)候,就能夠得到通知。
這里最巧妙的地方是:將元素 LayoutUpdated 事件的處理委托以依賴屬性(DependencyProperty) 的形式存在該元素自身的屬性中,這樣就省去了自行管理很多 event handler 的煩惱了。。。依賴屬性真的是個(gè)好東西啊!
代碼:
///?ApplyModeProperty?property?changed?handler.
///?</summary>
///?<param?name="dependencyObject">FrameworkElement?that?changed?its?
///?ApplyMode.</param>
///?<param?name="eventArgs">Event?arguments.</param>
private?static?void?OnApplyModePropertyChanged(DependencyObject?dependencyObject,?DependencyPropertyChangedEventArgs?eventArgs)
{
????FrameworkElement?element?=?dependencyObject?as?FrameworkElement;
????if?(element?==?null)
????{
????????throw?new?ArgumentNullException("dependencyObject");
????}
????ImplicitStylesApplyMode?oldMode?=?(ImplicitStylesApplyMode)eventArgs.OldValue;
????ImplicitStylesApplyMode?newMode?=?(ImplicitStylesApplyMode)eventArgs.NewValue;
????ImplicitStyleManager.SetHasBeenStyled(element,?false);
????EventHandler?eventHandler?=?ImplicitStyleManager.GetLayoutUpdatedHandler(element);
????//?If?element?is?automatically?styled?(once?or?always)?attach?event?
????//?handler.
????if?((newMode?==?ImplicitStylesApplyMode.Auto?||?newMode?==?ImplicitStylesApplyMode.OneTime)
????????&&?oldMode?==?ImplicitStylesApplyMode.None)
????{
????????if?(eventHandler?==?null)
????????{
????????????eventHandler?=
????????????????(sender,?args)?=>
????????????????{
????????????????????ImplicitStyleManager.PropagateStyles(element,?false);
????????????????};
????????????ImplicitStyleManager.SetLayoutUpdatedHandler(element,?eventHandler);
????????????element.LayoutUpdated?+=?eventHandler;
????????}
????}
????else?if?((oldMode?==?ImplicitStylesApplyMode.Auto?||?oldMode?==?ImplicitStylesApplyMode.OneTime)
????????&&?newMode?==?ImplicitStylesApplyMode.None)
????{
????????if?(eventHandler?!=?null)
????????{
????????????element.LayoutUpdated?-=?eventHandler;
????????????ImplicitStyleManager.SetLayoutUpdatedHandler(element,?null);
????????}
????}
}
3. 在上述 LayoutUpdated 的事件處理函數(shù)中,遍歷控件的視覺(jué)樹(shù),對(duì)符合條件的元素設(shè)置 Style(也只能設(shè)置一次)。
這里值得一說(shuō)的是遍歷樹(shù)的代碼技巧,為了避免遞歸或者類似方法遍歷樹(shù)造成的開(kāi)銷,這里實(shí)際使用了一種很巧妙的 Stack 來(lái)訪問(wèn)樹(shù)節(jié)點(diǎn)。并且,在所有需要遍歷的地方,盡可能的使用了 yield return, 以一種函數(shù)式編程的寫(xiě)法來(lái)延遲實(shí)際對(duì)節(jié)點(diǎn)的操作。
具體代碼不細(xì)細(xì)解釋了,這里把 MS 的代碼貼來(lái)僅供欣賞一下 Functional Programming,有興趣的朋友可以自己研究:
///?This?method?propagates?the?styles?in?the?resources?associated?with
///?a?framework?element?to?its?descendents.?This?results?in?a??
///?style?inheritance?that?mimics?WPF's?behavior.
///?</summary>
///?<param?name="element">The?element?that?will?have?its?styles?
///?propagated?to?its?children.</param>
///?<param?name="recurse">Whether?to?recurse?over?styled?elements?that
///?are?set?to?OneTime?and?have?already?been?styled.</param>
private?static?void?PropagateStyles(FrameworkElement?element,?bool?recurse)
{
????BaseMergedStyleDictionary?initialDictionary?=?GetMergedStyleDictionary(element);
????//?Create?stream?of?elements?and?their?base?merged?style?
????//?dictionaries?by?traversing?the?logical?tree.
????IEnumerable<Tuple<FrameworkElement,?BaseMergedStyleDictionary>>?elementsToStyleAndDictionaries?=
????????FunctionalProgramming.Traverse(
????????????new?Tuple<FrameworkElement,?BaseMergedStyleDictionary>(element,?initialDictionary),
????????????(elementAndDictionary)?=>?
????????????????elementAndDictionary
????????????????????.First
????????????????????.GetLogicalChildrenDepthFirst()
????????????????????.Select(childElement?=>?
????????????????????????new?Tuple<FrameworkElement,?BaseMergedStyleDictionary>(
????????????????????????????childElement,?
????????????????????????????new?MergedStyleResourceDictionary(
????????????????????????????????ImplicitStyleManager.GetExternalResourceDictionary(childElement)????childElement.Resources,
????????????????????????????????elementAndDictionary.Second))),
????????????(elementAndDictionary)?=>?recurse?||
????????????????(ImplicitStyleManager.GetApplyMode(elementAndDictionary.First)?!=?ImplicitStylesApplyMode.OneTime?||
????????????????!ImplicitStyleManager.GetHasBeenStyled(elementAndDictionary.First)));
????foreach?(Tuple<FrameworkElement,?BaseMergedStyleDictionary>?elementToStyleAndDictionary?in?elementsToStyleAndDictionaries)
????{
????????FrameworkElement?elementToStyle?=?elementToStyleAndDictionary.First;
????????BaseMergedStyleDictionary?styleDictionary?=?elementToStyleAndDictionary.Second;
????????bool?styleApplied?=?false;
????????if?(elementToStyle.Style?==?null)
????????{
????????????Style?style?=?styleDictionary[GetStyleKey(elementToStyle)];
????????????if?(style?!=?null)
????????????{
????????????????elementToStyle.Style?=?style;
????????????????styleApplied?=?true;
????????????}
????????}
????????if?(ImplicitStyleManager.GetApplyMode(elementToStyle)?==?ImplicitStylesApplyMode.OneTime?&&?(VisualTreeHelper.GetChildrenCount(elementToStyle)?>?0?||?styleApplied))
????????{
????????????ImplicitStyleManager.SetHasBeenStyled(elementToStyle,?true);
????????}
????}
}
參考:
http://www.beacosta.com/blog/?p=54
http://www.beacosta.com/blog/?p=55
(好像是 MS Silverlight 團(tuán)隊(duì)的一個(gè)美女,推薦訂閱或關(guān)注她的博客)
轉(zhuǎn)載于:https://www.cnblogs.com/RChen/archive/2008/12/16/1355906.html
總結(jié)
以上是生活随笔為你收集整理的Silverlight 里如何实现隐式样式,ImplicitStyleManager 的实现思想的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux shell 字体颜色样板,B
- 下一篇: linux环境生成pdf字体问题,字体概