Windows Forms: November 2006 Archives

Designer error
If you've used Visual Studio with Windows Forms objects, you're used to double clicking on a file in the Solution Explorer and seeing the visual representation of the Form object in the designer. This works for controls as well as forms. I ran into a problem when I created the base class CrystalControl. First, I right clicked on the Solution Explorer, clicked Add, New Item, selected Custom Control, named the file CrystalControl.cs, and started working. The problem was that Visual Studio then decided that CrystalControl needed to be displayed in the designer by default, rather than code. (In this case, CrystalControl is a base class that doesn't draw anything and is only useful when inherited.) No doubt many people have seen the nasty error page "The class X can be designed, but is not the first class in the file…"

namespace Attilan.Crystal.Controls
{
    /// <summary>
    /// Base class for all Crystal controls, derives from ScrollableControl.
    /// </summary>
    [System.ComponentModel.DesignerCategory("code")]
    [ToolboxItem(false)]
    public class CrystalControl : ScrollableControl


If you look at the documentation, Visual Studio is supposed to treat a .cs file as a Designer item by the System.ComponentModel.DesignerCategory attribute inside the code. Seeing that I had omitted this, I included it before the class declaration and recompiled.

designer attributes in solution explorer
Unfortunately, I still had the same result: double clicking on CrystalControls.cs resulted in the error page. Even though you can click on the Code button in the Solution Explorer, it's one of those little things that drive me nuts. Looking at the Solution Explorer, I could see that CrystalControls.cs and CrystalLabel.cs had different icons from my other .cs (code only) extension files that behaved correctly. Then I figured out that because of the way I created these .cs files (selecting Custom Control in the Add dialog), extra metadata must have been written to my project files.

Crystal Toolkit project file
Sure enough, on close examination of CrystalToolkit.csproj, I found extra attributes for the c-sharp files with bad behavior and incorrect icon status. Under each of them was the <subtype>Component</subtype> attribute. When I removed these attributes and saved the .csproj file, everything was fine.

correct attributes in solution explorer
All my files reverted to code-only status and the default double-click action was restored, along with my sanity. Just to be clear, you absolutely need to set the DesignerCategory attribute to "code" in order for this to work, but if Visual Studio somehow inserts that metadata, you may be slightly hosed.

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.