Early experiences from .NET 2.0 Winforms
One of the reasons I wrote
this little application is to get to know the .NET 2.0 Winforms API a little better.
Here are some of the things I have on my mind after playing with it for a while:
Data Binding
The Data Binding Story in .NET 2.0 winforms is absolutely cool. data binding to custom objects is so much easier and less clunky than it used to be in 1.1, there really is no way to compare the two. I was able to create a custom class and bind it to a grid, pictures and all, in less than a minute, and setting the data source is one line of code.
Configuration
- The configuration story in .NET 2.0 is, again, much much better. The project contains a Settings object which contains a strongly typed property list of the values you put in in the project properties page. Those settings are easily accessible, and, at last, can actually be changed and persisted at runtime. Settings have two modes: user and application mode. Application mode is still read only, but the user mode is writable at runtime. Setting a value as a user mode setting is simply a matter of choosing from a combo.
- In a VB.NET project you get some really nice out of the box usability bonuses in the project settings dialogs. Some examples would be a check box for "Save configuration settings on exit" and another one for "Keep only one instance of the application running". These are both possible to do manually, but this saves you from writing that repetitive code. Lots of other small nuggets in there...
- In a VB.NET project you get some really nice out of the box usability bonuses in the project settings dialogs. Some examples would be a check box for "Save configuration settings on exit" and another one for "Keep only one instance of the application running". These are both possible to do manually, but this saves you from writing that repetitive code. Lots of other small nuggets in there...
Data binding to configuration settings
This totally deserves a section on its own. It's really simple and intuitive to bind practically any simple property of a form or other component to a configuration setting. It's done through the properties window and allows seamless customizability of the application by the user. For example, in my little app, I created a setting called "LastSearch" in the project settings dialog, and then simply set a binding on a text box on the main form to that setting. Couple that with a "Save settings on exit" check box, and you've got zero lines of code history (a simple one, but still!).
You can use it to save Form locations, size and anything else you might think of.
Writing code in VB.NET - Refactor is not enough!
I still feel that using VS.NET 2003 with
Resharper installed is much much more productive that using VS 2005 in either C# or VB.NET with Refactor! installed (Freely downloadable). C# does not have better code snippets than VB.NET, but some of them, like creating properties are better and nicer than VB (which, IIRC, does not have a snippet for properties??)
The main problem in the IDE is Navigation. It's hard to find a fine in a large project without using the mouse or hitting the find in files dialog. It's hard to find a method in a large file, it's hard to find all the references to a method or a class in a solution (although there is such a menu) and frankly, after using Resharper for a log time, everything is much harder and tedious to do than it used to be.
Yes, i have a "rename" refactoring, but it's really hard to trigger. In fact, all the refactorings are really hard to trigger because the shortcuts to them suck(Shift+Alt+F10? C'Mon!)
The Class Designer - a work in progress
I like the
class designer. It's a nice visual way of adding stuff or changing stuff in classes. Sort of like a visual refactoring tool, though a very limited one. One key thing its missing (in both languages) is some common sense. If I'm adding a property "Name" of type string, it should be able to figure out that I also want a private member "mName" of type string and to return and set that variable in the property get and set methods. In the 0.2% of the time I *wouldn't* want this behavior I could just delete it but right now, it's the most annoying thing in the world: visually creating a property, visually creating a private member and then
manually going to the code and connecting the two together. Here's a tip - If I need to switch two views to create a property - something's missing.
The BackgroundWorker Component - a work in progress
(See update on this at the end of this post)
The
BackgroundWorker component is one of those things where everyone says "Why didn't I think of that?", and it's been around for a while now,
even as a 1.1 version courtesy of Juval Lowy. It's nice and I've used it, but it's still clunky to use in some obvious well lit corners. For example - it's really easy to display progress to the client by calling the worker.ReportProgress(percentage, someDataObject) method. However, if I wanted to send an
end result of my long processing, there isn't any easy obvious way to do it. Basically, by calling the "ReportProgress" with a percentage of 100, the "WorkEnded" event is triggered at the GUI thread, but the "UserData" object that is send when triggering the event is lost, which is a shame. The event arguments actually contain an object called "result" but I couldn't find any way of setting it from the triggering code. In short - it's nice, but I don't want to have a global variables to send end results to my GUI code, thanks.
Menus
Finally! we get a "Set image" when right clicking on a menu item! How hard was that?
I'll add more as it comes to me.
Update on BackgroundWorker
Here's an update on the query I had regarding the backgroundworker component in winforms, directly from
Keith Yedlin, the Product Unit Manager for Winforms:
"Essentially in the DoWork method, you can assign a result set (as a BindingList or other object) to e.result as follows:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string fullpath = e.Argument as string;
int idx = fullpath.LastIndexOf('\\');
BindingList<FileSystemObject> bl = new BindingList<FileSystemObject>();
AddFiles(fullpath.Substring(0, idx),
fullpath.Substring(idx + 1), bl);
e.Result = bl;
}
When the DoWork method exits, the RunWorkCompleted event is thrown which in turn will execute the RunWorkerCompleted method defined. One can take the result set returned in the EventArgs e, and bind as follows:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Text = "Formatting Data....";
//Bind
bl = e.Result as BindingList<FileSystemObject>;
this.dataGridView1.DataSource = e.Result;
this.Text = "Data Loaded!!";
this.listCount.Text = "Rows Loaded...." + (e.Result as IList).Count.ToString();
//Change Cursor
this.Cursor = Cursors.Default;
}
So you can see there is an easy way to utilize the results of the long running background worker. "