WPF Data binding один ко многим

Есть DTO IOption класс ObjectGroupOptions. Который может меняться со временем из-за изменения файла конфигурации.

Стоит задача слушать изменения DTO IOption в разных ViewModel.

Из очевидных пришли следующие решения:

  • Pub/Sub. Подписываться на изменения в DTO IOption классе в нужных ViewModel. И после в самих ViewModel через рефлексию и аттрибутов устанавливать полученное значение.:
private void OnOptionChange(string optionName, string propertyName, object value)
{
    var property = GetType().GetProperties()
        .FirstOrDefault(property =>
            string.Equals(property.Name, propertyName, StringComparison.CurrentCultureIgnoreCase) &&
            property.GetCustomAttribute<BindToOptionAttribute>() is not null &&
            string.Equals(
                property.GetCustomAttribute<BindToOptionAttribute>()?.optionPropertyName,
                optionName, StringComparison.CurrentCultureIgnoreCase));

    property?.SetValue(this, value);
}

Но решил отложить подобное решение на крайний случай.

  • Из еще одного решения - Rx/ReactiveUI. Но решил отказаться от подобного решения, так как тянет множество за собой кода, в такой небольшой части проекта.

  • И после поиска было найдено интересно решение в виде Bindings в WPF. Что позволяет сделать так:

public class ObjectGroupOptions : FrameworkElement 
{
    private static readonly Type ThisType = typeof(ObjectGroupOptions);

    public static readonly DependencyProperty FirstObjectEnableProperty =
        DependencyProperty.Register(
            nameof(FirstObjectEnable), typeof(bool),
            ThisType, new PropertyMetadata(default(bool)));
    
    public bool FirstObjectEnable
    {
        get => (bool)GetValue(FirstObjectEnableProperty);
        set => SetValue(FirstObjectEnableProperty, value);
    }
}

ObjectGroupOptions наследует FrameworkElement, чтобы было удобнее делать Binding в самом классе. И в конструкторе ViewModel можно сделать так

public class FirstViewModel 
{
    private readonly ObjectGroupOptions old;
    public bool FirstObjectEnable {get; set;}

    public FirstViewModel(IOptionsMonitor<ObjectGroupOptions> optionsMonitor)
    {
        this.optionsMonitor.OnChange(Listener);
        old = optionsMonitor.CurrentValue;
        
        var bingind = new Binding(nameof(FirstObjectEnable)) { Source = this, Mode = BindingMode.TwoWay };
    }
    
    private void Listener(ObjectGroupOptions arg1, string? arg2)
    {
        BindingOperations.ClearBinding(old, ObjectGroupOptions.FirstObjectEnableProperty);

        var bingind = new Binding(nameof(FirstObjectEnable)) { Source = this, Mode = BindingMode.TwoWay };
        arg1.SetBinding(ObjectGroupOptions.FirstObjectEnableProperty, bingind);
    }
}

Это отлично будет работать, до тех пор, пока не понадобится добавить в другой ViewModel, еще один Binding к такому же DependencyProperty. Или же если View будет устанавливать новое значение для ObjectGroupOptions из-за чего Binding меняется.

public partial class ObjectsGroupSettings
{
    private readonly IOptionsMonitor<ObjectGroupOptions> objectGroupOptionsMonitor;

    public ObjectsGroupSettings(IOptionsMonitor<ObjectGroupOptions> optionsMonitor)
    {
        objectGroupOptionsMonitor = optionsMonitor;

        InitializeComponent();
    }

    private void OnCheckBoxGroupChange(object sender, RoutedEventArgs e)
    {
        if (sender is not CheckBox checkbox) return;
        if (checkbox.IsChecked == null) return;

        var objectGroupProperty = ObjectGroupOptionsType?.GetProperties()
            .FirstOrDefault(p => p.Name.Contains(checkbox.Name.Replace("CheckBox", "")));

        ArgumentNullException.ThrowIfNull(objectGroupProperty);

        objectGroupProperty.SetValue(objectGroupOptionsMonitor.CurrentValue, checkbox.IsChecked.Value);

    }
}

Было найдено решение в виде биндинга один ко многим, как-то так:

public ObjectsGroupSettings()
{
    InitializeComponent();

    var binding = BindingOperations.GetBinding(FirstCheckBox, CheckBox.IsCheckedProperty);

    FirstChild.SetBinding(CheckBox.IsCheckedProperty, binding);
    SecondChild.SetBinding(CheckBox.IsCheckedProperty, binding);
    ThirdChild.SetBinding(CheckBox.IsCheckedProperty, binding);
}

Вопрос, как сделать подобный биндинг один ко многим для ObjectGroupOptions и его слушателей ViewModels.

Заранее спасибо.


Ответы (0 шт):