更新控件 属性 会破坏 OneWay 绑定?
Updating control property breaks OneWay binding?
考虑这个非常简单的例子,我有一个像这样的 UserControl:
用户控件XAML:
<UserControl x:Class="BindingTest.SomeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="SomeControlElement">
<Grid>
<TextBlock Text="{Binding ElementName=SomeControlElement, Path=Counter}" />
</Grid>
</UserControl>
隐藏代码:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace BindingTest
{
public partial class SomeControl : UserControl
{
public SomeControl()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 5);
timer.Tick += (s, e) => Counter = Counter + 1;
timer.Start();
}
public int Counter
{
get { return (int)GetValue(CounterProperty); }
set { SetValue(CounterProperty, value); }
}
public static readonly DependencyProperty CounterProperty = DependencyProperty.Register(nameof(Counter), typeof(int), typeof(SomeControl), new PropertyMetadata(0));
}
}
所以控件只显示一个 TextBlock,每 5 秒递增一次计数器。然后我当然有一个消费者:
主窗口XAML:
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingTest"
x:Name="MainWindowName" Width="200" Height="300">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=OneWay}" />
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=TwoWay}" />
</StackPanel>
</Grid>
</Window>
最后是后面的主要代码:
using System;
using System.Windows;
using System.ComponentModel;
using System.Windows.Threading;
namespace BindingTest
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += (s, e) => SomeSource = SomeSource + 1;
timer.Start();
}
private int someSource;
public int SomeSource
{
get => someSource;
set
{
if (someSource != value)
{
someSource = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeSource)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
是的,所以 main 在后面的代码中有一个计数器,它每秒更新一个 属性。 XAML 有 2 个 UserControl 实例。一种具有 OneWay 绑定,一种具有 TwoWay 绑定。
我在这里看到的是,当 "SomeControl.cs" 中的计数器更新时,第一个 UserControl (OneWay) 的绑定被破坏。 TwoWay 的持续更新中
这是设计使然(为什么)?更重要的是,如果我需要更新我的 UserControls 中的属性,我将如何在我的示例中执行此操作 - 为了支持 OneWay 绑定?请注意,我真的对这个例子中的 TwoWay 绑定不感兴趣,因为它会更新 "MySource",这 而不是 我想要的!
谢谢。
这是设计使然。当您将所谓的 local value 分配给依赖项 属性 时,先前分配的单向绑定将被替换。双向绑定保持活动状态并更新其源 属性。
但是有一个解决方法。不要设置本地值,而是 "current value"。替换
timer.Tick += (s, e) => Counter = Counter + 1;
与
timer.Tick += (s, e) => SetCurrentValue(CounterProperty, Counter + 1);
考虑这个非常简单的例子,我有一个像这样的 UserControl:
用户控件XAML:
<UserControl x:Class="BindingTest.SomeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="SomeControlElement">
<Grid>
<TextBlock Text="{Binding ElementName=SomeControlElement, Path=Counter}" />
</Grid>
</UserControl>
隐藏代码:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace BindingTest
{
public partial class SomeControl : UserControl
{
public SomeControl()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 5);
timer.Tick += (s, e) => Counter = Counter + 1;
timer.Start();
}
public int Counter
{
get { return (int)GetValue(CounterProperty); }
set { SetValue(CounterProperty, value); }
}
public static readonly DependencyProperty CounterProperty = DependencyProperty.Register(nameof(Counter), typeof(int), typeof(SomeControl), new PropertyMetadata(0));
}
}
所以控件只显示一个 TextBlock,每 5 秒递增一次计数器。然后我当然有一个消费者:
主窗口XAML:
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingTest"
x:Name="MainWindowName" Width="200" Height="300">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=OneWay}" />
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=TwoWay}" />
</StackPanel>
</Grid>
</Window>
最后是后面的主要代码:
using System;
using System.Windows;
using System.ComponentModel;
using System.Windows.Threading;
namespace BindingTest
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += (s, e) => SomeSource = SomeSource + 1;
timer.Start();
}
private int someSource;
public int SomeSource
{
get => someSource;
set
{
if (someSource != value)
{
someSource = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeSource)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
是的,所以 main 在后面的代码中有一个计数器,它每秒更新一个 属性。 XAML 有 2 个 UserControl 实例。一种具有 OneWay 绑定,一种具有 TwoWay 绑定。
我在这里看到的是,当 "SomeControl.cs" 中的计数器更新时,第一个 UserControl (OneWay) 的绑定被破坏。 TwoWay 的持续更新中
这是设计使然(为什么)?更重要的是,如果我需要更新我的 UserControls 中的属性,我将如何在我的示例中执行此操作 - 为了支持 OneWay 绑定?请注意,我真的对这个例子中的 TwoWay 绑定不感兴趣,因为它会更新 "MySource",这 而不是 我想要的!
谢谢。
这是设计使然。当您将所谓的 local value 分配给依赖项 属性 时,先前分配的单向绑定将被替换。双向绑定保持活动状态并更新其源 属性。
但是有一个解决方法。不要设置本地值,而是 "current value"。替换
timer.Tick += (s, e) => Counter = Counter + 1;
与
timer.Tick += (s, e) => SetCurrentValue(CounterProperty, Counter + 1);