Note: I posted this same question at the following location, but have also posted here because it references something from Rocky's book that doesn't seem to be working correctly for us:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3214089&SiteID=1&mode=1
I've been running into a problem with data binding on a .NET Windows Forms application. I've found a couple work-arounds, but neither one really feels right to me -- it just seems like there should be a better way. So I guess I'm asking what is the best practice approach to solving the scenario I'll describe below?
First off, I have a business object, Customer, with several customer-related properties and an additional property to return a BindingList of another type of business object, we'll just call that property "MySubList".
Next, I have a UserControl, call it CustomerCtrl, that will display some of the various customer-related properties in Label controls and will display the BindingList property, "MySubList", in a DataGridView. I plan to accomplish this via data binding using a BindingSource intermediary. When I created the UserControl in the VS designer, I placed the Labels, DataGridView, and CustomerBindingSource on the control, so those controls are declared in the designer-generated code.
I intend to use the CustomerCtrl on the MainForm for the application. It will always be visible. There will be menu options to create a new customer, delete the current customer (displayed in the CustomerCtrl), and open a different customer (to replace the currently display customer in the CustomerCtrl). From the MainForm or application logic, I need to be able to tell the CustomerCtrl to now use a different Customer business object for its data binding. It is at that point where I run into problems.
The basics of what needs to happen for data binding the CustomerCtrl to a new Customer is something like the following:
private Customer _Customer = null;
public CustomerCtrl()
{
InitializeComponent();
}
public void SetCustomer(Customer customer)
{
_Customer = customer;
CustomerBindingSource.DataSource = _Customer;
MySubListGrid.DataSource = CustomerBindingSource;
MySubListGrid.DataMember = "MySubList";
}
public void SetupDataBindings()
{
// Setup the data bindings to the controls (most omitted for brevity)
CustomerNameLbl.DataBindings.Add("Text", .....);
}
There are problems with that approach. If you try to pass in a null customer, you get an exception, I think due to the DataMember not being found. And even if you didn't get that, you can't set the data bindings on the controls multiple times.
You could somehow only call the SetupDataBindings() method once initially (or as necessary). That would prevent the control data bindings from being setup multiple times. But what about passing in a null customer? This will happen, for instance, when the delete customer option is selected. As part of our control data binding setup, we can establish a null string to be displayed, such as "No Customer", which is desirable.
I have seen a few pieces of advice / techniques that we have tried -- some that have worked, and some that haven't. In Rockford Lhotka's "Expert C# 2005 Business Objects" (second edition), page 492, he says that you rebind the BindingSource object to the new business object by adding these lines of code immediately after your operation that results in a new business object:
this.rolesBindingSource.DataSource = null;
this.rolesBindingSource.DataSource = _roles;
He continues to say that you can't simply set the DataSource property to a new object. You must first set the property to null, and then to the new object. If you don't do this, the BindingSource will not bind to the new object and will silently remain bound to the old object, resulting in hard-to-debug problems in your application.
I tried this approach and it is essentially the same as when you pass in a null customer. The point at which the CustomerBindingSource.DataSource is set to null, an exception is thrown, complaining about the DataMember not being found. Is it necessary to somehow clear that MySubListGrid.DataMember prior to resetting the CustomerBindingSource.DataSource?
Also, I have tried removing all of the control data bindings prior to resetting the CustomerBindingSource and then setting them up again. That seems ridiculous that one should have to go to all of that work. It seems like there should be a much more elegant way of handling this, and I just don't know what it is.
I have also tried protecting against allowing a null customer. For instance, if a null customer is passed into the SetCustomer() method, then I create a new Customer and set that to the CustomerBindingSource.DataSource. Unfortunately, then I lose the control data binding null string text, such as the "No Customer". Also, we really don't want a new Customer in that situation.
Any help / advice would be very much appreciated.
Thanks for your reply. Sorry it took me so long to get back here... And sorry about the lengthy post. I guess I didn't want to put too little information, but probably did turn a few people away with so much to read. :) I'm going to be gone this weekend, but I'll give your suggestions a try next week and let you know how it goes.
In your second code listing, where you assign
_Customer = customer;
what happens if customer is null? It seems like you're saying that by adding in an additional intermediary BindingSource between the CustomerBindingSource and the MySubListGrid, that is what makes it possible to 'use' a null customer. Am I interpreting that correctly? If so, could you elaborate on why that is the case? I must be missing a key point...
Also, if you had an additional control on the form, like a TextBox bound to a property on the Customer object, such as FirstName, what would be the result of customer being null? I think that should be fine, and you can even set the text to be displayed in the control if the bound object is null when you establish the binding relationship.
Thanks,
Kyle
Copyright (c) Marimer LLC