【问题标题】:ListBox item not updated when property changed属性更改时列表框项未更新
【发布时间】:2013-03-02 01:33:36
【问题描述】:

当集合的属性发生更改时,我在更新包含 ObservableCollection 的列表框时遇到问题(从列表中添加/删除项目可以正常工作):

listbox 已设置ItemsSource="{Binding Path=AllPerson}",并且代码后面的数据上下文设置如下this.DataContext = allPersonClass;。

allPersonClass 包含ObservableCollection<Person> allPerson

Person 类包含名称等属性。

我已覆盖人员的 ToString 方法以返回 Name 属性,因此 listBox 显示有效数据

我试图让Person 实现INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(object sender, string propertyName) {
    if (this.PropertyChanged != null) {
        PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
    }
}

public string Name {
     get { return name; }
     set {
        name = value;
        onPropertyChanged(this, "allPersonClass");
     }
}

在每个属性设置器中都有onPropertyChanged(this, "propertyName");,它被执行但listBox永远不会更新已经创建的项目

知道可能出了什么问题吗?

这是带有 listBox xaml 的窗口

     <Button x:Name="btnDetail" Content="Detail" HorizontalAlignment="Left" Margin="361,249,0,0" VerticalAlignment="Top" Width="75" Click="ButtonDetailClick"/>
     <ListBox x:Name="listPerson" ItemsSource="{Binding Path=AllPerson}" HorizontalAlignment="Left" Height="170" Margin="33,29,0,0" VerticalAlignment="Top" Width="155" IsSynchronizedWithCurrentItem="True"/>
     <Button x:Name="btnLoad" Content="Load" HorizontalAlignment="Left" Margin="58,249,0,0" VerticalAlignment="Top" Width="75" Click="btnLoad_Click"/>
     <Button x:Name="btnSave" Content="Save" HorizontalAlignment="Left" Margin="138,249,0,0" VerticalAlignment="Top" Width="75" Click="ButtonSaveClick"/>

这是进行修改的 DetailView 窗口的一部分(绑定到 Person)

<TextBox Text="{Binding Path=Name}" Height="23" HorizontalAlignment="Left" Margin="118,20,0,0" Name="txtName" VerticalAlignment="Top" Width="141" />

这里是 AllPersonClass 的一部分:

public class AllPersonClass {
  private ObservableCollection<Person> allPerson;

public AllPersonClass() {
     allPerson = new ObservableCollection<Person>();
  }

public ObservableCollection<Person> AllPerson {
     get { return allPerson; }
     set { allPerson = value; }
  }

 public void addPerson(Person newPerson) {
     allPerson.Add(newPerson);
  }


public Person getPerson(int personIndex) {
     return allPerson[personIndex];
  }
}

编辑

这是在详细视图中保存更改的方法的相关部分

private void OnBtnSaveClick(object sender, RoutedEventArgs e) {
     person.Name = txtName.Text;
     person.SurName = txtSurName.Text;
}

请注意,在“ObservableCollection allPerson”中进行了更改,只有 listBox 一直显示旧数据

【问题讨论】:

  • 为了提供帮助,您必须发布一个 Xaml 示例,以及一个显示属性更改通知的 ViewModel 实现示例。
  • 提供你的类的实现allPersonClass
  • 我想我需要睡觉,然后我会调查一下并发布我的进度
  • 那个属性改变的饲养员让我紧张不安。值得庆幸的是,至少有一个答案解决了这个问题。

标签: c# wpf data-binding observablecollection


【解决方案1】:

我想您可能想查看一下您是如何在项目级别进行绑定的。查看此链接,它将帮助您入门:

How to determine if a row in ObservableCollection<T> actually changed

