【问题标题】:WPF SubControl (like TextBlock) doesn't inherit Style from window with TemplateSelectorWPF SubControl(如 TextBlock)不使用 TemplateSelector 从窗口继承样式
【发布时间】:2014-11-21 09:07:39
【问题描述】:

我需要帮助,因为我不明白为什么来自数据模板的控件不继承窗口资源中定义的样式。 可能有解决方法吗?

如果有人能给我一个解决方案,我将非常感激,因为我花了很多时间来寻找一些东西。

这是我的榜样。例如水平模板中的 Texblock 不对齐:

学习: 我添加了背景颜色。样式应用于标签,但不应用于数据模板定义的文本块和文本框。

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:localview="clr-namespace:WpfApplication3"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="{x:Type TextBlock}" TargetType="TextBlock" >
            <Setter Property="Background" Value="Cyan"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
        </Style>
        <Style x:Key="{x:Type Label}" TargetType="Label">
            <Setter Property="Background" Value="Red"/>
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style x:Key="{x:Type TextBox}" TargetType="TextBox">
            <Setter Property="Background" Value="Cyan"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
        </Style>
        <Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
        </Style>

        <localview:TemplateSelector x:Key="TemplateSelector">
            <localview:TemplateSelector.DataTemplateH>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="Value"/>
                        <TextBox Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}"/>
                    </StackPanel>
                </DataTemplate>
            </localview:TemplateSelector.DataTemplateH>
            <localview:TemplateSelector.DataTemplateV>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Label Content="Value"/>
                        <StackPanel Orientation="Horizontal">
                            <Label Content="new line"/>
                            **<TextBlock Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}" TextAlignment="Right"/>**
                        </StackPanel>
                    </StackPanel>
                    </DataTemplate>
            </localview:TemplateSelector.DataTemplateV>
        </localview:TemplateSelector>

    </Window.Resources>


    <StackPanel Orientation="Vertical">

        <StackPanel>
            <TextBlock Text="Texblock"/>
            <TextBox Text="Texblock"/>
            <StackPanel Orientation="Horizontal">
                <Label Content="Value"/>
                <ComboBox Name="Combo">
                    <ComboBox.Items>
                        <ComboBoxItem Content="H"/>
                        <ComboBoxItem Content="V"/>
                    </ComboBox.Items>
                </ComboBox>
            </StackPanel>
            <ContentControl  ContentTemplateSelector="{StaticResource TemplateSelector}" 
                                      Content="{Binding Path=SelectedItem.Content ,ElementName=Combo}" />
        </StackPanel>

    </StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Reflection;


namespace WpfApplication3
{
    public class TemplateSelector : DataTemplateSelector
    {

        public DataTemplate DataTemplateH
        {
            get;
            set;
        }

        public DataTemplate DataTemplateV
        {
            get;
            set;
        }


        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string s = (string)item;

            if (s == "H")
                return DataTemplateH;

            if (s == "V")
                return DataTemplateV;

