【问题标题】:Gui not updating if property of other class object changed如果其他类对象的属性发生更改,则 Gui 不会更新
【发布时间】: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 并将 PathTextBlock)设置为 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 类中引发相同的事件。
  • 不将this aka MainWindow 类绑定到DataContext,为什么不直接将Foo 绑定到DataContext?那么TextBlockText可以设置绑定到FooString。这让生活更轻松。
  • @Bijington 看看解决方案 1。你是那个意思吗? @ kurakura88 在我的应用程序中,我有多个不同的类对象,而不仅仅是一个。
  • 请注意,在 MainWindow 中实现 INotifyPropertyChanged 是没有意义的。没有应该触发更改通知的属性。
  • @DominicJonas 是的,这就是我的意思。我不会推荐这种方法,因为它违背了推荐的做法。你真的应该坚持你原来的方法我只是指出这在技术上是可行的。

标签: wpf xaml data-binding


【解决方案1】:

我可能没有完全理解您想要什么,但这是一个数据绑定的工作示例,它与您的示例有些接近。

两个主要变化是:

  1. 将 datacontext 设置为 VM 而不是背后的代码
  2. 实际上给 OnPropertyChanged 正确触发刷新所需的参数,属性的名称。

结果:

MainWindow.xaml

<Window x:Class="ListViewColor.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <TextBlock Text="{Binding Foo.FooString}" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Aqua"/>
    </Grid>
</Window>

MainWindow.xaml.cs

namespace ListViewColor
{
    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();
        }
    }
}

FooClass.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class FooClass : INotifyPropertyChanged
{
    private string _FooString = "Empty";
    public string FooString
    {
        get
        {
            return _FooString;
        }
        set
        {
            if (_FooString == value) return;
            _FooString = value;
            OnPropertyChanged();
        }
    }

    public void ChangeTheProperty()
    {
        FooString = "Loaded";
    }

    #region PropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

希望对你有帮助!

【讨论】:

  • 感谢您的回复。 1. 实际上它正在使用DataContext = this。然后你必须定义你的路径,比如Path=Foo.FooString。 2.看看stackoverflow.com/questions/22580623/…
  • @DominicJonas 嘿,你两次都是对的!我学到了一些东西。但是我不明白这个问题,因为即使进行了这些更改,它也能完美运行!除了上面编辑的代码,你还需要什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-01
  • 1970-01-01
  • 2018-11-16
  • 2013-10-26
  • 1970-01-01
  • 1970-01-01
  • 2022-08-17
相关资源
最近更新 更多