Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animated "glow" in owner-drawn Progress Bar (ListView/DataGridView)

I've noticed that the bog-standard ProgressBar in .NET 2.0 (Winforms) does show up as the fancy animated glowing bar in Vista; however, using the ProgressBarRenderer (as one generally has to when trying to draw a progress bar in an owner-drawn list view, grid view, or other such control) just gives the visual style without the pretty animation.

I guess it was silly to expect that this would just magically work - I imagine the native control in Vista must have some sort of embedded timer or thread that obviously doesn't exist when drawing a static image. I did see that if you redraw a ProgressBar control several times in succession (using DrawToBitmap) that you can actually see the distinct stages of the animated glow, so I experimented with using a timer to keep redrawing automatically, but something's just not quite right about the look, and it also eats up a lot more CPU time than an actual ProgressBar does.

This seems to leave me with two sub-standard options: a) Use the ProgressBarRenderer and end up with a Vista "look" but no animation; or b) Use a timer to continually redraw several ProgressBars to bitmaps, and waste CPU cycles to get it looking better but still not perfect.

I was wondering if anybody has had experience embedding progress bars inside owner-drawn controls and might know of a better way than the two options above - something that can accurately reproduce the glow/glint without relying on timers and/or hammering the CPU.

like image 955
Aaronaught Avatar asked Nov 17 '08 20:11

Aaronaught


1 Answers

I had to pull some pretty crazy stunts to make this work. Unfortunately, MSFT didn't update the VisualStyleElement.ProgressBar class to add the parts that Vista added. And the constructor is private. And I had to guess a bit at the parts that produce the animation. I got fairly close with this code, it should give you something to experiment with:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Reflection;

namespace WindowsFormsApplication1 {
  public partial class Form1 : Form {
    VisualStyleElement pulseOverlay;
    VisualStyleElement moveOverlay;
    VisualStyleRenderer pulseRenderer;
    VisualStyleRenderer moveRenderer;
    Timer animator = new Timer();
    public Form1() {
      InitializeComponent();
      ConstructorInfo ci = typeof(VisualStyleElement).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
        null, new Type[] { typeof(string), typeof(int), typeof(int) }, null);
      pulseOverlay = (VisualStyleElement)ci.Invoke(new object[] { "PROGRESS", 7, 0 });
      moveOverlay = (VisualStyleElement)ci.Invoke(new object[] { "PROGRESS", 8, 0 });
      pulseRenderer = new VisualStyleRenderer(pulseOverlay);
      moveRenderer = new VisualStyleRenderer(moveOverlay);
      animator.Interval = 20;
      animator.Tick += new EventHandler(animator_Tick);
      animator.Enabled = true;
      this.DoubleBuffered = true;
    }
    void animator_Tick(object sender, EventArgs e) {
      Invalidate();
    }

    int xpos;
    protected override void OnPaint(PaintEventArgs e) {
      Rectangle rc = new Rectangle(10, 10, 100, 20);
      ProgressBarRenderer.DrawHorizontalBar(e.Graphics, rc);
      rc = new Rectangle(10, 10, 50, 20);
      ProgressBarRenderer.DrawHorizontalChunks(e.Graphics, rc);
      xpos += 3;
      if (xpos >= 30) xpos = -150;  // Note: intentionally too far left
      rc = new Rectangle(xpos, 10, 50, 20);
      pulseRenderer.DrawBackground(e.Graphics, rc);
      moveRenderer.DrawBackground(e.Graphics, rc);
    }
  }

}
like image 138
Hans Passant Avatar answered Nov 17 '22 18:11

Hans Passant