更新ItemsControl當一個項目在一個觀察點更新時。

[英]Update ItemsControl when an item in an ObservableCollection is updated


The Problem:

存在的問題:

  • You declare an ItemsControl ( or a control derived from ItemsControl) in the view.
  • 在視圖中聲明ItemsControl(或源自ItemsControl的控件)。
  • You bind the ItemsControl.ItemsSource property to an ObservableCollection in your ViewModel.
  • 你將ItemsControl綁定。ItemsSource屬性到視圖模型中的觀察匯總。
  • Your view updates as expected when an item is added to /removed from the ObservableCollection.
  • 當一個條目添加到/從ObservableCollection中刪除時,您的視圖會按照預期進行更新。
  • BUT, the view does not update when you change a property of an item in the ObservableCollection.
  • 但是,當您在ObservableCollection中更改項目的屬性時,視圖不會更新。

Background:

背景:

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

這似乎是許多WPF開發人員遇到的一個常見問題。有人問過幾次:

Notify ObservableCollection when Item changes

當項目更改時通知觀察記錄

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

注意事項不注意事項發生變化(即使是INotifyPropertyChanged)

ObservableCollection and Item PropertyChanged

ObservableCollection propertychange和項

My Implementation:

我的實現:

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.

當項目發生變化時,我嘗試在Notify ObservableCollection中實現已接受的解決方案。基本的思想是在MainWindowViewModel中為ObservableCollection中的每個條目連接一個PropertyChanged處理程序。當項的屬性發生更改時,將調用事件處理程序並以某種方式更新視圖。

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

Main Window ViewModel:

主窗口視圖模型:

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

View:

觀點:

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

My Results:

我的結果:

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.

為了驗證我的實現,我創建了這樣的視圖。TextBlock。文本綁定到集合中的第一個項。ItemsControl綁定到集合本身。

  • Pressing the "Add Employee" button adds an EmployeeViewModel object in the collection and both the TextBlock and ItemsControl are updated as expected.
  • 按下“Add Employee”按鈕,將在集合中添加EmployeeViewModel對象,並按預期更新TextBlock和ItemsControl。
  • Pressing the "Add Employee" again, the ItemsControl is updated with another entry. Great!
  • 再次按下“Add Employee”,ItemsControl將與另一個條目一起更新。太棒了!
  • 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.
  • 按下“增加雇員年齡”按鈕。每個項目的年齡屬性增加1。引發PropertyChanged事件。調用ItemPropertyChanged事件處理程序。文本塊按預期更新。但是,ItemsControl沒有更新。

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.

我的印象是,當員工的時候,項目控制也應該更新。當項目發生變化時,年齡根據通知觀察記錄中的答案而變化。

enter image description here

1 个解决方案

#1


10  

I found the answer using Snoop to debug XAML.

我找到了使用Snoop調試XAML的答案。

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.

問題是您正在嘗試綁定到ToString()方法,這不會引發PropertyChanged事件。如果您查看XAML綁定,您會注意到ObservableCollection實際上正在發生變化。

Snoop Showing Correct Binding

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

現在看看每個項目控件,它的文本綁定在“Text”屬性中。沒有,只是文本。

Snoop showing no data binding to elements in the items control

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

要修復這個問題,只需添加一個帶有DataTemplate的ItemsControl ItemTemplate,該模板包含您想要顯示的元素。

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

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

我們現在對裝訂有了綠燈。RaisePropertyChanged被稱為。

Binding correctly shows green

Ta-da!

哈!

Solution shown


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2015/02/10/30100e581100956f3a9f4a22906f1132.html



 
  © 2014-2022 ITdaan.com 联系我们: