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.

Crystal Toolkit is a Windows Forms (.NET Framework 2.0) control library written in C#. This first release containing the following classes:

  • CrystalGradientControl: a control that can either have a gradient background or a transparent background.
  • CrystalTrackBar: a homegrown trackbar that can have a gradient or transparent background.
  • CrystalToolStripTrackBar: a host for CrystalTrackBar that allows it to work in a ToolStrip.

Bonus: if you don't like CrystalTrackBar on a ToolStrip, this library also contains ToolStripTrackBar, which hosts System.Windows.Forms.TrackBar on the ToolStrip.

This release comes in a ZIP file. Simply unzip the contents to your hard drive, navigate to the root Attilan folder, and double click on CrystalDemo.sln. This solution file contains the Crystal Toolkit plus demo programs. Just build the solution and run the demo programs to see how they work.

I've tested the demos under Windows XP SP2 and Windows Vista RC2. Needless to say, buyers beware, this is alpha software and it's free open-source code: you get what you pay for! I'd welcome any feedback, bugs (or bug fixes), and thumb-bitmap replacements. Send email to richard at attilan dot com.

Download: Crystal Toolkit 0.66 (zip file, 580K)

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

The Silicon Valley Code Camp 2006 was executed very well. I arrived early at 8am, since I had signed up for the Vista Install Fair slot at 9am. I had hoped to get Vista installed on my laptop (Dell Inspiron E1505) in time to attend the Windows Communication Foundation seminar at 9:45am. No such luck, as I was in the Microsoft room from 8:45am until 1pm. Microsoft gave everyone who attended the event a disk with the VUA (Vista Upgrade Advisor) and a sheet of instructions. Included on the disk were diagnostic tools that recorded data about our computers, so the Microsoft engineers could run diagnostics on the later. My computer had two problems: one, the VUA failed to execute successfully, and two, the Vista Upgrade failed with a blue screen dump. And it failed so miserably that we couldn't even perform the rollback to Windows XP.

The clean install of Vista worked perfectly (and I am using the computer right now). I installed Visual Studio 2005 and Office 2007--they both performed well in this environment. It just confirmed for me what I already firmly believed, namely that it's better to freshly install Vista than doing the upgrade. I saw a number of people having problems due to the Upgrade process. Nevertheless, I am happy to help out Microsoft in exchange for a free product key!

Despite missing out on 2 seminars, I enjoyed hanging out in the room with the Microsoft engineers. They answered a number of questions and were very patient looking at everyone's computer. They demoed a number of new Vista features, including ReadyBoost, where you get a performance boost from a USB 2.0 flash drive. I had just bought one at CompUSA, but I was disappointed when Vista reported that it didn't perform well enough for ReadyBoost. I wish I had read this excellent article over at ExtremeTech where they test 9 USB 2.0 Flash drives and see if they can be used by ReadyBoost.

In an earlier article (Accessing Embedded Resources using GetManifestResourceStream) I showed how to retrieve resources from an Assembly. In that particular case, the executing Assembly contained the embedded resource that we wanted to retrieve. What happens when the embedded resource is not in the executing Assembly? When it's located in a different C# Assembly, in a library built for controls or standalone classes? You can still do it, but you can't use GetExecutingAssembly.

In my case, I am building a library containing some Image controls, called CrystalUtils. This particular library contains a C# class for displaying Thumbnail images. When the control is used within the Visual Studio designer, I want to display a default thumbnail image. To access the default image, I have a class called CrystalTools which contains a static method called GetDefaultImage:

public class CrystalTools
{
  static private Bitmap _defaultImage = null;
  static private Image _defaultThumbnailImage = null;
  static public string DefaultImageName = 
        "CrystalUtils.resources.DefaultImage.bmp";

  static public Image GetManifestImage(Assembly theAssembly,
      string imageResourceName)
  {
    if (theAssembly != null)
    {
      Stream _imageStream;
      _imageStream = theAssembly.GetManifestResourceStream(
            imageResourceName);
      return new Bitmap(_imageStream);
    }

    return null;
  }

