Как избежать использования дополнительного прикрепленного свойства? - PullRequest
1 голос
/ 18 октября 2019

Я прикрепил поведение с 2 свойствами. Здесь - это то, что я пытаюсь сделать, для этого вопроса детали необязательны.

Первый используется для включения / выключения поведения:

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, не удалось, нашел мой собственный вопрос и этот дубликат с небезопасным ответом,поэтому BindingBase мне кажется неправильным.

Идеи?

1 Ответ

2 голосов
/ 18 октября 2019

Я не знаю ни одного способа избежать этого для вашего конкретного случая.

Для более сложных вариантов поведения, подобных этому, может быть полезно использовать прикрепленное поведение. Прикрепленные поведения имеют методы, которые вызываются, когда поведение присоединяется или отсоединяется, и которые можно использовать для подписки на события или отписки от них. Однако они значительно более многословны в использовании.

Например:

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 для .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
            }
        }));
}

При этом используетсяначальное значение Sentinel initialBindingTarget, и проверяет, когда привязка изменяет значение с этого.

...