属性更改时未更新ListBox项

时间:2022-09-12 08:44:34

I have problem updating Listbox containing ObservableCollection when property of collection changes (adding/removing items from list works fine):

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

listbox has set ItemsSource="{Binding Path=AllPerson}" and data context in code behind is set like this this.DataContext = allPersonClass;.

listbox设置了ItemsSource =“{Binding Path = AllPerson}”,后面的代码中的数据上下文设置如下this.DataContext = allPersonClass;。

allPersonClass contains ObservableCollection<Person> allPerson

allPersonClass包含ObservableCollection allPerson

Class Person contains properties like Name etc.

类Person包含Name等属性。

I've overwritten person's ToString method to return Name property so listBox shows valid data

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

I've tried to make Person implement INotifyPropertyChanged

我试图让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");
     }
}

and in every property setter has onPropertyChanged(this, "propertyName"); which is executed but listBox never updates already created item

并且在每个属性中setter都有onPropertyChanged(this,“propertyName”);执行但listBox永远不会更新已创建的项目

Any idea what might be wrong?

知道什么可能是错的吗?

here is window with listBox xaml

这是带有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"/>

this is part of DetailView window where modifications are made (binded to Person)

这是DetailView窗口的一部分,在那里进行了修改(绑定到Person)

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

here is part AllPersonClass:

这是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];
  }
}

EDIT

编辑

here is relevant part of method to save changes in detail view

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

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

note that changes are made in ´ObservableCollection allPerson´ only listBox keeps showing old data

请注意,更改是在'ObservableCollection allPerson'中进行的,只有listBox保持显示旧数据

4 个解决方案

#1


10  

If you want the UI to update when you make changes to Person properties you have to notifiy that that property has changed, not the class

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

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

<TextBox Text="{Binding Path=Name}" .....

Should be

应该

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

<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" .....

And you dont have to override ToString() on the Person model to show correctly in your ListBox, you can set the ListBox DisplayMemberPath to show the property you want in the ListBox

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

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

Edit: to answer your comment question:

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

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; }
}

#2


5  

In your DetailView you need to define a TwoWay-Binding to update the Property Name of your person class.

在DetailView中,您需要定义TwoWay-Binding以更新人员类的属性名称。

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

Also if you want to update the property every time text is written you also need to define the UpdateSourceTrigger with PropertyChanged. Otherwise is the property only update when the TextBox loses focus.

此外,如果要在每次写入文本时更新属性,还需要使用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" />

#3


4  

your problem is that your collection get never Notifed about your PropertyChanged

你的问题是你的收藏品从未在你的PropertyChanged上发布过

this should help you

这应该对你有帮助

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

see also this Link

另见这个链接

#4


3  

I think you might want to review how you're doing the binding at the item level. Check out this link, it will get you started:

我想您可能想要查看您在项目级别进行绑定的方式。看看这个链接,它会让你开始:

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

如何确定ObservableCollection 中的行是否实际更改

#1


10  

If you want the UI to update when you make changes to Person properties you have to notifiy that that property has changed, not the class

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

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

<TextBox Text="{Binding Path=Name}" .....

Should be

应该

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

<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" .....

And you dont have to override ToString() on the Person model to show correctly in your ListBox, you can set the ListBox DisplayMemberPath to show the property you want in the ListBox

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

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

Edit: to answer your comment question:

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

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; }
}

#2


5  

In your DetailView you need to define a TwoWay-Binding to update the Property Name of your person class.

在DetailView中,您需要定义TwoWay-Binding以更新人员类的属性名称。

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

Also if you want to update the property every time text is written you also need to define the UpdateSourceTrigger with PropertyChanged. Otherwise is the property only update when the TextBox loses focus.

此外,如果要在每次写入文本时更新属性,还需要使用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" />

#3


4  

your problem is that your collection get never Notifed about your PropertyChanged

你的问题是你的收藏品从未在你的PropertyChanged上发布过

this should help you

这应该对你有帮助

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

see also this Link

另见这个链接

#4


3  

I think you might want to review how you're doing the binding at the item level. Check out this link, it will get you started:

我想您可能想要查看您在项目级别进行绑定的方式。看看这个链接,它会让你开始:

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

如何确定ObservableCollection 中的行是否实际更改