【发布时间】:2018-05-15 01:26:47
【问题描述】:
我知道你的想法:现在是 2017 年,请不要再想这个,但我真的找不到任何有价值的解释。
请查看此 XAML 代码中的 ActiveNotes 属性。
我的 XAML 中有这个 TwoWay 绑定,效果很好。如果 ScaleNotes 的 PropertyChanged 事件被触发并且绑定设置为 TwoWay,它总是会更新。
<c:Keyboard
Grid.Row="2"
Grid.Column="0"
PlayCommand="{Binding PlayCommand}"
StopCommand="{Binding StopCommand}"
ActiveNotes="{Binding ScaleNotes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
ViewModel 中的 ScaleNotes 属性如下所示。每当它发生变化时,保证会触发 PropertyChanged 事件。我检查并仔细检查了它。 ViewModel 中的业务逻辑有效。
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;
}
到这里一切正常。每当 VM 中的 ScaleNotes 属性发生更改时,都会更新目标属性 ActiveNotes。
现在的问题:
如果我只将 XAML 中的绑定更改为 OneWay 并且 VM 中的业务逻辑保持 100% 相同,那么即使 PropertyChanged 事件,目标对象中的 ActivesNotes 属性仅更新一次被解雇。我检查并仔细检查了它。 ScaleNotes 属性的 PropertyChanged 事件总是被触发。
<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 的帮助下解决:(参见 cmets)
代码需要这样改:
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 绑定,这都毫无意义。
【问题讨论】:
-
看起来您在其 DP 更改处理程序中为
ActiveNotes设置了一个新的本地值。这将清除单向绑定,但不会清除双向绑定。您无需在其自己的更改处理程序中分配 DP — 新值已被应用。 -
我会测试它。请给我 2 分钟。
-
@Mike Strobel 这是正确答案。如果我删除 DP OnActiveNotesChanged 中的分配,它会按预期与 OneWay 一起工作。请您在回答中解释一下“清除”是什么意思。这是否意味着,它删除了绑定?
-
@MikeStrobel 你已经给出了正确的答案。非常感谢,你让我很开心。如果你喜欢,就写一个单独的答案,这样我就可以给你积分/积分。
标签: c# wpf xaml binding oneway