【发布时间】:2018-10-19 04:57:16
【问题描述】:
我想将自定义类 (FooClass) 的一些属性 (FooClass.FooString) 绑定到我的 MainWindow。如果将某些数据绑定到 gui,则下面的(工作已知行为)是默认的工作解决方案。
我想要做的是在第二个代码块中(不工作,但需要的行为)。暴露一些 properties 和另一个 class objectto the gui and update it.
**Problem**: TheTestStringis not getting updated (on the gui, code behind works). ThePropertyChangedeventis alsonull`(未订阅?!)。
这是绑定数据的错误方式吗?
如果我将完整的 FooClass object 绑定到 gui 并将 Path(TextBlock)设置为 Foo.FooString,则 gui 和 string 将更新。但我不想这样做。
这样解决吗?
工作已知行为
public partial class MainWindow : Window
{
public FooClass Foo { get; } = new FooClass();
public MainWindow()
{
DataContext = this;
InitializeComponent();
Loaded += _OnLoaded;
}
private async void _OnLoaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);
Foo.ChangeTheProperty();
}
}
public class FooClass : INotifyPropertyChanged
{
public string FooString
{
get => _FooString;
set
{
if (_FooString == value) return;
_FooString = value;
OnPropertyChanged();
}
}
private string _FooString = "empty";
public void ChangeTheProperty()
{
FooString = "Loaded";
}
// ##############################################################################################################################
// PropertyChanged
// ##############################################################################################################################
#region PropertyChanged
/// <summary>
/// The PropertyChanged Eventhandler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise/invoke the propertyChanged event!
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindow}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding Path=Foo.FooString}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Window>
不工作,但需要的行为
public partial class MainWindow : Window
{
public string TestString => _Foo.FooString;
private readonly FooClass _Foo;
public MainWindow()
{
_Foo = new FooClass();
DataContext = this;
InitializeComponent();
Loaded += _OnLoaded;
}
private async void _OnLoaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);
_Foo.ChangeTheProperty();
}
}
public class FooClass : INotifyPropertyChanged
{
public string FooString
{
get => _FooString;
set
{
if (_FooString == value) return;
_FooString = value;
OnPropertyChanged();
}
}
private string _FooString = "empty";
public void ChangeTheProperty()
{
FooString = "Loaded";
}
// ##############################################################################################################################
// PropertyChanged
// ##############################################################################################################################
#region PropertyChanged
/// <summary>
/// The PropertyChanged Eventhandler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise/invoke the propertyChanged event!
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindow}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding Path=TestString}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Window>
解决方案 1
订阅Foo.PropertyChangedevent 并将其路由到MainWindow.PropertyChanged。
public partial class MainWindow : Window, INotifyPropertyChanged
{
public FooClass Foo { get; } = new FooClass();
public MainWindow()
{
Foo.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName);
DataContext = this;
InitializeComponent();
Loaded += _OnLoaded;
}
private async void _OnLoaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);
Foo.ChangeTheProperty();
}
// ##############################################################################################################################
// PropertyChanged
// ##############################################################################################################################
#region PropertyChanged
/// <summary>
/// The PropertyChanged Eventhandler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise/invoke the propertyChanged event!
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
【问题讨论】:
-
是的,您是正确的,您需要通过“工作已知行为”部分进行绑定。您需要订阅 INotifyPropertyChanged.PropertyChanged 事件,然后在 MainWindow 类中引发相同的事件。
-
不将
thisakaMainWindow类绑定到DataContext,为什么不直接将Foo绑定到DataContext?那么TextBlock的Text可以设置绑定到FooString。这让生活更轻松。 -
@Bijington 看看解决方案 1。你是那个意思吗? @ kurakura88 在我的应用程序中,我有多个不同的类对象,而不仅仅是一个。
-
请注意,在 MainWindow 中实现 INotifyPropertyChanged 是没有意义的。没有应该触发更改通知的属性。
-
@DominicJonas 是的,这就是我的意思。我不会推荐这种方法,因为它违背了推荐的做法。你真的应该坚持你原来的方法我只是指出这在技术上是可行的。
标签: wpf xaml data-binding