.NET Framework: November 2008 Archives

I once heard Chris Sells (in a podcast with Scott Hanselman) mention that the .NET Framework is so vast, that he was constantly uncovering objects/methods that he had already written himself.  Here's a few things that I discovered during the past year, thanks to my co-workers (Andy, Jon, Geoff).  Some of these items are so simple that I was embarrassed not to know about them.  Here is the list:

1.  The simple string.IsNullOrEmpty method. 

 if (string.IsNullOrEmpty(imageFileName))
   return false;

Before I encountered this, I was always writing C++ style tests like "if (string != null) && (if string.Length > 0)".  Doing both in one shot is a great piece of shorthand.

2.  The powerful Path object. 

Path.GetDirectoryName(filePathName);

One of the first things I did in C# was to port over a number of C++ methods that dissected a path string, chopping off the extension, returning the folder path, etc.  A nice exercise, but it was totally unnecessary.  The Path object has the following methods: GetDirectoryName, GetExtension, GetFileName, GetFileNameWithoutExtension.

3.  The beautiful BackgroundWorker object.

public class PSPUnpackWorker : BackgroundWorker 
{
    public PSPUnpackWorker()
    {
        WorkerReportsProgress = true;
        WorkerSupportsCancellation = true;
        DoWork += PSPUnpackWorker_DoWork;
    }

Have you ever wanted to perform a task in a background thread?  You can easily spawn off a worker thread on your own by creating a Thread object and calling Start.  But what if you wanted your worker thread to raise an event, to tell you it's completed step X out of 100?  And perhaps hook up this data to a progress bar control?

Don't do it the hard way, the .NET Framework is all setup to accommodate this pattern already with the BackgroundWorker object.

The ComicShowControllerDemo in the Crystal Toolkit has an example of a BackgroundWorker communicating to a ProgressBar control.  First, as shown above, I derive my own specialized class from the BackgroundWorker.  In the constructor, I set the properties in base class to true, WorkerReportsProgress and WorkerSupportsCancellation.  The former property allows me to call the ReportProgress method.  The control containing the ProgressBar can subscribe to the ProgressChanged event of the background worker and determine the percentage of completion.

PSPUnpackWorker _unpackWorker = new PSPUnpackWorker();
_unpackWorker.ProgressChanged += unpackWorker_ProgressChanged;
_unpackWorker.RunWorkerCompleted += unpackWorker_RunWorkerCompleted;
_unpackWorker.RunWorkerAsync();

When RunWorkerAsync is called, the DoWork method inside PSPUnpackWorker is executed:

void PSPUnpackWorker_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (CrystalImageItem imageItem in Collector.GridModel)
    {
        if (CancellationPending)
        {
            e.Cancel = true;
            return;
        }        
        ....
        if (CanRaiseEvents)
        {
            float percent = ((float)++index/(float)Collector.GridModel.Count)*100f;
            int percentDone = Convert.ToInt32(percent);
            ReportProgress(percentDone);
        }
    }
    e.Result = true;
}

Inside the DoWork method, you can perform whatever tasks are required in the background worker.  I've sandwiched these between checking to see if a cancel action was requested at the beginning, and reporting the progress at the end.

4.  The mighty MethodInvoker object.

_imageForm.Invoke(new MethodInvoker(delegate
 {
     _viewerMain.SetImageInfo(imageInfo);
     _viewerMain.ToolTipText = imageItem.ToolTipText;

SetScale();
}));


In Windows Forms programming, you often encounter the problem of needing to update the controls on the main form, running in the main UI thread.  If you try to update these controls without using Invoke or BeginInvoke, you will no doubt encounter an error such as "Cross-thread operation not valid: Control X accessed from a thread other than the thread it was created on."

You could take the main Windows Form, test InvokeRequired, and then call Invoke or BeginInvoke.  You would have to create a delegate and pass this as a parameter.  The delegate would take care of calling the controls on the main form.

There's an easier way: use an Anonymous method, coupled with the MethodInvoker (as shown above).  Using this technique, it's like creating an inline delegate.  The code inside the delegate has access to the outerscope variables, such as imageInfo.  It's another great piece of shorthand.

When the code inside the delegate portion becomes too large, I will place them inside a new method, and call that method within the MethodInvoker:

ImageGridView.BeginInvoke(
    new MethodInvoker(delegate
    {
        InitInitialImageImp(index, ignoreGroupHeader);
    }));

You can find some other interesting tips about MethodInvoker and using invoke with UI controls on TimL's .NET Blog.

Here's a short but useful static funtion: converting a uint into a properly formatted hexadecimal string value in C#.

        public static string ConvertToHexString(uint value)
        {
            StringBuilder builder = new StringBuilder("0x");
            builder.Append(Convert.ToString(value, 16).PadLeft(8, '0'));
            return builder.ToString();
        }

This is pretty self-explanator, but the ToString method on Convert takes an optional second parameter for the base, which we sit to base 16 for the hexadecimal string value.  If you run this test code:

string test = ConvertToHexString(177);

The hex string value for 177 returned is: 0x000000b1.  The PadLeft method after the ToString call gives us the leading zeroes.  Without PadLeft, all we would see is 0xb1.