  static public Image GetDefaultImage(Assembly theAssembly)
  {
    if (_defaultImage == null)
      _defaultImage = (Bitmap)GetManifestImage(theAssembly, 
            DefaultImageName);

    return _defaultImage;
  }

GetDefaultImage takes an Assembly object as the first parameter. The Assembly object you pass cannot be the executable, you need to get the Assembly object that the caller knows contains the desired resource. In this case, a non-static object called CrystalFlipper is calling default image:

Image theImage = CrystalTools.GetDefaultImage(
    Assembly.GetAssembly(typeof(CrystalFlipper)));

Assembly.GetAssembly takes a Type parameter. Each C# class has its own unique type identifier. The typeof operator returns the correct type for the class CrystalFlipper. When I first tried this, I called it this way:

Image theImage = CrystalTools.GetDefaultImage(
    Assembly.GetAssembly(GetType()));

GetType() did work in my test application just fine. When I used it in a real application, I derived a class from CrystalFlipper and overrode some virtual methods. At that point, GetDefaultImage threw an exception. GetType() returned the type of the child class, not CrystalFlipper. While I could see that behavior working in different situations, in this case the typeof() operator gave me access to the same Assembly that my thumbnail classes originated from.

I've never been much of a graphics oriented programmer. Somehow I've always gotten by, using List and Tree controls, or various graphical toolkits to do my dirty work. Fortunately, GDI+ offers a lot of ways to draw your own content within a class derived from ScrollableControl if you need to do so. I wrote a control (which you will see here later in October) for viewing images in a horizontal slider, and everything looked great until I moved the scroll bar to the right. Instead of seeing the next set of images, I got a disjointed or improperly painted set of images.

After a day of trying to debug my code, I decided to go back to basics. I really didn't understand how to scroll a control and handle even the most basic painting events. I created a demo project called TestControlPaint (VS 2005, 38 K) :

Auto Scroll Test with a ScrollableControl

It has a class called TestControl (derived from ScrollableControl) which is designed to do one thing: draw a grid of 20 squares either horizontally or vertically.

/// <summary>
/// Tests scrolling a control horizontally and vertically
/// </summary>
public partial class TestControl : ScrollableControl

Within the class, there is a field called HorizontalMode, which can be set from the designer the comments tell you what it does:

/// <summary>
/// True means that the control displays horizontally; false means it displays
/// a vertical grid.
/// </summary>
public bool HorizontalMode
{
    get { return _horizontalMode; }
    set
    {
        if (_horizontalMode != value)
        {
            _horizontalMode = value;
            Invalidate();
        }
    }
}

In the picture below, I've put a horizontal Split Container into my Form, with one horizontal instance of TestControl in the upper panel, and a vertical instance of TestControl in the bottom panel. Horizontally we have squares 1-20 and vertically we have square 1-20 in 4 rows of 5 cells. Scroll bars magically appear and appear to work well! How did this happen? Some of the key steps were the following:

1. Make sure that the AutoScroll property is set to true on the control. It could set in the designer or in another method, but if it's not set somewhere, none of this will work:

AutoScroll = true;

2. Set AutoScrollMinSize to the virtual size of the content within your control:

// Set the auto scroll size to the total width and  
// height of the content drawn within the control.
AutoScrollMinSize = new Size(_cumulativeWidth, 
                            _cumulativeHeight);

3. In your OnPaint method override, call TranslateTransform with the current AutoScrollPosition:

//handle possibility that the viewport is 
//scrolled,adjust my origin coordintates 
//to compensate
Point pt = AutoScrollPosition;
gfx.TranslateTransform(pt.X, pt.Y);

The first two bullet items I had figured out from reading books and articles, but the third one eluded me. TranslateTranform, as soon as I put it in my code, made the scrolling work instantly. This makes sense when you see what I was drawing within OnPaint:

// Create the grid...
for (int index = 1; index < (_numSquares+1); index++)
{
//...
    // Calculate the coordinates for a square rectangle
    Rectangle theRect = new Rectangle(_squareWidth * 
        (startX++) + _margin, startY, 
        _squareWidth, _squareHeight);
    //...
    // Draw the rectangle with a black brush
    Pen borderPen = new Pen(theBrush);
    gfx.DrawRectangle(borderPen, theRect);

A series of squares are drawn: from 0 - 100, 105 - 205, 210 - 310, etc. When a paint message is received, the control drew the first few visible squares correctly. What happens when you need to draw square 4 at starting location (315, 5) and the client area is only 250 wide? Without TranslateTransform, the Graphics object (gfx in this case) cannot draw anything beyond the client rectangle of the control. BTW, I forgot where I saw TranslateTransform, but it was Bob Powell's comment on a message board that gave me the clue to look into this.

Speaking of Mr. Powell, he helped solve another problem. When you have a virtual drawing surface, how can you accurately determine the mouse coordinates? OnMouseClick only returns the mouse coordinates within the client rectangle, not from your virtual rect setup with AutoScrollMinSize. To get the virtual mouse location, take a look at Powell's Back-track the mouse example. He takes the AutoScrollPosition and transforms it into the virtual mouse location. I've included this method in TestControl.cs:

/// <summary>
/// Gets the mouse location according to the virtual 
/// position in the control.
/// </summary>
/// <returns>The virtual mouse location</returns>
protected Point GetVirtualMouseLocation(MouseEventArgs e)

TestControl receives a mouse click event in the OnMouseClick override. It calls GetVirtualMouseLocation for the virtual location, and passes this Point object to a method called getSquareID:

// get the virtual mouse location using the transforms
Point mouseLocation = GetVirtualMouseLocation(e);

// Translate the virtual mouse location into a square ID
int squareID = getSquareID(mouseLocation);

StringBuilder strBuild = new StringBuilder();
strBuild.AppendFormat("You clicked on Square ID {0}", 
                        squareID);

// if we found a valid square, show the id in 
// the message box
if (squareID > 0)
    MessageBox.Show(strBuild.ToString());

The test Form will display the ID of the square that you click on within either the horizontal or vertical forms. This simple technique worked equally well within my larger, more complex control.

Download:
TestControlPaint VS 2005 Project (38K)

I attended an MSDN Event yesterday, over at the Regal Hacienda theaters in Dublin. I always enjoy these events because they are free and there is always useful information presented. One of the speakers, Anand Iyer, is very enjoyable to watch. He's very good at presenting technical information and sprinkling little humorous comments throughout. A year ago my friend Dave got a kick out of Anand explaining ClickOnce: "You click once to download the application from a web site...and then you click once again to install it on your PC. It should have been called Click Twice!" Yesterday, Anand was slapping himself for promoting Google Maps when talking about AJAX and Atlas (Microsoft's AJAX sdk for ASP.NET): "I should be promoting Live Local Maps instead!"

CodeCamp at FootHill College.  Click Here for Details and RegistrationAnand mentioned the Silicon Valley Code Camp 2006 that will be held on October 7th and 8th (the weekend) at Foothill College. This is also a free event, and the sessions will cover all kinds of topics, from .NET related technologies to Amazon Web Services. There's also a free Vista Install Fair, where you can upgrade your current Windows XP SP 2 computer to Windows Vista Ultimate. There are some restrictions for memory, graphics cards, etc., and you should backup your data. But it sounds like a good deal, getting some help with an upgrade, and you will receive a free copy of Windows Vista Ultimate after it is finally released. You can just bring your computer without the monitor, keyboard, or mouse.

External Link:
Silicon Valley Code Camp 2006

In my previous entry, "Using ToolStripControlHost with a Trackbar", I noted that the problem with this method resulted in the ToolStripTrackBar looking unlike anything else on the ToolStrip when it was in ManagerRenderMode. This has driven me nuts during the preceding week. Here are some of the steps that I took to solve this problem, all of which failed:

Tried to set Backcolor on ToolStripTrackBar: no effect.
Override OnPaint method with ToolStripTrackBar: no effect.
Derive MyTrackBar from TrackBar, override OnPaintBackground, put MyTrackBar in ToolStripControlHost: color, but no trackbar!

The latter step failed because TrackBar is one of those .NET controls that wants to perform all the drawing--if you override OnPaint, it won't be called unless you call SetStyle, as Tom Holt described in this CodeProject article:

// this call says "I'll draw it myself"
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);

