BindingList Detect Changing (filter) - c#

I have class with property, i'm using INotifyPropertyChanged for detecting update.
public class Employee : INotifyPropertyChanged
{
string _lastname;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public string LastName { get { return _lastName; } set { _lastName = value; NotifyPropertyChanged("LastName"); } }
public Employee(string lastName)
{
LastName = lastName;
}
}
I bind it to DataGriView and it's work fine, LastName auto update, if changing.
But if i trying to do this:
BindingList<Employee> employees = new BindingList<Employee>();
private void BindMain()
{
employees.Add(new Employee("Mike"));
employees.Add(new Employee("Mike"));
employees.Add(new Employee("Joo"));
employees.Add(new Employee("Kate"));
dataGridView.DataSource = employees; // That's working fine, all data auto update
}
private void BindFiltered()
{
List<Employee> filtered = employees.Where(r=>r.LastName.Equals("Mike").ToList()); //
BindingList<Employee> filteredEmployee = new BindingList<Employee>(filtered);
dataGridView.DataSource = filteredEmployee; //not working auto update
// Trying also with BindingSource
BindingSource bs = new BindingSource();
bs.DataSource = employees.Where(r=>r.LastName.Equals("Mike"));
dataGridView.DataSource = bs;
}
..auto update does not work.

Related

Changing text of one item in a ComboBox

I have a ComboBox that contains a list of names: LastName + ", " + FirstName.
When a name is selected, it populates two text boxes with the First and Last names, respectively.
What I am trying to do is, if the name is changed in the text boxes, I want the change to be updated to the ComboBox as well without having to reload the entire thing. My ComboBox is NOT loaded directly from a database, so I cannot use RefreshItem()
Is this even possible?
You could implement the INotifyPropertyChanged interface and use a BindingSource as the DataContext of your ComboBox. Please refer to the following sample code.
Person.cs:
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; NotifyPropertyChanged(); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; NotifyPropertyChanged(); }
}
public string FullName { get { return LastName + ", " + FirstName; } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Form1.cs:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<Person> people = new List<Person>()
{
new Person() { FirstName = "Donald", LastName = "Duck" },
new Person() { FirstName = "Mickey", LastName = "Mouse" }
};
BindingSource bs = new BindingSource();
bs.DataSource = people;
comboBox1.DataSource = bs;
comboBox1.DisplayMember = "FullName";
textBox1.DataBindings.Add(new Binding("Text", bs, "FirstName", false, DataSourceUpdateMode.OnPropertyChanged));
textBox2.DataBindings.Add(new Binding("Text", bs, "LastName", false, DataSourceUpdateMode.OnPropertyChanged));
}
}

DataGrid WPF StackOverFlow Exception

