Анимированное «свечение» в нарисованной владельцем панели прогресса (ListView / DataGridView) - PullRequest
5 голосов
/ 17 ноября 2008

Я заметил, что стандартный ProgressBar в .NET 2.0 (Winforms) действительно отображается как причудливая анимированная светящаяся панель в Vista; однако использование ProgressBarRenderer (как это обычно делается при попытке нарисовать индикатор выполнения в представлении списка, нарисованном владельцем, в виде сетки или в другом подобном элементе управления) просто дает визуальный стиль без красивой анимации.

Полагаю, было бы глупо ожидать, что это будет просто волшебно работать - я думаю, что нативный элемент управления в Vista должен иметь какой-то встроенный таймер или поток, которого, очевидно, не существует при рисовании статического изображения. Я видел, что если вы несколько раз подряд перерисовываете элемент управления ProgressBar (используя DrawToBitmap), вы можете увидеть отдельные этапы анимированного свечения, поэтому я экспериментировал с использованием таймера для автоматического перерисовки, но что-то не так правильно внешний вид, и он также потребляет гораздо больше процессорного времени, чем фактический ProgressBar.

Это, кажется, оставляет меня с двумя нестандартными вариантами: a) Используйте ProgressBarRenderer и в конечном итоге получите «внешний вид» Vista, но без анимации; или же б) Используйте таймер, чтобы непрерывно перерисовывать несколько ProgressBars в растровые изображения, и тратить циклы ЦП, чтобы он выглядел лучше, но все еще не идеально.

Мне было интересно, имел ли кто-нибудь опыт встраивания индикаторов выполнения в нарисованные владельцем элементы управления и мог бы знать о лучшем способе, чем два вышеупомянутых варианта - что-то, что может точно воспроизвести свечение / блеск, не полагаясь на таймеры и / или не нажимая CPU.

1 Ответ

5 голосов
/ 18 ноября 2008

Мне пришлось сделать несколько довольно сумасшедших трюков, чтобы сделать эту работу. К сожалению, MSFT не обновил класс VisualStyleElement.ProgressBar для добавления частей, добавленных Vista. И конструктор является частным. И мне пришлось немного угадать, какие части создают анимацию. Я довольно близко подошел к этому коду, он должен дать вам кое-что для эксперимента:

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);
    }
  }

}
...