我知道你的想法:现在是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%相同,则目标对象中的ActivesNotes属性仅更新,即使PropertyChanged事件也是如此被解雇了。我检查并仔细检查了一下。始终触发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的帮助下解决方案:(见评论)
代码需要以这种方式改变:
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绑定都没有任何意义。
答案 0 :(得分:5)
依赖项属性具有precedence的复杂系统。任何给定时间的依赖项属性的值可能来自各种来源:绑定,样式设置器,触发器设置器等。本地值具有最高优先级,当您设置本地值时,您将抑制来自其他源的值。 / p>
如果是绑定,设置本地值将导致源到目标绑定(ern code-push <miniapps..>
Options:
--help Show help [boolean]
--descriptor, -d Full native application selector (target native application version for the push)
--force, -f Force upgrade (ignore compatibility issues -at your own risks-)
--appName Application name
--deploymentName Deployment to release the update to [string]
--platform, -p Platform name (android / ios) [string]
--targetBinaryVersion, -t Semver expression that specifies the binary app version(s) this release is targeting (e.g. 1.1.0, ~1.2.3) [string]
--mandatory, -m Specifies whether this release should be considered mandatory [default: false]
--rollout, -r Percentage of users this release should be immediately available to [string] [default: "100%"]
--skipConfirmation, -s Skip final confirmation prompt if no compatibility issues are detected
More info about this command @ https://electrode.gitbooks.io/electrode-native/content/cli/code-push.html
或OneWay
) *删除* 。但是,当您在具有目标到源绑定(OneTime
或TwoWay
)的属性上设置本地值时,将保持绑定,并且您分配的本地值将传播回来源。
在您的情况下,问题在于:
OneWayToSource
在keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;
处理程序中,您需要为OnActiveNotesChanged
分配新的本地值,这会导致您的ActiveNotes
绑定被删除。幸运的是,解决方案很简单:您可以完全删除此行,因为它是多余的。在大多数情况下,不必在自己的更改处理程序中分配依赖项属性 - 已经应用了新值。 (如果您希望有机会在应用之前替换建议的值,那么这样做的地方将是OneWay
,您也可以在CoerceValueCallback
中指定。)
答案 1 :(得分:1)
让我在这个问题上度过不眠之夜后分享我的经验!
我在一个稍微不同的用例中偶然发现了这个问题,我找不到解决方案,但多亏了上面的建议,我终于可以解决它了。
简而言之,我遇到了同样的绑定分离问题,但根本没有访问被控属性的代码!问题发生在 CustomControl
!
事实上,您还必须注意,如果 CustomControl
声明了 DependencyProperty
,Binding
中的消费者都使用了 CustomControl
{1}} 和 CustomControl
的模板,那么您必须确保模板中的绑定 CustomControl
的 是 TemplateBinding
类型的八进制或使用 OneWay
或 OneWayToSource
的常规绑定。
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(QAToggle), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None));
public bool IsChecked
{
get => (bool)this.GetValue(IsCheckedProperty);
set => this.SetValue(IsCheckedProperty, value);
}
如果没有,更新时Binding
的模板中使用的UserControl
也会为CustomControl
的属性设置一个新值,从而移除消费者设置的外部绑定CustomControl
。
<Style TargetType="{x:Type ccont:QAToggle}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="MinHeight" Value="23" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ccont:QAToggle}">
<ToggleButton x:Name = "QAToggle"
Command = "{TemplateBinding Command}"
CommandParameter = "{TemplateBinding CommandParameter}"
FontSize = "{TemplateBinding FontSize}"
FontWeight = "{TemplateBinding FontWeight}"
FontFamily = "{TemplateBinding FontFamily}"
IsThreeState = "False"
IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}}">
此处的行 IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}}"
将破坏外部绑定。为避免这种情况,请将其更改为:
IsChecked = "{TemplateBinding IsChecked}"
或
IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}, Mode=OneWay}"