Recently in .NET Framework Category

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.

I thought it might be interesting to document the recent code modifications to CrystalGradientControl to allow sub-classes (CrystalLabel and CrystalPictureBox) to have transparent backgrounds.

As most Windows Forms programmers know, setting the BackColor to Transparent is the way to allow transparent backgrounds in most controls. I wanted to support the same convention. However, setting the BackColor to Transparent result in a nasty message ("Control does not support transparent background colors"). To fix this, I had to call SetStyles to declare that my color does support transparency:

public CrystalGradientControl()
{
  SetStyle(ControlStyles.SupportsTransparentBackColor, 
           true);
}

Next, I wanted to override the property (from the base class Control), BackColor. Why? Because when BackColor is set to Transparent, I needed to set an internal property called TransparentMode to True. TransparentMode triggers a number of things best described on Bob Powell's article on transparent controls. This was all I needed to do:

public override Color BackColor
{
    get
    {
        return base.BackColor;
    }
    set
    {
        if (base.BackColor != value)
        {
          base.BackColor = value;
          TransparentMode = 
            (base.BackColor == Color.Transparent);
          this.InvalidateEx();
        }
    }
}

Now my controls allowed the transparent color, but the background was still getting painted black. Why? Because in my override of OnPaintBackground, I needed to call the base class when TransparentMode was set to true, to allow the parent Form to repaint the background:

protected override void OnPaintBackground
                    (PaintEventArgs pevent)
{
    if (TransparentMode)
    {
        base.OnPaintBackground(pevent);
        return;
    }

    PaintGradientBackground(pevent.Graphics);
}

There you have it, a few simple fixes. When I first started this toolkit, I didn't know how programmers set the transparent color. I made a mistake in having the TransparentMode property be public and totally ignoring BackColor. Now I've hidden TransparentMode from the Forms designer and allow programmers to do it the traditional way.

One of the things I wanted to achieve in Crystal Toolkit release 74 was the ability to sort my generic List<> of images (CrystalImageItem objects). I needed to be able to sort on many different properties within CrystalImageItem, and also to sort in ascending or descending order. Realizing that I probably needed to implement IComparer and IComparable, I decided that was way too much work, and my XBox Achivements score is really ridiculously low. A Google search on the topic lead me to this CodeProject article by Johannes Hansen on Dynamic List sorting. I found his approach intriguing, because he uses Reflection to key on properties within an object and use them as the basis for a compare operation. The resulting sort times are impressive, though I saw a couple of glitches reported on the CodeProject message board. Luckily, another programmer (Marc Brooks) took up the baton and released a C# library that includes a new, improved DynamicComparer. I highly recommend downloading Marc's library as it contains some nifty C# utilities.

Getting DynamicComparer to work was very easy. First, I had to make sure that the properties within the CrystalImageItem object were public, so that Reflection could find them. No problem there, I had four properties, two strings and two dates within CrystalImageItem:

public class CrystalImageItem
{
    public string ImageName
    ...
    public string ImageFormatString
    ...
    public DateTime LastWriteTime
    ...
    public DateTime CreationTime
    ...

To make it convenient to specify which property I wanted to sort, I created an enumerator called CrystalSortType:

public enum CrystalSortType
{
    ImageName = 0,
    ImageType = 1,
    CreationTime = 2,
    LastWriteTime = 3
}

The CrystalFileCollector is the controller class that manages a CrystalImageGridView and a CrystalImageGridModel. I created a public method called SortCrystalList, which takes a CrystalSortType enumerator, a boolean to indicate ascending or descending sort, and a boolean that tells the controller to redraw the grid (inside CrystalImageGridView) when the sort is completed:

public virtual void SortCrystalList(CrystalSortType sortType, 
  bool ascendingOrder, bool reDrawGrid)

The protected SortCrystalList method translates the enumerator key to a string and calls a final overloaded method:

protected virtual void SortCrystalList(CrystalSortType sortType, 
                                        bool ascendingOrder)
{
    string orderBy = string.Empty;

    switch (sortType)
    {
        case CrystalSortType.ImageName:
            orderBy = "ImageName";
            break;
        case CrystalSortType.ImageType:
            orderBy = "ImageFormatString";
            break;
        case CrystalSortType.CreationTime:
            orderBy = "CreationTime";
            break;
        case CrystalSortType.LastWriteTime:
            orderBy = "LastWriteTime";
            break;
    }

    SortCrystalList(orderBy, ascendingOrder);
}

The last version of SortCrystalList takes the string key, adds either ASC or DESC to the string (for ascending or descending sort) and creates the DynamicComparer. The List<CrystalImageItem> object inside the GridModel is sorted according to this key:

protected virtual void SortCrystalList(string orderBy, 
                                bool ascendingOrder)
{
    if (_gridModel == null)
        return;

    if (ascendingOrder)
        orderBy += " ASC";
    else
        orderBy += " DESC";

    // Create the comparer for Person types and define 
    // the sort fields.
    DynamicComparer<CrystalImageItem> comparer = 
        new DynamicComparer<CrystalImageItem>(orderBy);

    // Sort the list.
    _gridModel.CrystalImageList.Sort(comparer.Compare);
}

By default, CrystalFileCollector sorts the internal list by the ImageName property in CrystalImageItem. When the dynamic comparer object is created, the orderBy key looks like "ImageName ASC". If you read Johannes article, you will see that properties can be combined for more interesting sorts like "FirstName, LastName ASC". This is rather like doing a query on your objects, and so far the performance has been really good. You can naturally see that the Linq Project is going to make this sort of thing obsolete, but kudos to Johannes for coming up with this way back in December 2005.

CrystalImageGridView Demo
The latest Crystal Toolkit version 0.70 has an early alpha version of classes to help you build image viewers using thumbnails. You may have seen the Microsoft Office Picture Viewer, which has a nice filmstrip view of thumbnails in a splitter like view. I wanted to build an image grid view control that has that nice look and feel, but I also wanted to build it in a classic model-view-controller pattern. And I also wanted to have my control use other tools to automatically thumbnail image folders that I need to browse.

Image Grid Vertical
CrystalImageGridView does all of this, working with CrystalImageGridModel, CrystalFileCollector, and the CrystalThumbnailer to provide all of these features. You can see from the screenshots (this demo is included in the zip file on the Downloads page) that you can view the thumbnails docked in the split container on the bottom. Click on the left hand button on the upper toolbar and the split container expands the thumbnail view to fill the client area.

I'll have more bug fixes, little features, and documentation to write later. If you want to play around with this control, do the following:

1. Build Crystal Toolkit and add a reference to your Form project.
2. Drag CrystalImageGridView from the toolbox to your Form, preferrably to a split container.
3. Hook up the Load event to your Form and add this code to the method:

CrystalFileCollector _collector = 

