【问题标题】:How to notify a parent class and reflect a change in a data bound observable collection如何通知父类并反映数据绑定可观察集合中的更改
【发布时间】:2020-10-12 22:44:33
【问题描述】:

我在反映数据绑定的可观察集合中的变化时遇到了一些困难。

我有一个列表框,其中填充了观察集合中的值。当我添加或删除项目时,这会反映在绑定控件中。但是,当我更改要显示的值时,这不会反映出来。

例如,添加一个标题为“第 1 周”的周对象(工作正常) - 现在,将标题更改为“爱好”(未反映在绑定到可观察列表的控件中)

下面是我的课程管理命名空间,父类 courses 和子类 weeks

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Windows;

namespace Management.Courses
{
    [DataContract(Name = "Course")]
    class cCourse
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            MessageBox.Show(propertyName);

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public cCourse()
        {
            Weeks = new ObservableCollection<cWeek>();
        }

        #region "Core Properties"
        [DataMember(Name = "Title")]
        public string Title { get; set; }

        [DataMember(Name = "Weeks")]
        private ObservableCollection<cWeek> _Weeks;
        public ObservableCollection<cWeek> Weeks
        {
            get
            {
                return _Weeks;
            }
            set
            {
                if (value != this._Weeks)
                {
                    _Weeks = value;
                    NotifyPropertyChanged();
                }

            }
        }
    }

    [DataContract(Name = "Week")]
    class cWeek
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region "Core Properties"
        [DataMember(Name = "Title")]
        private string _Title { get; set; }
        public string Title
        {
            get
            {
                return _Title;
            }
            set
            {
                if (value != this.Title)
                {
                    _Title = value;
                    NotifyPropertyChanged();
                }

            }
        }
        #endregion
    }
}

在我的窗口中,我有以下内容:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Core;

namespace Management.Courses
{
    public partial class MgnCourses_Add : Window
    {
        private cCourse oCourse;

        public MgnCourses_Add()
        {
            InitializeComponent();

            oCourse = new cCourse();

            //Set Data Context
            DataContext = oCourse;


            cWeek oWeek = new cWeek();

            //Get/Set week title
            oWeek.Title = "Week " + e.NewValue;

           //Update week listing
           oCourse.Weeks.Add(oWeek);
        }

private void ctrlTopics_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            oCourse.Weeks[ctrlTopics.SelectedIndex].Title = "New String";
        }
    }
}

列表框的控件绑定是:

<ListBox x:Name="ctrlTopics" Margin="100,131,439,49" ItemsSource="{Binding Weeks}" DisplayMemberPath="Title" MouseDoubleClick="ctrlTopics_MouseDoubleClick">

我可以通过调试看到可观察列表值正在正确更改 - 它只是没有反映在列表框中。

任何帮助/指针将不胜感激。

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    当想要使用 INotifyPropertyChanged 绑定并看到它反映 UI 的更改时,您需要使用项目 Listbox.Itemtemplate。这是因为DisplayMemberPath 用作指针,对象内的property 应用于显示。一旦设置,它就不会跟踪对象内部所做的更改。

    所以举个例子说明这应该如何工作。

    <ListBox x:Name="ctrlTopics" Margin="100,131,439,49" ItemsSource="{Binding Weeks}" 
             MouseDoubleClick="ctrlTopics_MouseDoubleClick">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <TextBlock Text="{Binding Title}"></TextBlock>
                                    </StackPanel>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
    

    为了解释上面的代码,Listbox.ItemTemplate不能和DisplayMemberPath结合使用。所以我们不使用DisplayMemberPath,而是在模板中创建一个绑定。

    UI 没有收到任何通知,因为没有调用 PropertyChanged 事件。 INotifyPropertyChanged 接口也应该在 cWeek 类上实现。 因此给它下面的定义。

    class cWeek : INotifyPropertyChanged

    最后,如下

    public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    可以简化为并且应该调用事件以通知 UI。

    public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
            {
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));   
            }
    

    【讨论】:

    • 我在上面对我的 xaml 进行了更改,但是我仍然看到与我最初的问题相同的问题。如果我添加新的一周或删除一周,它会反映更改,但不会反映属性名称的更改。
    • 显示的值是什么?
    • 'Week 1' not 'New String' 。本质上,双击标题的变化并没有反映在 UI 中
    • 代码中哪里可以改标题?你也试过在标题的设置器中设置一个调试点吗?你能不能也改变一下 NotifyPropertyChanged();到 NotifyPropertyChanged(nameof(Title));。我目前无法查明为什么它没有更新的问题..
    • 啊,你应该在cWeek类中实现NotifyPropertyChanged接口。并且应该在 PropertyChanged 事件上调用 Invoke。
    【解决方案2】:

    @M.B 已经为您的情况提供了最佳解决方案。但是,我想让事情更清楚一点,并为这个问题添加一个走动。您的 UI 没有反映您对数据所做的更改的原因是您的代码仅通知 UI 整个对象更改

    Week = other_week;
    

    但对象的属性不会改变

    Week.Title = new_week_title;
    

    现在,我之前提到的漫游:只需明确通知 UI 属性更改即可:

    Week.Title = new_week_title;
    PropertyChange(this, nameof(Week));
    

    您还可以为当前对象分配新值以调用通知事件:

    Week = new Week(new_week_title);
    

    但是,此操作会影响可观察集合,因为您分配了一个不属于现有集合的对象,并且会引入很多问题。话虽如此,您应该采取的最佳解决方案是 @M.B 的解决方案。

    【讨论】:

    • 对不起。我不确定我是否完全理解。我已经实现了 M.B 的解决方案,但这并不反映属性的变化。我曾尝试使用'PropertyChange(this, nameof(Week));'但我不确定它应该去哪里。
    • 我的意思是,在你更改了本周的标题之后,只需调用 PropertyChange() 来通知 Ui 当前周已更改,以便 Ui 可以更新 CurrentWeek 的新值。标题。像这样: CurrentWeek.Title = "新标题"; PropertyChange(this, nameof(CurrentWeek));
    猜你喜欢
    • 2011-09-30
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多