3

I wanted to do a Vertical Progress Bar so i found this: Vertical progress bar

But now like if you have Horizitonal Progress Bar you can make it work from LeftToRight / RightToLeft so i want my Vertical one to Work from UpToDown and not from DownToUp like it works now..

Is it possible?

Here is my code

public class VerticalProgressBar : ProgressBar
{
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.Style |= 0x04;
            return cp;
        }
    }
}

I'm using C# .NET 3.5 Windows Forms

Community
  • 1
  • 1
Danpe
  • 18,668
  • 21
  • 96
  • 131

4 Answers4

2

Code, that support visual styles contains some bug. This code:

 ProgressBarRenderer.DrawVerticalBar(e.Graphics, e.ClipRectangle);

must be replaced with this one:

ProgressBarRenderer.DrawVerticalBar(e.Graphics, ClientRectangle);

And i repost full source code without this bug:

public class VerticalProgressBar : ProgressBar
    {
        protected override CreateParams CreateParams
        {
            get
            {
                // Avoid CA2122
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();

                CreateParams cp = base.CreateParams;
                cp.Style |= 0x04;
                return cp;
            }
        }

        public VerticalProgressBar()
        {
            // Enable OnPaint overriding
            this.SetStyle(ControlStyles.UserPaint, true);
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            if (ProgressBarRenderer.IsSupported)
            {
                ProgressBarRenderer.DrawVerticalBar(e.Graphics, ClientRectangle);

                const int HORIZ_OFFSET = 3;
                const int VERT_OFFSET = 2;

                if (this.Minimum == this.Maximum || (this.Value - Minimum) == 0 ||
                        this.Height < 2 * VERT_OFFSET || this.Width < 2 * VERT_OFFSET)
                    return;

                int barHeight = (this.Value - this.Minimum) * this.Height / (this.Maximum - this.Minimum);
                barHeight = Math.Min(barHeight, this.Height - 2 * VERT_OFFSET);
                int barWidth = this.Width - 2 * HORIZ_OFFSET;

                if (this.RightToLeftLayout && this.RightToLeft == System.Windows.Forms.RightToLeft.No)
                {
                    ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                            new Rectangle(HORIZ_OFFSET, VERT_OFFSET, barWidth, barHeight));
                }
                else
                {
                    int blockHeight = 10;
                    int wholeBarHeight = Convert.ToInt32(barHeight / blockHeight) * blockHeight;
                    int wholeBarY = this.Height - wholeBarHeight - VERT_OFFSET;
                    int restBarHeight = barHeight % blockHeight;
                    int restBarY = this.Height - barHeight - VERT_OFFSET;
                    ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                        new Rectangle(HORIZ_OFFSET, wholeBarY, barWidth, wholeBarHeight));
                    ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                        new Rectangle(HORIZ_OFFSET, restBarY, barWidth, restBarHeight));
                }
            }

            base.OnPaint(e);
        }
    }
Dmitry
  • 33
  • 5
1

There doesn't seem to be any CreateParams that support inverted vertical ProgressBar. These are the style parameters from the Windows API:

#define PBS_SMOOTH          0x01
#define PBS_VERTICAL        0x04
#define PBS_MARQUEE         0x08
#define PBS_SMOOTHREVERSE   0x10

#define PBST_NORMAL         1
#define PBST_ERROR          2
#define PBST_PAUSED         3

I tried changing the RightToLeft values to no avail. There also doesn't seem to be a way to arbitrarily rotate a Windows Forms control.

A possible solution may be to use the WPF ProgressBar. You could rotate it 90 degrees and it should do what you're looking for. Another option is to use a third party Progressbar control or create a custom rendered one. It should be fairly easy to render a simple flat progressbar.

