wpf 应用程序中的隐式样式,资源vs窗口资源?

cig3rfwq  于 2023-05-01  发布在  其他
关注(0)|答案(5)|浏览(151)

我正在查看this question,并注意到在Application.Resources中放置一个隐式TextBlock样式会将该样式应用于所有TextBlock,甚至包括其他控件(如ButtonsComboBoxes等)中的TextBlock

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

将隐式样式放置在Window.Resourcesdoes 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中的样式与Window中的样式处理方式不同

chy5wohz

chy5wohz1#

这实际上是添加到WPF中的唯一特殊处理,并且它是通过设计完成的。实现它的代码可以在FrameworkElement中找到,在方法FindImplicitStyleResource中,它实际上是这样做的:

internal static object FindImplicitStyleResource(FrameworkElement fe, object resourceKey, out object source)
{
        // ...
        DependencyObject boundaryElement = null;
        if (!(fe is Control))
            boundaryElement = fe.TemplatedParent;
        // ...
}

因此,经验法则是,隐式样式总是应用于控件(即。即从Control导出)。假设可以找到隐式样式。
对于ControlTemplate中使用的不是从Control派生的元素,例如TextBlock,隐式Style查找不会跨越它的模板化父元素。在上面的例子中,这将是ComboBox
我相信这样做是为了防止TextBlock的非应用程序隐式样式被无意中应用到控件模板中使用的TextBlock元素,开发人员可能知道也可能不知道这些元素。隐式样式将仅应用于实际上由开发人员在其自己的XAML中创建的TextBlocks。
应用程序隐式样式仍然允许全局样式,例如增加字体大小。但可能造成了更多的混乱。
没有一个好的答案来说明何时使用一个或另一个,因为它们都有自己的功能。显然,如果不想影响应用程序中的每个TextBlock,就不应该将样式放在应用程序资源中。
但请记住,这会影响任何非Control元素,例如Shape元素。

8tntrjer

8tntrjer2#

就这么简单。
如果您希望资源在整个应用程序中共享,则可以使用Application.Resources
如果你想在整个窗口中共享资源,你可以使用Window.Resources
如果您希望资源在单个控件之间共享,您可以使用(Whatever Control).Resources
假设你有多个窗口,但你只想在一个窗口中使用默认样式,而不想在另一个窗口中使用默认样式,那么你可以使用Window.Resources

4smxwvx5

4smxwvx53#

瑞秋,我不觉得“时尚”有什么特别的此外,不存在“跨越模板边界”的问题。原因是不同的,它会在WPF应用程序中使用不同的“树”。通过你的问题,我推测你正在描绘一个具有以下层次结构的世界:

  • 应用程序=〉窗口=〉控件=〉控件中的元素
    没有这样的等级制度。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 =〉Grid =〉Button =〉String
    按钮内的文本块不是逻辑树的一部分(但它是VisualTree的一部分)。
    通过LogicalTree查找资源,但有一点不同。在它到达顶部对象之后,查找资源算法将按以下顺序查看 Application 资源字典,然后查看 Theme 资源字典,然后查看 System 资源字典。
    参见以下文章:

最后,为了证明我的观点,将以下资源添加到应用程序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(包括在其他程序集中定义的那些)来创建。当这是你想要实现的时候,你会使用它:-)
u。

rhfm7lfc

rhfm7lfc4#

不同之处在于样式的范围:

  • 在应用程序中放置时。资源,则样式将应用于应用程序中的所有控件
  • 放在Windows内。资源,则样式将应用于窗口中的所有控件

这里的区别是非常微妙的,但它意味着无论我们谈论的是什么控件(包括另一个控件模板中的控件)都将从application获得样式。但是只有窗口的直接子控件才能从窗口获取样式。控件模板中的控件将不具有Window中定义的样式。资源,因为它不直接在窗口中,而它将具有在Application中定义的样式。资源,因为它在应用程序中。
至于你的第二点,我认为它与依赖属性优先级有关:
http://msdn.microsoft.com/en-us/library/ms743230.aspx

1wnzp6jl

1wnzp6jl5#

CodeNaked回答正确。它是由WPF设计。我打开了机票问题here,这是一个充分的解释,它是由设计。另外,什么是在线文档(dependency-property-precedence-list),function (DependencyPropertyHelper.GetValueSource)desktop tools (snoopwpf)可以用来帮助您浏览这些行为。虽然我们作为WPF用户感到不高兴(我们期望相同的行为),但我们对此无能为力。选项:明确定义内容,如“自定义模板”或“引用“,以做更多的工作-自定义模板,自定义内容类型,新控件等<Label.Content>。我觉得没必要“

相关问题