C# 绑定OneWayToSource-奇怪的行为
最近我用C# 绑定OneWayToSource-奇怪的行为,c#,wpf,C#,Wpf,最近我用Binding Mode=OneWayToSource做了一系列测试,但我仍然不知道为什么会发生某些事情 例如,我在类构造函数中的依赖项属性上设置了一个值。现在,当绑定初始化时,Target属性被设置为其默认值。表示依赖项属性设置为null,并且我丢失了在构造函数中初始化的值 为什么会这样?绑定模式的工作方式与名称描述的方式不同。它只能更新源代码,而不能更新目标代码 以下是代码: public partial class MainWindow : Window { public
Binding Mode=OneWayToSource
做了一系列测试,但我仍然不知道为什么会发生某些事情
例如,我在类构造函数中的依赖项属性上设置了一个值。现在,当绑定初始化时,Target
属性被设置为其默认值。表示依赖项属性
设置为null
,并且我丢失了在构造函数
中初始化的值
为什么会这样?绑定模式
的工作方式与名称描述的方式不同。它只能更新源代码,而不能更新目标代码
以下是代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
private void OnClick(object sender, RoutedEventArgs e)
{
this.DataContext = new MyViewModel();
}
}
这是XAML:
<StackPanel>
<local:MyCustomControl Txt="{Binding Str, Mode=OneWayToSource}"/>
<Button Click="OnClick"/>
</StackPanel>
这是ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
private string str;
public string Str
{
get { return this.str; }
set
{
if (this.str != value)
{
this.str = value; this.OnPropertyChanged("Str");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null && propertyName != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
这将用本地值替换绑定。看见你实际上是在你真正想要的时候打电话。此外,您需要等到生命周期的后期才能执行此操作,否则WPF将更新Str
两次:一次使用“123”,然后再次使用null
:
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
this.SetCurrentValue(TxtProperty, "123");
}
如果在用户控件的构造函数中执行此操作,则当WPF实例化它时,它会执行,但当WPF加载、反序列化并应用BAML时,它会立即被替换
更新:抱歉,我误解了你的确切问题,但现在有一个副本,复制如下。我缺少随后更新DataContext
的部分。我通过在数据上下文更改时设置当前值(但在单独的消息中)修复了此问题。否则,WPF会忽略将更改转发到新数据源
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SO18779291
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.setNewContext.Click += (s, e) => this.DataContext = new MyViewModel();
this.DataContext = new MyViewModel();
}
}
public class MyCustomControl : Control
{
public static readonly DependencyProperty TxtProperty =
DependencyProperty.Register("Txt", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata(OnTxtChanged));
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
public MyCustomControl()
{
this.DataContextChanged += (s, e) =>
{
this.Dispatcher.BeginInvoke((Action)delegate
{
this.SetCurrentValue(TxtProperty, "123");
});
};
}
public string Txt
{
get { return (string)this.GetValue(TxtProperty); }
set { this.SetValue(TxtProperty, value); }
}
private static void OnTxtChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("Changed: '{0}' -> '{1}'", e.OldValue, e.NewValue);
}
}
public class MyViewModel : INotifyPropertyChanged
{
private string str;
public string Str
{
get { return this.str; }
set
{
if (this.str != value)
{
this.str = value; this.OnPropertyChanged("Str");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null && propertyName != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
XAML:
新语境
只有在初始化目标属性后,绑定才会使用本地设置的值。
dependency属性被设置为null,并且我丢失了值I
在构造函数中初始化。为什么会发生这种情况
自从<>代码>初始化代码>(或代码>在您的代码> UserControl < /代码> cTor中丢失,您可以在它之前或之后设置<代码> txt < /代码>,让我们考虑两种情况,假定“代码> txt<代码>在RealAlgEnCyMoMutual())/>代码>中初始化。在这里,Txt
的初始化意味着它被分配到XAML中声明的值。如果事先在本地设置了Txt
,则来自XAML的绑定将替换此值。如果之后设置了该值,则每当有机会对该值进行求值时,绑定都会考虑该值,这对于双向和单向ToSource绑定都是如此。(绑定评估的触发将在后面解释。)
为了证明我的理论,我用三个元素做了一个测试,每个元素都有不同的绑定模式
<TextBox Text="{Binding TwoWayStr,Mode=TwoWay}"></TextBox>
<local:UserControl1 Txt="{Binding OneWayToSourceStr, Mode=OneWay}" />
<Button Content="{Binding OneWayStr,Mode=OneWay}"></Button>
然而,结果表明,在这两种情况下都忽略了局部值。因为与其他元素不同,当InitializeComponent()
退出并触发Initialized
事件时,UserControl
的属性尚未初始化,包括Txt
初始化
- (开始)在
窗口中输入初始化组件()
Text
初始化并双向绑定尝试附加绑定源
文本框
已初始化
UserControl
已初始化
Txt
已初始化且OneWayToSource绑定尝试附加绑定源
Content
已初始化且单向绑定尝试附加绑定源
按钮
已初始化
窗口
已初始化
- (结束)退出
窗口中的初始化组件()
加载/渲染
- (开始)退出
窗口
- 双向绑定在未附加的情况下尝试附加绑定源
- OneWayToSource绑定如果未附加,则尝试附加绑定源
- 单向绑定在未附加的情况下尝试附加绑定源
窗口
已加载
文本框
已加载
UserControl
已加载
按钮
已加载
- (完)已加载所有元素
后装
- (开始)加载所有元素
- 双向绑定在未附加的情况下尝试附加绑定源
- OneWayToSource绑定如果未附加,则尝试附加绑定源
- 单向绑定未附加时附加绑定源的初始尝试
- (结束)
窗口显示
中讨论了UserControl
的这种特殊行为,这些行为之后会初始化属性。如果使用此处提供的方法,则调用OnInitialized
覆盖以及触发Initialized
事件将延迟到所有属性初始化之后。如果在OnInitialized
override或Initialized
的处理程序中调用BindingOperations.GetBindingExpression(这是MyCustomControl.TxtProperty)
,则返回值将不再为null
此时,可以安全地指定局部值。但是绑定求值不会立即触发以传输值,因为绑定源(DataContext)仍然不可用,请注意,DataContext在窗口
初始化之后才设置。事实上,如果检查返回的绑定表达式的Status
属性,则该值为Unattached
进入加载阶段后,第二次尝试附加绑定源将占用DataContext,然后将由绑定源的第一次附加触发计算,其中Txt
(在本例中为“123”)的值将通过setter传输到源属性Str
。这个biniding表达式的状态现在变为Active
,表示绑定源的解析状态
如果不使用该问题中提到的方法,可以将局部值移动到assig
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SO18779291
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.setNewContext.Click += (s, e) => this.DataContext = new MyViewModel();
this.DataContext = new MyViewModel();
}
}
public class MyCustomControl : Control
{
public static readonly DependencyProperty TxtProperty =
DependencyProperty.Register("Txt", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata(OnTxtChanged));
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
public MyCustomControl()
{
this.DataContextChanged += (s, e) =>
{
this.Dispatcher.BeginInvoke((Action)delegate
{
this.SetCurrentValue(TxtProperty, "123");
});
};
}
public string Txt
{
get { return (string)this.GetValue(TxtProperty); }
set { this.SetValue(TxtProperty, value); }
}
private static void OnTxtChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("Changed: '{0}' -> '{1}'", e.OldValue, e.NewValue);
}
}
public class MyViewModel : INotifyPropertyChanged
{
private string str;
public string Str
{
get { return this.str; }
set
{
if (this.str != value)
{
this.str = value; this.OnPropertyChanged("Str");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null && propertyName != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
<Window x:Class="SO18779291.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SO18779291"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:MyCustomControl Txt="{Binding Str, Mode=OneWayToSource}"/>
<Button x:Name="setNewContext">New Context</Button>
<TextBlock Text="{Binding Str, Mode=OneWay}"/>
</StackPanel>
</Window>
<TextBox Text="{Binding TwoWayStr,Mode=TwoWay}"></TextBox>
<local:UserControl1 Txt="{Binding OneWayToSourceStr, Mode=OneWay}" />
<Button Content="{Binding OneWayStr,Mode=OneWay}"></Button>