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

时间:2011-10-13 19:30:31

标签: c# wpf xaml .net-3.5

问题

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

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

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

您是否覆盖了DefaultStyleKey控件中的OmniBox媒体资源?

答案 2 :(得分:1)

GridStyle指定TargetType="Grid",因此setter <Setter Property="Margin" Value="0,2" />适用于控件模板根目录的Grid。设置包含Margin的{​​{1}}属性不会影响该网格的边距。

尝试在模板中指定:

OmniBox

注意我没有像在模板中那样设置<Grid x:Name="PART_Grid" Margin="{TemplateBinding Margin}"> 属性。这是因为网格的Style属性将始终反映包含它的Margin的{​​{1}}属性,从而否定MarginOmniBox属性的影响。相反,您需要默认Margin属性并完全删除GridStyle

OmniBox.Margin

答案 3 :(得分:0)

this question发生后,我想出了我需要做什么。在控件的类中,我需要覆盖margin属性的默认值:

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

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

非常感谢你的建议和努力。