Does anyone know why this code doesn\'t work:
public class CollectionViewModel : ViewModelBase {
public ObservableCollection Con
If i know ObservableCollection make event only when we add/delete or move items in our collection. When we simly update some properties in collection items collection don`t signalize about it and UI will not be updated.
You can simly implement INotifyPropertyChange in your Model class. And than when we update some propery in collection item it automatically will update UI.
public class Model:INotifyPropertyChange
{
//...
}
and than
public ObservableCollection<Model> {get; set;}
In my case i used ListView to Bind for this collection and in ItemTemplate set Binding to Model property and it work good.
Here is some snippet
Windows XAML :
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView
Margin="10"
BorderBrush="Black"
HorizontalAlignment="Center"
SelectedItem="{Binding SelectedPerson}"
ItemsSource="{Binding Persons}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}"/>
<Label Content="-"/>
<Label Content="{Binding Age}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid
Grid.Row="1"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label
VerticalAlignment="Center"
Content="Name:"/>
<TextBox
Text="{Binding SelectedPerson.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Margin="10"
Grid.Column="1"
Width="100"/>
<Label
VerticalAlignment="Center"
Grid.Row="1"
Content="Age:"/>
<TextBox
Text="{Binding SelectedPerson.Age,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Margin="10"
Grid.Row="1"
Grid.Column="1"
Width="100"/>
</Grid>
</Grid>
Model code example:
public class PersonModel:INotifyPropertyChanged
{
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public int Age
{
get => _age;
set
{
_age = value;
OnPropertyChanged();
}
}
private string _name;
private int _age;
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And ViewModel implementation:
public class ViewModel:INotifyPropertyChanged
{
public ViewModel()
{
Persons = new ObservableCollection<PersonModel>
{
new PersonModel
{
Name = "Jack",
Age = 30
},
new PersonModel
{
Name = "Jon",
Age = 23
},
new PersonModel
{
Name = "Max",
Age = 23
},
};
}
public ObservableCollection<PersonModel> Persons { get;}
public PersonModel SelectedPerson
{
get => _selectedPerson;
set
{
_selectedPerson = value;
OnPropertyChanged();
}
}
//INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private PersonModel _selectedPerson;
}
Instead of an ObservableCollection or TrulyObservableCollection, consider using a BindingList and calling the ResetBindings method.
For example:
private BindingList<TfsFile> _tfsFiles;
public BindingList<TfsFile> TfsFiles
{
get { return _tfsFiles; }
set
{
_tfsFiles = value;
NotifyPropertyChanged();
}
}
Given an event, such as a click your code would look like this:
foreach (var file in TfsFiles)
{
SelectedFile = file;
file.Name = "Different Text";
TfsFiles.ResetBindings();
}
My model looked like this:
namespace Models
{
public class TfsFile
{
public string ImagePath { get; set; }
public string FullPath { get; set; }
public string Name { get; set; }
public string Text { get; set; }
}
}
Just adding my 2 cents on this topic. Felt the TrulyObservableCollection required the two other constructors as found with ObservableCollection:
public TrulyObservableCollection()
: base()
{
HookupCollectionChangedEvent();
}
public TrulyObservableCollection(IEnumerable<T> collection)
: base(collection)
{
foreach (T item in collection)
item.PropertyChanged += ItemPropertyChanged;
HookupCollectionChangedEvent();
}
public TrulyObservableCollection(List<T> list)
: base(list)
{
list.ForEach(item => item.PropertyChanged += ItemPropertyChanged);
HookupCollectionChangedEvent();
}
private void HookupCollectionChangedEvent()
{
CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollectionChanged);
}
I know that I'm too late for this party, but maybe - it will help to someone..
Here you can find my implementation of ObservableCollectionEx. It has some features:
Of course, any comments are appreciated ;)
This uses the above ideas but makes it a derived 'more sensitive' collection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
namespace somethingelse
{
public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
// this collection also reacts to changes in its components' properties
public ObservableCollectionEx() : base()
{
this.CollectionChanged +=new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ObservableCollectionEx_CollectionChanged);
}
void ObservableCollectionEx_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach(T item in e.OldItems)
{
//Removed items
item.PropertyChanged -= EntityViewModelPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach(T item in e.NewItems)
{
//Added items
item.PropertyChanged += EntityViewModelPropertyChanged;
}
}
}
public void EntityViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
//This will get called when the property of an object inside the collection changes - note you must make it a 'reset' - dunno why
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(args);
}
}
}
I used Jack Kenyons answer to implement my own OC, but I'd like to point out one change i had to make to make it work. Instead of:
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach(T item in e.NewItems)
{
//Removed items
item.PropertyChanged -= EntityViewModelPropertyChanged;
}
}
I used this:
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach(T item in e.OldItems)
{
//Removed items
item.PropertyChanged -= EntityViewModelPropertyChanged;
}
}
It seems that the "e.NewItems" produces null if action is .Remove.