Strange Behavior of CollectionView WPF - c#

I've faced a really strange behavior while using CollectionViews and wonder if somebody can explain me that behavior.
I've got an ObservableCollection, lets call it _sourcelist. Then I create several CollectionViews of _sourcelist, in that way:
CollectionViewSource cvs = new CollectionViewSource() { Source = _sourcelist };
ICollectionView list = cvs.View;
These CollectionViews are each used with a different filter and one CollectionView with no filter (so representing the original source). In my Application I have a TabControl where every TabItem contains a ListBox with one of these CollectionViews as ItemsSource. When I know delete an item from the sourcelist, the ListBox with the CollectionView with no filter gets updated, but the other ListBoxes that contains filtered CollectionViews and contain the deleted item don't get updated. So the deleted item remains in that ListBoxes.
Now, the strange behavior is following:
When I add the following code after code written above:
list.CollectionChanged += (o,e) => { };
Then the other ListBoxes get updated, too.
Why that? I mean, I just registered an event handler for the CollectionChanged event, which does nothing!
So, here is my Code Sample. I know it is a big amount of code, but it is ready to run and reproduces the strange behavior.
When you run the code and try to delete an item in the "All" List, then the corresponding item in the filtered "Under 30" or "Over 30" list will not be deleted. Now uncomment the commented line in the ListViewModel (it is just a registration of an empty Eventhandler), then the corresponding items in the filtered lists will be deleted.
If you have any questions, then please ask! :-)
Thank you for your interest in solving the problem. :-)
P.S.: The RelayCommand is from the assembly: Microsoft.TeamFoundation.Controls
MainWindow.xaml:
<Window x:Class="CollectionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CollectionTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ListViewModel}">
<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding Selected}" BorderBrush="Transparent" Margin="0" HorizontalContentAlignment="Stretch"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PersonViewModel}">
<TextBlock Margin="5,0" VerticalAlignment="Center" Text="{Binding Name}"/>
</DataTemplate>
<Style x:Key="{x:Type TabItem}" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</Window.Resources>
<DockPanel>
<Button DockPanel.Dock="Bottom" Name="DeleteButton" Content="Delete" Command="{Binding DeleteCommand}"/>
<TabControl Name="TabControl" ItemsSource="{Binding Lists}" Padding="0" SelectedItem="{Binding Selected}"/>
</DockPanel>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MainViewModel(CreateRepo());
this.DataContext = mvm;
}
private Repository CreateRepo()
{
Repository repo = new Repository();
for (int i = 20; i < 45; i++)
{
Person p = new Person() { Name = "Person" + i, Age = i };
repo.Add(p);
}
return repo;
}
}
Person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Repository:
public class Repository
{
private readonly List<Person> _list;
public event EventHandler<RepositoryChangedEventArgs> Added;
public event EventHandler<RepositoryChangedEventArgs> Removed;
public Repository()
{
this._list = new List<Person>();
}
public void Add(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (!this._list.Contains(person))
{
this._list.Add(person);
if (this.Added != null)
{
this.Added(this, new RepositoryChangedEventArgs(person));
}
}
}
public void Remove(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (this._list.Contains(person))
{
this._list.Remove(person);
if (this.Removed != null)
{
this.Removed(this, new RepositoryChangedEventArgs(person));
}
}
}
public List<Person> Get()
{
return new List<Person>(this._list);
}
}
public class RepositoryChangedEventArgs : EventArgs
{
public Person Entity { get; set; }
public RepositoryChangedEventArgs(Person entity)
{
this.Entity = entity;
}
}
PersonViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
private Person _person;
private Repository _repository;
public string Name
{
get
{
return this._person.Name;
}
set
{
if (this._person.Name != value)
{
this._person.Name = value;
this.OnPropertyChanged("Name");
}
}
}
public int Age
{
get
{
return this._person.Age;
}
set
{
if (this._person.Age != value)
{
this._person.Age = value;
this.OnPropertyChanged("Age");
}
}
}
public PersonViewModel(Person person, Repository repository)
{
this._person = person;
this._repository = repository;
}
public void Delete()
{
this._repository.Remove(this._person);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
ListViewModel:
public class ListViewModel : INotifyPropertyChanged
{
private IEnumerable _sourceList;
private ICollectionView _list;
private PersonViewModel _selected;
private string _name;
public ICollectionView List
{
get
{
return this._list;
}
}
public PersonViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public string Name
{
get
{
return this._name;
}
set
{
if (this._name != value)
{
this._name = value;
this.OnPropertyChanged("Name");
}
}
}
public ListViewModel(string name, IEnumerable list)
{
this.Name = name;
this._sourceList = list;
CollectionViewSource cvs = new CollectionViewSource() { Source = list };
this._list = cvs.View;
// cvs.View.CollectionChanged += (o, e) => { }; // uncomment this line to let it work
}
public ListViewModel(String name, IEnumerable list, Predicate<object> filter)
: this(name, list)
{
this._list.Filter = filter;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
protected ObservableCollection<PersonViewModel> _sourceList;
protected ObservableCollection<ListViewModel> _lists;
protected Repository _repository;
protected Dictionary<Person, PersonViewModel> _dictionary;
private RelayCommand _deleteCommand;
private ListViewModel _selected;
public ListViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public RelayCommand DeleteCommand
{
get
{
if (this._deleteCommand == null)
{
this._deleteCommand = new RelayCommand(this.ExecutedDelete, this.CanExecuteDelete);
}
return this._deleteCommand;
}
}
public IEnumerable<ListViewModel> Lists
{
get
{
return this._lists;
}
}
public MainViewModel(Repository repository)
{
this._sourceList = new ObservableCollection<PersonViewModel>();
this._lists = new ObservableCollection<ListViewModel>();
this._dictionary = new Dictionary<Person, PersonViewModel>();
this._repository = repository;
this._repository.Added += this.OnAdded;
this._repository.Removed += this.OnRemoved;
this.CreateSourceList(repository);
this.CreateLists();
if (this._lists.Count > 0)
{
this.Selected = this._lists[0];
}
}
protected void CreateSourceList(Repository repository)
{
foreach (Person person in repository.Get())
{
PersonViewModel pvm = new PersonViewModel(person, repository);
this._dictionary.Add(person, pvm);
this._sourceList.Add(pvm);
}
}
protected void CreateLists()
{
this._lists.Add(new ListViewModel("All", this._sourceList));
this._lists.Add(new ListViewModel("Under 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age < 30);
}));
this._lists.Add(new ListViewModel("Over 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age > 30);
}));
}
protected void Remove(Person person)
{
PersonViewModel pvm = this._dictionary[person];
this._dictionary.Remove(person);
this._sourceList.Remove(pvm);
}
protected void OnAdded(object sender, RepositoryChangedEventArgs args)
{
Person addedPerson = args.Entity;
if (addedPerson != null)
{
PersonViewModel pvm = new PersonViewModel(addedPerson, this._repository);
this._dictionary.Add(addedPerson, pvm);
this._sourceList.Add(pvm);
}
}
protected void OnRemoved(object sender, RepositoryChangedEventArgs args)
{
Person removedPerson = args.Entity;
if (removedPerson != null)
{
this.Remove(removedPerson);
}
}
private bool CanExecuteDelete(object o)
{
return true;
}
private void ExecutedDelete(object o)
{
PersonViewModel pvm = null;
if (this.Selected != null && (pvm = this.Selected.Selected) != null)
{
pvm.Delete();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

Related

Model and ViewModel Consept MVVM WPF

I want to read some data from database and do some process on them and then view them in the view.
I read a lot about MVVM and now I am confused.
Imaging I read a person entity from database with Name attribute.
please make a small code and show me how should I make my model and ViewModel.
I guess it we will be something like this :
public class PersonModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string Name;
public string name
{
get
{
return Name;
}
set
{
Name = value;
onpropertychanged("name");
}
}
public PersonModel( string s)
{
name = s;
}
public void onpropertychanged(string PName)
{
if (PropertyChanged !=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PName));
}
}
}
public class PersonViewModel
{
public ObservableCollection <PersonModel> list { get; set; }
public PersonViewModel()
{
list = new ObservableCollection<model>();
list.Add(new model("abc"));
list.Add(new model("def"));
}
public void change()
{
list[1].name = "changed";
}
}
public class ViewModelBase
{
public PersonViewModel vperson { get; set; }
public ViewModelBase()
{
vperson = new PersonViewModel();
vperson.change();
}
}
Edite : Where should database connections be?
Edite :
<Grid>
<TextBox Text="{Binding vperson.list[1].name}" />
</Grid>
</Window>
I edited your classes and is working
public class PersonModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged();
}
}
public PersonModel(string name)
{
_name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class PersonViewModel
{
public ObservableCollection<PersonModel> Items { get; set; }
public PersonViewModel()
{
Items = new ObservableCollection<PersonModel> { new PersonModel("abc"), new PersonModel("def") };
}
public void Change()
{
Items[1].Name = "changed";
}
}
public class ViewModelBase
{
public PersonViewModel PersonViewModel { get; set; }
public ViewModelBase()
{
PersonViewModel = new PersonViewModel();
PersonViewModel.Change();
}
}
//Use the dataContext in this way, will help you with the strong type
xmlns:viewModels="clr-namespace:WpfApp1.ViewModels"
<Window.DataContext>
<viewModels:ViewModelBase />
</Window.DataContext>
<Grid>
<TextBox Text="{Binding PersonViewModel.Items[1].Name}" />
</Grid>

"Cascade" binding on ObservableCollection, containing other ObservableCollection

I have a project where I need to display a list of contract (Class Affaire).
Each contract has a list of phases (Class Phase).
I display each of them in 2 different ListView using binding.
Problem is when I remove a Phase from ListView, nor the ListView where are displayed Phases, nor my ObjectCollection are updated.
I created a context with two distinct ObservableCollection :
ObservableCollection, where is the list of Affaire.
ObservableCollection that are list of phases present in selected Affaire
My context is made as follow :
public class Contexte : INotifyPropertyChanged
{
private Affaire selectedAffaire;
private Phase selectedPhase;
private Assemblage selectedAssemblage;
public Affaire SelectedAffaire
{
get { return selectedAffaire; }
set
{
selectedAffaire = value;
this.NotifyPropertyChanged("SelectedAffaire");
}
}
public Phase SelectedPhase
{
get { return selectedPhase; }
set
{
selectedPhase = value;
this.NotifyPropertyChanged("SelectedPhase");
}
}
public Assemblage SelectedAssemblage
{
get { return selectedAssemblage; }
set
{
selectedAssemblage = value;
this.NotifyPropertyChanged("SelectedAssemblage");
}
}
private ObservableCollection<Affaire> listeDesAffaires;
public ObservableCollection<Affaire> ListeDesAffaires
{
get { return listeDesAffaires; }
set { NotifyPropertyChanged(ref listeDesAffaires, value); }
}
/**************************************************/
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string nomPropriete)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(nomPropriete));
}
private bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string nomPropriete = null)
{
if (object.Equals(variable, valeur)) return false;
variable = valeur;
NotifyPropertyChanged(nomPropriete);
return true;
}
}
My Class Affaire :
public class Affaire : INotifyPropertyChanged
{
public long ID { get; set; }
private string nom;
public string Nom
{
get { return this.nom; }
set
{
if (this.nom != value)
{
this.nom = value;
this.NotifyPropertyChanged("Nom");
}
}
}
private string code;
public string Code
{
get { return this.code; }
set
{
if (this.code != value)
{
this.code = value;
this.NotifyPropertyChanged("Code");
}
}
}
private string comm;
public string Comm
{
get { return this.comm; }
set
{
if (this.comm != value)
{
this.comm = value;
this.NotifyPropertyChanged("Comm");
}
}
}
private ObservableCollection<Phase> listPhases;
public ObservableCollection<Phase> ListPhases {
get { return listPhases; }
set
{
listPhases = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("ListPhases");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
....
}
My class Phase :
public class Phase : INotifyPropertyChanged
{
private string nomPhase;
public string NomPhase
{
get { return this.nomPhase; }
set
{
if (this.nomPhase != value)
{
this.nomPhase = value;
this.NotifyPropertyChanged("NomPhase");
}
}
}
private int priorite;
public int Priorite
{
get { return this.priorite; }
set
{
if (this.priorite != value)
{
this.priorite = value;
this.NotifyPropertyChanged("Priorite");
}
}
}
private string commPhase;
public string CommPhase
{
get { return this.commPhase; }
set
{
if (this.commPhase != value)
{
this.commPhase = value;
this.NotifyPropertyChanged("CommPhase");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public long IdAffaire { get; set; }
public long ID { get; set; }
private ObservableCollection<Assemblage> listAssemblages;
public ObservableCollection<Assemblage> ListAssemblages
{
get { return listAssemblages; }
set
{
listAssemblages = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("ListAssemblages");
}
}
}
...
}
1) When I doubleclick on a contract(in ListView1), I display in ListView2 list of its phases, I do the following :
contexte.SelectedAffaire = (Affaire)ListView1.SelectedItem;
contexte.SelectedAffaire.getListPhases();
afficherListview("2");
2) When I edit my Phase, all is updated correctly in my view, and also in my ObservableCollection
3) For adding/removing a new Phase, all is solved.
Edit :
Example given by Netstep works perfectly but I still meet an issue :
I have a third level "Assembly", this is an ObservableCollection in class "Phase", I have no problem to navigate with the four ListViews, but I meet a problem when I want to make something else : When I right-click on the header of "Phase", I want to display in the list of Assembly, the list of all assemblies contained in Affaire, without filtering on Phase.
For this I do the following : I am on the 3rd level listview Assemblies, I make right click on Phase header and event is as following :
Phase ph = new Phase();
ph.IdAffaire = contexte.SelectedAffaire.ID;
ph.ListAssemblages = new ObservableCollection<Assemblage>(contexte.SelectedAffaire.getListAssemblages(true));
contexte.SelectedPhase = ph;
I "shit" a bit making a new empty phase, just to put an ObservableCollection inside and bind the ObservableCollection to my ListView3.
Edit : That part of code works good... I just refreshed the context somewhere in my code and forgot about it, sure that there was something wrong in biding... All solved thanks
Here is the example, based on your source, that will handle all stuff.
a View Models:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApp2
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string nomPropriete)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(nomPropriete));
}
}
public class Contexte : BaseViewModel
{
private Affaire _selectedAffaire;
private Phase _selectedPhase;
public ObservableCollection<Affaire> ListeDesAffaires { get; set; }
public Affaire SelectedAffaire
{
get { return _selectedAffaire; }
set
{
_selectedAffaire = value;
this.NotifyPropertyChanged("SelectedAffaire");
}
}
public Phase SelectedPhase
{
get { return _selectedPhase; }
set
{
_selectedPhase = value;
this.NotifyPropertyChanged("SelectedPhase");
}
}
public Contexte()
{
ListeDesAffaires = new ObservableCollection<Affaire>
{
new Affaire("Affaire1"),
new Affaire("Affaire2")
};
}
}
public class Affaire : BaseViewModel
{
private string nom;
public string Nom
{
get { return this.nom; }
set
{
this.nom = value;
this.NotifyPropertyChanged("Nom");
}
}
public ObservableCollection<Phase> ListPhases { get; set; }
public Affaire(string n)
{
nom = n;
ListPhases = new ObservableCollection<Phase>
{
new Phase { NomPhase = nom + "_Phase1" },
new Phase { NomPhase = nom + "_Phase2" }
};
}
}
public class Phase : BaseViewModel
{
private string nomPhase;
public string NomPhase
{
get { return this.nomPhase; }
set
{
this.nomPhase = value;
this.NotifyPropertyChanged("NomPhase");
}
}
public ObservableCollection<Assemblage> ListAssemblages { get; set; }
}
public class Assemblage : BaseViewModel
{
private string nom;
public string Nom
{
get { return this.nom; }
set
{
this.nom = value;
this.NotifyPropertyChanged("Nom");
}
}
}
}
a MainWindow.xaml:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:Contexte x:Name="Contexte" d:IsDataSource="True" />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding ListeDesAffaires}" DisplayMemberPath="Nom" SelectedItem="{Binding SelectedAffaire}"/>
<ListBox Grid.Column="1" ItemsSource="{Binding SelectedAffaire.ListPhases}" DisplayMemberPath="NomPhase" />
<Button Grid.Column="2" VerticalAlignment="Top" Click="FillClick">Fill</Button>
<ListBox Grid.Column="2" ItemsSource="{Binding SelectedPhase.ListAssemblages}" DisplayMemberPath="Nom" Margin="0,20,0,0"/>
</Grid>
</Window>
And some code from your question (MainWindow.xaml.cs):
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApp2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void FillClick(object sender, RoutedEventArgs e)
{
Phase ph = new Phase();
ph.NomPhase = "SomeId";
ph.ListAssemblages = new ObservableCollection<Assemblage>()
{
new Assemblage { Nom = "Assemblage1" },
new Assemblage { Nom = "Assemblage2" }
};
Contexte.SelectedPhase = ph;
}
}
}
And here is the result:
This is basic sample that you can extend. It handles all field modification and Add/Remove objects and displays on screen. There is no additional code in MainWindow.xaml.cs. Please ask questions, if any.

