在 class 层次结构中用 AttachedProperty 替换 DependencyProperty - 这可能吗?

Replacing DependencyProperty by AttachedProperty in class hierarchy - is that possible?

我希望我的一些 ButtonToggleButtonRadioButton 有一个 Geometry 属性 以便我可以在 ControlTemplates 中使用以避免在分配实例时使用样板代码-这些控件的特定几何形状。

例如,目前我可以这样写:

<my:GeometryButton Geometry="{StaticResource OneGeometry}"/>
<my:GeometryButton Geometry="{StaticResource OtherGeometry}"/>

<!-- ...and inside the Style for GeometryButton: -->
<ContentControl TargetType="{x:Type my:GeometryButton}">
    <Border>
        <Path Data={TemplateBinding Geometry}/>
    </Border>
</ContentControl>

有了这个 GeometryButton class:

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


    public Geometry Geometry
    {
        get { return (Geometry)GetValue(GeometryProperty); }
        set { SetValue(GeometryProperty, value); }
    }

    public static readonly DependencyProperty GeometryProperty =
        DependencyProperty.Register("Geometry",
                                    typeof(Geometry), 
                                    typeof(GeometryButton), 
                                    new PropertyMetadata(default(Geometry)));

}

问题是,如果我要定义 GeometryToggleButtonGeometryRadioButton classes,我应该在每个 class 中重复 DependencyProperty 代码,这违反了 DRY . 另外,由于 RadioButton 派生自 ToggleButton,后者和 Button 又派生自 ButtonBase,我想我可以利用这一点,但如果我需要分别从每个 class 继承,我不会从继承中受益完全没有。

所以我考虑使用AttachedProperties,但是教程和示例通常会提到DockPanel.DockGrid.LeftControl.Foreground这样的示例,暗示存在一些"Parent" ,所以我不确定:

创建一个常规附件属性。在您的控件模板中,使用它。

说真的,没有比这更多的了。

例如,我写了一个附件CornerRadius 属性,这样许多不同的控件样式都可以指定一个CornerRadius,它们将被它们的模板使用。

public static class Attached
{
    public static readonly DependencyProperty CornerRadiusProperty
            = DependencyProperty.RegisterAttached(
                "CornerRadius",
                typeof(CornerRadius), 
                typeof(Attached),
                new FrameworkPropertyMetadata(
                    new CornerRadius(0), 
                    FrameworkPropertyMetadataOptions.AffectsRender 
                        | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
                    PropertyChanged)
                );

    public static CornerRadius GetCornerRadius(DependencyObject uie)
    {
        return (CornerRadius)uie.GetValue(CornerRadiusProperty);
    }

    public static void SetCornerRadius(DependencyObject uie, CornerRadius value)
    {
        uie.SetValue(CornerRadiusProperty, value);
    }
}

XAML

N.B. Binding(edcorpext:Attached.CornerRadius)两边的括号很关键,所以它理解字符串是一个不可分割的路径段参考附件属性;否则它会尝试将其解析为 Binding.Source 的 属性 的路径,命中 : 并抛出异常。

<ControlTemplate x:Key="EdCorpButtonTemplate" TargetType="{x:Type Button}">
    <Grid>
        <Border 
            x:Name="PART_BackgroundBorder"
            CornerRadius="{Binding (edcorpext:Attached.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
            BorderThickness="1.3"
            BorderBrush="{StaticResource ControlBorderBrush}"
            Background="{StaticResource EdCorpGrayMediumGradientBrush}"
            SnapsToDevicePixels="True"
            />
        <!-- etc. etc. etc. -->

<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
    <Setter Property="edcorpext:Attached.CornerRadius" Value="{StaticResource ButtonCornerRadius}" />
    <Setter Property="Template" Value="{StaticResource EdCorpButtonTemplate}" />

    <!-- etc. etc. etc. -->

他们告诉我我们需要 UI 在这个应用程序上看起来 "more modern" 并且由于我们没有真正知道他在做什么的设计师,所以我在东西上放置了不对称的圆角。之前的情况其实差了很多。