Jesse Johnston
.NET Dev. Good times with .NET and coffee

DataView for Objects: Final Cut

Sunday, 19 November 2006 21:25 by jesse

 

Note: You can download the complete implementation here.

In my last post I described the implementation of IBindingListView interface in ObjectListView, my implementation of a view for collections of arbitrary objects.  This was the last interface needed to support binding to controls such as the DataGridView.

In this post, I'll show some sample code that demonstrates the use of ObjectListView.

The Demo

I'm going to display data from the Microsoft sample Northwind database in a ComboBox and a DataGridView, showing how to filter and sort with ObjectListView.  Here's the main window:

 

After installing the Northwind database (available here), alter the configuration file Demo.exe.config to reflect your computer name (in place of "DADBOX").  Alternatively, you can modify the connection string in the text box of the demo program main window.

Run demo.exe, set the connection string if needed, and press the Get Data button.  This causes two lists to be populated from the database.  For the demo, I've created two classes, Customer and Order, which do nothing but hold the data from a row in the corresponding database table.  The two lists I'm populating are a List<Customer> and List<Order>.  Then, I create two instances of ObjectListView, binding one to the customers list, and one to the orders list:

private void buttonGetData_Click(object sender, EventArgs e)
{
    Database db = new Database();
    db.ConnectionString = this.textBoxConnectionString.Text;
 
    viewCompanies = new ObjectListView(db.GetCustomers());
    viewOrders = new ObjectListView(db.GetOrders());
 
    this.comboBoxCustomers.DataSource = viewCompanies;
    this.comboBoxCustomers.DisplayMember = "Company";
    this.comboBoxCustomers.ValueMember = "Id";
 
    this.dataGridView.AutoGenerateColumns = false;
    this.dataGridView.DataSource = viewOrders;
 
    this.textBoxFilter.Text = "";
    this.textBoxFilter.Enabled = true;
}

Finally, I set the data source of the company ComboBox to the customers ObjectListView, specifying that the Company property of each Customer will be displayed, and that the Id property will be returned from ComboBox.SelectedValue.  The data source of the grid is set to the orders ObjectListView.  Each grid column is bound through it's DataPropertyName property to a different property of Order.  The column binding code is in designer-generated code.

Now the ComboBox contains a list of all of the customers.  When we select a customer, the orders ObjectListView is filtered to present only the orders for the selected customer:

private void comboBoxCustomers_SelectedValueChanged(object sender, EventArgs e)
{
    if (this.comboBoxCustomers.SelectedValue == null)
        this.viewOrders.Filter = "CustomerId=null";
    else
        this.viewOrders.Filter = "CustomerId='" + this.comboBoxCustomers.SelectedValue.ToString() + "'";
}

Note the usage of CustomerId=null above, to indicate that the CustomerId property of Order should be compared to null when no customer is selected.

Once the grid is populated with orders, you can click on the column headers to sort the orders.  This is done behind the scenes by DataGridView, which delegates to the IBindingList.ApplySort implementation of ObjectListView.

You can also filter the companies displayed in the ComboBox by entering text in the filter TextBox.  As the text is changed, the customers ObjectListView filter is updated:

private void textBoxFilter_TextChanged(object sender, EventArgs e)
{
    this.viewCompanies.Filter = "Company=" + this.textBoxFilter.Text + "*";
}

That's all there is to it!  We've bound lists of arbitrary objects to both a DataGridView and a ComboBox, sorting and filtering our views of the lists without changing the lists themselves, in just a few lines of code.

What's ahead

There's a few things I know want to add to ObjectListView, and I'd like your feedback on other enhancements that you'd like.  Please email me and let me know how you're using ObjectListView and what you think should be added or changed.  In the meantime, I plan on adding:

  • More complex filter expressions (!=, <, >, LIKE, NOT, etc).
  • Support for code-based filter predicates (provide a callback method rather than a filter string expression).
  • BeginUpdate() and EndUpdate() methods to suppress ListChanged events while doing a large number of list insertions/deletions.
  • User-provided comparer for custom sorting.

I've enjoyed building ObjectListView - I hope you find it useful!

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Comments

November 22. 2006 15:41

pat

Thanks for your fine work. We were binding DataGridViews to binding sources but didn't have much success. Heck, it's complicated.

One small suggestion: Stable Sorts. I suppose the easiest way to implement it is to make the pre-sort ordinal position of each row the second sort key.

pat

November 22. 2006 15:56

Jesse

Good point!  We don't want rows that compare equally before and after the sort to be reordered.  Thanks.

Jesse

November 29. 2006 02:36

Ric'

Hi,

Thanks you for your amazing piece of code.
Quick question, what is the licence of you code?

Thanks and regards!

Ric'

November 29. 2006 10:04

Jesse

Hi Ric,

You're free to use the code in your applications at no charge and with no attribution required.  I hope it helps!  Let me know if you find any problems or enhancements that would make ObjectListView better.

Cheers,
Jesse

Jesse

December 2. 2006 07:47

'Ric

Hi,

Thanks a lot for your answer and your work.

I've an additional question concerning your implementation: How to bind a textbox to a property of the currently selected object inside the view?

Thanks, regards and have a nice WE!

Ric

'Ric

December 3. 2006 22:04

Jesse

Hi Ric,

I just posted a new version of ObjectListView that includes a Master/Details demo.  This code illustrates how to set up the bindings for a textbox to bind to the current item in the view.  In short, it looks something like this:

this.textBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bindingSource, "Region", true));

In this case I used a BindingSource, which had it's DataSource property set to the ObjectListView, but you could just as easily put the ObjectListView itself in the textbox binding.  "Text" is the property of the textbox we're binding to.  "Region" is the property of the list item that is being bound.

Hope that helps!

Cheers,
Jesse

Jesse

Comments are closed