c# UWP binding ComboBox SelectedItem

Hello I can't seem to figure out why my combox stays empty :/
On page load the first combox gets populated with countries (from JSON), the second combobox should populate when a country is selected in the first combobox. I'm trying to fetch the SelectedItem (country) as string in a property ... SelectedItem is of type ComboBoxItem ? I think that is where it goes wrong.
The (view)model where the sorting bindable properties are:
public class LocalityModel : NotifyProp
{
#region properties
private static List<LocalityJSON> dataList;
public List<LocalityJSON> DataList
{
get
{
return dataList;
}
set {
dataList = value;
RaisePropertyChanged("Landen");
RaisePropertyChanged("Gewesten");
}
}
public List<string> Landen
{
get { if (DataList == null) return null; return (from s in DataList orderby s.Land select s.Land).Distinct().ToList<string>(); }
}
public string SelectedLand { get; set; }
public List<string> Gewesten {
get { if (DataList == null) return null; return (from s in DataList where s.Land.Equals(SelectedLand) select s.Gewest).Distinct().ToList<string>(); }
}
#endregion
#region ctor
public LocalityModel()
{
FillDataList();
}
#endregion
#region methodes
public async void FillDataList()
{
if (DataList == null)
{
DataList = await EVNT.Entries();
}
}
#endregion
}
MainPage XAML (the bindings):
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{Binding Source={StaticResource LocalityModel}}">
...
<TextBlock x:Name="txbCountry" Style="{StaticResource InfoLabelCountry}" />
<ComboBox x:Name="cboCountry" Style="{StaticResource CountryBox}" ItemsSource="{Binding Landen}" SelectedItem="{Binding SelectedLand, Mode=TwoWay}" />
<TextBlock x:Name="txbGewest" Style="{StaticResource InfoLabelGewest}" />
<ComboBox x:Name="cboGewest" Style="{StaticResource GewestBox}" ItemsSource="{Binding Gewesten}" />
INotifyPropertyChanged:
public class NotifyProp : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The model for the JSON:
public class LocalityJSON
{
public string FB_ID { get; set; }
public string Land { get; set; }
public string Gewest { get; set; }
public string City { get; set; }
}
The JSON deserialisation (less important for the question):
public class EVNT
{
public async static Task<List<LocalityJSON>> Entries()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(#"http://e-vnt.com/admin/core/api/");
HttpResponseMessage response = await client.GetAsync("localityApi");
if (response.IsSuccessStatusCode)
{
String s = await response.Content.ReadAsStringAsync();
List<LocalityJSON> entries = JsonConvert.DeserializeObject<List<LocalityJSON>>(s);
return entries;
}
else
return null;
}
}
}
In your SelectedLand Property setter you need to fire PropertyChanged event the for both SelectedLand and for Gewesten.
It would probably look something like this
private string _SelectedLand;
public string SelectedLand
{
get
{
return _SelectedLand;
}
set
{
_SelectedLand = value;
RaisePropertyChanged("SelectedLand");
RaisePropertyChanged("Gewesten");
}
}
if you don't fire PropertyChanged event for Gewesten then that combobox will not know to reload is values.

