Noobish question - BindingList.Filter not working

  • Thread starter Thread starter Chris Bordeman
  • Start date Start date
C

Chris Bordeman

Pardon the fairly noobish question here...

I've created two classes that expose some properties. I've declared two
List<T>s based on them:

List<MyClass1> MyList1 ...
List<MyClass2> MyList2 ...

I then used the Data Source Wizard to add both of them as Data Sources in my
project and created 2 DataGridViews based on those sources (drag and drop
from Data Sources window), which automatically creates 2 BindingSources too.
The *DESIGNER* initiliazes these with code similar to this:

MyClass1_BindingSource.DataSource = typeof(MyClass1)
MyClass2_BindingSource.DataSource = typeof(MyClass2)

Of course I only used the wizard as a convenience (creating those columns is
a PITA), but I want it to use MY lists, not some internal List object, so I
assign:

MyClass1_BindingSource.DataSource = MyList1
MyClass2_BindingSource.DataSource = MyList2

Which seems to work fine.

*NOW*, my problem is I want these browses to work as Parent-Child. On the
MyClass1_BindingSource.CurrentItemChanged event, I am checking the selected
item (contains what I expect it contain), then I set
MyClass2_BindingSource.Filter accordingly.

I've verified the filter is changing, to the value I want, but the second
Grid is NOT reflecting any change in the filter.

Whew...so what mistake am I making? I tried using BindingList<T> instead of
List<T> but no difference. I've read Chris Sells' book, nothing there that
helped me.

Many thanks!
 
Hi,

"Chris Bordeman"
Pardon the fairly noobish question here...

I've created two classes that expose some properties. I've declared two
List<T>s based on them:

List<MyClass1> MyList1 ...
List<MyClass2> MyList2 ...

I then used the Data Source Wizard to add both of them as Data Sources in
my project and created 2 DataGridViews based on those sources (drag and
drop from Data Sources window), which automatically creates 2
BindingSources too. The *DESIGNER* initiliazes these with code similar to
this:

MyClass1_BindingSource.DataSource = typeof(MyClass1)
MyClass2_BindingSource.DataSource = typeof(MyClass2)

If you want them to behave parent-child like, then it should be:
MyClass1_BindingSource.DataSource = typeof(MyClass1);

MyClass2_BindingSource.DataSource = MyClass1_BindingSource;
MyClass2_BindingSource.DataMember = "PropertyThatReturnsList";
Of course I only used the wizard as a convenience (creating those columns
is a PITA), but I want it to use MY lists, not some internal List object,
so I assign:

MyClass1_BindingSource.DataSource = MyList1
MyClass2_BindingSource.DataSource = MyList2

It's good practice to set the DataSource explicit like that, but you must
not do it for MyClass2_BindingSource anymore.
Which seems to work fine.

*NOW*, my problem is I want these browses to work as Parent-Child. On the
MyClass1_BindingSource.CurrentItemChanged event, I am checking the
selected item (contains what I expect it contain), then I set
MyClass2_BindingSource.Filter accordingly.

I've verified the filter is changing, to the value I want, but the second
Grid is NOT reflecting any change in the filter.

Whew...so what mistake am I making? I tried using BindingList<T> instead
of List<T> but no difference. I've read Chris Sells' book, nothing there
that helped me.

Neither BindingSource nor BindingList<T> implements filtering,
BindingList<T> can be extended (derived from) to provide your own
implementation. But as explained you don't need it for parent-child at
least not if your class1 has a property that returns a class2 list.

BindingList<T> does implement list change and support for item change
notifications which List doesn't.

HTH,
Greetings
 
Thanks, Bart. Could you give me a small start on that derived class to
implement filtering?
 
Hi,

"Chris Bordeman"
Thanks, Bart. Could you give me a small start on that derived class to
implement filtering?

I should probely have said this is quit complex. Implementing a full-blown
filter-able BindingList<T> isn't that easy, because it would require an
Expression Evaluator. It can be a bit more easy using a Preditcate<T> to do
the filtering (see example at the bottom).

It would be better to have some sort of "object view" which implements
filtering, sorting and wraps your data list. Unfortunately i haven't found
a lot of implementations, one can be found here:
http://sourceforge.net/projects/blw/ (untested)

Some commercial ORM (object-relational mappers) seem to include an object
view.

Very basic filter-able BindingList:

FilterBindingList<Person> personList = new FilterBindingList<Person>();
personList.Add(new Person("aaa"));
personList.Add(new Person("bbb"));

// only show the persons whose name starts with "a",
// using Predicate and unnamed method
personList.Filter(delegate(Person p)
{ return p.Name.StartsWith("a"); } );

// clear filter
personList.Fitler(null);