From what I read on various message boards, no one had my specific problem (needing a TrackBar on a ToolStrip), but plenty of people had a situation where they wanted the TrackBar to be transparent on a Form that contained a bitmap image or specific visual style. I could see that type of TrackBar being useful and potentially the key to solving this problem, and I found a great article from Bob Powell showing how to create a transparent C# control. After finding this MSDN article with sample code for the TrackBarRenderer class, I decided to create my own control: CrystalTrackBar.

CrystalTrackBar
CrystalTrackBar has two different modes you can set. One is "TransparencyMode", which allows the background of the Form to show through, as it does with the second control in this screenshot. I then proceeded to take the CrystalTrackBar, and wrap it in a ToolStripControlHost to create CrystalToolStripTrackBar. The transparency mode allows the finely rendered background of the ToolStrip to show through, creating a seemless effect. As a bonus, I created a second mode that works when TransparencyMode is set to false--a gradient effect is created, as in the first control within the form. You can set Color1, Color2, and the angle of the gradient.

I'll be sharing the code soon. CrystalTrackBar isn't yet as feature rich as the TrackBar and I want to include some additional modes. I've got a small painting bug when I move the thumb around, but I may release it in the hopes that someone will suggest a better way of doing things.

Previous 1 2 3 4 5 6 Next