Manipulating Collection inside ViewModel from Code-Behind

My Model :
public class Cont: INotifyPropertyChanged
{
private string _Contact;
public string Contact
{
get { return _Contact; }
set
{
_Contact= value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Contact"));
}
}
private string _Age;
public string Age
{
get { return _Age; }
set
{
_Age = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Age"));
}
}
My CollectionViewSource:
<CollectionViewSource x:Key="group" Source="{Binding GroupedCollection}" ItemsPath="{Binding GroupedCollection[0].ContactColl}" IsSourceGrouped="True">
</CollectionViewSource>
My GridView :
<GridView ItemsSource="{Binding Source={StaticResource group}}"
SelectionMode="None" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<GridView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock x:Name="keyHeader" Text="{Binding Key}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Contact}"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
My ViewModel :
public class ContVM: INotifyPropertyChanged
{
public ObservableCollection<Cont> Contlist = new ObservableCollection<Cont>();
private ObservableCollection<ContItem> _GroupedCollection;
public ObservableCollection<ContItem> GroupedCollection
{
get
{
if (_GroupedCollection == null)
{
_GroupedCollection= new ObservableCollection<ContItem>();
}
return _GroupedCollection;
}
set
{
_GroupedCollection = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("GroupedCollection"));
}
}
public void SetInitialCollection()
{
var keyList = Contlist .GroupBy(c => c.Age).Select(g => g.Key);
foreach (var key in keyList)
{
var contItem= new ContItem();
contItem.Key = key;
var contList = Contlist.Where(c => c.Age == key);
foreach (var item in contList )
{
contItem.ContactColl.Add(item);
}
GroupedCollection.Add(contItem);
}
}
public void AddNew(Cont Item)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == Item.Age );
if ( contItem != null )
{
contItem.ContactColl.Add(Item);
}
else
{
contItem = new ContItem();
contItem.Key = Item.Age ;
GroupedCollection.Add(contItem);
}
}
public void Delete(Cont Item)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == Item.Age);
if (contItem != null)
{
if (contItem.ContactColl.Contains(Item))
{
contItem.ContactColl.Remove(Item);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ContItem : INotifyPropertyChanged
{
public string Key { get; set; }
private ObservableCollection<Cont> _ContactColl;
public ObservableCollection<Cont> ContactColl
{
get
{
if (_ContactColl== null)
{
_ContactColl= new ObservableCollection<Cont>();
}
return _ContactColl;
}
set
{
_ContactColl= value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ContactColl"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My code-behind cs :
ContVM vm = new ContVM();
private void Add_new_cont_Click(object sender, RoutedEventArgs e)
{
// a button that add a new data from 2 textbox
Cont item = new Cont();
item.Contact = textbox1.Text;
item.Contact = textbox2.Text;
vm.AddNew(item); // error line
}
Already tried this, but I get error 0xC0000005: Access violation reading location 0x00000004. and it redirect me at line which is vm.AddNew(item);. All these codes have been updated/guided by #Bells.
Each time when a new item is being added to the collection, you are regrouping the items and resetting the source. That will cause the grid view to reset its position. In order to maintain the position, you have to assign a source collection initially as the ItemsSource and manipulations should be done on the same collection.
public class Cont
{
public string Contact { get; set; }
public string Age { get; set; }
}
public class ContItem
{
public string Key { get; set; }
private ObservableCollection<Cont> _contactColl;
public ObservableCollection<Cont> ContactColl
{
get
{
if (_contactColl == null)
_contactColl = new ObservableCollection<Cont>();
return _contactColl;
}
}
}
public class TestClass
{
public ObservableCollection<Cont> Contlist = new ObservableCollection<Cont>();
private ObservableCollection<ContItem> _groupedCollection;
public ObservableCollection<ContItem> GroupedCollection
{
get
{
if (_groupedCollection == null)
_groupedCollection = new ObservableCollection<ContItem>();
return _groupedCollection;
}
}
public void SetInitialCollection()
{
//Add the existing items in ContactList (if any) to a grouped collection.
var keyList = Contlist.GroupBy(c => c.Age).Select(g => g.Key);
foreach (var key in keyList)
{
var contItem = new ContItem();
contItem.Key = key;
var contList = Contlist.Where(c => c.Age == key);
foreach (var item in contList)
{
contItem.ContactColl.Add(item);
}
GroupedCollection.Add(contItem);
}
}
public void AddNewItem()
{
var cont = new Cont();
cont.Age = "32";
cont.Contact = "";
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == cont.Age);
if (contItem != null)
{
contItem.ContactColl.Add(cont);
}
else
{
contItem = new ContItem();
contItem.Key = cont.Age;
contItem.ContactColl.Add(cont);
GroupedCollection.Add(contItem);
}
}
public void DeleteItem(Cont cont)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == cont.Age);
if (contItem != null)
{
if (contItem.ContactColl.Contains(cont))
{
contItem.ContactColl.Remove(cont);
}
}
}
}
<CollectionViewSource x:Key="group" Source="{Binding GroupedCollection}"
ItemsPath="ContactColl" IsSourceGrouped="True" />

How to refresh a data-bound collection in c#?

Im writing a simple wpf application, but Im stuck. I'd like to achieve, that I have a filter class, and If the id has been changed in the filter class by a user input, a list should refresh applying the filter. All the initial bindings are working. The list is displayed properly along with the CompanyId.
the databinding in xaml:
<ListBox Height="212" HorizontalAlignment="Left" Margin="211,31,0,0" Name="listBoxProducts" VerticalAlignment="Top" Width="267" ItemsSource="{Binding ElementName=this, Path=Products}" DisplayMemberPath="CompanyId" />
<TextBox Height="28" HorizontalAlignment="Left" Margin="12,31,0,0" Name="textBoxCompanyId" VerticalAlignment="Top" Width="170" Text="{Binding ElementName=this, Path=Company.Id}" />
The code-behind for the xaml:
private TestFactory _testFactory = new TestFactory();
private Company _company;
public Company Company
{
get { return _company; }
}
private IProductList _products;
public IProductList Products
{
get { return _products; }
}
public MainWindow()
{
_company = _testFactory.Company;
_products = _testFactory.Products;
InitializeComponent();
_company.FilterChanged += _testFactory.FilterChanging;
}
The (dummy)factory class:
private IProductList _products;
public IProductList Products
{
get { return _products; }
}
private Company _company = new Company();
public Company Company
{
get { return _company; }
}
public TestFactory()
{
_company = new Company() { Id = 2, Name = "Test Company" };
GetProducts();
}
public void GetProducts()
{
var products = new List<Product>();
products.Add(new Product() { ProductNumber = 1, CompanyId = 1, Name = "test product 1" });
products.Add(new Product() { ProductNumber = 2, CompanyId = 1, Name = "test product 2" });
products.Add(new Product() { ProductNumber = 3, CompanyId = 2, Name = "test product 3" });
if (Company.Id != 2)
{
products = products.Where(p => p.CompanyId == Company.Id).ToList();
}
_products = new ProductList(products);
}
public void FilterChanging(object sender, EventArgs e)
{
GetProducts();
}
The ProductList interface:
public interface IProductList : IList<Product>, INotifyCollectionChanged {}
The productlist class:
public class ProductList : IProductList
{
private readonly IList<Product> _products;
public ProductList() { }
public ProductList(IList<Product> products)
{
_products = products;
}
public IEnumerator<Product> GetEnumerator()
{
return _products.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(Product item)
{
_products.Add(item);
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
public void Clear()
{
_products.Clear();
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public bool Contains(Product item)
{
return _products.Contains(item);
}
public void CopyTo(Product[] array, int arrayIndex)
{
_products.CopyTo(array, arrayIndex);
}
public bool Remove(Product item)
{
var removed = _products.Remove(item);
if (removed)
{
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
}
return removed;
}
public int Count
{
get { return _products.Count; }
}
public bool IsReadOnly
{
get { return _products.IsReadOnly; }
}
public int IndexOf(Product item)
{
return _products.IndexOf(item);
}
public void Insert(int index, Product item)
{
_products.Insert(index, item);
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveAt(int index)
{
_products.RemoveAt(index);
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public Product this[int index]
{
get { return _products[index]; }
set
{
_products[index] = value;
notifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, _products[index]));
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void notifyCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (CollectionChanged != null)
{
CollectionChanged(this, args);
}
}
}
The Company class (filter class):
public class Company : INotifyPropertyChanged
{
private int _id;
public int Id
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
OnPropertyChanged("Id");
OnFilterChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler FilterChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged == null)
return;
var eventArgs = new PropertyChangedEventArgs(name);
PropertyChanged(this, eventArgs);
}
private void OnFilterChanged(NotifyCollectionChangedEventArgs e)
{
if (FilterChanged == null)
return;
FilterChanged(this, e);
}
}
The list is refreshed in factory, but there is no change in the view. I probably do something wrong, maybe my whole approach is not best. Maybe I have to use ObservableCollection type with valueconverter? Any help would be greatly appreciated. Cheers!
Use an ObservableCollection<Product> instead of creating your own list based on IList
The purpose of an ObservableCollection is to track changes to the collection, and it will automatically update the UI when the collection changes.
You might also consider using ICollectionView for this use-case.
Refer this post.

Categories

Resources