SetBinding 未注册 PropertyChanged 事件
SetBinding not registering for PropertyChanged event
在我正在处理的应用程序中,我以编程方式创建了多个具有不同数据源的 FrameworkElement。不幸的是,数据绑定失败。
我设法将问题提炼成以下程序:
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace TestBinding
{
public class BindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
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;
}
private string _text = "Initial Text";
public string Text
{
get { return _text; }
set { SetField(ref _text, value); }
}
}
public sealed partial class MainPage : Page
{
BindingSource bs = new BindingSource();
public MainPage()
{
this.InitializeComponent();
TextBlock tb = new TextBlock();
tb.HorizontalAlignment = HorizontalAlignment.Center;
tb.VerticalAlignment = VerticalAlignment.Center;
tb.FontSize = 36;
Binding bind = new Binding() { Source = bs.Text, Mode=BindingMode.OneWay, UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged };
tb.SetBinding(TextBlock.TextProperty, bind);
Patergrid.Children.Add(tb);
}
private void ClickText1(object sender, RoutedEventArgs e)
{
bs.Text = "First text button clicked!";
}
private void ClickText2(object sender, RoutedEventArgs e)
{
bs.Text = "Second text button stroked!";
}
}
}
这里是 xaml:
<Page
x:Class="TestBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="Patergrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Text 1" Margin="25" HorizontalAlignment="Center" Click="ClickText1" />
<Button Content="Text 2" Margin="25" HorizontalAlignment="Center" Click="ClickText2" />
</StackPanel>
</Grid>
</Page>
通过在属性更改时单步执行代码,我发现没有 PropertyChanged 事件的订阅者。
为什么 SetBinding 没有注册 PropertyChanged 事件?
你想要这个:
Binding bind = new Binding()
{
Source = bs,
Path = new PropertyPath("Text"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
这里好像有两个问题:
- 您发布的代码尝试使用
bs.Text
属性 返回的 string
对象,这不是可观察的绑定源。绑定系统足够灵活,可以复制一次值,但在那之后,它就没有办法知道对象已经改变了(即使它可以......并且 string
是一个不可变类型,所以对象不会'无论如何都不会改变)。
相反,您想绑定到 BindingSource
对象本身作为源,使用 Text
属性 作为路径。
- 此外,将
BindingMode.OneWay
与 UpdateSourceTrigger
的任何特定设置结合使用没有任何意义。当绑定模式是单向时,源永远不会更新,所以触发器是什么并不重要。
使用 BindingMode.TwoWay
将允许将 TextBlock.Text
属性 中的更改复制回源。
或者,不用设置 UpdateSourceTrigger
。 TextBlock
无论如何都不是用户可编辑的,所以也许您确实打算将绑定设置为单向绑定,而只是将 UpdateSourceTrigger
的无关初始化从其他地方保留下来。很难从您的问题中得知哪种实施方式适合您的需求。
在我正在处理的应用程序中,我以编程方式创建了多个具有不同数据源的 FrameworkElement。不幸的是,数据绑定失败。
我设法将问题提炼成以下程序:
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace TestBinding
{
public class BindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
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;
}
private string _text = "Initial Text";
public string Text
{
get { return _text; }
set { SetField(ref _text, value); }
}
}
public sealed partial class MainPage : Page
{
BindingSource bs = new BindingSource();
public MainPage()
{
this.InitializeComponent();
TextBlock tb = new TextBlock();
tb.HorizontalAlignment = HorizontalAlignment.Center;
tb.VerticalAlignment = VerticalAlignment.Center;
tb.FontSize = 36;
Binding bind = new Binding() { Source = bs.Text, Mode=BindingMode.OneWay, UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged };
tb.SetBinding(TextBlock.TextProperty, bind);
Patergrid.Children.Add(tb);
}
private void ClickText1(object sender, RoutedEventArgs e)
{
bs.Text = "First text button clicked!";
}
private void ClickText2(object sender, RoutedEventArgs e)
{
bs.Text = "Second text button stroked!";
}
}
}
这里是 xaml:
<Page
x:Class="TestBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="Patergrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Text 1" Margin="25" HorizontalAlignment="Center" Click="ClickText1" />
<Button Content="Text 2" Margin="25" HorizontalAlignment="Center" Click="ClickText2" />
</StackPanel>
</Grid>
</Page>
通过在属性更改时单步执行代码,我发现没有 PropertyChanged 事件的订阅者。
为什么 SetBinding 没有注册 PropertyChanged 事件?
你想要这个:
Binding bind = new Binding()
{
Source = bs,
Path = new PropertyPath("Text"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
这里好像有两个问题:
- 您发布的代码尝试使用
bs.Text
属性 返回的string
对象,这不是可观察的绑定源。绑定系统足够灵活,可以复制一次值,但在那之后,它就没有办法知道对象已经改变了(即使它可以......并且string
是一个不可变类型,所以对象不会'无论如何都不会改变)。
相反,您想绑定到 BindingSource
对象本身作为源,使用 Text
属性 作为路径。
- 此外,将
BindingMode.OneWay
与UpdateSourceTrigger
的任何特定设置结合使用没有任何意义。当绑定模式是单向时,源永远不会更新,所以触发器是什么并不重要。
使用 BindingMode.TwoWay
将允许将 TextBlock.Text
属性 中的更改复制回源。
或者,不用设置 UpdateSourceTrigger
。 TextBlock
无论如何都不是用户可编辑的,所以也许您确实打算将绑定设置为单向绑定,而只是将 UpdateSourceTrigger
的无关初始化从其他地方保留下来。很难从您的问题中得知哪种实施方式适合您的需求。