Привязка кнопки WPF к свойству Convertback IMultiValueConverter не обновляет значение

#c# #wpf #binding #converters #multibinding

#c# #wpf #обязательный #преобразователи #множественная привязка #привязка

Вопрос:

У меня есть привязка от кнопки переключения, подключенной в коде позади. Я хочу привязать состояние IsChecked от одной кнопки к 4 элементам управления видео, чтобы переключить функцию отключения звука. Я использую multibinding для привязки кнопки переключения к 4 различным элементам управления. Моя проблема заключается в использовании точек останова, я вижу, что все запускается вплоть до свойства отключения звука для каждого объекта, но параметр свойства «значение» никогда не обновляется. При создании экземпляров элементов управления оно остается с настройками по умолчанию.

Итак, сначала я создаю привязки в коде, лежащем в основе

         IMultiValueConverter converter = new EmptyMultiValueConverter();
        MultiBinding myMultiBinding = new MultiBinding();
        myMultiBinding.Converter = converter;
        myMultiBinding.UpdateSourceTrigger = UpdateSourceTrigger.Defau<
        myMultiBinding.Mode = BindingMode.OneWayToSource;      
        myMultiBinding.NotifyOnSourceUpdated = true;  



        for (int i = 1; i < _maxNumberofPlayers; i  )
        {
            VideoPlayer player = new VideoPlayer()
            {
                Mute = false
            };

          myMultiBinding.Bindings.Add(new Binding("Mute") { Source = player 
         });

        }


     btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, myMultiBinding);
  

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

 public class EmptyMultiValueConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object 
    parameter, System.Globalization.CultureInfo culture)
    {
        // gets from the object source
        return (bool)values[0];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object 
   parameter, System.Globalization.CultureInfo culture)
    {

        return new Object[] {value,value,value,value};
    }

    #endregion
}
  

На данный момент я могу подтвердить, что оно попадает в свойство Mute и запускает SET 4 раза, но при прохождении через него параметр value остается на ранее установленном значении и не обновляется, чтобы отразить значение, переданное ему через ConvertBack

    // mute property in the media player user control
    public bool Mute
    {
        get { return  _media.IsMuted; }
        set
        {       
            if (_media.IsMuted == value)
                return;
            else
            {
                _media.IsMuted = value;
                NotifyPropertyChanged("Mute");
            }

        }
    }
  

Кто-нибудь может помочь, пожалуйста.
Я рвал на себе волосы в течение 3 дней.

Мне показалось, что использование multibinding — эффективный способ подключить 4 отдельных элемента управления и привязать их к одному нажатию кнопки.

Комментарии:

1. я прочитал это 3 раза и не понимаю, что вы пытаетесь сделать в целом..

2. Я пытаюсь заставить работать мультисвязки, чтобы нажать одну кнопку переключения и использовать статус ischecked (true или false) для привязки к 4 различным элементам управления медиаплеером и переключить свойство состояния отключения звука на этих элементах управления.

3. если у вас есть 4 элемента управления мультимедиа и вы хотите, чтобы все они отключались с помощью одной кнопки, нет необходимости в множественной привязке, просто привяжите их все к кнопке напрямую без конвертера

4. Я попробовал это в коде, создав 4 отдельных экземпляра привязки к одной и той же кнопке переключения. но единственным, который функционировал, была последняя привязка. Привязка одной кнопки к одному элементу управления видео в коде за ним отлично работает в противном случае.

5. Имеет смысл! Тем не менее, вы можете привязать все эти элементы управления к одному и тому же переключателю. Здесь вам не нужна множественная привязка, и вам не нужен конвертер. Если все это сводит вас с ума, вы все равно можете использовать icommand для отключения звука и включения их при нажатии кнопки переключения. Это также привело бы к тому, что вы смогли бы восстановить предыдущее состояние, в котором они находились. Пример: все отключено, кроме одного->Щелчок-> Все отключенные-> Щелчок -> все включенные -> 4 медиаплеера, воспроизводящих звук, заменяют только тот, который был ранее

Ответ №1:

ну, я снова попробовал несколько вариантов, и ни один из них не работает, за исключением кода в событии click button

Множественная привязка просто вообще не работает. В точках останова я вижу, что свойство Mute вызывается 4 раза для 4 элементов управления, но параметр value никогда не обновляется до нового значения свойства.

Я также пытался привязать одну кнопку к 4 различным элементам управления

 foreach(Player player in lsPlayers)
{
    btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, new Binding("Mute")
       {
         Source = player,
         UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
         Mode = BindingMode.OneWayToSource
        });
}
  

Это просто приводит к вызову свойства отключения звука одного объекта player.
Вы не можете привязать более одного элемента управления к одной и той же кнопке.
Это просто не работает.

поэтому я просто вызвал 4 объекта в событии click.

Ответ №2:

Я полагаю, что в этом случае вы можете захотеть привязать эти 4 свойства к свойству mute по отдельности и убедиться, что для каждого установлен режим TwoWay. Но если MultiValueConverter поддерживает это, я не могу особо спорить с вашим подходом.

Похоже, что вы не создали свойство зависимости для отключения звука. это жизненно важно для рабочего процесса привязки WPF и, как правило, масштабируется проще, чем NotifyPropertyChanged, поскольку NotifyPropertyChanged может потребовать от вас большего управления. Свойство DependencyProperty абстрагирует как отправку, так и получение обновлений вашего свойства, и поддержка редактора Xaml для него лучше.

https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-implement-a-dependency-property

Я нахожу, что привязка WPF хороша в теории, но все еще требует намного больше работы, чем нужно. Есть функция. Я добавляю ее во все мои viewmodels и элементы управления WPF.

 /// <summary>
/// simplify wpf binding
/// </summary>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="preferNull">Fair Warning. if you create an instance, you will have also created a singleton of this property for all instances of this class. </param>
/// <returns></returns>
private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
    return DependencyProperty.Register(name, type, typeof(Setting),
        new PropertyMetadata((!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null));
}
  

Просто наследуйте от DependencyObject, и вы можете сделать mute более похожим на это:

 public static readonly DependencyProperty MuteDp = AddDp(nameof(Mute), typeof(bool));
public bool Mute
{
    get => (bool)GetValue(MuteDp);
    set { if(value != Mute) SetValue(MuteDp, value); }
}
  

Но будьте осторожны!
Иногда привязка WPF к свойству зависимости обновляет внутренние значения свойства зависимости без ввода параметра соответствующего свойства! Это имело место при многозначной привязке, которую я использовал.
это означает, что оно может никогда не коснуться средства доступа Mute::Set. Это может быть довольно проблематично, чтобы решить эту проблему, вы можете использовать различные обратные вызовы, доступные для DependencyObject!

 private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
    var dp = DependencyProperty.Register(name, type, typeof(Setting),
        new PropertyMetadata(
            (!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null
            ,new PropertyChangedCallback((dobj, dpe)=>
            {
                //after property changed.
                //forcing usage of set accessor
                ((MuteContainerObj)dobj).Value = ((strong text)dobj).Value;
                //or forcibly use an update function
                ((MuteContainerObj)dobj).MuteUpdated();
            }),new CoerceValueCallback((dobj, o)=> {
                //before property changed
                return o;
            })), new ValidateValueCallback((o)=> {
                //before property change events
                return true;
            }));
    return dp;
        
}