2013-03-19 120 views
6

我正在构建一个Control派生类,它支持Opcacity属性。
这种控制可以同时承载的文字和图片,并会比布尔淡化出来,并在
这里是我的代码: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时,这是不可避免的。所以不要使用它。 – 2013-03-19 14:08:28

+0

[淡入一个面板 - Windows窗体]的可能重复(http://stackoverflow.com/questions/10178559/fade-a-panel-windows-forms) – 2013-03-19 14:08:36

+0

链接上的控件不支持透明背景色。我希望cotrol能够在包含PNG图像和文本的情况下淡入淡出。 – toy4fun 2013-03-19 14:48:16

回答

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不是实现双缓冲,而是保留父级内容的副本而不提供控制。每次更新都会更新,我知道还有更高效的解决方案...... *但是这种方法可以简化并且不应该成为瓶颈,除非您在同一个容器上有太多的这些控件。

*:更好的解决方案是每次父节点无效时更新它。在同一个家长的所有FadeControls中,还有更多的分享。

+0

刚刚有机会测试它 - 完美的作品。谢谢。 – toy4fun 2013-03-30 16:02:36