WPF 样式不适用于已设置样式的用户控件

Posted

技术标签:

【中文标题】WPF 样式不适用于已设置样式的用户控件【英文标题】:WPF Style is not applied for a UserControl who is already styled 【发布时间】:2013-08-11 08:16:17 【问题描述】:

我来找你是因为我有几个小时对控件样式感到头疼。 通过为用户控件定义样式,它不起作用!

我的用户控件声明:

<uiComponent:NumericTextBox Text="Binding myProperty"/>

我要应用的样式:

<Style TargetType="uiComponent:NumericTextBox">
   <Setter Property="Background" Value="Black"/>
</Style>

为什么它不能与 Background 属性一起使用,尽管它可以与 Visibility 属性一起使用! 我用 TargetType=FrameworkElement 试过了,没有效果....

我的用户控件是一个数字文本框,它定义了自己的样式,如下所示:

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
  xmlns:l="clr-namespace:LSX.Space.PropertyUI.NumericTextBox">

    <SolidColorBrush x:Key="CustomTextBox_Background" Color="White" />
    <SolidColorBrush x:Key="CustomTextBox_Foreground" Color="Black" />
    <LinearGradientBrush x:Key="CustomTextBox_Border" StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#FFABADB3" Offset="0.05" />
        <GradientStop Color="#FFE2E3EA" Offset="0.07" />
        <GradientStop Color="#FFE3E9EF" Offset="1" />
    </LinearGradientBrush>

    <Style x:Key="x:Type l:NumericTextBox" TargetType="x:Type l:NumericTextBox">
        <Setter Property="Background" Value="StaticResource CustomTextBox_Background" />
        <Setter Property="BorderBrush" Value="StaticResource CustomTextBox_Border" />
        <Setter Property="Foreground" Value="StaticResource CustomTextBox_Foreground" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="FocusVisualStyle" Value="x:Null"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="x:Type l:NumericTextBox">
                    <Border x:Name="Border"
                      Background="TemplateBinding Background"
                      BorderBrush="TemplateBinding BorderBrush"
                      BorderThickness="TemplateBinding BorderThickness">
                        <Grid x:Name="LayoutGrid">
                            <ScrollViewer Margin="2" x:Name="PART_ContentHost" />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <!--Message validation des erreurs-->
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="HasText" Value="True" />
                                <Condition Property="Validation.HasError" Value="True" />
                            </MultiTrigger.Conditions>
                            <Setter Property="ToolTip" Value="Binding RelativeSource=RelativeSource Self, Path= TextError"/>
                            <Setter Property="Validation.ErrorTemplate">
                                <Setter.Value>
                                    <ControlTemplate>
                                        <DockPanel LastChildFill="True">
                                            <Image x:Name="ValidationIcon" DockPanel.Dock="Left" Stretch="None" Width="15" Height="15" Source="pack://application:,,,/LS.Net.Telcom.Space.PropertyUI;component/Images/validationError.png" />
                                            <Border BorderBrush="Red" BorderThickness="1">
                                                <AdornedElementPlaceholder />
                                            </Border>
                                        </DockPanel>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </MultiTrigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

非常感谢您的帮助。

【问题讨论】:

由于您设置的是UserControlControlTemplate 而不是直接的内容,我相信您可能需要修改Binding 以向上移动模板...Background="TemplateBinding Background, RelativeSource=RelativeSource TemplatedParent" 对不起,它不起作用。 这个怎么样...Background="TemplateBinding Background, RelativeSource=RelativeSource AncestorType=x:Type l:NumericTextBox" uiComponent:NumericTextBox 是否派生自 TextBox ? 是的,它来自文本框 【参考方案1】:

我刚刚对您的Styles 进行了快速测试,一切正常。但是,要使 WPF Styles 正常工作,您需要做几件事。首先是您的自定义控件需要在其静态构造函数中覆盖DefaultStyleKey

public class NumericTextBox : TextBox

    static NumericTextBox()
    
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(NumericTextBox),
            new FrameworkPropertyMetadata(typeof(NumericTextBox)));
    

    ...

第二个是您的NumericTextBox 的默认Style 需要在程序集中的特定位置定义才能被拾取。标准位置是Project\Themes\Generic.xaml

如果您仍在为如何创建自定义 WPF 控件和样式而苦恼,这里有一个很棒的介绍性 CodeProject article。

bgcode 的评论