Nithin Philips
  • 331
  • 1
  • 6
  • Can i rotate a Windows Forms Control? – Danpe Aug 09 '11 at 16:20
  • No, you cannot rotate the control itself. You can however override the Paint() method and rotate the contents, but in your case that won't help. If you don't want the WPF overhead, you can find plenty of custom progressbar controls at [CodeProject](http://http://www.codeproject.com/). You can at least use one of them as a starting point for a custom control. – Nithin Philips Aug 09 '11 at 16:34
  • And can i just add WPF libraries ? or i need to create a new WPF project for that? If i can only add the libraries can you show me an example? i'm new to WPF. – Danpe Aug 09 '11 at 16:45
  • 1
    This blog post is a good start: http://www.nayyeri.net/host-wpf-controls-in-windows-forms Basically you can use the [ElementHost](http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost.aspx) Windows Forms control. There is also a Microsoft page on WPF & Winforms interoperability that might be helpful to you: http://msdn.microsoft.com/en-us/library/ms751797.aspx – Nithin Philips Aug 09 '11 at 16:57
1

You have to override OnPaint() like this:

public class VerticalProgressBar : ProgressBar
{
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.Style |= 0x04;
            return cp;
        }
    }

    public VerticalProgressBar()
    {
        // Enable OnPaint overriding
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        Graphics dc = e.Graphics;

        if (this.Minimum == this.Maximum || (this.Value - Minimum) == 0)
            return;

        int width = this.Width;                                                                 // The bar width
        int height = (this.Value - this.Minimum) * this.Height / (this.Maximum - this.Minimum); // The bar height
        int x = 2;                          // The bottom-left x pos of the bar (or upper left on upsidedown bar)
        int y = this.Height - 1;            // The bottom-left y pos of the bar (or upper left on upsidedown bar)

        int blockheight = width * 3 / 4;    // The height of the block

        if (this.RightToLeftLayout && this.RightToLeft == System.Windows.Forms.RightToLeft.No)
            for (int currentpos = 0; currentpos < height; currentpos += blockheight + 1)
                dc.FillRectangle(new SolidBrush(this.ForeColor), x, currentpos, width, blockheight);
        else
            for (int currentpos = y; currentpos > y - height; currentpos -= blockheight + 1)
                dc.FillRectangle(new SolidBrush(this.ForeColor), x, currentpos - blockheight, width, blockheight);

        base.OnPaint(e);
    }
}

Now you can use it in the same way as Vertical progress bar you linked and LeftToRight / RightToLeft functionality will mimic the one from normal ProgressBar (regarding progress drawing orientation).

Community
  • 1
  • 1
1

I noticed the code above does not work well with visual styles. Here is an improved version of vertical progress bar that should cover visual styles:

public class VerticalProgressBar : ProgressBar
{
    protected override CreateParams CreateParams
    {
        get
        {
            // Avoid CA2122
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();  

            CreateParams cp = base.CreateParams;
            cp.Style |= 0x04;
            return cp;
        }
    }

    public VerticalProgressBar()
    {
        // Enable OnPaint overriding
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        if (ProgressBarRenderer.IsSupported)
        {
            ProgressBarRenderer.DrawVerticalBar(e.Graphics, e.ClipRectangle);

            const int HORIZ_OFFSET = 3;
            const int VERT_OFFSET = 2;

            if (this.Minimum == this.Maximum || (this.Value - Minimum) == 0 ||
                    this.Height < 2 * VERT_OFFSET || this.Width < 2 * VERT_OFFSET)
                return;

            int barHeight = (this.Value - this.Minimum) * this.Height / (this.Maximum - this.Minimum);
            barHeight = Math.Min(barHeight, this.Height - 2 * VERT_OFFSET);
            int barWidth = this.Width - 2 * HORIZ_OFFSET;

            if (this.RightToLeftLayout && this.RightToLeft == System.Windows.Forms.RightToLeft.No)
            {
                ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                        new Rectangle(HORIZ_OFFSET, VERT_OFFSET, barWidth, barHeight));
            }
            else
            {
                int blockHeight = 10;
                int wholeBarHeight = Convert.ToInt32(barHeight / blockHeight) * blockHeight;
                int wholeBarY = this.Height - wholeBarHeight - VERT_OFFSET;
                int restBarHeight = barHeight % blockHeight;
                int restBarY = this.Height - barHeight - VERT_OFFSET;
                ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                    new Rectangle(HORIZ_OFFSET, wholeBarY, barWidth, wholeBarHeight));
                ProgressBarRenderer.DrawVerticalChunks(e.Graphics,
                    new Rectangle(HORIZ_OFFSET, restBarY, barWidth, restBarHeight));
            }
        }

        base.OnPaint(e);
    }
}

I had to draw separate chunks for bottom-up progress bar because I wanted to keep look and feel of other progress bars. Otherwise the bar would have appeared to be drawn from the middle.