    new CrystalFileCollector();

_collector = new CrystalFileCollector();

_collector.AddView(crystalImageGridView1);

_collector.CollectImages();


It's that easy. Run this simple form you just made, and the image grid starts previewing images from your MyPictures folder. The CrystalFileCollector spins off a background thread and pops the images into the grid as they are thumbnailed. If you don't like that location, you can modify the ImageLocation property.

More later, much more, I hope. If you have any comments, bug fixes, or whatever, contact me at richard {at} attilan [dot] com.

BUG: Invisible CrystalTrackBars
A little over a week ago, a user named MG reported a problem that I couldn't duplicate until now. When he ran the demo program CrystalTrackBarDemo.exe, all the CrystalTrackBar objects were invisible. No trackbar, no ticks, no thumb, only the gradient background remained on the non-transparent objects. After a second user reported this bug on the CodeProject article, I suddenly remembered about my usage of TrackBarRenderer.IsSupported in CrystalTrackBar OnPaint:

protected override void OnPaint(PaintEventArgs e)
{
    if (!TrackBarRenderer.IsSupported)
    {
        this.Parent.Text = 
            "CrystalTrackBar Disabled";
        return;
    }

    DrawTrackBar(e.Graphics);
    DrawTicks(e.Graphics);
    DrawThumb(e.Graphics);
}



On the MSDN Forum, Mick Doherty pointed out that TrackBarRenderer.IsSupported == VisualStylesRenderer.IsSupported. According to the documentation, one of the things that can make the VisualStylesRenderer is the VisualStyleInformation.IsEnabledByUser property. Users can enable visual styles in the Appearance tab of the Display option in Control Panel.

Windows Classic Style
I was able to reenact the bug by setting my Appearance setting for Windows and Buttons to Windows Classic Style (on XP: Control Panel->Display->Appearance tab). As some people are addicted to the old Windows NT look and feel, it makes sense this how they ran into the bug. When I ran my application again, TrackBarRenderer.IsSupported returned false and all I got was the rectangles, no trackbar or ticks.

I'll need to draw the TrackBar and Ticks myself if TrackBarRenderer isn't supported. I'm already drawing the Thumb, so this should be an easy bug to fix. I hope to have it posted in a few days.

CrystalLabel.jpg

One question I often see getting asked on forums is how to make a label transparent on Windows Forms. Since I already had CrystalGradientControl working with the TransparentMode property (setting to true makes the background transparent), I decided to make the CrystalLabel control. As with CrystalTrackBar, you can use this with a gradient background (when TransparentMode is false) also.

The base class does most of the work, all I had to do was to override OnPaint and draw the text:

protected override void OnPaint(PaintEventArgs pe)
{
    // Paint text
    Rectangle rc = new Rectangle(0,0,this.Width,this.Height);
    TextRenderer.DrawText(pe.Graphics,this.Text,this.Font,
        rc, this.ForeColor, Color.Transparent);

    // Calling the base class OnPaint
    base.OnPaint(pe);
}

The control must resize the rectangle with the text changes by overriding OnTextChanged (and OnFontChanged as well):

protected override void OnTextChanged(EventArgs e)
{
    this.Size = TextRenderer.MeasureText(this.Text,this.Font);
    RedrawControl();
    base.OnTextChanged(e);
}

I'm not sure how far I'm going to proceed with Transparent controls. I implemented CrystalTrackBar for a specific application and CrystalLabel was very quick to implement. I would be curious to know which controls would be a good candidate for transparent or gradient properties.

You can download CrystalLabel (and the Crystal Toolkit) in the Downloads section. It comes with full source code and demo programs.