如何为我的自定义控件提供可覆盖的默认边距?

发布于 2024-12-09 20:32:43 字数 2450 浏览 0 评论 0原文

问题

我创建了一个自定义控件(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)));

无论如何它不起作用。上面的依赖属性中设置的默认边距仍然覆盖我尝试在样式模板中设置的边距。

Problem

I've created a custom control (OmniBox), which has its base style set with:

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

But when I'm using my control, I want to be able to do something like:

<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"... />
           ...

And have all instances of my control take on that default margin. Unfortunately, my controls are not responding to the style set in the resources. They are just keeping their default margin of "0,2".

Strangely, if I explicitly set the margin on my controls like so:

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

They DO use the margin of "0,10" rather than "0,2". How come the template type isn't working?

If it's relevant, my OmniBox control templates all look like this:

<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>

First Attempt

In my grid style, I've tried setting Margin to

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

But it didn't help in sucking down the templated margin.

Second Attempt

I tried creating a custom margin dependency property and binding the grid to that:

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

My custom property was defined as:

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

Anyways it didn't work. The default margin set in the dependency property above is still overriding the margin I'm trying to set in the style template.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

滴情不沾 2024-12-16 20:32:43

您可以通过覆盖 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>

You can add a default style for a custom control by overriding the metadata for the DefaultStyleKey:

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

You then create a resource dictionary called Generic.xaml that is located in a directory called Themes in the root of the project (so the path will be "/Themes/Generic.xaml"). In that resource dictionary you create a default style for your control:

<!-- 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>

If you just add a MyButton control it will get the default style, but you can override properties set in the default style by applying a new style:

<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>
橙幽之幻 2024-12-16 20:32:43

GridStyle 指定 TargetType="Grid",因此 setter 适用于控件模板根部的Grid。设置包含的 OmniBoxMargin 属性对该网格的边距没有影响。

尝试在模板中指定这一点:

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

请注意,我没有像您在模板中那样设置 Style 属性。这是因为网格的 Margin 属性将始终反映包含它的 OmniBoxMargin 属性,从而否定 Margin< 的效果GridStyle 中的 /code> 属性。相反,您需要默认 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>

GridStyle specifies TargetType="Grid", so the setter <Setter Property="Margin" Value="0,2" /> applies to the Grid at the root of the control template. Setting the Margin property of the containing OmniBox has no effect of the margin of that grid.

Try specifying this in the template:

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

Notice I did not set the Style property as you did in the template. This is because the grid's Margin property will always reflect the Margin property of the OmniBox containing it, negating the effect of the Margin property in GridStyle. Instead you will want to default the OmniBox.Margin property and remove GridStyle entirely:

<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
    <Setter Property="Margin" Value="0 2" />
    <Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
岁吢 2024-12-16 20:32:43

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

Have you overridden the DefaultStyleKey property in your OmniBox control?

对你而言 2024-12-16 20:32:43

发生这个问题,我明白了我需要做什么。在控件的类中,我需要覆盖边距属性的默认值:

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

之后,我完全删除多功能框的“网格”组件上的边距,因为控件本身带有边距。现在,当用户在 OmniBox 上设置“Margin”属性时,它会接受它,如果不接受,则会使用默认值。

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

After happening on this question, I figured out what I needed to do. In the control's class, I need to override the margin property's default value:

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

After that, I get rid of the margin on the "Grid" component of the omnibox completely, since the control itself carries a margin. Now when the user sets the "Margin" property on the OmniBox, it accepts it, if they don't, it uses the default value.

Thank you all so much for your suggestions and effort.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文