如何给我的自定义控件一个可重写的默认保证金

本文关键字:一个 可重写 保证金 默认 我的 自定义控件 | 更新日期: 2023-09-27 18:11:23

问题

我创建了一个自定义控件(OmniBox(,它的基本样式设置为:

<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
    <Setter Property="Margin" Value="0,2" />
</Style>

但当我使用我的控制时,我希望能够做一些事情,比如:

<UserControl.Resources>
    <Style TargetType="{x:Type ui:OmniBox}">
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="Margin" Value="0,10"/> <!--Not Working?-->
    </Style>
</UserControl.Resources>
<Grid>
     <StackPanel>
           <ui:OmniBox x:Name="One"... />
           <ui:OmniBox x:Name="Two"... />
           ...

让我控制的所有实例都承担违约保证金。不幸的是,我的控件没有响应资源中设置的样式。他们只是保持默认保证金"0,2"。

奇怪的是,如果我像这样明确地设置控件的保证金:

           <ui:OmniBox x:Name="One" Margin="0,10" Style="OBDefaultStyle" ... />
           <ui:OmniBox x:Name="Two" Margin="0,10" ... />
           ...

他们确实使用了"0,10"的边距,而不是"0,2"。为什么模板类型不起作用?

如果相关的话,我的OmniBox控制模板都是这样的:

<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
    <Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
<ControlTemplate TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultTemplate">
    <Grid x:Name="PART_Grid" Style="{StaticResource GridStyle}">
        ... (Content)
    </Grid>
</ControlTemplate>

第一次尝试

在我的网格风格中,我尝试将保证金设置为

<Setter Property="Margin" 
        Value="{Binding Path=Margin, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:OmniBox}}}" />

但这并没有帮助降低模板化的利润率。

第二次尝试

我尝试创建一个自定义的保证金依赖属性,并将网格绑定到该属性:

<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
    <Setter Property="Margin" Value="{Binding Path=MyMargin, RelativeSource={RelativeSource TemplatedParent}}" />
</Style>

我的自定义属性被定义为:

public static readonly DependencyProperty MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2), new PropertyChangedCallback(OnMarginChanged)));

不管怎样,它没有起作用。在上面的dependency属性中设置的默认边距仍然覆盖我试图在样式模板中设置的边距。

如何给我的自定义控件一个可重写的默认保证金

您可以通过覆盖DefaultStyleKey:的元数据来为自定义控件添加默认样式

public class MyButton : Button
{
    static MyButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
    }
}

然后,您创建一个名为Generic.xaml的资源字典,该字典位于项目根目录中的Themes目录中(因此路径为"/Themes/Generic.xaml"(。在该资源字典中,您为控件创建了一个默认样式:

<!-- Base the style on the default style of the base class, if you don't want to completely
     replace that style. If you do, remember to specify a new control template in your style as well -->
<Style TargetType="SomeNamespace:MyButton" BasedOn="{StaticResource {x:Type Button}}">
    <Setter Property="Margin" Value="10" />    
</Style>

如果您只添加一个MyButton控件,它将获得默认样式,但您可以通过应用新样式来覆盖默认样式中设置的属性:

<Window x:Class="SomeNamespace.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:SomeNamespace="clr-namespace:SomeNamespace"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="SomeNamespace:MyButton">
            <Setter Property="Margin" Value="20" />
        </Style>
    </Window.Resources>
    <Grid>
        <SomeNamespace:MyButton />
    </Grid>
</Window>

GridStyle指定TargetType="Grid",因此设置器<Setter Property="Margin" Value="0,2" />应用于控制模板根处的Grid。设置包含OmniBoxMargin属性不会影响该网格的边距。

尝试在模板中指定:

<Grid x:Name="PART_Grid" Margin="{TemplateBinding Margin}">

请注意,我没有像您在模板中那样设置Style属性。这是因为网格的Margin属性将始终反映包含它的OmniBoxMargin属性,从而否定GridStyleMargin属性的影响。相反,您将希望默认OmniBox.Margin属性并完全删除GridStyle

<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
    <Setter Property="Margin" Value="0 2" />
    <Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>

是否覆盖了OmniBox控件中的DefaultStyleKey属性?

在解决了这个问题之后,我明白了我需要做什么。在控件的类中,我需要覆盖margin属性的默认值:

    static OmniBox()
    {
        MarginProperty.OverrideMetadata(typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2)));
    }

在那之后,我完全去掉了omnibox的"网格"组件上的空白,因为控件本身带有空白。现在,当用户在OmniBox上设置"Margin"属性时,它会接受它,如果不接受,则使用默认值。

非常感谢大家的建议和努力。