WPF: TwoWay binding ALWAYS updated - OneWay binding ONCE ONLY updated

I know what you think: this is 2017, please do not invent it again, but I really can not find a meaningful explanation for this.

Pay attention to the ActiveNotes property in this XAML code.

I have a TwoWay binding in my XAML that works fine. It is ALWAYS updated if the PropertyChanged event for ScaleNotes is fired, and if the binding is set to TwoWay.

<c:Keyboard 
            Grid.Row="2" 
            Grid.Column="0" 
            PlayCommand="{Binding PlayCommand}" 
            StopCommand="{Binding StopCommand}" 
            ActiveNotes="{Binding ScaleNotes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

The ScaleNotes property in ViewModel looks like this. Whenever it changes, the PropertyChanged event is guaranteed to fire. I checked and double checked it. The business logic in ViewModel works.

private ReadOnlyCollection<eNote> _ScaleNotes;
public ReadOnlyCollection<eNote> ScaleNotes
{
    get { return _ScaleNotes; }
    set { SetField(ref _ScaleNotes, value); }
}

[DebuggerStepThrough]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

[DebuggerStepThrough]
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

. , ScaleNotes , ActiveNotes.

:

XAML OneWay, - VM 100% , ActivesNotes , PropertyChanged . . PropertyChanged ScaleNotes .

<c:Keyboard 
            Grid.Row="2" 
            Grid.Column="0" 
            PlayCommand="{Binding PlayCommand}" 
            StopCommand="{Binding StopCommand}" 
            ActiveNotes="{Binding ScaleNotes, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>

, DP .

public static DependencyProperty ActiveNotesProperty = DependencyProperty.Register(
        "ActiveNotes", 
        typeof(ReadOnlyCollection<eNote>), 
        typeof(Keyboard), 
        new PropertyMetadata(OnActiveNotesChanged));


public ReadOnlyCollection<eNote> ActiveNotes
{
    get
    {
        return (ReadOnlyCollection<eNote>)GetValue(ActiveNotesProperty);
    }
    set
    {
        SetValue(ActiveNotesProperty, value);
    }
}

private static void OnActiveNotesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Keyboard keyboard    = (Keyboard)d;
    keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;

    if ((keyboard.ActiveNotes != null) && (keyboard.ActiveNotes.Count > 0))
    {
        keyboard.AllKeys.ForEach(k => { if ( k.Note != eNote.Undefined) k.IsActiveKey = true; });
        keyboard.AllKeys.ForEach(k => { if ((k.Note != eNote.Undefined) && (!keyboard.ActiveNotes.Contains(k.Note))) k.IsActiveKey = false; });
    }
    else
    {
        keyboard.AllKeys.ForEach(k => { if (k.Note != eNote.Undefined) k.IsActiveKey = true; });
    }
}

. , OneWay TwoWay , , .

, TwoWay, - 100% , OneWay - .

, : OneWay. - . TwoWay, OneWay , .

@MikeStrobel: (. )

:

private static void OnActiveNotesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Keyboard keyboard    = (Keyboard)d;

    //THIS LINE BREAKED THE CODE, WHEN USING OneWay binding BUT NOT WITH TwoWay binding
    //keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;

    if ((keyboard.ActiveNotes != null) && (keyboard.ActiveNotes.Count > 0))
    {
        keyboard.AllKeys.ForEach(k => { if ( k.Note != eNote.Undefined) k.IsActiveKey = true; });
        keyboard.AllKeys.ForEach(k => { if ((k.Note != eNote.Undefined) && (!keyboard.ActiveNotes.Contains(k.Note))) k.IsActiveKey = false; });
    }
    else
    {
        keyboard.AllKeys.ForEach(k => { if (k.Note != eNote.Undefined) k.IsActiveKey = true; });
    }
}

OneWay, OnActiveNotesChanged . , , . , , OneWay TwoWay.

+4
1

precedence. : , , .. , , , .

, (OneWay OneTime) * *. , (TwoWay OneWayToSource), , , , .

:

keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;

OnActiveNotesChanged ActiveNotes, OneWay. , : , . - . ( , , CoerceValueCallback, PropertyMetadata.)

+5

Source: https://habr.com/ru/post/1690104/


All Articles