I am new to WPF and am having a problem with setting up binding to a DataGrid. My issue is that I keep getting a StackOverFlowException and the debugger breaks on the set statement of the FirstName property. I have referred to the follow resources and was unable to solve my problem:
msdn databinding overview
stackoverflow-with-wpf-calendar-when-using-displaydatestart-binding
how-to-get-rid-of-stackoverflow-exception-in-datacontext-initializecomponent
Any help is greatly appreciated.
My code is:
namespace BindingTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<Person> persons = new ObservableCollection<Person>()
{
new Person(){FirstName="john", LastName="smith"},
new Person(){FirstName="foo", LastName="bar"}
};
dataGrid1.ItemsSource = persons;
}
class Person : INotifyPropertyChanged
{
public string FirstName
{
get
{
return FirstName;
}
set
{
FirstName = value;
NotifyPropertyChanged("FirstName");
}
}
public string LastName
{
get
{
return LastName;
}
set
{
LastName = value;
NotifyPropertyChanged("LastName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
}
Note about answer
For information about the recursion with property settings for anyone else who has the same issue, pleasee see this:
Why would this simple code cause a stack overflow exception?
FirstName = value; causes recursive call of the property setter. Make something like this:
private string firstName;
public string FirstName
{
get { return firstName;}
set
{
this.firstName = value;
/*...*/
}
}

React on object changed in ObservableCollection

I have an ObservableCollection<Person> in my viewmodel. This is bound as an ItemsSource to a DataGrid in the view. The class Person only has threeProperties:
public class Person : ViewModelBase
{
private Guid id;
public Guid Id
{
get { return this.id; }
set
{
this.id = value;
OnPropertyChanged("Id");
}
}
private string firstname;
public string Firstname
{
get { return this.firstname; }
set
{
this.firstname = value;
OnPropertyChanged("Firstname");
}
}
private string lastname;
public string Lastname
{
get { return this.lastname; }
set
{
this.lastname = value;
OnPropertyChanged("Lastname");
}
}
}
The class ViewModelBase implements INotifyPropertyChanged.
The items in the collection are updated perfect if I add or remove an entry in the dategrid. The item is then also removed in the collection.
My problem is that the content of an person-item is updated, but I don't know how I can react on this.
Do I have to add an event or something else to the person-class to get informed or is there another way to do this?
Implement INotifyPropertyChanged interface on your class Person so that any change in Person properties gets reflected back on UI.
Sample -
public class Person : INotifyPropertyChanged
{
private Guid id;
public Guid Id
{
get { return id; }
private set
{
if(id != value)
{
id = value;
NotifyPropertyChanged("Id");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Data binding to custom object

I have a simple custom class (Person), which I want to bind to a label as a whole (not to separate properties of this class). The label should just present whatever the Person.ToString() returns (in this case FirstName + LastName).
How do I properly bind it using the person as a Source.
How do I make sure that any change in one of the properties of the Person will be reflected in the label?
public class Person : INotifyPropertyChanged {
private string firstName;
public string FirstName {
get {
return firstName;
}
set {
firstName = value;
OnPropertyChanged("FirstName");
}
}
private string lastName;
public string LastName {
get {
return lastName;
}
set {
lastName = value;
OnPropertyChanged("LastName");
}
}
public override string ToString() {
return FirstName + " " + LastName;
}
private void OnPropertyChanged(string name) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public Window1() {
myPerson = new Person() {
FirstName = "AAA",
LastName = "BBB"};
InitializeComponent();
}
public Person MyPerson {
get {
return myPerson;
}
set {
myPerson = value;
}
}
Label Content="{Binding Source=MyPerson}"
Create a new property FullName which returns the full name and raise PropertyChanged for FullName in the setters of FirstName and LastName as well. You should never bind to the object itself.

Bind ListView to ObservableCollection and Linq

Hi Im trying to bind a ListView to a Linq, and make it react on changes in the collection.
public partial class MainWindow
{
readonly ObservableCollection<Person> _persons = new ObservableCollection<Person>();
readonly Team _team1 = new Team { Name = "Team 1" };
readonly Team _team2 = new Team { Name = "Team 2" };
readonly Team _team3 = new Team { Name = "Team 3" };
public MainWindow()
{
InitializeComponent();
_persons.Add(new Person {Name = "James", Team = _team1});
_persons.Add(new Person {Name = "John", Team = _team2});
_persons.Add(new Person {Name = "Peter", Team = _team3});
_persons.Add(new Person {Name = "Jack", Team = _team1});
_persons.Add(new Person {Name = "Jones", Team = _team2});
_persons.Add(new Person {Name = "Bauer", Team = _team3});
_persons.Add(new Person {Name = "Bo", Team = _team1});
_persons.Add(new Person {Name = "Ben", Team = _team2});
_persons.Add(new Person {Name = "Henry", Team = _team3});
TeamList1.ItemsSource = from person in _persons where person.Team == _team1 select person;
TeamList2.ItemsSource = from person in _persons where person.Team == _team2 select person;
TeamList3.ItemsSource = from person in _persons where person.Team == _team3 select person;
}
private void ButtonClick(object sender, RoutedEventArgs e)
{
_persons[0].Team = _team2;
}
}
And my Models
public class Person : INotifyPropertyChanged
{
private string _name;
public String Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
private Team _team;
public Team Team
{
get { return _team; }
set { _team = value; NotifyPropertyChanged("Team"); }
}
public override string ToString()
{
return string.Format("Name {0}\t{1}", Name, Team);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
public class Team : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public override string ToString()
{
return Name;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
And the Xaml
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel>
<ListView Name="TeamList1"/>
<ListView Name="TeamList2"/>
<ListView Name="TeamList3"/>
</StackPanel>
<Button Content="Click Me" Click="ButtonClick"/>
</StackPanel>
</Grid>
When the ButtonClick is called I would like my ListView to reflect the changes it made on the _persons.
Just don't use linq, there are CollectionViews for this sort of thing which have filters, you then only have to refresh the views (See: How to: Filter data in a view). (If you must use linq there are also bindable extensions to it)
The LINQ query is evaluated when it is bound to the data source and the items in the list view are generated. The LINQ query will not be re-evaluated whenever you change the properties of a person inside the original list _persons. To achieve this, you have to do a custom implementation that listens to the PropertyChanged event of each Person in the original list and updates an ObservableCollection<Person> accordingly. This observable collection you would bind to the list view.
You need my ObservableComputations library. Using this library you can code like this:
TeamList1.ItemsSource = _persons.Filtering(person.Team == _team1);
TeamList2.ItemsSource = _persons.Filtering(person.Team == _team2);
TeamList3.ItemsSource = _persons.Filtering(person.Team == _team3);

Categories