|
Notes on Data Binding in .Net Winforms
WinForm application development is a lot
easier than the days of doing Windows Desktop application development in
C++ and MFC. Yes there were other ways of buidling Windows Applications,
such as VB etc but while they provided a RAD approach they were quite crappy
with regards to OO design and just sheer capability. .Net and Winform
application development provides most (not all) of the flexibility you had in
the C++ MFC world. But this does not mean it is without its own quirks.
This article is a bunch of notes I made while
working with data binding and BindingNavgators. I will update it as I
learn more or have more to say.
One nice functionality is the
BindingNavigator. It provides a toolbar required for navigating through
data and provides functionality to bind all controls on your form to the same
data source as the navigator. As a result you can do the next, previous,
delete, add new, edit etc with great ease. Not terribly difficult to do
in MFC but unlike in MFC I no longer have to create a toolbar and handle all
navigation events etc. So all in all this is a very convenient feature.
However, aha! you must have known there is a
catch. The data binding and BindingNavigator functionality has its own
quirks. In this article I intend to talk about some of these and propose
ways to deal with them.
Data binding with primitives
I find this to be one of my biggest problems
with how the data binding has been implemented. You can bind to an object
and then display data from a property of that object. So if you had a
DataGridView or a ListView in which you wish to display string contained in a
String[], StringCollection, or List<string>, well you cannot.
Because strings do not have a property that return the string itself.
Actually you can bind a string to a control. It will display the length
of the string as Length is the only property on the String object.
What this means is that you have to
encapsulate your primitives (in this case string) into another class, and give
that class a property to return the string value. This can be a pain
if what you are receiving is a large string collection of some sort. Data
received by an application is not always controlled by the developer of the
application.
So if you had an XML like:
<DATA> <ITEM>One</ITEM> <ITEM>Two</ITEM> <ITEM>Three</ITEM> </DATA>
To read the above if you created a class Data with a List<string> with the
items in it you could get into trouble. The correct way would be to
create a class Data with a List<Item> and class Item with a property
callled Value.
Wrong Way
class Data {
List<string> _items;
}
Right Way
class Data {
List<Item> _items;
}
class Item {
private string _itemValue;
public string Value {
get....
set....
}
}
So if I say that creating the second class
Item is the right way then why am I complaining? Its not so much of a
complaint as much as it is a warning. As mentioned earlier, sometimes we
receive data and cannot dictate what we get, in which case you may have to loop
through the string collection and encapsulate each string. And sometimes
we get lazy and we do not create the class to encapsulate even when we have
control over the code. Also xsd.exe (a tool that will create classes for
you based on an XSD) will generate string[] rather than the final level of
encapsulations. So this is something you should keep in mind.
Always initialize data in classes that are
bound
Another strange quirk I found is how the data
binding code behaves when you have un-instantiated class data. When you
bind a control in a form to a BindingSource you do so by binding a property of
the source to a property of a control.
For example:
//This will bind a text box called textBoxUsername by binding its Text property to the UserName
//property of the binding source.
textBoxUsername.DataBindings.Add( new Binding( "Text", myBindingSource, "UserName" ) );
//Here I bind a ComboBox SelectedValue property to my Binding Source categoryName property
comboBoxCategory.DataBindings.Add( new Binding( "SelectedValue", myBindingSource, "CategoryName" ) );
This BindingSource myBinding source is
assigned to data using its DataSource property.
class Data {
private List<SubData> _subData; //THIS IS BAD
// private List<SubData> _subData = new List<SubData>(); //THIS IS RIGHT
public List<SubData> SubData {
get...
set...
}
}
class SubData {
private string _dataValue;
public string DataValue {
get....
set....
}
}
//Now in some Form you may have code like
myBindingSource.DataSource = _someLocalData.Data;
Always add one item to a collection
or Disable your UI
If you have a list add one blank item to
it. This way when you display your form you are on the first item.
If you do not do so you will be on a non existant item and the user will have
to click + on the navigator to add a new item. Many users start typing in
data without clicking the + on the navigator. By adding a blank item you
are providing your user with a better experience.
Alternatively, disable all controls except for
the navigator on your UI. Thereby forcing the user to click on the add
new item (+) before typing in any data. So check your datasource and set
controls to Enabled = false if datasource count is zero.
|