系统默认的提示框必须要点击后才能进行后续的动作,并且系统提供的提示框带有一定的提示音,有时候不是很应景
废话不多说,直接上效果图
代码示例:
public static class MessageUTip
{
/// <summary>
/// 显示良好消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。为负时使用全局时长</param>
/// <param name="floating">是否漂浮,不指定则使用全局设置</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowInfo(string text = null, int delay = -1, bool floating = true, bool centerByPoint = false)
{
MessageTip.ShowOk(text, delay, floating, centerByPoint);
}
/// <summary>
/// 显示警告消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。默认1秒,若要使用全局时长请设为-1</param>
/// <param name="floating">是否漂浮。默认不漂浮。若要使用全局设置请设为null</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowWarning(string text = null, int delay = 1500, bool floating = true, bool centerByPoint = false)
{
MessageTip.ShowWarning(text, delay, floating, centerByPoint);
}
/// <summary>
/// 显示出错消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。默认1秒,若要使用全局时长请设为-1</param>
/// <param name="floating">是否漂浮。默认不漂浮。若要使用全局设置请设为null</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowError(string text = null, int delay = 1500, bool floating = true, bool centerByPoint = false)
{
MessageTip.ShowError(text, delay, floating, centerByPoint);
}
}
public static class MessageTip
{
//文本格式。用于测量和绘制
static readonly StringFormat DefStringFormat = StringFormat.GenericTypographic;
/// <summary>
/// 获取或设置默认消息样式
/// </summary>
public static TipStyle DefaultStyle { get; set; }
/// <summary>
/// 获取或设置良好消息样式
/// </summary>
public static TipStyle OkStyle { get; set; }
/// <summary>
/// 获取或设置警告消息样式
/// </summary>
public static TipStyle WarningStyle { get; set; }
/// <summary>
/// 获取或设置出错消息样式
/// </summary>
public static TipStyle ErrorStyle { get; set; }
/// <summary>
/// 获取或设置全局淡入淡出时长(毫秒)。默认100
/// </summary>
public static int Fade { get; set; }
/// <summary>
/// 全局消息停留时长(毫秒)。默认600
/// </summary>
public static int Delay { get; set; }
/// <summary>
/// 是否使用漂浮动画。默认true
/// </summary>
public static bool Floating { get; set; }
static MessageTip()
{
Fade = 100;
Floating = true;
Delay = 600;
DefaultStyle = TipStyle.Gray;
OkStyle = TipStyle.Green;
WarningStyle = TipStyle.Orange;
ErrorStyle = TipStyle.Red;
}
/// <summary>
/// 显示良好消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。为负时使用全局时长</param>
/// <param name="floating">是否漂浮,不指定则使用全局设置</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowOk(string text = null, int delay = -1, bool floating = true, bool centerByPoint = false)
{
// var sds = OkStyle == null ? TipStyle.Gray : OkStyle;
var sds = TipStyle.InfoGray;
Show(text, sds, delay, floating, centerByPoint);
}
/// <summary>
/// 显示警告消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。默认1秒,若要使用全局时长请设为-1</param>
/// <param name="floating">是否漂浮。默认不漂浮。若要使用全局设置请设为null</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowWarning(string text = null, int delay = 1000, bool floating = true, bool centerByPoint = false)
{
var sds = TipStyle.WarGray;
Show(text, sds, delay, floating, centerByPoint);
}
/// <summary>
/// 显示出错消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="delay">消息停留时长(ms)。默认1秒,若要使用全局时长请设为-1</param>
/// <param name="floating">是否漂浮。默认不漂浮。若要使用全局设置请设为null</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void ShowError(string text = null, int delay = 1000, bool floating = true, bool centerByPoint = false)
{
var sds = TipStyle.ErrorGray;
Show(text, sds, delay, floating, centerByPoint);
}
/// <summary>
/// 显示消息
/// </summary>
/// <param name="text">消息文本</param>
/// <param name="style">消息样式。不指定则使用默认样式</param>
/// <param name="delay">消息停留时长(ms)。为负时使用全局时长</param>
/// <param name="floating">是否漂浮,不指定则使用全局设置</param>
/// <param name="point">消息窗显示位置。不指定则智能判定,当由工具栏项(ToolStripItem)弹出时,请指定该参数或使用接收控件的重载</param>
/// <param name="centerByPoint">是否以point参数为中心进行呈现。为false则是在其附近呈现</param>
public static void Show(string text, TipStyle style = null, int delay = -1, bool floating = false, bool centerByPoint = false)
{
var basePoint = DetemineActive();
new Thread(arg =>
{
LayeredWindow layer = null;
try
{
bool floatDown;
Rectangle contentBounds;
layer = new LayeredWindow(CreateTipImage(string.IsNullOrEmpty(text) ? string.Empty : text, (style == null ? (DefaultStyle == null ? TipStyle.Gray : DefaultStyle) : style), out contentBounds))
{
Alpha = 0,
Location = GetLocation(contentBounds, basePoint, centerByPoint, out floatDown),
MouseThrough = true,
TopMost = true,
Tag = new object[] { delay < 0 ? Delay : delay, floating, floatDown }
};
layer.Showing += layer_Showing;
layer.Closing += layer_Closing;
layer.Show();
}
finally
{
if (layer != null)
{
layer.Showing -= layer_Showing;
layer.Closing -= layer_Closing;
layer.Dispose();
}
}
})
{
IsBackground = true,
Name = "T_Showing"
}.Start();
}
static void layer_Showing(object sender, EventArgs e)
{
var layer = (LayeredWindow)sender;
var args = layer.Tag as object[];
if (args == null || args.Length <= 2) return;
var delay = (int)args[0];
var floating = (bool)args[1];
var floatDown = (bool)args[2];
if (floating)
{
//另起线程浮动窗体
new Thread(arg =>
{
int adj = floatDown ? 1 : -1;
while (layer.Visible) //layer.IsDisposed有lock,不适合此循环
{
layer.Top += adj;
Thread.Sleep(30);
}
}) { IsBackground = true, Name = "T_Floating" }.Start();
}
FadeEffect(layer, true);
Thread.Sleep(delay < 0 ? 0 : delay);
layer.Close();
}
static void layer_Closing(object sender, CancelEventArgs e)
{
FadeEffect((LayeredWindow)sender, false);
}
/// <summary>
/// 淡入淡出处理
/// </summary>
private static void FadeEffect(LayeredWindow window, bool fadeIn)
{
byte target = fadeIn ? byte.MaxValue : byte.MinValue;
const int Updateinterval = 10;//动画更新间隔(毫秒)
int step = Fade < Updateinterval ? 0 : (Fade / Updateinterval);
for (int i = 1; i <= step; i++)
{
Thread.Sleep(Updateinterval);
if (i == step) { break; }
var tmp = (double)(fadeIn ? i : (step - i));
window.Alpha = (byte)(tmp / step * 255);
}
window.Alpha = target;
}
/// <summary>
/// 判定活动点
/// </summary>
private static Point DetemineActive()
{
var point = Control.MousePosition;
var focusControl = Control.FromHandle(GetFocus());
if (focusControl is TextBoxBase)//若焦点是文本框,取光标位置
{
var pt = GetCaretPosition();
pt.Y += focusControl.Font.Height / 2;
point = focusControl.PointToScreen(pt);
}
else if (focusControl is ButtonBase)//若焦点是按钮,取按钮中心点
{
point = GetCenterPosition(focusControl);
}
return point;
}
/// <summary>
/// 默认字体
/// </summary>
public static Font Font()
{
return new Font("微软雅黑", FontSize, FontStyle.Regular, GraphicsUnit.Point, 1);
}
public static float FontSize = 12;
public static bool EqualsFloat(this float valueA, float valueB, float eps = 1E-08f)
{
return Math.Abs(valueA - valueB) < eps;
}
static readonly Font DefaultFont = Font();
static Font tmpFont;
/// <summary>
/// 创建消息窗图像,同时输出内容区,用于外部定位
/// </summary>
[MethodImpl(MethodImplOptions.Synchronized)] //避免争用style中的图标画笔等资源,否则在同时调用时容易引发资源占用异常
private static Bitmap CreateTipImage(string text, TipStyle style, out Rectangle contentBounds)
{
var size = Size.Empty;
var iconBounds = Rectangle.Empty;
var textBounds = Rectangle.Empty;
Font font = style.TextFont ?? DefaultFont;
tmpFont = font;
if (style.Icon != null)
{
size = style.Icon.Size;
iconBounds.Size = size;
textBounds.X = size.Width;
}
if (text.Length != 0)
{
if (style.Icon != null)
{
size.Width += style.IconSpacing;
textBounds.X += style.IconSpacing;
}
textBounds.Size = Size.Truncate(GraphicsUtils.MeasureString(text, tmpFont, 0, DefStringFormat));
size.Width += textBounds.Width;
if (size.Height < textBounds.Height)
{
size.Height = textBounds.Height;
}
else if (size.Height > textBounds.Height)//若文字没有图标高,令文字与图标垂直居中,否则与图标平齐
{
textBounds.Y += (size.Height - textBounds.Height) / 2;
}
textBounds.Offset(style.TextOffset);
}
size += style.Padding.Size;
iconBounds.Offset(style.Padding.Left, style.Padding.Top);
textBounds.Offset(style.Padding.Left, style.Padding.Top);
contentBounds = new Rectangle(Point.Empty, size);
var fullBounds = GraphicsUtils.GetBounds(contentBounds, style.Border, style.ShadowRadius, style.ShadowOffset.X, style.ShadowOffset.Y);
contentBounds.Offset(-fullBounds.X, -fullBounds.Y);
iconBounds.Offset(-fullBounds.X, -fullBounds.Y);
textBounds.Offset(-fullBounds.X, -fullBounds.Y);
var bmp = new Bitmap(fullBounds.Width, fullBounds.Height);
Graphics g = null;
Brush backBrush = null;
Brush textBrush = null;
try
{
g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
backBrush = (style.BackBrush ?? (r => new SolidBrush(style.BackColor)))(contentBounds);
GraphicsUtils.DrawRectangle(g, contentBounds,
backBrush,
style.Border,
style.CornerRadius,
style.ShadowColor,
style.ShadowRadius,
style.ShadowOffset.X,
style.ShadowOffset.Y);
if (style.Icon != null)
{
g.DrawImageUnscaled(style.Icon, iconBounds.Location);
}
if (text.Length != 0)
{
textBrush = new SolidBrush(style.TextColor);
g.DrawString(text, tmpFont, textBrush, textBounds.Location, DefStringFormat);
}
g.Flush(FlushIntention.Sync);
return bmp;
}
finally
{
if (g != null)
g.Dispose();
if (backBrush != null)
backBrush.Dispose();
if (textBrush != null)
textBrush.Dispose();
}
}
/// <summary>
/// 根据基准点处理窗体显示位置
/// </summary>
/// <param name="contentBounds">内容区。依据该区域处理定位,而不是根据整个消息窗图像,因为阴影也许偏移很大</param>
/// <param name="basePoint">定位参考点</param>
/// <param name="centerByBasePoint">是否以参考点为中心呈现。false则是在参考点附近呈现</param>
/// <param name="floatDown">指示是否应当向下浮动(当太靠近屏幕顶部时)。默认是向上</param>
private static Point GetLocation(Rectangle contentBounds, Point basePoint, bool centerByBasePoint, out bool floatDown)
{
//以基准点所在屏为界
var screen = Screen.FromPoint(basePoint).Bounds;
var p = basePoint;
p.X -= contentBounds.Width / 2;
//横向处理。距离屏幕左右两边太近时的处理
//多屏下left可能为负,所以right = width - (-left) = width + left
int spacing = 10; //至少距离边缘多少像素
int left, right;
if (p.X < (left = screen.Left + spacing))
{
p.X = left;
}
else if (p.X > (right = screen.Width + screen.Left - spacing - contentBounds.Width))
{
p.X = right;
}
//纵向处理
if (centerByBasePoint)
{
p.Y -= contentBounds.Height / 2;
}
else
{
spacing = 20;//错开基准点上下20像素
p.Y -= contentBounds.Height + spacing;
}
floatDown = false;
if (p.Y < screen.Top + 50)//若太靠屏幕顶部
{
if (!centerByBasePoint)
{
p.Y += contentBounds.Height + 2 * spacing;//在下方错开
}
floatDown = true;//动画改为下降
}
p.Offset(-contentBounds.X, -contentBounds.Y);
return p;
}
/// <summary>
/// 获取控件中心点的屏幕坐标
/// </summary>
private static Point GetCenterPosition(Component controlOrItem)
{
if (controlOrItem is Control)
{
var c = controlOrItem as Control;
var size = c.ClientSize;
return c.PointToScreen(new Point(size.Width / 2, size.Height / 2));
}
if (controlOrItem is ToolStripItem)
{
var item = controlOrItem as ToolStripItem;
var pos = item.Bounds.Location;
pos.X += item.Width / 2;
pos.Y += item.Height / 2;
return item.Owner.PointToScreen(pos);
}
throw new ArgumentException("参数只能是Control或ToolStripItem!");
}
#region Win32 API
/// <summary>
/// 获取输入光标位置,文本框内坐标
/// </summary>
private static Point GetCaretPosition()
{
Point pt;
GetCaretPos(out pt);
return pt;
}
[DllImport("User32.dll", SetLastError = true)]
private static extern bool GetCaretPos(out Point pt);
[DllImport("user32.dll")]
private static extern IntPtr GetFocus();
#endregion
}
internal static class GraphicsUtils
{
/// <summary>
/// 计算指定边界添加描边和阴影后的边界
/// </summary>
public static Rectangle GetBounds(Rectangle rectangle, Border border = null, int shadowRadius = 0, int offsetX = 0, int offsetY = 0)
{
if (border != null)
{
rectangle = border.GetBounds(rectangle);
}
var boundsShadow = DropShadow.GetBounds(rectangle, shadowRadius);
boundsShadow.Offset(offsetX, offsetY);
return Rectangle.Union(rectangle, boundsShadow);
}
/// <summary>
/// 测量文本区尺寸
/// </summary>
public static SizeF MeasureString(string text, Font font, int width = 0, StringFormat stringFormat = null)
{
return MeasureString(text, font, new SizeF(width, width > 0 ? 999999 : 0), stringFormat);
}
/// <summary>
/// 测量文本区尺寸
/// </summary>
public static SizeF MeasureString(string text, Font font, SizeF area, StringFormat stringFormat = null)
{
IntPtr dcScreen = IntPtr.Zero;
Graphics g = null;
try
{
dcScreen = GetDC(IntPtr.Zero);
g = Graphics.FromHdc(dcScreen);
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
return g.MeasureString(text, font, area, stringFormat);
}
finally
{
if (g != null)
g.Dispose();
if (dcScreen != IntPtr.Zero)
{
ReleaseDC(IntPtr.Zero, dcScreen);
}
}
}
/// <summary>
/// 构造圆角矩形
/// </summary>
private static GraphicsPath GetRoundedRectangle(Rectangle rectangle, int radius)
{
//合理化圆角半径
radius = Math.Min(radius, Math.Min(rectangle.Width, rectangle.Height) / 2);
var path = new GraphicsPath();
if (radius < 1)
{
path.AddRectangle(rectangle);
return path;
}
int d = radius * 2;
var arc = new Rectangle(rectangle.X, rectangle.Y, d, d);
path.AddArc(arc, 180, 90);
arc.X = rectangle.X + rectangle.Width - d;
path.AddArc(arc, 270, 90);
arc.Y = rectangle.Y + rectangle.Height - d;
path.AddArc(arc, 0, 90);
arc.X = rectangle.X;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
/// <summary>
/// 绘制矩形。可带圆角、阴影
/// </summary>
/// <param name="g"></param>
/// <param name="rectangle">矩形</param>
/// <param name="brush">用于填充的画刷。为null则不填充</param>
/// <param name="border">边框描述对象。对象无效则不描边</param>
/// <param name="radius">圆角半径</param>
/// <param name="shadowColor">阴影颜色</param>
/// <param name="shadowRadius">阴影羽化半径</param>
/// <param name="offsetX">阴影横向偏移</param>
/// <param name="offsetY">阴影纵向偏移</param>
public static void DrawRectangle(Graphics g, Rectangle rectangle, Brush brush, Border border, int radius, Color shadowColor, int shadowRadius = 0, int offsetX = 0, int offsetY = 0)
{
if (shadowColor.A == 0 || (shadowRadius == 0 && offsetX == 0 && offsetY == 0))
{
DrawRectangle(g, rectangle, brush, border, radius);
return;
}
GraphicsPath path = null;
Bitmap shadow = null;
try
{
path = GetRoundedRectangle(rectangle, radius);
shadow = DropShadow.Create(path, shadowColor, shadowRadius);
var shadowBounds = DropShadow.GetBounds(rectangle, shadowRadius);
shadowBounds.Offset(offsetX, offsetY);
g.DrawImageUnscaled(shadow, shadowBounds.Location);
DrawPath(g, path, brush, border);
}
finally
{
if (path != null)
path.Dispose();
if (shadow != null)
shadow.Dispose();
}
}
/// <summary>
/// 画矩形
/// </summary>
/// <param name="g"></param>
/// <param name="rectangle">矩形</param>
/// <param name="brush">用于填充的画刷。为null则不填充</param>
/// <param name="border">边框描述对象。对象无效则不描边</param>
/// <param name="radius">圆角半径</param>
public static void DrawRectangle(Graphics g, Rectangle rectangle, Brush brush = null, Border border = null, int radius = 0)
{
using (var path = GetRoundedRectangle(rectangle, radius))
{
DrawPath(g, path, brush, border);
}
}
/// <summary>
/// 画路径
/// </summary>
/// <param name="g"></param>
/// <param name="path">路径</param>
/// <param name="brush">用于填充的画刷。为null则不填充</param>
/// <param name="border">边框描述对象。对象无效则不描边</param>
public static void DrawPath(Graphics g, GraphicsPath path, Brush brush = null, Border border = null)
{
if (border == null) return;
if (Border.IsValid(border) && border.Behind)
{
g.DrawPath(border.Pen, path);
}
if (brush != null)
{
g.FillPath(brush, path);
}
if (Border.IsValid(border) && !border.Behind)
{
g.DrawPath(border.Pen, path);
}
}
/// <summary>
/// 阴影生成类
/// </summary>
/* =================================================================================
* 算法源于:http://blog.ivank.net/fastest-gaussian-blur.html
* C#版实现取自:http://stackoverflow.com/questions/7364026/algorithm-for-fast-drop-shadow-in-gdi
* 修改+优化:AhDung
* - unsafe转safe
* - RGB色值处理。解决边缘羽化区域黑化问题
* - 减少运算量
* =================================================================================
*/
private static class DropShadow
{
const int CHANNELS = 4;
const int InflateMultiple = 2;//单边外延radius的倍数
/// <summary>
/// 获取阴影边界。供外部定位阴影用
/// </summary>
/// <param name="path">形状</param>
/// <param name="radius">模糊半径</param>
/// <param name="pathBounds">形状边界</param>
/// <param name="inflate">单边外延像素</param>
public static Rectangle GetBounds(GraphicsPath path, int radius, out Rectangle pathBounds, out int inflate)
{
var bounds = pathBounds = Rectangle.Ceiling(path.GetBounds());
inflate = radius * InflateMultiple;
bounds.Inflate(inflate, inflate);
return bounds;
}
/// <summary>
/// 获取阴影边界
/// </summary>
/// <param name="source">原边界</param>
/// <param name="radius">模糊半径</param>
public static Rectangle GetBounds(Rectangle source, int radius)
{
var inflate = radius * InflateMultiple;
source.Inflate(inflate, inflate);
return source;
}
/// <summary>
/// 创建阴影图片
/// </summary>
/// <param name="path">阴影形状</param>
/// <param name="color">阴影颜色</param>
/// <param name="radius">模糊半径</param>
public static Bitmap Create(GraphicsPath path, Color color, int radius = 5)
{
Rectangle pathBounds;
int inflate;
var bounds = GetBounds(path, radius, out pathBounds, out inflate);
var shadow = new Bitmap(bounds.Width, bounds.Height);
if (color.A == 0)
{
return shadow;
}
//将形状用color色画在阴影区中心
Graphics g = null;
GraphicsPath pathCopy = null;
Matrix matrix = null;
SolidBrush brush = null;
try
{
matrix = new Matrix();
matrix.Translate(-pathBounds.X + inflate, -pathBounds.Y + inflate);//先清除形状原有偏移再向中心偏移
pathCopy = (GraphicsPath)path.Clone(); //基于形状副本操作
pathCopy.Transform(matrix);
brush = new SolidBrush(color);
g = Graphics.FromImage(shadow);
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.FillPath(brush, pathCopy);
}
finally
{
if (g != null)
g.Dispose();
if (brush != null)
brush.Dispose();
if (pathCopy != null)
pathCopy.Dispose();
if (matrix != null)
matrix.Dispose();
}
if (radius <= 0)
{
return shadow;
}
BitmapData data = null;
try
{
data = shadow.LockBits(new Rectangle(0, 0, shadow.Width, shadow.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
//两次方框模糊就能达到不错的效果
//var boxes = DetermineBoxes(radius, 3);
BoxBlur(data, radius, color);
BoxBlur(data, radius, color);
//BoxBlur(shadowData, radius);
return shadow;
}
finally
{
shadow.UnlockBits(data);
}
}
/// <summary>
/// 方框模糊
/// </summary>
/// <param name="data">图像内存数据</param>
/// <param name="radius">模糊半径</param>
/// <param name="color">透明色值</param>
#if UNSAFE
private static unsafe void BoxBlur(BitmapData data, int radius, Color color)
#else
private static void BoxBlur(BitmapData data, int radius, Color color)
#endif
{
#if UNSAFE //unsafe项目下请定义编译条件:UNSAFE
IntPtr p1 = data1.Scan0;
#else
byte[] p1 = new byte[data.Stride * data.Height];
Marshal.Copy(data.Scan0, p1, 0, p1.Length);
#endif
//色值处理
//这步的意义在于让图片中的透明像素拥有color的色值(但仍然保持透明)
//这样在混合时才能合出基于color的颜色(只是透明度不同),
//否则它是与RGB(0,0,0)合,就会得到乌黑的渣特技
byte R = color.R, G = color.G, B = color.B;
for (int i = 3; i < p1.Length; i += 4)
{
if (p1[i] == 0)
{
p1[i - 1] = R;
p1[i - 2] = G;
p1[i - 3] = B;
}
}
byte[] p2 = new byte[p1.Length];
int radius2 = 2 * radius + 1;
int First, Last, Sum;
int stride = data.Stride,
width = data.Width,
height = data.Height;
//只处理Alpha通道
//横向
for (int r = 0; r < height; r++)
{
int start = r * stride;
int left = start;
int right = start + radius * CHANNELS;
First = p1[start + 3];
Last = p1[start + stride - 1];
Sum = (radius + 1) * First;
for (int column = 0; column < radius; column++)
{
Sum += p1[start + column * CHANNELS + 3];
}
for (var column = 0; column <= radius; column++, right += CHANNELS, start += CHANNELS)
{
Sum += p1[right + 3] - First;
p2[start + 3] = (byte)(Sum / radius2);
}
for (var column = radius + 1; column < width - radius; column++, left += CHANNELS, right += CHANNELS, start += CHANNELS)
{
Sum += p1[right + 3] - p1[left + 3];
p2[start + 3] = (byte)(Sum / radius2);
}
for (var column = width - radius; column < width; column++, left += CHANNELS, start += CHANNELS)
{
Sum += Last - p1[left + 3];
p2[start + 3] = (byte)(Sum / radius2);
}
}
//纵向
for (int column = 0; column < width; column++)
{
int start = column * CHANNELS;
int top = start;
int bottom = start + radius * stride;
First = p2[start + 3];
Last = p2[start + (height - 1) * stride + 3];
Sum = (radius + 1) * First;
for (int row = 0; row < radius; row++)
{
Sum += p2[start + row * stride + 3];
}
for (int row = 0; row <= radius; row++, bottom += stride, start += stride)
{
Sum += p2[bottom + 3] - First;
p1[start + 3] = (byte)(Sum / radius2);
}
for (int row = radius + 1; row < height - radius; row++, top += stride, bottom += stride, start += stride)
{
Sum += p2[bottom + 3] - p2[top + 3];
p1[start + 3] = (byte)(Sum / radius2);
}
for (int row = height - radius; row < height; row++, top += stride, start += stride)
{
Sum += Last - p2[top + 3];
p1[start + 3] = (byte)(Sum / radius2);
}
}
#if !UNSAFE
Marshal.Copy(p1, 0, data.Scan0, p1.Length);
#endif
}
// private static int[] DetermineBoxes(double Sigma, int BoxCount)
// {
// double IdealWidth = Math.Sqrt((12 * Sigma * Sigma / BoxCount) + 1);
// int Lower = (int)Math.Floor(IdealWidth);
// if (Lower % 2 == 0)
// Lower--;
// int Upper = Lower + 2;
//
// double MedianWidth = (12 * Sigma * Sigma - BoxCount * Lower * Lower - 4 * BoxCount * Lower - 3 * BoxCount) / (-4 * Lower - 4);
// int Median = (int)Math.Round(MedianWidth);
//
// int[] BoxSizes = new int[BoxCount];
// for (int i = 0; i < BoxCount; i++)
// BoxSizes[i] = (i < Median) ? Lower : Upper;
// return BoxSizes;
// }
}
#region Win32 API
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
#endregion
}
/// <summary>
/// 存储一系列边框要素并产生合适的画笔
/// <para>- 边框居中+奇数粗度时是介于两像素之间画,所以粗细在视觉上不精确,建议错开任一条件</para>
/// </summary>
internal class Border : IDisposable
{
//编写本类除了整合边框信息外,更重要的原因是如果不对画笔做额外处理,
//Draw出来的边框是不理想的。本类的原理是:
// - 偶数边框(这是得到理想效果的前提)
// - 再利用画笔的CompoundArray属性将边框裁切掉一半,
// 同时根据不同参数偏移描边位置,达到可内可外可居中的效果
float[] _compoundArray;
/// <summary>
/// 根据_direction处理线段
/// </summary>
float[] CompoundArray
{
get
{
if (_compoundArray == null)
{
_compoundArray = new float[2];
}
switch (_direction)
{
case Direction.Middle: goto default;
case Direction.Inner: _compoundArray[0] = 0.5f; _compoundArray[1] = 1f; break;
case Direction.Outer: _compoundArray[0] = 0f; _compoundArray[1] = 0.5f; break;
default: _compoundArray[0] = 0.25f; _compoundArray[1] = 0.75f; break;
}
return _compoundArray;
}
}
readonly Pen _pen;
/// <summary>
/// 获取用于画本边框的画笔。建议销毁本类而不是该画笔
/// </summary>
public Pen Pen
{
get { return _pen; }
}
/// <summary>
/// 边框宽度。默认1
/// </summary>
public int Width
{
get { return (int)Pen.Width / 2; }
set { Pen.Width = value * 2; }
}
/// <summary>
/// 边框颜色
/// </summary>
public Color Color
{
get { return Pen.Color; }
set { Pen.Color = value; }
}
Direction _direction;
/// <summary>
/// 边框位置。默认居中
/// </summary>
public Direction Direction
{
get { return _direction; }
set
{
if (_direction == value) { return; }
_direction = value;
Pen.CompoundArray = CompoundArray;
}
}
/// <summary>
/// 描边是否躲在填充之后。默认false
/// <para>- 如果躲,则处于内部的部分会被填充遮挡,反之则是填充被这部分边框遮挡</para>
/// <para>- 此属性仅供外部在绘制时确定描边和填充的次序</para>
/// </summary>
public bool Behind { get; set; }
/// <summary>
/// 获取指定矩形加上本边框后的边界
/// </summary>
public Rectangle GetBounds(Rectangle rectangle)
{
if (!IsValid() || _direction == Direction.Inner)
{
return rectangle;
}
var inflate = _direction == Direction.Middle
? (int)Math.Ceiling(Width / 2d)
: Width;
rectangle.Inflate(inflate, inflate);
return rectangle;
}
public Border(Color color) : this(new Pen(color), false) { }
/// <summary>
/// 基于现有画笔的副本构造
/// </summary>
public Border(Pen pen) : this(pen, true) { }
protected Border(Pen pen, bool useCopy)
{
_pen = useCopy ? (Pen)pen.Clone() : pen;
_pen.Alignment = PenAlignment.Center;
_pen.Width = Convert.ToInt32(_pen.Width * 2);
_pen.CompoundArray = CompoundArray;
}
/// <summary>
/// 是否有效边框。无宽度或完全透明视为无效
/// </summary>
public bool IsValid() { return Width > 0 && (Pen.PenType != PenType.SolidColor || Color.A > 0); }
/// <summary>
/// 是否有效边框。无宽度或完全透明视为无效
/// </summary>
public static bool IsValid(Border border) { return border != null && border.IsValid(); }
/// <summary>
/// 析构函数
/// </summary>
public void Dispose()
{
if (Pen != null)
Pen.Dispose();
}
}
/// <summary>
/// 描边位置
/// </summary>
internal enum Direction
{
/// <summary>
/// 居中
/// </summary>
Middle,
/// <summary>
/// 内部
/// </summary>
Inner,
/// <summary>
/// 外部
/// </summary>
Outer
}
//干脆自建一个委托,不依赖Func了
/// <summary>
/// 画刷选择器委托
/// </summary>
public delegate Brush BrushSelector<in T>(T arg);
/// <summary>
/// 消息窗样式
/// </summary>
public sealed class TipStyle : IDisposable
{
bool _isPresets;
bool _keepFont;
bool _keepIcon;
readonly Border _border;
/// <summary>
/// 获取边框信息。内部用
/// </summary>
internal Border Border { get { return _border; } }
/// <summary>
/// 获取或设置图标。默认null
/// </summary>
public Bitmap Icon { get; set; }
/// <summary>
/// 获取或设置图标与文本的间距。默认为3像素
/// </summary>
public int IconSpacing { get; set; }
/// <summary>
/// 获取或设置文本字体。默认12号的消息框文本
/// </summary>
public Font TextFont { get; set; }
/// <summary>
/// 获取或设置文本偏移,用于微调
/// </summary>
public Point TextOffset { get; set; }
/// <summary>
/// 获取或设置文本颜色(默认黑色)
/// </summary>
public Color TextColor { get; set; }
/// <summary>
/// 获取或设置背景颜色(默认浅白)
/// <para>- 若想呈现多色及复杂背景,请使用BackBrush属性,当后者不为null时,本属性被忽略</para>
/// </summary>
public Color BackColor { get; set; }
/// <summary>
/// 获取或设置背景画刷生成方法
/// <para>- 是个委托,入参矩形由绘制函数传入,表示内容区,便于构造画刷</para>
/// <para>- 默认null,为null时使用BackColor绘制单色背景</para>
/// <para>- 方法返回的画刷需释放</para>
/// </summary>
public BrushSelector<Rectangle> BackBrush { get; set; }
/// <summary>
/// 获取或设置边框颜色(默认深灰)
/// </summary>
public Color BorderColor
{
get { return _border.Color; }
set { _border.Color = value; }
}
/// <summary>
/// 获取或设置边框粗细(默认1)
/// </summary>
public int BorderWidth
{
get { return _border.Width / 2; }
set { _border.Width = value * 2; }
}
/// <summary>
/// 获取或设置圆角半径(默认3)
/// </summary>
public int CornerRadius { get; set; }
/// <summary>
/// 获取或设置阴影颜色(默认深灰)
/// </summary>
public Color ShadowColor { get; set; }
/// <summary>
/// 获取或设置阴影羽化半径(默认4)
/// </summary>
public int ShadowRadius { get; set; }
/// <summary>
/// 获取或设置阴影偏移(默认x=0,y=3)
/// </summary>
public Point ShadowOffset { get; set; }
/// <summary>
/// 获取或设置四周空白(默认left,right=10; top,bottom=5)
/// </summary>
public Padding Padding { get; set; }
/// <summary>
/// 初始化样式
/// </summary>
public TipStyle()
{
_border = new Border(PresetsResources.Colors[0, 0])
{
Behind = true,
Width = 2
};
IconSpacing = 5;
TextFont = new Font("微软雅黑", 12, FontStyle.Regular, GraphicsUnit.Point, 1);
var fontName = TextFont.Name;
if (fontName == "宋体") { TextOffset = new Point(1, 1); }
TextColor = Color.Black;
BackColor = Color.FromArgb(252, 252, 252);
CornerRadius = 3;
ShadowColor = PresetsResources.Colors[0, 2];
ShadowRadius = 4;
ShadowOffset = new Point(0, 3);
Padding = new Padding(10, 5, 10, 5);
}
/// <summary>
/// 清理本类使用的资源
/// </summary>
/// <param name="keepFont">是否保留字体</param>
/// <param name="keepIcon">是否保留图标</param>
public void Clear(bool keepFont = false, bool keepIcon = false)
{
_keepFont = keepFont;
_keepIcon = keepIcon;
((IDisposable)this).Dispose();
}
static TipStyle _Infogray;
/// <summary>
/// 预置的绿色样式
/// </summary>
public static TipStyle InfoGray
{
get
{
if (_Infogray == null)
_Infogray = CreatePresetsStyle(0, 1);
return _Infogray;
}
}
static TipStyle _errorgray;
/// <summary>
/// 预置的灰色样式
/// </summary>
public static TipStyle ErrorGray
{
get
{
if (_errorgray == null)
_errorgray = CreatePresetsStyle(0, 3);
return _errorgray;
}
}
static TipStyle _Wargray;
/// <summary>
/// 预置的橙色样式
/// </summary>
public static TipStyle WarGray
{
get
{
if (_Wargray == null)
_Wargray = CreatePresetsStyle(0, 2);
return _Wargray;
}
}
static TipStyle _gray;
/// <summary>
/// 预置的灰色样式
/// </summary>
public static TipStyle Gray
{
get
{
if (_gray == null)
_gray = CreatePresetsStyle(0);
return _gray;
}
}
static TipStyle _green;
/// <summary>
/// 预置的红色样式
/// </summary>
public static TipStyle Green
{
get
{
if (_green == null)
_green = CreatePresetsStyle(1);
return _green;
}
}
static TipStyle _orange;
/// <summary>
/// 预置的橙色样式
/// </summary>
public static TipStyle Orange
{
get
{
if (_orange == null)
_orange = CreatePresetsStyle(2);
return _orange;
}
}
static TipStyle _red;
/// <summary>
/// 预置的红色样式
/// </summary>
public static TipStyle Red
{
get
{
if (_red == null)
_red = CreatePresetsStyle(3);
return _red;
}
}
private static TipStyle CreatePresetsStyle(int index)
{
var style = new TipStyle
{
Icon = PresetsResources.Icons[index],
BorderColor = PresetsResources.Colors[index, 0],
ShadowColor = PresetsResources.Colors[index, 2],
_isPresets = true
};
style.BackBrush = r =>
{
var brush = new LinearGradientBrush(r,
PresetsResources.Colors[index, 1],
Color.White,
LinearGradientMode.Horizontal);
brush.SetBlendTriangularShape(0.5f);
return brush;
};
return style;
}
private static TipStyle CreatePresetsStyle(int index, int index2)
{
var style = new TipStyle
{
Icon = PresetsResources.Icons[index2],
BorderColor = PresetsResources.Colors[index, 0],
ShadowColor = PresetsResources.Colors[index, 2],
_isPresets = true
};
style.BackBrush = r =>
{
var brush = new LinearGradientBrush(r,
PresetsResources.Colors[index, 1],
Color.White,
LinearGradientMode.Horizontal);
brush.SetBlendTriangularShape(0.5f);
return brush;
};
return style;
}
bool _disposed;
[Obsolete("请改用Clear指定是否清理字体和图标")]
[MethodImpl(MethodImplOptions.Synchronized)]
void IDisposable.Dispose()
{
if (_disposed || _isPresets)//不销毁预置对象
{
return;
}
_border.Dispose();
BackBrush = null;
if (!_keepFont && TextFont != null && !TextFont.IsSystemFont)
{
TextFont.Dispose();
TextFont = null;
}
if (!_keepIcon && Icon != null)
{
Icon.Dispose();
Icon = null;
}
_disposed = true;
}
/// <summary>
/// 预置资源
/// </summary>
private static class PresetsResources
{
public static readonly Color[,] Colors =
{
//边框色、背景色、阴影色
/*灰*/ {Color.Gray, Color.FromArgb(245, 245,245), Color.FromArgb(110, 0, 0, 0)},
/*绿*/ {Color.Green, Color.LightGreen, Color.FromArgb(150, 0,150, 0)},
/*橙*/ {Color.Orange, Color.FromArgb(251, 245, 233), Color.FromArgb(150,250,100, 0)},
/*红*/ {Color.Red, Color.FromArgb(251, 238, 238), Color.FromArgb(140,255, 30, 30)}
};
//CreateIcon依赖Colors,所以需在Colors后初始化
public static readonly Bitmap[] Icons =
{
CreateIcon(0),
CreateIcon(1),
CreateIcon(2),
CreateIcon(3)
};
/// <summary>
/// 创建图标
/// </summary>
/// <param name="index">0=i;1=√;2=!;3=×;其余=null</param>
private static Bitmap CreateIcon(int index)
{
if (index < 0 || index > 3)
{
return null;
}
Graphics g = null;
Pen pen = null;
Brush brush = null;
Bitmap bmp = null;
try
{
bmp = new Bitmap(24, 24);
g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
var color = Colors[index, 0];
if (index == 0) //i
{
brush = new SolidBrush(Color.FromArgb(103, 148, 186));
g.FillEllipse(brush, 3, 3, 18, 18);
pen = new Pen(Colors[index, 1], 2);
g.DrawLine(pen, new Point(12, 6), new Point(12, 8));
g.DrawLine(pen, new Point(12, 10), new Point(12, 18));
}
else if (index == 1) //√
{
pen = new Pen(color, 4);
g.DrawLines(pen, new[] { new Point(3, 11), new Point(10, 18), new Point(20, 5) });
}
else if (index == 2) //!
{
var points = new[] { new Point(12, 3), new Point(3, 20), new Point(21, 20) };
pen = new Pen(color, 2) { LineJoin = LineJoin.Bevel };
g.DrawPolygon(pen, points);
brush = new SolidBrush(color);
g.FillPolygon(brush, points);
pen.Color = Colors[index, 1];
g.DrawLine(pen, new Point(12, 8), new Point(12, 15));
g.DrawLine(pen, new Point(12, 17), new Point(12, 19));
}
else //×
{
pen = new Pen(color, 4);
g.DrawLine(pen, 5, 5, 19, 19);
g.DrawLine(pen, 5, 19, 19, 5);
}
return bmp;
}
catch
{
if (bmp != null)
bmp.Dispose();
throw;
}
finally
{
if (pen != null)
pen.Dispose();
if (brush != null)
brush.Dispose();
if (g != null)
g.Dispose();
}
}
}
}
/// <summary>
/// 简易层窗体
/// </summary>
[SuppressUnmanagedCodeSecurity]
public class LayeredWindow : IDisposable
{
const string ClassName = "AhDungLayeredWindow";
static WndProcDelegate _wndProc;
readonly object syncObj = new object();
readonly PointOrSize _size;
IntPtr _dcMemory;
IntPtr _hBmp;
IntPtr _oldObj;
IntPtr _activeWindow; //用于模式显示时,记录并disable原窗体,然后在本类关闭后enable它
bool _continueLoop = true;
short _wndClass;
IntPtr _hWnd;
BLENDFUNCTION _blend = new BLENDFUNCTION
{
BlendOp = 0, //AC_SRC_OVER
BlendFlags = 0,
SourceConstantAlpha = 255, //透明度
AlphaFormat = 1 //AC_SRC_ALPHA
};
/// <summary>
/// 窗体显示时
/// </summary>
public event EventHandler Showing;
/// <summary>
/// 窗体关闭时
/// </summary>
public event CancelEventHandler Closing;
static IntPtr _hInstance;
static IntPtr HInstance
{
get
{
if (_hInstance == IntPtr.Zero)
{
Assembly assembly = Assembly.GetEntryAssembly();
if (assembly != null)
{
_hInstance = Marshal.GetHINSTANCE(assembly.ManifestModule);
}
}
return _hInstance;
}
}
/// <summary>
/// 获取窗体位置。内部用
/// </summary>
PointOrSize LocationInternal { get { return new PointOrSize(_left, _top); } }
/// <summary>
/// 指示窗体是否已显示
/// </summary>
public bool Visible { get; private set; }
int _left;
/// <summary>
/// 获取或设置左边缘坐标
/// </summary>
public int Left
{
get { return _left; }
set
{
if (_left == value) { return; }
_left = value;
Update();
}
}
int _top;
/// <summary>
/// 获取或设置上边缘坐标
/// </summary>
public int Top
{
get { return _top; }
set
{
if (_top == value) { return; }
_top = value;
Update();
}
}
/// <summary>
/// 获取或设置定位
/// </summary>
public Point Location
{
get { return new Point(_left, _top); }
set
{
if (_left == value.X && _top == value.Y) { return; }
_left = value.X;
_top = value.Y;
Update();
}
}
/// <summary>
/// 获取窗体宽度
/// </summary>
public int Width { get; private set; }
/// <summary>
/// 获取窗体高度
/// </summary>
public int Height { get; private set; }
/// <summary>
/// 获取或设置窗体透明度。0=完全透明;255=不透明
/// </summary>
public byte Alpha
{
get { return _blend.SourceConstantAlpha; }
set
{
if (_blend.SourceConstantAlpha == value) { return; }
_blend.SourceConstantAlpha = value;
Update();
}
}
/// <summary>
/// 获取或设置名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 指示窗体是否以模式状态打开
/// </summary>
public bool IsModal { get; private set; }
/// <summary>
/// 是否置顶
/// </summary>
public bool TopMost { get; set; }
/// <summary>
/// 是否在显示后激活本窗体。模式显示时强制为true
/// </summary>
public bool Activation { get; set; }
/// <summary>
/// 是否在任务栏显示
/// </summary>
public bool ShowInTaskbar { get; set; }
/// <summary>
/// 是否让鼠标事件穿透本窗体
/// </summary>
public bool MouseThrough { get; set; }
/// <summary>
/// 指示窗体是否已释放
/// </summary>
public bool IsDisposed
{
get { lock (syncObj) { return _disposed; } }
}
/// <summary>
/// 获取或设置自定对象
/// </summary>
public object Tag { get; set; }
/// <summary>
/// 创建层窗体
/// </summary>
/// <param name="bmp">窗体图像</param>
/// <param name="keepBmp">是否保留图像,为false会销毁图像</param>
public LayeredWindow(Bitmap bmp, bool keepBmp = false)
{
try
{
RegisterWindowClass();
//初始化绘制相关
_dcMemory = CreateCompatibleDC(IntPtr.Zero);
_hBmp = bmp.GetHbitmap(Color.Empty);
_oldObj = SelectObject(_dcMemory, _hBmp);
Width = bmp.Width;
Height = bmp.Height;
_size = new PointOrSize(Width, Height);
}
finally
{
if (!keepBmp)
{
bmp.Dispose();
}
}
}
/// <summary>
/// 注册私有窗口类
/// </summary>
private void RegisterWindowClass()
{
if (_wndProc == null)
{
_wndProc = WndProc;
}
var wc = new WNDCLASS
{
hInstance = HInstance,
lpszClassName = ClassName,
lpfnWndProc = _wndProc,
hCursor = LoadCursor(IntPtr.Zero, 32512/*IDC_ARROW*/),
};
_wndClass = RegisterClass(wc);
if (_wndClass == 0)
{
//ERROR_CLASS_ALREADY_EXISTS
if (Marshal.GetLastWin32Error() != 0x582)
{
throw new Win32Exception();
}
}
}
/// <summary>
/// 创建窗口
/// </summary>
private void CreateWindow()
{
int exStyle = 0x80000; //WS_EX_LAYERED
if (TopMost) { exStyle |= 0x8; } //WS_EX_TOPMOST
if (!Activation) { exStyle |= 0x08000000; } //WS_EX_NOACTIVATE
if (MouseThrough) { exStyle |= 0x20; } //WS_EX_TRANSPARENT
if (ShowInTaskbar) { exStyle |= 0x40000; } //WS_EX_APPWINDOW
int style = unchecked((int)0x80000000) //WS_POPUP。不能加WS_VISIBLE,会抢焦点,改用ShowWindow显示
| 0x80000; //WS_SYSMENU
_hWnd = CreateWindowEx(exStyle, ClassName, null, style,
0, 0, 0, 0, //坐标尺寸全由UpdateLayeredWindow接管,这里无所谓
IntPtr.Zero, IntPtr.Zero, HInstance, IntPtr.Zero);
if (_hWnd == IntPtr.Zero)
{
throw new Win32Exception();
}
}
private int DoMessageLoop()
{
var m = new MSG();
int result;
while (_continueLoop && (result = GetMessage(ref m, IntPtr.Zero, 0, 0)) != 0)
{
if (result == -1)
{
return Marshal.GetLastWin32Error();
}
TranslateMessage(ref m);
DispatchMessage(ref m);
}
return 0;
}
protected virtual IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
//Debug.WriteLine((hWnd == _hWnd) + ":0x" + Convert.ToString(msg, 16), "WndProc");
switch (msg)
{
case 0x10://WM_CLOSE
Close();
break;
case 0x2://WM_DESTROY
EnableWindow(_activeWindow, true);
_continueLoop = false;
break;
}
return DefWndProc(hWnd, msg, wParam, lParam);
}
protected virtual IntPtr DefWndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
private void ShowCore(bool modal)
{
if (IsDisposed)
{
throw new ObjectDisposedException(Name ?? string.Empty);
}
lock (syncObj)
{
if (Visible) { return; }
if (modal)
{
IsModal = true;
Activation = true;
_activeWindow = GetActiveWindow();
EnableWindow(_activeWindow, false);
}
CreateWindow();
ShowWindow(_hWnd, Activation ? 5/*SW_SHOW*/: 8/*SW_SHOWNA*/);
Visible = true;
}
OnShowing(EventArgs.Empty);
if (IsDisposed) { return; }//Showing事件中也许会关闭窗体
Update();
if (modal)
{
var result = DoMessageLoop();
SetActiveWindow(_activeWindow);
if (result != 0)
{
throw new Win32Exception(result);
}
}
}
/// <summary>
/// 显示窗体
/// </summary>
public void Show() { ShowCore(false); }
protected virtual void OnShowing(EventArgs e)
{
var handle = Showing;
if (handle != null)
handle.Invoke(this, e);
}
protected virtual void OnClosing(CancelEventArgs e)
{
var handle = Closing;
if (handle != null)
handle.Invoke(this, e);
}
/// <summary>
/// 更新窗体
/// </summary>
protected virtual void Update()
{
if (!Visible) { return; }
//后续更新其实在nt6开启桌面主题的情况下,一干参数可以为null,
//但是为了兼容其他情况,还是都指定
if (!UpdateLayeredWindow(_hWnd,
IntPtr.Zero, LocationInternal, _size,
_dcMemory, PointOrSize.Empty,
0, ref _blend, 2/*ULW_ALPHA*/))
{
//忽略窗体句柄无效ERROR_INVALID_WINDOW_HANDLE
if (Marshal.GetLastWin32Error() == 0x578) { return; }
throw new Win32Exception();
}
}
/// <summary>
/// 关闭并销毁窗体
/// </summary>
public void Close()
{
var e = new CancelEventArgs();
OnClosing(e);
if (!e.Cancel)
{
Visible = false;
Dispose();
}
}
bool _disposed;
/// <summary>
/// 析构函数
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
lock (syncObj)
{
if (_disposed) { return; }
if (disposing)
{
Tag = null;
}
//销毁窗体
Debug.WriteLineIf(!DestroyWindow(_hWnd), "Failed", "DestroyWindow");
_hWnd = IntPtr.Zero;
//注销窗口类
//窗口类是共用的,每个实例都尝试注册和注销
//实际效果就是先开的注册,后关的注销
if (_wndClass != 0)
{
if (UnregisterClass(ClassName, HInstance))
{
_wndProc = null;//只有注销成功时才解绑窗口过程
}
_wndClass = 0;
}
Debug.WriteLineIf(SelectObject(_dcMemory, _oldObj) == IntPtr.Zero, "Failed", "Restore _oldObj");
Debug.WriteLineIf(!DeleteDC(_dcMemory), "Failed", "Delete _dcMemory");
Debug.WriteLineIf(!DeleteObject(_hBmp), "Failed", "Delete _hBmp");
_oldObj = IntPtr.Zero;
_dcMemory = IntPtr.Zero;
_hBmp = IntPtr.Zero;
_disposed = true;
}
}
//窗口过程委托
private delegate IntPtr WndProcDelegate(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
#region Win32 API
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnableWindow(IntPtr hWnd, bool bEnable);
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int iconId);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern short RegisterClass(WNDCLASS wc);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetMessage(ref MSG msg, IntPtr hWnd, int wMsgFilterMin, int wMsgFilterMax);
[DllImport("user32.dll")]
private static extern IntPtr DispatchMessage(ref MSG msg);
[DllImport("user32.dll")]
private static extern bool TranslateMessage(ref MSG msg);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UpdateLayeredWindow(IntPtr hWnd, IntPtr hdcDst, PointOrSize pptDst, PointOrSize pSizeDst, IntPtr hdcSrc, PointOrSize pptSrc, int crKey, ref BLENDFUNCTION pBlend, int dwFlags);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyWindow(IntPtr hWnd);
// ReSharper disable NotAccessedField.Local
// ReSharper disable FieldCanBeMadeReadOnly.Local
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable UnusedField.Compiler
#pragma warning disable 414
#pragma warning disable 649
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class WNDCLASS
{
public int style;
public WndProcDelegate lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
}
[StructLayout(LayoutKind.Sequential)]
private struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSG
{
public IntPtr HWnd;
public int Message;
public IntPtr WParam;
public IntPtr LParam;
public int Time;
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
private class PointOrSize
{
public int XOrWidth, YOrHeight;
public static readonly PointOrSize Empty = new PointOrSize();
public PointOrSize() { XOrWidth = 0; YOrHeight = 0; }
public PointOrSize(int xOrWidth, int yOrHeight)
{
XOrWidth = xOrWidth;
YOrHeight = yOrHeight;
}
}
// ReSharper restore UnusedField.Compiler
// ReSharper restore MemberCanBePrivate.Local
// ReSharper restore FieldCanBeMadeReadOnly.Local
// ReSharper restore NotAccessedField.Local
#pragma warning restore 649
#pragma warning restore 414
#endregion
}