Recently in .NET Framework Category

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.

ToolStrip Items
I'm finally ready to release alpha version 0.66 of Crystal Toolkit that contains the CrystalTrackBar control for Windows Forms and .NET 2.0 Framework. It's a replacement for the TrackBar control that contains many of the same properties (Maximum, Minimum, TickFrequency, Orientation, etc.) with some new features. Before I dive into that list, I'd like to answer the question: "Why replace the TrackBar at all? Doesn't it already work just fine?" It does work great, but I ran into a couple of situations where it wasn't right for my applications.

ToolStripTrackBar
My first problem with the TrackBar was the visual look that it has when hosted on a ToolStrip (see my earlier article which how to do this using ToolStripControlHost). When the ToolStrip's RenderMode is set to ManagerRenderMode, the background of the control has a nice, smooth gradient effect (depending on the Theme you selected in Windows). But the TrackBar doesn't know anything about the ToolStrip or the RenderMode. It continues to paint its background using the default control color. The picture above is an example of this. Not a pleasing sight for anyone, is it?

Crystal Toolkit class diagram
I fiddled with the ToolStripControlHosted TrackBar for a while, trying all kinds of hacks to make it work seamlessly. I came to the conclusion that I was going to have to write my own TrackBar control in order to have full authority over the painting. I started playing around with some base classes that would allow me to create controls with gradient backgrounds and color angles. I tried this with my early versions of CrystalTrackBar and liked the results I saw when it was placed on a Form. When placed on a ToolStrip, it was better than what I had before, but still not great. Then I discovered how to make a control's background totally transparent (see my earlier article on my sources for transparent controls). My solution was to put a property in my base class (CrystalGradientControl) called TransparentMode.

CrystalToolStripTrackBar
When TransparentMode is set to true, the control's background is invisible. When TransparentMode is set to false, the control's background can have a gradient effect (or a single solid color if you set both Color1 and Color2 to the same value). With CrystalTrackBar in TransparentMode, I was able to make it blend in perfectly with a Form that contains a background image. I created a ToolStrip host called CrystalToolStripTrackBar and the result was the same: the transparent background allowed the gradient effect of the RenderMode to be exposed. I decided to set the TickStyle to none on the CrystalToolStripTrackBar by default, because most applications that have a slider on the ToolStrip don't have them.

CrystalTrackBar Demo App
In order to produce CrystalTrackBar, I had to take full control of all the painting. For the slider bar (or TrackBar as I call it inside the code) and the ticks running next to the slider, I used the TrackBarRenderer class. I started using the TrackBarRenderer class to draw the thumb as well, but ran into a few problems. TrackBarRenderer's methods to draw the thumb take a regular Rectangle structure with integers. In order to make the Thumb position itself correctly above each tick, the coordinates I calculated were floats-so I had to use RectangleF. I decided to create my own bitmaps for the Thumb and an enum called CrystalThumbState to represent different states (Normal, Hover, Pressed, Erasing). CrystalTrackBar's thumb has a light blue glow when you hover over the control and a dark blue outline when you drag it. I'm not the greatest icon artist in the world, and if you care to replace my thumb bitmaps with your own, please do so-just send me a copy (richard at attilan dot com).

It wasn't my initial goal to make a full TrackBar replacement and I don't think I've matched it 100%. However, I didn't feel right releasing even an alpha version without supporting TrackBar's Minimum, Maximum, TickFrequency, TickStyle, and Orientation properties, in addition to supporting the ValueChanged event. CrystalTrackBar has a couple of other minor features. KeyboardControl is a Boolean value that will allow you to turn off keyboard input for the trackbar. By default, if the CrystalTrackBar is horizontal, a few keystrokes (Left, Right, Home, End) can change the values. In vertical mode the keystrokes are Up, Down, Home, End. Some applications use these keystrokes for other operations, so you can turn this off here. TickValues is a list (of integers) representing the tick marks on the CrystalTrackBar. If you have set Minimum to 0, Maximum to 100, and TickFrequency to 10, the TickValues list will be 0, 10, 20, 30, etc., all the way to 100. This could be useful for adding these values to a ComboBox.

You can download CrystalTrackBar in the Downloads section. It comes with full source code and demo programs.

NDoc and Sandcastle

| | Comments (0)

NDoc 2.0 Alpha
I've been polishing the CrystalTrackBar for the first alpha release of Crystal Toolkit. I wanted to provide some documentation. I've known that C#'s comments can be assembled into an XML file, by checking "XML documentation file" under Project\Properties, Build tab. There's an open source program called NDoc over at SourceForge which takes the XML file along with your assembly and generates a variety of documentation formats, with everything from HTML files to Microsoft's .CHM format. The only problem is that NDoc at SourceForge was last updated with .NET Framework 1.1. If you're using .NET 2.0, you're in trouble if you are using some of the new features like Generics. Luckily, there's an NDoc 2.0 Alpha release at a website calls Kynosarges. It seemed to work well enough with my project, although it's not using the new feature set. There's Microsoft tool called Sandcastle which just had a 2nd CTP release this month, but it's unclear to me whether this has a GUI in addition to a user interface.

External Link:
NDoc 2.0 Alpha
Microsoft Sandcastle Blog