Listing 84 Bind FilterFields Helper Method

private void BindFilterFields() {

if (mjnitializing) return;

List<string> cols = new List<string>(); // Check if you have something to work with if (m_BindingSource.List.Count > 0) {

object dataltem = m_BindingSource.List[Oj; // Get the property descriptors for the object type // currently contained in the collection PropertyDescriptorCollection props =

Type Descriptor.GetProperties(data Item);

foreach (PropertyDescriptor prop in props) {

// Exclude any properties that are references to // child collections if (prop.Property Type.Getlnterface("IList") == null) {


// Populate the combo box with the column/property names m_FieldsCombo. DataSource = cols;

The first thing this helper method does is to check your mjnitializing flag to see if this method is being called while you are in the initialization stage, based on your implementation of the ISupportlnitialize interface. If so, the method just returns without doing anything, because it will assume that the data source itself might not be done initializing yet.

Next, a list of strings is created using the List<T> generic type to hold the field names as they are discovered. The binding source exposes a List property that gives you direct access to whatever list of objects it contains as a data source. You could just go through the DataSource property of the binding source, but then you would have to manually resolve the data member within that data source if there was one. Using the List property gives you direct access to the resolved collection of data, which returns arlList reference.

As long as the list contains some items, the method then accesses the first item in the collection. Binding sources only support lists containing a homogeneous collection (objects of a single type), so accessing the first item should give you all the information you need to know. The method then obtains the collection of property descriptors for the properties on the first object in the list and loops through

The code checks each property descriptor to see if the property it represents implements the I List interface itself by using the Property Type property on the property descriptor, which returns aType instance describing the actual type of the property. The code calls the Getlnterface method on the Type instance to determine if the property represents a child collection. If so, it doesn't add the property to the list. This protects against two scenarios.

® If you have a data source that is a data set with data tables that have relations between them, and the binding source is bound to one of the tables with the data relation, the relation will show up as a property on the list through the property descriptors. However, it doesn't make sense to show the relation in the filter fields list, because the data relation doesn't represent a single-valued property that can be filtered through the text box entry.

® If the list contains business objects, those objects can have properties that are collections of data as well, and those properties wouldn't be displayed by the grid, so they shouldn't be included in the combo box.

So, as long as the property isn't a list property, the property's name is added to the string collection and the loop continues on through the rest of the data item's properties. When the method ends, it sets the string collection as the data source on the m_FieldsCombo, which will automatically display those strings as the items in the combo box.

Handling the ListChanged Event

You need to call this helper method from somewhere appropriate, which is whenever you know that the underlying data collection has changed. You can be notified of this by the ListChanged event raised by the binding source. To continue in the vein of letting the designer do as much coding as possible for you, take the following steps.

1. Open the FilteredGrid control in the designer, and select them_BindingSource in the components tray.

2. Go to the Events view of the Properties window and typeOnListChanged into the text box next to theListChanged event.

3. When you press the Enter key, the designer declares the event handler for you with the appropriate signature in your user control code file and subscribes the handler to the event in the designer-generated code partial class file behind the scenes.

4. Add the following code to theOnListChanged method:

private void OnListChanged(object sender, ListChangedEventArgs e)

if (e.ListChangedType == ListChangedType.Reset || e.ListChangedType == ListChangedType.PropertyDescriptorAdded

e.ListChangedType ==

ListChangedType. Property DescriptorDeleted) {

if (!m_FilterlnProgress) // Don't reinit when filtering {

// Fill the combo box with the column names BindFilterFields();

// Populate the autotext string collection with the // contents of the filter column values BuildAutoCompleteStrings();

m_FilterlnProgress = false;

The ListChangedType property of the event arguments passed to this event let you filter those changes that affect the schema of the contained objects. A reset means the entire source has changed, and thePropertyDescriptorAdded and PropertyDescriptorDeleted change types let you handle the case if the data source is being programmatically modified at runtime. The guard condition checking the m_FilterlnProgress flag keeps the combo box from resetting when the list has reset due to a filter being applied. This lets the combo box keep the current selection, and the autocomplete string list (discussed next) will remain as it was before the filter was applied.

If you run the Windows application that contains the filtered grid at this point, you should see that the combo box gets populated with the data source's field namesjust what you were looking for.


0 0

Post a comment