29 June 2017

ShibumiWare Common Controls: Progress Bar

Progress Bar

Introduction

I have been working on a set of controls for years. At a previous company I had access to DevExpress' Suite of controls and loved working with the controls, but for a hobbyist, they are simply too expensive. Work on the control is organic. Each time I run into a user interface problem and the out-of-the-box.NET controls don't do the job; I look for an open source project first. Unfortunately, I found many open source projects lacking. Also, I prefer to write to the control through an API versus using design-time features. I also love composition; some my controls are easily combined to create other controls--also something I found difficult to do with open source projects. Not that I haven't used any open source. I have an HTML Editor built by a former Microsoft Consulting Services friend who made a decent control; it just needs modernizing. That is on my list, and since it began open source, I will release it accordingly.

Control Library

Tonight I made my first video for YouTube. My son is getting into it. I bought him a new computer, a high-gain condenser microphone with a studio stand, and gave him a decent set of headphones. I decided I better catch up to him so I can help him out in this new endeavor. Of course, our choice of topics is unique.

The first control I cover is the ShibumiWare Common Control Progress Bar. For those who have been around Windows development for a while, you are likely all too familiar with the built-in progress bar's shortcomings. It was quite an event when Microsoft added continuous and marquee rendering to block rendering. The first thing I wanted that Windows' version couldn't give me was control over the border: I wanted to change the border's color, thickness, and provide the ability to have a normal and selected border.

I will show the general architecture of the control library in a later post, but for now, understand that each control inherits from SWBaseControl, which provides many features common to all controls, including borders. The next thing was getting rid of the green color. It is a beautiful color, but in many of my apps, it sticks out and isn't consistent with the look-and-feel.




Part I of Breaking Down the Code

I have to admit that I had a long day and I am actually tired, even though it is only 1:45 AM.   I work strange hours and I do my hobby projects on an even stranger schedule. I am going to give you just a taste, and actually the crux, of solving the paint problem.  I wanted a different background than white.  I wanted colors other than green.  I wanted borders, as mentioned earlier (and not covered in this post), and after a while, I figured out how to do gradients. 

The Progress Bar Control inherits from Windows progress bar.    The following code is critical in even starting to gain control over the paint operation:

public void SetStyle(ProgressBarStyle style)
{
    if (style != ProgressBarStyle.Marquee)
    {
        SetStyle(ControlStyles.UserPainttrue);
    }
}

This has the effect of telling the framework "hey, painting is done by this control so don't do anything."   Overriding the OnPaint event is where the bulk of the work is done:

protected override void OnPaint(PaintEventArgs e)
{
    if (_PaintNormal)
    {
        return;
    }
 
    if (OverrideBackground)
    {
        PrepareClientRectangle(BackColor);
    }
 
    if (Value == 0 || Maximum == 0)
    {
        return;
    }
 
    using (Image image = new Bitmap(WidthHeight))
    {
        using (Graphics graphics = Graphics.FromImage(image))
        {
            Rectangle rect = new Rectangle(0, 0, WidthHeight);
 
            if (ProgressBarRenderer.IsSupported)
            {
                ProgressBarRenderer.DrawHorizontalBar(graphics, rect);
            }
 
            rect.Width = (int)Math.Ceiling(rect.Width * ((double)Value / Maximum));
 
            if (rect.Width == 0)
            {
                rect.Width = 1;
            }
 
            using (LinearGradientBrush brush = new LinearGradientBrush(rect, BackColorProgressMarkerColorGradientMode))
            {
                graphics.FillRectangle(brush, 0, 0, rect.Width, rect.Height);
            }
 
            e.Graphics.DrawImage(image, rect, rect, GraphicsUnit.Pixel);
        }
    }
}

  I won't explain in detail tonight, but I can easily disable the advanced painting and revert back to the normal behavior.  If I want something besides the gray background, I set OverrideBackground = true:

public void PrepareClientRectangle(Color backColor)
{
    if (backColor == default(Color))
    {
        backColor = BackColor;
    }
 
    using (Graphics graphics = CreateGraphics())
    {
        using (SolidBrush brush = new SolidBrush(backColor))
        {
            graphics.FillRectangle(brush, Location.XLocation.YWidthHeight);
        }
    }
}

The rest is self-explanatory, but I will say that there are some rectangles that don't need to be there and I call an overload for filling the rectangle that is much simpler, but those are items left over from some experiments I did--and I left the code alone to remind me how to pickup that original train of thought if I need to. 


Wrap-Up

As you can see from the folder structure image above, I have quite a few controls I could cover.  I chose the progress bar because I was working with it tonight on another hobby project and figured out something new I wanted so it was at the for-front of my mind.  The reason I did this post, mainly, was to accompany my first YouTube video so when I see Parker next, I can show him that I am following his lead. I think he will like that.


Colby-Tait

No comments :

Post a Comment