Application.Resources与Window.Resources中的隐式样式?

时间:2012-01-27 15:38:23

标签: wpf resources

我正在查看this question,并注意到在TextBlock中放置一个隐式Application.Resources样式会将该样式应用于所有TextBlock,即使是Buttons等其他控件内的那些样式, ComboBoxes等等

<Application.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Blue" />
    </Style>
</Application.Resources>

将隐式样式放在Window.Resources does not cross control template boundaries中,因此ButtonsComboBoxes之类的内容会保留默认的黑色文字。

<Window.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Blue" />
    </Style>
</Window.Resources>

此外,在Application.Resources中添加默认样式会使您无法使用其他隐式样式覆盖该样式。

<!-- Doesn't work if implicit style with same property is in Application.Resources -->
<ComboBox.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red" />
    </Style>
</ComboBox.Resources>

我的问题是:

  • 这是为什么?
  • Application.ResourcesWindows.Resources之间是否存在其他差异?
  • 何时应该使用一个而不是另一个?

    我了解Application.Resources适用于整个应用,而Window.Resources仅适用于该窗口,但我想知道为什么Application中的样式与{中的样式的处理方式不同{1}}

4 个答案:

答案 0 :(得分:23)

这实际上是WPF中唯一的特殊处理,它是由设计完成的。实现它的代码可以在方法FrameworkElement的{​​{1}}中找到,它实际上有效:

FindImplicitStyleResource

因此,经验法则是隐式样式总是应用于控件(即从internal static object FindImplicitStyleResource(FrameworkElement fe, object resourceKey, out object source) { // ... DependencyObject boundaryElement = null; if (!(fe is Control)) boundaryElement = fe.TemplatedParent; // ... } 派生)。假设可以找到隐式Style。

对于Control中未使用ControlTemplate派生的元素,例如Control,隐式样式查找不会跨越它的模板化父级。在您的情况下,这将是TextBlock

我相信这样做是为了使非应用程序隐式的TextBlock样式不会无意中应用于控件模板中使用的ComboBox元素,开发人员可能会或可能不知道这些元素。隐式样式只会应用于开发人员在他们自己的XAML中实际创建的TextBlock。

应用程序隐式样式仍然允许全局样式,例如增加字体大小。但可能造成了更多的混乱而不是它的价值。

没有什么好的答案可以说明何时使用它们,因为它们各自都有它们的功能。显然,如果您不想影响应用程序中的每个TextBlock,则不应将样式放在应用程序资源中。

但请注意,这会影响任何非TextBlock元素,例如Control元素。

答案 1 :(得分:2)

非常简单明了

如果您希望在ENTIRE应用程序之间共享资源,您可以使用Application.Resources

如果您希望在整个窗口之间共享资源,您可以使用Window.Resources

如果您希望在您使用的单个控件之间共享资源(Whatever Control).Resources

假设您有多个窗口,但您只想在一个窗口中使用默认样式而不是另一个窗口,那么您将使用Windoe.Resources

答案 2 :(得分:0)

不同之处在于风格的范围:

  • 放置在Application.Resources中时,样式将应用于应用程序中的所有控件
  • 放置在Windows.Resources中时,样式将应用于窗口中的所有控件

差异在那里非常微妙,但它意味着无论我们谈论什么控件(包括在另一个控件模板中的控件)都将从application.Resources获得样式。但只有控件直接窗口的子窗口才会从window.Resources获取样式。 antoher控件模板中的控件将不具有Window.Resources中定义的样式,因为它不直接在窗口中,而它将具有Application.Resources中定义的样式,因为它在应用程序中。

关于你的第二点,它与我认为的依赖属性优先级有关:

http://msdn.microsoft.com/en-us/library/ms743230.aspx

答案 3 :(得分:0)

瑞秋,我认为“样式”没有什么特别之处。而且,没有“跨越模板边界”的问题。其原因是不同的,它适用于WPF应用程序中的不同“树”。 通过你的问题,我想你正在描绘一个具有以下等级的世界:
- 申请=&gt; Window =&gt; Control =&gt;控件中的元素

没有这样的等级。 WPF应用程序中有不同的树,最着名的是逻辑树和可视树,但还有更多(路由事件树和资源查找树,语义略有不同)。

假设以下XAML:

<Window x:Class="SO.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button x:Name="btn" Click="click">Click Me</Button>
    </Grid>
</Window>

对于此XAML,逻辑树将如下所示:
- Window =&gt;网格=&gt;按钮=&gt;串

按钮内的文本块不是逻辑树的一部分(虽然它是VisualTree的一部分)。

通过LogicalTree查找资源,但有一点不同。到达顶层对象后,查找资源算法将查看 Application 资源字典,然后查看 Theme 资源字典,然后查看 System < / em>此顺序的资源字典。

参见以下文章:

Finnaly,为了证明我的观点,将以下资源添加到应用程序XAML中:

<Application x:Class="SO.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:clr="clr-namespace:System;assembly=mscorlib"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <clr:String x:Key="MyResource">Hello Application Resource</clr:String>
    </Application.Resources>
</Application>

以及后面的代码:

private void click(object sender, RoutedEventArgs e)
{
    // Logical Children of btn
    Debug.WriteLine("Children of btn:");
    foreach( var x in LogicalTreeHelper.GetChildren(btn) ) {
        Debug.WriteLine("{0} : {1}", x, x.GetType());
    }

    // Walk the visual tree
    Debug.WriteLine("The Visual Tree:");
    WalkVisual(0, this);

    // Find the textblock within the button
    DependencyObject p = btn;
    while (p.GetType() != typeof(TextBlock))
        p = VisualTreeHelper.GetChild(p, 0);
     TextBlock tb = p as TextBlock;

    // Now climp the textblock through the logical tree
    while (p != null)
    {
        Debug.WriteLine("{0}", p.GetType());
        p = LogicalTreeHelper.GetParent(p);
    }

    // Find a resource for the textbox
    string s = tb.FindResource("MyResource") as string;
    Debug.WriteLine("MyResource Content: {0}", s);
}

private void WalkVisual(int indent, DependencyObject p)
{
    string fmt = string.Format("{{0,{0}}}{{1}}", indent * 4);
    Debug.WriteLine(fmt, "", p.GetType());
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(p); ++i)
    {
        WalkVisual(indent+1,VisualTreeHelper.GetChild(p, i));
    }
}

所以......一旦你理解了第一个问题(“为什么会这样”),其他问题就会崩溃。应用程序资源和窗口资源之间的不同之处在于,应用程序资源可以由应用程序中的任何DependencyObject(包括在其他程序集中定义的那些)来配置。当你想要实现这个目标时,你会使用它: - )

Ú