• 如何在Silverlight里实现隐式样式


    概述:WPF 中,我们可以方便的在全局范围定义一个样式,就可以应用到所有这种类型的对象,这就是所谓的隐式样式。但是在 Silverlight 里没有办法这样做。我们必须手工对每一个需要设置样式的控件添加 Style="{StaticResource someStyle}" 这样的语句,挨个设置,非常麻烦。好在 Silverlight Toolkit 里提供了一个类似的实现,叫做 ImplicitStyleManager (隐式样式管理器,可以简称 ISM)。

      WPF 中,我们可以方便的在全局范围定义一个样式,就可以应用到所有这种类型的对象,这就是所谓的隐式样式。但是在 Silverlight 里没有办法这样做。我们必须手工对每一个需要设置样式的控件添加 Style="{StaticResource someStyle}" 这样的语句,挨个设置,非常麻烦。好在 Silverlight Toolkit 里提供了一个类似的实现,叫做 ImplicitStyleManager (隐式样式管理器,可以简称 ISM)。

      在 WPF 中,我们可以方便的在全局范围定义一个样式,就可以应用到所有这种类型的对象,这就是所谓的隐式样式(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>
    <!-- 针对一种类型设置全局样式 -->
    <Style TargetType="Button">
    <Setter Property="Background" Value="AliceBlue" />
    </Style>
    </Grid.Resources>
    <StackPanel>
    <Button>Button a</Button>
    <Button>Button b</Button>
    </StackPanel>
    </Grid>
    </Window>

      这样之后,两个按钮就都变成了浅蓝色的背景。

      但是在 Silverlight 里没有办法这样做。我们必须手工对每一个需要设置样式的控件添加 Style="{StaticResource someStyle}" 这样的语句,挨个设置,非常麻烦。

      好在 Silverlight Toolkit 里提供了一个类似的实现,叫做 ImplicitStyleManager (隐式样式管理器,可以简称 ISM)。

      该类的使用方法,是在某个根元素上设置一个附加属性(Attached Property),然后,该元素下属的视觉树里符合特定类型的子元素的样式,就可以被自动应用隐式样式了。

      例子如下:

    <UserControl x:Class="ImplicitStyleTest.Page"
    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>
    <!-- 在根元素上设置一次就可以了 -->
    <StackPanel theming:ImplicitStyleManager.ApplyMode="Auto">
    <Button Content="Button a"></Button>
    <Button Content="Button b"></Button>
    </StackPanel>
    </Grid>
    </UserControl>

      运行一下例子试试就会发现,两个按钮的样式都被设置了,这样就实现了类似 WPF 里的隐式样式行为。

      在这个例子里可以看到,ApplyMode 属性被设置成了 Auto. 其实它一共有3个可选值,分别代表如下含义:

    1. Auto

      每当 layout updated 的时候,ISM 会重新应用隐式样式。在这种模式下如果元素在以后被动态的加入到视觉树中, 它们将被应用隐式样式。
    需要注意的是,LayoutUpdated 事件发生的非常频繁,并且不光是当你添加元素后才发生。而我们又没有类似 ItemAddedToTree 的事件,如果视觉树比较大的话,ISM 遍历它的时候就会花费比较多的时间,这可能给性能带来一定的影响。但是为了方便,这里只好做一些折中的权衡,牺牲一点性能。如果视觉树很大的时候,可以考 虑改用 OneTime 模式。

    2. OneTime

      仅在第一次加载时起作用,对后面动态加到 visual tree 里的元素不起作用。
    有时候,你的视觉树很大,所以你不考虑用 Auto 模式。这时候你可以用 OneTime 模式一次性应用样式;同时,需要在添加新节点之后,在代码里手工调用 ISM 的 Apply 方法,这样可以重新应用一次样式。这样的办法可以避免 Auto 模式的一些性能损失。

    3. None

      效果跟没设置 ApplyMode 属性一样。

      了解了 ISM 如何使用,我们来看看它是怎么实现的。

      我们知道,Silverlight 元素里面的 Style 在运行时只能被设置一次,否则就会出错,ISM 也不例外,也要受这个制约。

    ISM 的实现原理大致如下:

      1. 定义一个叫做 ApplyMode 的附加属性(Attached Property),提供给需要设置样式的“根”元素使用。
    而我们知道,附加属性可以在 xaml 里被设置,这就像上面的例子里所写的那样;同时,它有一个最大的好处,就是可以定义属性改变时触发的回调函数(注册时定义在 PropertyMetadata 里面)。这样,当我们在代码里设置了 ApplyMode 后,ISM 就能触发这个回调函数进行处理了。

      2. 在这个回调函数中,注册元素的 LayoutUpdated 事件处理函数,这样,在该元素不管因为什么原因更新其 layout 的时候,就能够得到通知。
    这里最巧妙的地方是:将元素 LayoutUpdated 事件的处理委托以依赖属性(DependencyProperty) 的形式存在该元素自身的属性中,这样就省去了自行管理很多 event handler 的烦恼了。。。依赖属性真的是个好东西啊!
    代码:

    /// <summary>
    /// 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 的事件处理函数中,遍历控件的视觉树,对符合条件的元素设置 Style(也只能设置一次)。
    这里值得一说的是遍历树的代码技巧,为了避免递归或者类似方法遍历树造成的开销,这里实际使用了一种很巧妙的 Stack 来访问树节点。并且,在所有需要遍历的地方,尽可能的使用了 yield return, 以一种函数式编程的写法来延迟实际对节点的操作。

      具体代码不细细解释了,这里把 MS 的代码贴来仅供欣赏一下 Functional Programming,有兴趣的朋友可以自己研究:

    /// <summary>
    /// 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.evget.com/zh-CN/info/catalog/16631.html

  • 相关阅读:
    emulate sh
    postmaster.c 中的 ListenAddresses
    PostgreSQL的postmaster的fork动作验证
    NotifyMyFrontEnd 函数背后的数据缓冲区(三)
    对${ZSH_VERSION+set}的验证
    微软正准备一个简易的Rootkit清除方案 助用户打补丁 狼人:
    创新与安全:云计算的两只跷跷板 狼人:
    苹果禁止iPhone黑客访问App Store应用商店 狼人:
    春节不回家 单身留守族“拼饭”“拼玩” 狼人:
    僵尸侵入全球 袭击者或为东欧黑帮 狼人:
  • 原文地址:https://www.cnblogs.com/aoldman/p/3133609.html
Copyright © 2020-2023  润新知