2013-03-19 1 views
6

Я собираюсь построить производный класс Control, который поддерживает свойство Opcacity.
Этот контроль может разместить текст и изображение и beable исчезать их и в
Вот мой код:.Управление затуханием в C#

internal class FadeControl : Control 
{ 
    private int opacity = 100; 

    public FadeControl() 
    { 
     SetStyle(ControlStyles.SupportsTransparentBackColor, true); 
    } 

    public int Opacity 
    { 
     get 
     { 
      return opacity; 
     } 
     set 
     { 
      if (value > 100) opacity = 100; 
      else if (value < 1) opacity = 1; 
      else opacity = value; 

      if (Parent != null) 
       Parent.Invalidate(Bounds, true); 
     } 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      cp.ExStyle = cp.ExStyle | 0x20; 
      return cp; 
     } 
    } 

    protected override void OnPaintBackground(PaintEventArgs e) 
    { 
     //do nothing 
    } 

    protected override void OnMove(EventArgs e) 
    { 
     RecreateHandle(); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     using (Graphics g = e.Graphics) 
     { 
      Rectangle bounds = new Rectangle(0, 0, Width - 1, Height - 1); 
      int alpha = (opacity * 255)/100; 

      using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor))) 
      { 
       if (BackColor != Color.Transparent) 
        g.FillRectangle(bckColor, bounds); 
      } 

      ColorMatrix colorMatrix = new ColorMatrix(); 
      colorMatrix.Matrix33 = (float)alpha/255; 
      ImageAttributes imageAttr = new ImageAttributes(); 
      imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 

      if (BackgroundImage != null) 
       g.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr); 

      if (Text != string.Empty) 
      { 
       using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor))) 
       { 
        g.DrawString(Text, Font, txtBrush, 5, 5); 
       } 
      } 
     } 
    } 

    protected override void OnBackColorChanged(EventArgs e) 
    { 
     if (Parent != null) 
      Parent.Invalidate(Bounds, true); 

     base.OnBackColorChanged(e); 
    } 

    protected override void OnParentBackColorChanged(EventArgs e) 
    { 
     Invalidate(); 

     base.OnParentBackColorChanged(e); 
    } 
} 

Я гнал управления на форме, которая имеет таймер на нем.
Таймер устанавливает непрозрачность элемента управления от 0 до 100 и обратно и его работоспособность.
Проблема, которую я пытаюсь решить, заключается в том, что элемент управления мерцает при изменении его непрозрачности.
Настройка элемента управления на ControlStyles.DoubleBuffer сделает управление невидимым для формы.

Любые советы приветствуются.

+3

Это неизбежно, когда вы используете WS_EX_TRANSPARENT. Так что не используйте его. –

+0

