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>
非常感谢您的帮助。
【问题讨论】:
由于您设置的是UserControl
的ControlTemplate
而不是直接的内容,我相信您可能需要修改Binding
以向上移动模板...Background="TemplateBinding Background, RelativeSource=RelativeSource TemplatedParent"
对不起,它不起作用。
这个怎么样...Background="TemplateBinding Background, RelativeSource=RelativeSource AncestorType=x:Type l:NumericTextBox"
uiComponent:NumericTextBox 是否派生自 TextBox ?
是的,它来自文本框
【参考方案1】:
我刚刚对您的Style
s 进行了快速测试,一切正常。但是,要使 WPF Style
s 正常工作,您需要做几件事。首先是您的自定义控件需要在其静态构造函数中覆盖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 样式不适用于已设置样式的用户控件的主要内容,如果未能解决你的问题,请参考以下文章