【问题标题】:Dependency Property - Inheriting from another property?依赖属性 - 从另一个属性继承?
【发布时间】:2019-04-11 11:56:23
【问题描述】:

我的 WPF UserControl 中有一个自定义依赖属性,名为 CustomForeground

如果CustomForeground 上没有指定值,我希望它回退到UserControl.ForeGround

我正在使用下面的代码,它可以工作,但确实感觉有点 hacky。

任何人都可以确认是否有实现此依赖属性的“正确”方式吗?

public SolidColorBrush CustomForeground
{
      get { return (SolidColorBrush)(GetValue(CustomForegroundProperty) ?? GetValue(ForegroundProperty)); }
      set { SetValue(CustomForegroundProperty, value); }
}

注意 - 我省略了 DependencyProperty 的声明,因为它只是样板文件。

【问题讨论】:

  • "如果没有指定值" - 这是否意味着如果该属性从未设置,或者当属性“重置”为例如时也应该使用后备值空值?在属性包装器的getter中做肯定是错误的,因为getter在某些情况下可能会被绕过。
  • 如果您尝试绑定到CustomForeground,这将不起作用——绑定不使用 C# 属性,它们直接转到 DependencyProperty。只是样板的 C# 属性 - DependencyProperty 是所有操作发生的地方!
  • @canton7 CustomForeground 是 UserControl 的依赖属性。 OP 只是省略了 CustomForegroundProperty 标识符字段的声明。
  • 不,它是一个包含 DependencyProperty 的 C# 属性。实际的 DependencyProperty 是您所说的遗漏的位,它以 public static readonly DependencyProperty CustomForegroundProperty = ... 开头。 C# 属性只是为了更好地使用 C# 代码中的 DependencyProperty - 它不被绑定使用。
  • @canton7 也许再读一遍这个问题。有一个依赖属性(与 DependencyProperty 标识符字段和 CLR 包装器一样)。问题是如何让这个依赖属性在没有设置的时候返回控件的Foreground属性的值。

标签: c# wpf dependency-properties


【解决方案1】:

您可以将 Style 添加到您的 UserControl,并为 CustomForeground 属性设置一个 Setter,该属性将 Binding 设置为其 Foreground 属性。

除非 CustomForeground 属性值被另一个 Binding 或本地值或动画等替换,否则将使用 Binding。

<UserControl ...>
    <UserControl.Style>
        <Style>
            <Setter
                Property="local:MyUserControl.CustomForeground"
                Value="{Binding Foreground, RelativeSource={RelativeSource Self}}"/>
        </Style>
    </UserControl.Style>
    ...
</UserControl>

【讨论】:

  • 这很好——比我的干净多了!您只需要确保没有其他任何东西在该控件上设置Style
【解决方案2】:

这应该可以解决问题:

public partial class MyControl : UserControl
{
    public MyControl()
    {
        InitializeComponent();

        var multiBinding = new MultiBinding()
        {
            Converter = FallbackColorConverter.Instance,
            Mode = BindingMode.TwoWay,
            Bindings =
            {
                new Binding()
                {
                    Source = this,
                    Path = new PropertyPath(CustomForegroundBackingProperty),
                    Mode = BindingMode.TwoWay
                },
                new Binding()
                {
                    Source = this,
                    Path = new PropertyPath(ForegroundProperty),
                    Mode = BindingMode.OneWay
                },
            },
        };

        SetBinding(CustomForegroundProperty, multiBinding);
    }

    public Brush CustomForeground
    {
        get => (Brush)GetValue(CustomForegroundProperty);
        set => SetValue(CustomForegroundProperty, value);
    }
    public static readonly DependencyProperty CustomForegroundProperty =
        DependencyProperty.Register(nameof(CustomForeground), typeof(Brush), typeof(MyControl), new PropertyMetadata(null));

    private static readonly DependencyProperty CustomForegroundBackingProperty =
        DependencyProperty.Register("CustomForegroundBacking", typeof(Brush), typeof(MyControl), new PropertyMetadata(null));

    private class FallbackColorConverter : IMultiValueConverter
    {
        public static readonly FallbackColorConverter Instance = new FallbackColorConverter();

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values[0] ?? values[1];
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }
    }
}

我们设置了两个 DependencyProperties。 CustomForegroundBackingProperty 充当用户设置的任何 CustomForeground 值的实际存储。 CustomForegroundProperty 只是充当一种代理。

然后设置从 ForegroundProperty 和 CustomForegroundBackingProperty 到 CustomForegroundProperty 的 MultiBinding。 MultiBinding 设置为 TwoWay(因此对 CustomForegroundProperty 的任何更改都会触发绑定,对 ForegroundProperty 或 CustomForegroundBackingProperty 的任何更改也会触发绑定)。

我们将与 CustomForegroundBackingProperty 的绑定设置为 TwoWay(因为我们希望写入 CustomForegroundProperty 以影响 CustomForegroundBackingProperty),但我们将与 ForegroundProperty 的绑定设置为 OneWay,因为我们不希望发生这种情况。

然后我们在MultiBinding上放一个转换器。当转换器写入 CustomForegroundProperty 时,它会同时查看 CustomForegroundBackingProperty 和 ForegroundProperty,如果 CustomForegroundBackingProperty 为 null,则选择 ForegroundProperty。如果 ForegroundProperty 或 CustomForegroundBackingProperty 发生更改,则会触发此操作。

当转换器以另一种方式写入时——即用户写入 CustomForegroundProperty——然后我们只返回他们设置的值。由于 MultiBinding 中绑定的模式,这意味着该值被设置为 CustomForegroundBackingProperty。

【讨论】:

    【解决方案3】:

    我不得不采用“自下而上”的解决方案,而不是像 @Clemens 那样的“自上而下”(因为我已经在 UserControl 上定义了一种样式。它看起来像这样:

    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="{Binding ValueBrush}" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding ValueBrush}" Value="{x:Null}">
                <Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    【讨论】:

    • 虽然这可能以某种方式为您提供解决方案,但它不能回答您的问题如何“如果在 CustomForeground 上未指定任何值,则回退到 UserControl.Foreground”。
    猜你喜欢
    • 2013-04-01
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多