【讨论】:

    【解决方案2】:

    在您的 DetailView 中,您需要定义一个 TwoWay-Binding 来更新您的 person 类的属性名称。

    <TextBox Text="{Binding Path=Name, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="118,20,0,0" Name="txtName" VerticalAlignment="Top" Width="141" />
    

    此外,如果您想在每次写入文本时更新属性,您还需要使用PropertyChanged 定义UpdateSourceTrigger。否则该属性仅在 TextBox 失去焦点时更新。

    <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="118,20,0,0" Name="txtName" VerticalAlignment="Top" Width="141" />
    

    【讨论】:

    • “Person”中的属性在单击“保存”按钮后手动更新(请参阅编辑),ListBox 可能没有收到属性更改事件
    【解决方案3】:

    如果您希望在更改 Person 属性时更新 UI,则必须通知该属性已更改,而不是类已更改

    public string Name 
    {
         get { return name; }
         set 
         {
            name = value;
            onPropertyChanged(this, "allPersonClass");
         }
    }
    
    <TextBox Text="{Binding Path=Name}" .....
    

    应该是

    public string Name 
    {
         get { return name; }
         set 
         {
            name = value;
            onPropertyChanged(this, "Name");
         }
    }
    
    <TextBox Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" .....
    

    并且您不必在Person 模型上覆盖ToString() 以在您的ListBox 中正确显示,您可以设置ListBox DisplayMemberPath 以在ListBox 中显示您想要的属性

      <ListBox ItemsSource="{Binding Path=AllPerson}" DisplayMemberPath="Name" />
    

    编辑:回答您的评论问题:

    public string Name 
    {
         get { return name; }
         set 
         {
            name = value;
            onPropertyChanged(this, "Name");
            onPropertyChanged(this, "Display");
         }
    }
    
    public string Surname
    {
         get { return surname; }
         set 
         {
            surname= value;
            onPropertyChanged(this, "Surname");
            onPropertyChanged(this, "Display");
         }
    }
    
    public string Display
    {
         get { return Name + " " + Surname; }
    }
    

    【讨论】:

    • 您答案的最后一部分关于添加和删除方法不正确。如果他使用变量allPerson 或属性AllPerson 来添加和删除人员,则相同,导致其引用相同。通知由 ObservableCollection 引发,而不是由属性 AllPerson
    • 嗯.. 我记得大约 3 年前遇到过这个问题,这是我找到的唯一解决方法,从那以后我一直坚持使用这种方法。我刚刚测试过,您确实是正确的,现在我想检查旧的 .NET 框架,看看是否可以复制我的旧问题:),感谢您的提醒,我将从我的答案中删除,以免造成混淆.
    • 这有点帮助,但并没有给我我想要的:使用 DisplayMemberPath="Name" 它工作正常,但在我的列表中只显示名称(并在更改后正确更新)。当我尝试显示更多信息时,例如 'listPerson.DisplayMemberPath = "Display";' 其中 'Display' 是 'Person' 的属性并返回 'Name + " " + Surname' 它不再起作用
    • @Abdul,确保如果您更改了Display 中使用的任何属性,您调用了onPropertyChanged(this, "Display");,您必须告诉 UI 属性已更改,因此在Name 和Surname您将致电onPropertyChanged 两次,一次为物业,一次为Display
    • @Abdul,在答案中添加了示例
    【解决方案4】:

    您的问题是您的收藏永远不会收到有关您的 PropertyChanged 的​​通知

    这应该对你有帮助

    ObservableCollection<INotifyPropertyChanged> items = new ObservableCollection<INotifyPropertyChanged>();
        items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(items_CollectionChanged);
    
    
        static void items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            foreach (INotifyPropertyChanged item in e.OldItems)
                item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
    
            foreach (INotifyPropertyChanged item in e.NewItems)
                item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
        }
    
        static void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            throw new NotImplementedException();
        }
    

    另请参阅Link

    【讨论】:

    • 在每个循环之前添加 if (e.OldItems != null) 或 if (e.NewItems != null) 会导致抛出 NotImplementedException - 现在我正在尝试根据您的答案以某种方式实现它链接
    • @Abdul 也许这个云也可以帮助你 [LINK] (stackoverflow.com/questions/4588359/…)
    • 您没有取消订阅活动。
    • @klm_ CollectionChanged 上的哪个?只是为了表明他需要绑定到这个 Event 他如何实现这个订阅和取消订阅将是他代码的一部分
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2011-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多