更新的ItemsControl当一个ObservableCollection的更新项目 [英] Update ItemsControl when an item in an ObservableCollection is updated

查看:248
本文介绍了更新的ItemsControl当一个ObservableCollection的更新项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:


  • 您声明的ItemsControl (或>从的ItemsControl 视图。

  • 您绑定 ItemsControl.ItemsSource 属性设置为的ObservableCollection 在你的视图模型。

  • 您的视图更新,当一个项目被添加到/从的ObservableCollection 删除预期。

  • 但是,当你更改的ObservableCollection A项目的属性视图不会更新。

  • You declare an ItemsControl ( or a control derived from ItemsControl) in the view.
  • You bind the ItemsControl.ItemsSource property to an ObservableCollection in your ViewModel.
  • Your view updates as expected when an item is added to /removed from the ObservableCollection.
  • BUT, the view does not update when you change a property of an item in the ObservableCollection.

背景:

看来,这是一个常见的问题很多开发商WPF都遇到过。它已经被问了几次:

It seems that this is a common problem many WPF developers have encountered. It has been asked a few times:

通知的ObservableCollection,当项目更改

不能当它改变项(即使有INotifyPropertyChanged的)

的ObservableCollection和项目的PropertyChanged

我的执行:

我试图实施的通知的ObservableCollection,当项目更改。其基本思想是挂钩在MainWindowViewModel一个的PropertyChanged 处理程序中的的ObservableCollection 每个项目。当一个项目的属性更改,事件处理程序将被调用,并以某种方式查看被更新。

I tried to implement the accepted solution in Notify ObservableCollection when Item changes. The basic idea is to hook up a PropertyChanged handler in your MainWindowViewModel for each item in the ObservableCollection. When an item's property is changed, the event handler will be invoked and somehow the View is updated.

我无法得到落实工作。下面是我的实现。

I could not get the implementation to work. Here is my implementation.

的ViewModels:

class ViewModelBase : INotifyPropertyChanged 
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName = "")
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}



项目视图模型:

Item ViewModel:

class EmployeeViewModel : ViewModelBase
{
    private int _age;
    private string _name;

    public int Age 
    {
        get { return _age; }
        set
        {
            _age = value;
            RaisePropertyChanged("Age");
        }
    }

    public string Name  
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }

    public override string ToString()
    {
        return string.Format("{0} is {1} years old", Name, Age);
    }
}

主窗口视图模型:

class MainWindowViewModel : ViewModelBase
{
    private ObservableCollection<EmployeeViewModel> _collection;

    public MainWindowViewModel()
    {
        _collection = new ObservableCollection<EmployeeViewModel>();
        _collection.CollectionChanged += MyItemsSource_CollectionChanged;

        AddEmployeeCommand = new DelegateCommand(() => AddEmployee());
        IncrementEmployeeAgeCommand = new DelegateCommand(() => IncrementEmployeeAge());
    }

    public ObservableCollection<EmployeeViewModel> Employees 
    {
        get { return _collection; }
    }

    public ICommand AddEmployeeCommand { get; set; }
    public ICommand IncrementEmployeeAgeCommand { get; set; }

    public void AddEmployee()
    {
        _collection.Add(new EmployeeViewModel()
            {
                Age = 1,
                Name = "Random Joe",
            });
    }

    public void IncrementEmployeeAge()
    {
        foreach (var item in _collection)
        {
            item.Age++;
        }
    }

    private void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            foreach (EmployeeViewModel item in e.NewItems)
                item.PropertyChanged += ItemPropertyChanged;

        if (e.OldItems != null)
            foreach (EmployeeViewModel item in e.OldItems)
                item.PropertyChanged -= ItemPropertyChanged;
    }

    private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        RaisePropertyChanged("Employees");
    }
}

查看:

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
    xmlns:d="clr-namespace:Iress.IosPlus.DynamicOE.Controls"
    Title="MainWindow" Height="350" Width="350">

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.3*"></ColumnDefinition>
        <ColumnDefinition Width="0.7*"></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Column="0">
        <Button Command="{Binding AddEmployeeCommand}">Add Employee</Button>
        <Button Command="{Binding IncrementEmployeeAgeCommand}">Increment Employee Age</Button>
    </StackPanel>

    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="0.1*"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="{Binding Path=Employees[0]}"></TextBlock>
        <ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees}" BorderBrush="Red" BorderThickness="1"></ItemsControl>
    </Grid>
</Grid>



我的结果:

要验证我的实现,我创建了像这样的景色。在 TextBlock.Text 绑定到集合中的第一项。在的ItemsControl 绑定到集合本身。

To verify my implementation, I create a view like so. The TextBlock.Text is bound to the first item in the collection. The ItemsControl is bound to the collection itself.


  • 按添加员工按钮,集合中增加了一个 EmployeeViewModel 对象,预期这两个的TextBlock 的ItemsControl 更新。

  • 按添加员工了,在的ItemsControl 与另一个条目更新。太棒了!

  • 按增量员工年龄按钮。在每个项目的年龄属性加1。的PropertyChanged 事件引发。在 ItemPropertyChanged 事件处理函数。在文本块如预期的那样更新。但是,的ItemsControl 不更新。

  • Pressing the "Add Employee" button adds an EmployeeViewModel object in the collection and both the TextBlock and ItemsControl are updated as expected.
  • Pressing the "Add Employee" again, the ItemsControl is updated with another entry. Great!
  • Pressing the "Increment Employee Age" button. The Age property of each item is incremented by 1. The PropertyChanged event is raised. The ItemPropertyChanged event handler is invoked. The Textblock is updated as expected. However, the ItemsControl is not updated.

我的印象是,的ItemsControl 应该过于时更新 Employee.Age 根据在的通知的ObservableCollection,当项目更改

I am under the impression that the ItemsControl should be updated too when the Employee.Age is changed according to the answer in Notify ObservableCollection when Item changes.

推荐答案

我发现使用的史努比来调试XAML。

I found the answer using Snoop to debug XAML.

的问题是,您要绑定到ToString()方法,并且不不会引发PropertyChanged事件。如果你看一下XAML绑定,你会注意到的ObservableCollection实际上是不断变化的。

The issue is that you are trying to bind to the ToString() method and that does not raise the PropertyChanged event. If you look at the XAML bindings you will notice that the ObservableCollection is actually changing.

现在看一下每个项目的控制,它的文本在绑定文本属性。有没有,它只是文字。

Now look at each item control and it's texts binding in the "Text" property. There are none, it's just text.

要解决这个问题只需添加一个ItemsControl ItemTemplate中使用包含你想显示的元素一个DataTemplate。

To fix this simply add an ItemsControl ItemTemplate with a DataTemplate that contains the elements you'd like to be displayed.

<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Red" BorderThickness="1" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat=" {0} is {1} years old">
                        <Binding Path="Name"/>
                        <Binding Path="Age"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我们现在有约束力的绿灯。 RaisePropertyChanged被调用。

We now have a green light on binding. RaisePropertyChanged is being called.

钽哒!

这篇关于更新的ItemsControl当一个ObservableCollection的更新项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