[英]Fading control in C#
我想構建一個支持Opcacity
屬性的Control
派生類。
這個控件可以同時容納文本和圖像,並且可以將它們淡出和放入。
這是我的代碼:
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
設置為ControlStyles.DoubleBuffer
將使控件在窗體上不可見。
歡迎任何建議。
我無法同時使用雙緩沖區和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在同一父母的所有FadeControl中共享它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.