            return base.SelectTemplate(item, container);
        }
    }
}

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    只是为了阐明为什么TextBlock 没有找到它的隐式样式,WPF 中的一个奇怪的规则是隐式样式只能由从模板边界继承的元素继承Control类;不从Control 继承的元素不会探测父模板之外的隐式样式。

    负责这个的代码可以在FrameworkElement找到:

    // FindImplicitSytle(fe) : Default: unlinkedParent, deferReference
    internal static object FindImplicitStyleResource(
        FrameworkElement fe,
        object resourceKey,
        out object source)
    {
        ...
    
        // For non-controls the implicit StyleResource lookup must stop at
        // the templated parent. Look at task 25606 for further details.
        DependencyObject boundaryElement = null;
        if (!(fe is Control))
        {
            boundaryElement = fe.TemplatedParent;
        }
    
        ...
    }
    

    微软的卡罗尔·斯奈德explains the reasons for this behavior

    给我的原因是控件比元素更明显,并且很可能应该在任何地方应用控件的隐式样式,而元素的隐式样式应该被普遍应用的可能性较小。这个论点是有道理的。考虑以下几点:

    <StackPanel>
      <StackPanel.Resources> 
        <Style TargetType="TextBlock"> 
          <Setter Property="FontSize" Value="16"/> 
          <Setter Property="Foreground" Value="Green"/> 
        </Style>
      </StackPanel.Resources>
    
      <TextBlock HorizontalAlignment="Center" Text="Hello!"/> 
      <Button Content="Click me!" Width="200"/> 
      <TextBlock HorizontalAlignment="Center" Text="Please click the button"/>
    </StackPanel>
    

    Button 通过最终创建一个 TextBlock 并将字符串添加到 TextBlock 来显示字符串。如果 Button 中的 TextBlock 使用了应用程序定义的隐式样式,XAML 会以这种方式呈现:

    这可能不是您想要的行为。另一方面,假设您正在创建一个很酷的 UI,并且您希望所有的重复按钮都具有特定的外观。如果您定义了一次重复按钮的外观,那么所有使用的重复按钮都将具有该外观,即使重复按钮位于 ControlTemplate 内。

    【讨论】:

    • 这确实有助于我理解 TextBlock(以及其他非控件)的奇怪样式外观行为,谢谢。如果第一次发布,这当然应该被接受(也许 OP 会考虑这一点并改变他的决定)。我不确定您在回答 OP 的问题之前是否知道这个原因,但如果您在尝试回答这个问题时才真正知道这一点,您应该对 OP 的问题投赞成票。 (我希望我能再次投票)。
    • 非常感谢这些我不知道的有用解释。
    【解决方案2】:

    我刚刚尝试了一些简单的演示,是的,答案是您不能将模板外部某处定义的默认样式应用于模板 inside 的某些 TextBlock(包括 DataTemplate和控制模板)。其他控件如Label、TextBox不会出现这种情况(虽然你也说过Style不适用于TextBox,但我试过了,其实不是这样)。

    要解决此问题,最好的方法是为 TextBlock 显式设置样式,如下所示:

    <TextBlock Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}" 
               TextAlignment="Right" Style="{StaticResource {x:Type TextBlock}}"/>
    

    请注意,正如我所说,它仅适用于 TextBlocks inside 模板(DataTemplate 和 ControlTemplate)。

    代码看起来相当荒谬,但它实际上可以工作,如果不这样做,你会看到它不会工作。

    【讨论】:

    • 非常感谢这个适合我的解决方案。对于文本框,你是对的。
    【解决方案3】:

    一些不明确样式的选项:

    • 如果您希望样式适用于任何地方,请将它们放在 App.xaml 中。即使在其他地方被阻止,隐式样式查找也会检查 App.xaml。
    • 如果没有,您可以将样式移动到 ResourceDictionary XAML 文件中,并将其合并到 DataTemplate 的资源中(绕过继承块),以及您想要的任何其他位置。
    • 如果您确实希望外部样式继承到模板中,您可以使用 C# 将控件的资源添加到其 DataTemplate 的资源中,绕过继承块。比如:dataTemplate.Resources.MergedDictionaries.Add(control.Resources)

    非常感谢Mike's great answer 让我明白了为什么 TextBlock 是特别的。

    我想总结一下:

    当隐式样式针对不从 Control 继承的元素(TextBlock、Ellipse 等)时,它们是特殊的。这些样式不会从控件外部继承到其模板中,除非它们在控件的资源或 App.xaml 中定义(或合并到)。在前一种情况下,它们会影响控件模板,但不会影响标题或内容模板。在后者中,它们会影响所有三个。

    【讨论】:

      猜你喜欢
      • 2021-10-30
      • 2014-12-03
      • 2011-06-02
      • 2023-03-26
      • 2014-06-12
      • 1970-01-01
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多