如何避免使用额外的附加属性?

How to avoid using extra attached property?

我已经附加了具有 2 个属性的行为。 是我想要做的,对于这个问题,详细信息是可选的。

第一个习惯enable/disable行为:

public static bool GetEnableHasErrors(DependencyObject obj) => (bool)obj.GetValue(EnableHasErrorsProperty);
public static void SetEnableHasErrors(DependencyObject obj, bool value) => obj.SetValue(EnableHasErrorsProperty, value);

public static readonly DependencyProperty EnableHasErrorsProperty =
    DependencyProperty.RegisterAttached("EnableHasErrors", typeof(bool), typeof(Behaviors), new PropertyMetadata((d, e) =>
    {
        var element = d as FrameworkElement;
        if (element == null)
            throw new ArgumentException("Only used with FrameworkElement");
        var handler = new RoutedEventHandler((s, a) => { ... }); // logic to set value of HasErrorsProperty attached property on element
        if ((bool)e.NewValue)
            element.SomeRoutedEvent += handler;
        else
            element.SomeRoutedEvent -= handler;
    }));

第二个用来把结果传出去:

public static bool GetHasErrors(DependencyObject obj) => (bool)obj.GetValue(HasErrorsProperty);
public static void SetHasErrors(DependencyObject obj, bool value) => obj.SetValue(HasErrorsProperty, value);

public static readonly DependencyProperty HasErrorsProperty =
    DependencyProperty.RegisterAttached("HasErrors", typeof(bool), typeof(Behaviors));

这个结果可以通过正常绑定进入视图模型或在视图中使用,无论如何:

<Grid local:Behaviors.EnableHasErrors="True"
      local:Behaviors.HasErrors="{Binding HasErrors, Mode=OneWayToSource}" >

我为此需要 2 个依赖属性感觉不对。是否可以只使用一个?难道我不能以某种方式推断出我通过设置绑定启用逻辑的内部行为吗?还不够吗?

我尝试使用 BindingBase 类型的单个 属性,失败,发现 my own question and this duplicate 没有令人满意的答案,所以 BindingBase 我觉得不对。

想法?

对于您的具体情况,我不知道有什么方法可以避免这种情况。

对于像这样更复杂的行为,使用附加行为会很有用。附加行为具有在附加或分离行为时调用的方法,您可以使用这些方法订阅/取消订阅事件。然而,这些使用起来要冗长得多。

例如:

public class ErrorsBehavior : Behavior<FrameworkElement>
{
    public bool HasErrors
    {
        get => (bool )this.GetValue(HasErrorsProperty);
        set => this.SetValue(HasErrorsProperty, value);
    }
    public static readonly DependencyProperty HasErrorsProperty =
        DependencyProperty.Register(nameof(HasErrors), typeof(bool), typeof(ErrorsBehavior ), new PropertyMetadata(false));

    protected override void OnAttached()
    {
        AssociatedObject.SomeRoutedEvent += ...
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SomeRoutedEvent -= ...
    }
}

用法将类似于:

<Grid>
    <i:Interaction.Behaviors>
        <my:ErrorsBehavior HasErrors="{Binding HasErrors, Mode=OneWayToSource}"/>
    </i:Interaction.Behaviors>
</Grid>

这些过去仅在您专门安装 "Blend for Visual Studio SDK for .NET" 组件(Visual Studio 2017)时可用,但现在在 Microsoft.Behaviors.Xaml.Wpf NuGet 包中可用。


如果你有一个双向绑定,它采用比 bool 更复杂的类型(例如 object),你可以使用一个技巧:

public static class MyExtensions
{
    private static readonly object initialBindingTarget = new object();

    public static object GetSomething(DependencyObject obj) => obj.GetValue(SomethingProperty);

    public static void SetSomething(DependencyObject obj, object value) => obj.SetValue(SomethingProperty, value);

    public static readonly DependencyProperty SomethingProperty =
        DependencyProperty.RegisterAttached(
            "Something",
            typeof(object),
            typeof(MyExtensions),
            new FrameworkPropertyMetadata(
                initialBindingTarget,
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) =>
        {
            // Trick to see if this is the first time we're set
            if (e.OldValue == initialBindingTarget)
            {
                // Do your first-time initialisation here
            }
        }));
}

这使用 initialBindingTarget 的标记初始值,并检查绑定何时更改此值。