class FilterBindingList<T> : BindingList<T>
{
private List<T> allItems = new List<T>();
private Predicate<T> predicate;

public void Filter(Predicate<T> Predicate)
{
predicate = Predicate;
Filter();
}

private void Filter()
{
base.Items.Clear();
foreach( T obj in allItems )
{
if ( (predicate==null) || predicate(obj))
base.Items.Add(obj);
}
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}

protected override void InsertItem(int index, T item)
{
allItems.Add(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
allItems.Remove(Items[index]);
base.RemoveItem(index);
}
protected override void ClearItems()
{
allItems.Clear();
base.ClearItems();
}
protected override void SetItem(int index, T item)
{
allItems[allItems.IndexOf(Items[index])] = item;
base.SetItem(index, item);
}
}


HTH,
Greetings
 
Thanks very much for your help, Bart.

Bart Mermuys said:
Hi,

"Chris Bordeman"
Thanks, Bart. Could you give me a small start on that derived class to
implement filtering?

I should probely have said this is quit complex. Implementing a
full-blown filter-able BindingList<T> isn't that easy, because it would
require an Expression Evaluator. It can be a bit more easy using a
Preditcate<T> to do the filtering (see example at the bottom).

It would be better to have some sort of "object view" which implements
filtering, sorting and wraps your data list. Unfortunately i haven't
found a lot of implementations, one can be found here:
http://sourceforge.net/projects/blw/ (untested)

Some commercial ORM (object-relational mappers) seem to include an object
view.

Very basic filter-able BindingList:

FilterBindingList<Person> personList = new FilterBindingList<Person>();
personList.Add(new Person("aaa"));
personList.Add(new Person("bbb"));

// only show the persons whose name starts with "a",
// using Predicate and unnamed method
personList.Filter(delegate(Person p)
{ return p.Name.StartsWith("a"); } );

// clear filter
personList.Fitler(null);

class FilterBindingList<T> : BindingList<T>
{
private List<T> allItems = new List<T>();
private Predicate<T> predicate;

public void Filter(Predicate<T> Predicate)
{
predicate = Predicate;
Filter();
}

private void Filter()
{
base.Items.Clear();
foreach( T obj in allItems )
{
if ( (predicate==null) || predicate(obj))
base.Items.Add(obj);
}
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}

protected override void InsertItem(int index, T item)
{
allItems.Add(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
allItems.Remove(Items[index]);
base.RemoveItem(index);
}
protected override void ClearItems()
{
allItems.Clear();
base.ClearItems();
}
protected override void SetItem(int index, T item)
{
allItems[allItems.IndexOf(Items[index])] = item;
base.SetItem(index, item);
}
}


HTH,
Greetings
 
Hate to bother you again, Bart, but regarding your:
If you want them to behave parent-child like, then it should be:
MyClass1_BindingSource.DataSource = typeof(MyClass1);

MyClass2_BindingSource.DataSource = MyClass1_BindingSource;
MyClass2_BindingSource.DataMember = "PropertyThatReturnsList";

How does the property know which row is 'current' in order to return the
correct subitems?
 
Hi,

"Chris Bordeman"
Hate to bother you again, Bart, but regarding your:


How does the property know which row is 'current' in order to return the
correct subitems?

The above code only works for a true object hierarchy, eg:

public class Customer
{
private BindingList<Order> orders = new BindingList<Order>();

public BindingList<Order> Orders
{
get { return orders; }
}
}

public class Order
{
....
}

So a property of Customer returns a list of child Orders. I assume that's
not what you have. But more relational data, objects from different Lists
are linked by a fk and pk property ?

If you look at DataTable's/ DataSets they are relational too, but the
DataView makes them hierarchical. Foreach child relation a column(property)
is added which returns the child rows. You could do something similar but
it's not that easy.

Greetings
 
The above code only works for a true object hierarchy, eg:
public class Customer
{
private BindingList<Order> orders = new BindingList<Order>();

public BindingList<Order> Orders
{
get { return orders; }
}
}

I seeee now. I have the pk and fk fields, so I loaded my list like so:
public class CustomerRecord
{
....
public List<OrderRecord> OrderList
{
get
{
List<OrderRecord> orderslist_subset = new List<OrderRecord>();
foreach (OrderRecord ord in OrdersList)
{
if (ord.CustomerID == this.CustomerID)
{
orderslist_subset.Add(ord);
}
}
return subset;
}
}
}
public class OrderRecord { ... }
List<OrderRecord> OrdersList = new List<OrderRecord>();


I inited my objects like so:

// typeof(CustomerRecord) was not necessary
CustomerRecordBindingSource.DataSource = CustomerRecords;
OrderRecordBindingSource.DataSource = CustomerRecordBindingSource;
OrderRecordBindingSource.DataMember = "OrderList";

All seems to work ok. This has been quite a good learning experience!

Thanks again, Bart.
 
Back
Top