TDefaultStyleKey,它仍然按照您的建议实现。 第二个是我的 NumericTextBox 的样式作为资源字典实现到另一个文件中,但我将它加载到构造函数中:

    public NumericTextBox ()
        : base()
    
        ResourceDictionary res = Application.LoadComponent(new Uri("/MyAssemblyName;component/NumericTextBox/NumericTextBoxStyle.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;
        if(res != null)
            this.Resources = res;
    

我认为这也是一个好方法,不是吗?

阿贝·海德布雷希特的回应

没有。不要那样做。如果要在单独的 ResourceDictionary 中定义默认 Style,请执行此操作。只需将其合并到 generic.xaml 中即可:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="MyAssemblyName;component/NumericTextBox/NumericTextBoxStyle.xaml" />
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

如果您真的不想拥有 generic.xaml,您可以将字典合并到您的 app.xaml 中的 App.Resources。您更愿意将它放在generic.xaml 中的原因是,如果在某个时候将此控件放在控件的程序集中,您将需要它在generic.xaml 中,否则 WPF 不会知道在哪里可以找到默认的Style。最好养成以正确方式做事的习惯。

【讨论】:

干得好@Abe Heidebrecht 现在效果很好。我从没想过这可能是问题所在。因为早些时候,当我开始使用 WPF 时,我遵循了一个关于组件创建、frameworkElement 继承的教程......它是这样制作的:Application.LoadComponent ....再次感谢 ;) 这样做可以工作,但我喜欢尽可能多地使用 XAML!【参考方案2】:

我想我明白你在做什么......

您在 ResourceDictionary 中设置样式,然后在其他地方再次设置样式。因此,您的 ResourceDictionary 正在加载 Style 中的背景,因此它会覆盖您在其他地方设置的内容。

这解释了为什么可见性对您有用,因为该属性未在您的 ResourceDictionary 的样式中设置。

您应该将 Style 设置为 StaticResource,然后基于该 Style 的任何后续样式。这基本上是 Sheridan 建议的,但您应该通过键名引用样式...

<Style x:Key="NumericBoxStyle" TargetType="x:Type l:NumericTextBox">
    <Setter Property="Background" Value="StaticResource CustomTextBox_Background" />
    <Setter Property="BorderBrush" Value="StaticResource CustomTextBox_Border" />
    <Setter Property="Foreground" Value="StaticResource CustomTextBox_Foreground" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="FocusVisualStyle" Value="x:Null"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="x:Type l:NumericTextBox">
                <Border x:Name="Border"
                  Background="TemplateBinding Background"
                  BorderBrush="TemplateBinding BorderBrush"
                  BorderThickness="TemplateBinding BorderThickness">
                    <Grid x:Name="LayoutGrid">
                        <ScrollViewer Margin="2" x:Name="PART_ContentHost" />
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <!--Message validation des erreurs-->
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="HasText" Value="True" />
                            <Condition Property="Validation.HasError" Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter Property="ToolTip" Value="Binding RelativeSource=RelativeSource Self, Path= TextError"/>
                        <Setter Property="Validation.ErrorTemplate">
                            <Setter.Value>
                                <ControlTemplate>
                                    <DockPanel LastChildFill="True">
                                        <Image x:Name="ValidationIcon" DockPanel.Dock="Left" Stretch="None" Width="15" Height="15" Source="pack://application:,,,/LS.Net.Telcom.Space.PropertyUI;component/Images/validationError.png" />
                                        <Border BorderBrush="Red" BorderThickness="1">
                                            <AdornedElementPlaceholder />
                                        </Border>
                                    </DockPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </MultiTrigger>

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

并调整样式...

<Style TargetType="uiComponent:NumericTextBox" BaseOn="StaticResource NumericBoxStyle">
   <Setter Property="Background" Value="Black"/>
</Style>

【讨论】:

【参考方案3】:

你在这里定义的样式

<Style TargetType="uiComponent:NumericTextBox">
   <Setter Property="Background" Value="Black"/>
</Style>

被 ResourceDictionary 中定义的样式覆盖

如果你尝试:

<uiComponent:NumericTextBox Text="Binding myProperty">
   <uiComponent:NumericTextBox.Style>
      <Style TargetType="uiComponent:NumericTextBox" BasedOn="StaticResource x:Type uiComponent:NumericTextBox">
         <Setter Property="Background" Value="Black"/>
      </Style>
   </uiComponent:NumericTextBox.Style>
</uiComponent:NumericTextBox>

..你的背景应该设置为黑色。

【讨论】:

【参考方案4】:

试试这个怎么样:

<Style TargetType="uiComponent:NumericTextBox" 
    BasedOn="StaticResource x:Type l:NumericTextBox">
    <Setter Property="Background" Value="Black"/>
</Style>

我不确定你的 XML 命名空间是否会匹配,但这个想法基本上是对 WPF 说,“这种风格是基于这种类型的默认风格”

【讨论】:

以上是关于WPF 样式不适用于已设置样式的用户控件的主要内容,如果未能解决你的问题,请参考以下文章

WPF 选项卡控件样式

在 wpf 中应用样式时,用户控件内的组合框消失

将 ErrorTemplate 添加到 WPF 用户控件会禁用 TextBox 输入

WPF 在控件上淡出

如果样式已经设置,如何覆盖 WPF 子控件样式?

为什么我的样式在WPF中设置用户按钮控件内容时消失了?