Возможный дубликат [Fade a panel-Windows forms] (http://stackoverflow.com/questions/10178559/fade-a-panel-windows-forms) –

+0

Элемент управления по ссылке не поддерживает прозрачную обратную цветность. Я хотел бы, чтобы cotrol мог исчезать и исчезать, имея PNG-изображения и тексты. – toy4fun

ответ

0

Мне не удалось использовать как двойной буфер, так и WS_EX_TRANSPARENT (0x20) для прозрачного фона. Поэтому я решил реализовать прозрачный фон, скопировав содержимое родительского элемента управления и используя двойной буфер для предотвращения мерцания.

Ниже конечный исходный код, протестирован и работает:

using System; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.Windows.Forms; 

internal class FadeControl : Control 
{ 
    private int opacity = 100; 
    private Bitmap backgroundBuffer; 
    private bool skipPaint; 

    public FadeControl() 
    { 
     SetStyle(ControlStyles.SupportsTransparentBackColor, true); 
     SetStyle(ControlStyles.ResizeRedraw, true); 
     SetStyle(ControlStyles.DoubleBuffer | 
       ControlStyles.AllPaintingInWmPaint | 
       ControlStyles.UserPaint, true); 
    } 

    public int Opacity 
    { 
     get 
     { 
      return opacity; 
     } 
     set 
     { 
      if (value > 100) opacity = 100; 
      else if (value < 1) opacity = 1; 
      else opacity = value; 

      if (Parent != null) 
       Parent.Invalidate(Bounds, true); 
     } 
    } 

    protected override void OnPaintBackground(PaintEventArgs e) 
    { 
     //do nothig 
    } 

    protected override void OnMove(EventArgs e) 
    { 
     RecreateHandle(); 
    } 

    private void CreateBackgroundBuffer(Control parent) 
    { 
     int offsetX; 
     int offsetY; 
     GetOffsets(out offsetX, out offsetY, parent); 
     backgroundBuffer = new Bitmap(Width + offsetX, Height + offsetY); 
    } 

    protected override void OnResize(EventArgs e) 
    { 
     var parent = Parent; 
     if (parent != null) 
     { 
      CreateBackgroundBuffer(parent); 
     } 
     base.OnResize(e); 
    } 

    private void GetOffsets(out int offsetX, out int offsetY, Control parent) 
    { 
     var parentPosition = parent.PointToScreen(Point.Empty); 
     offsetY = Top + parentPosition.Y - parent.Top; 
     offsetX = Left + parentPosition.X - parent.Left; 
    } 

    private void UpdateBackgroundBuffer(int offsetX, int offsetY, Control parent) 
    { 
     if (backgroundBuffer == null) 
     { 
      CreateBackgroundBuffer(parent); 
     } 
     Rectangle parentBounds = new Rectangle(0, 0, Width + offsetX, Height + offsetY); 
     skipPaint = true; 
     parent.DrawToBitmap(backgroundBuffer, parentBounds); 
     skipPaint = false; 
    } 

    private void DrawBackground(Graphics graphics, Rectangle bounds) 
    { 
     int offsetX; 
     int offsetY; 
     var parent = Parent; 
     GetOffsets(out offsetX, out offsetY, parent); 
     UpdateBackgroundBuffer(offsetX, offsetY, parent); 
     graphics.DrawImage(backgroundBuffer, bounds, offsetX, offsetY, Width, Height, GraphicsUnit.Pixel); 
    } 

    private void Draw(Graphics graphics) 
    { 
     Rectangle bounds = new Rectangle(0, 0, Width, Height); 
     DrawBackground(graphics, bounds); 

     int alpha = (opacity * 255)/100; 

     using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor))) 
     { 
      if (BackColor != Color.Transparent) 
      { 
       graphics.FillRectangle(bckColor, bounds); 
      } 
     } 

     ColorMatrix colorMatrix = new ColorMatrix(); 
     colorMatrix.Matrix33 = (float)alpha/255; 
     ImageAttributes imageAttr = new ImageAttributes(); 
     imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 

     if (BackgroundImage != null) 
     { 
      graphics.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr); 
     } 

     if (Text != string.Empty) 
     { 
      using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor))) 
      { 
       graphics.DrawString(Text, Font, txtBrush, 5, 5); 
      } 
     } 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     if (!skipPaint) 
     { 
      Graphics graphics = e.Graphics; 
      Draw(graphics); 
     } 
    } 

    protected override void OnBackColorChanged(EventArgs e) 
    { 
     if (Parent != null) 
     { 
      Parent.Invalidate(Bounds, true); 
     } 
     base.OnBackColorChanged(e); 
    } 

    protected override void OnParentBackColorChanged(EventArgs e) 
    { 
     Invalidate(); 
     base.OnParentBackColorChanged(e); 
    } 
} 

Обратите внимание, что метод CreateParams больше не присутствует, и я изменил застройщик.

Поле skipPaint должно знать, когда не рисовать, чтобы иметь возможность сообщить родительскому объекту рисовать в растровое изображение во время OnPaint без бесконечной рекурсии.

backgroundBuffer не должен выполнять двойную буферизацию, но сохранить копию содержимого родителя без обработанного элемента управления. Я обновляю каждую краску, я знаю, что есть более эффективные решения ... *, но этот подход делает его простым и не должен быть узким местом, если у вас слишком много этих элементов управления в одном контейнере.

*: Лучшим решением было бы обновить его каждый раз, когда родительский недействителен. Futhermore разделил его среди всех FadeControls в одном и том же родителе.

+0

Просто был шанс проверить его - отлично работает. Благодарю. – toy4fun