1. 不规则窗口的创建
方法一:
让图片的背景色与显示部分的颜色明显不同,将 FormBorderStyle 属性设置为 None。
将窗体的 BackgroundImage 属性设置为先前创建的位图文件。 设置窗体的 BackColor 图片
背景色,在窗体的构造函数里添加 this.TransparencyKey = this.BackColor; 一切OK。
缺点:1) 不能胜任24位色以上环境。实际上,即使16色的环境,效果也不理想,图片边缘的阴影
显示为窗体背景。不可能对图片进行任意放大。
2) 图片边缘锯齿明显。
方法二:
采用无Alpha通道的位图图片,通过扫描图片的每一点,取出与边缘颜色不同的所以像素,合并到
GraphicsPath中,然后使用这个 GraphicsPath 创建一个 Region并赋给窗体。代码如下:
1
public
static
class
WindowsRegionService
{
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void SetWindowRegion(Form mainForm, Bitmap bmpBack)
{
3
Color TransparentColor = bmpBack.GetPixel(1, 1);
4
SetWindowRegion(mainForm, bmpBack, TransparentColor);
5
}
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private static void SetWindowRegion(Form mainForm, Bitmap bitmap, Color transparentColor)
{
8
mainForm.FormBorderStyle = FormBorderStyle.None;
9
mainForm.BackgroundImageLayout = ImageLayout.None;
10
mainForm.SetBounds(mainForm.Location.X, mainForm.Location.Y, bitmap.Width, bitmap.Height);
11
mainForm.BackgroundImage = bitmap;
12![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
int width = bitmap.Width;
14
int height = bitmap.Height;
15
GraphicsPath gp = new GraphicsPath();
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (int y = 0; y < height; ++y)
{
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (int x = 0; x < width; ++x)
{
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (bitmap.GetPixel(x, y) != transparentColor)
{
19
int x0 = x;
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while (++x < width && bitmap.GetPixel(x, y) != transparentColor)
{
21
}
22
Rectangle rect = new Rectangle(x0, y, x - x0, 1);
23
gp.AddRectangle(rect);
24
}
25
}
26
}
27
mainForm.Region = new Region(gp);
28
}
29
}
这种方法除了可以解决方法一中的不胜任24色以上环境的问题外,与方法一的缺点是一样的。想使用带有阴影
的图片也是不可能的。
方法三(最优解):
这种方法是这个软件最后采用的方法。主要利用 Win32 API 函数 UpdateLayeredWindow 来完成。听起来很简单的
样子,实际上要做的工作是不少的。首先要设置 Window 的ExStyle支持 WS_EX_LAYERED,这可以通过 GetWindowLog
和 SetWindowLong API实现,也可以重载 Form 的 CreateParams 属性。如下:
protected override CreateParams CreateParams {
get {
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= PInvokeService.WS_EX_LAYERED;
return createParams;
}
}
其中 PInvokeService.WS_EX_LAYERED 的值是 0x80000
UpdateLayeredWindow API 也比较复杂,在 C#里调用也不方便,所以还是写在一个 class 里面吧,另外还要绘制
时钟的指针和其他一些东西,这个是不能在 直接重载 Form 的 OnPaint或者处理 Paint事件了,如果你这样做,你
会发现是没有效果的。所以干脆把相关的东西先列出来吧,这里面可能有一些东西跟这个主题无关,但是也不删除了:
1
//
PInvokeService.cs
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public
static
class
PInvokeService
{
3
public static readonly int SE_PRIVILEGE_ENABLED = 0x00000002;
4
public static readonly int TOKEN_QUERY = 0x00000008;
5
public static readonly int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
6
public static readonly string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
7
public static readonly int EWX_LOGOFF = 0x00000000;
8
public static readonly int EWX_SHUTDOWN = 0x00000001;
9
public static readonly int EWX_REBOOT = 0x00000002;
10
public static readonly int EWX_FORCE = 0x00000004;
11
public static readonly int EWX_POWEROFF = 0x00000008;
12
public static readonly int EWX_FORCEIFHUNG = 0x00000010;
13
public static readonly int ULW_ALPHA = 0x02;
14
public static readonly byte AC_SRC_OVER = 0x00;
15
public static readonly byte AC_SRC_ALPHA = 0x01;
16
public static readonly int WS_EX_LAYERED = 0x80000;
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
public static bool ShouldExitWindows = false;
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
[StructLayout(LayoutKind.Sequential)]
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public struct POINT
{
22
public Int32 x;
23
public Int32 y;
24![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public POINT(Int32 x, Int32 y)
{
26
this.x = x;
27
this.y = y;
28
}
29
}
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31
[StructLayout(LayoutKind.Sequential)]
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public struct SIZE
{
33
public Int32 cx;
34
public Int32 cy;
35![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public SIZE(Int32 cx, Int32 cy)
{
37
this.cx = cx;
38
this.cy = cy;
39
}
40
}
41![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
42
[StructLayout(LayoutKind.Sequential, Pack = 1)]
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public struct _BLENDFUNCTION
{
44
public byte BlendOp;
45
public byte BlendFlags;
46
public byte SourceConstantAlpha;
47
public byte AlphaFormat;
48
}
49![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
50
[StructLayout(LayoutKind.Sequential, Pack = 1)]
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public struct TokPriv1Luid
{
52
public int Count;
53
public long Luid;
54
public int Attr;
55
}
56![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
57
[StructLayout(LayoutKind.Sequential)]
58![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public struct MEMORY_INFO
{
59
public uint dwLength;
60
public uint dwMemoryLoad;
61
public uint dwTotalPhys;
62
public uint dwAvailPhys;
63
public uint dwTotalPageFile;
64
public uint dwAvailPageFile;
65
public uint dwTotalVirtual;
66
public uint dwAvailVirtual;
67
}
68![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
69
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
70
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref POINT pptDst, ref SIZE psize,
71
IntPtr hdcSrc, ref POINT pprSrc, Int32 crKey, ref _BLENDFUNCTION pblend, Int32 dwFlags);
72![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
73
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
74
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
75![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
76
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
77
public static extern IntPtr GetDC(IntPtr hWnd);
78![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
79
[DllImport("user32.dll", ExactSpelling = true)]
80
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
81![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
82
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
83
public static extern bool DeleteDC(IntPtr hdc);
84![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
85
[DllImport("gdi32.dll", ExactSpelling = true)]
86
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
87![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
88
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
89
public static extern bool DeleteObject(IntPtr hObject);
90![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
91
[DllImport("user32.dll", ExactSpelling = true, SetLastError = false)]
92
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
93![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
94
[DllImport("user32.dll", ExactSpelling = true, SetLastError = false)]
95
public static extern IntPtr SetForegroundWindow(IntPtr hWnd);
96![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
97
[DllImport("user32.dll", CharSet = CharSet.Auto)]
98
public static extern uint GetWindowLong(IntPtr hwnd, int nIndex);
99![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
100
[DllImport("user32.dll", CharSet = CharSet.Auto)]
101
public static extern uint SetWindowLong(IntPtr hwnd, int nIndex, uint dwNewLong);
102![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
103
[DllImport("kernel32.dll", ExactSpelling = true)]
104
public static extern void GlobalMemoryStatus(ref MEMORY_INFO meminfo);
105![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
106
[DllImport("kernel32.dll", ExactSpelling = true)]
107
public static extern IntPtr GetCurrentProcess();
108![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
109
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
110
public static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
111![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
112
[DllImport("advapi32.dll", SetLastError = true)]
113
public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
114![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
115
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
116
public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
117
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
118![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
119
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
120
public static extern bool ExitWindowsEx(int flg, int rea);
121![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
122
[DllImport("CPPCode.Shutdown.dll", ExactSpelling = true, SetLastError = true)]
123
public static extern void ShowShutdownDialog();
124![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
125![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void DoExitWin(int flg)
{
126
bool ok;
127
TokPriv1Luid tp;
128
IntPtr hproc = GetCurrentProcess();
129
IntPtr htok = IntPtr.Zero;
130
ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
131
tp.Count = 1;
132
tp.Luid = 0;
133
tp.Attr = SE_PRIVILEGE_ENABLED;
134
ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
135
ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
136
ok = ExitWindowsEx(flg, 0);
137
}
138
}
下面的 WindowShapeMaker 类负责窗体形状的创建,实际中,RefreshWindow每隔一秒钟就会调用
一次,以刷新时间。图片是具有 Alpha 通过的 32bpp bitmap, 一般为 PNG 格式。在刷新前,首先
处理这个图片,将传入的图片做一个拷贝,这样一方便是可以根据程序设置缩放图片,一方面是保证
源图片不会被更改。从这个图片创建一个 Graphics 对象,然后在上面画出指针以及其他必要的内容,
最后调用 UpdateLayeredWindow 更新窗体。这里面用到很多 GDI 的操作,如下:
1
//
WindowShapeMaker.cs
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public
class
WindowShapeMaker : IDisposable
{
3
private Form mainForm;
4
private ClockOption clockOpt;
5
private ClockHand clockHand;
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public WindowShapeMaker(Form mainForm, ClockOption clockOpt)
{
8
this.mainForm = mainForm;
9
this.clockOpt = clockOpt;
10
this.clockHand = new ClockHand(mainForm.ClientSize);
11
this.mainForm.SizeChanged += MainFormOnSizeChanged;
12
}
13![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
14![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
~WindowShapeMaker()
{
15
Dispose(false);
16
}
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void RefreshWindow(Bitmap bitmap)
{
19
IntPtr screenDc = PInvokeService.GetDC(IntPtr.Zero);
20
IntPtr memDc = PInvokeService.CreateCompatibleDC(screenDc);
21
IntPtr hBitmap = IntPtr.Zero;
22
IntPtr hOldBitmap = IntPtr.Zero;
23![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
try
{
25
int bitmapWidth = (int)(bitmap.Width * clockOpt.SizeFactor);
26
int bitmapHeight = (int)(bitmap.Height * clockOpt.SizeFactor);
27
28
bitmap = new Bitmap(bitmap, new Size(bitmapWidth, bitmapHeight));
29
mainForm.ClientSize = bitmap.Size;
30![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
using (Graphics g = Graphics.FromImage(bitmap))
{
31
Draw(g);
32
}
33![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
34
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
35
hOldBitmap = PInvokeService.SelectObject(memDc, hBitmap);
36![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
37
PInvokeService.SIZE newSize = new PInvokeService.SIZE(bitmap.Width, bitmap.Height);
38
PInvokeService.POINT sourceLocation = new PInvokeService.POINT(0, 0);
39
PInvokeService.POINT newLocation = new PInvokeService.POINT(mainForm.Location.X, mainForm.Location.Y);
40
PInvokeService._BLENDFUNCTION blend = new PInvokeService._BLENDFUNCTION();
41
blend.BlendOp = PInvokeService.AC_SRC_OVER; // Only works with a 32bpp bitmap
42
blend.BlendFlags = 0;
43
blend.SourceConstantAlpha = clockOpt.PreviewOpacity;
44
blend.AlphaFormat = PInvokeService.AC_SRC_ALPHA;
45![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46
PInvokeService.UpdateLayeredWindow(mainForm.Handle, screenDc, ref newLocation, ref newSize,
47
memDc, ref sourceLocation, 0, ref blend, PInvokeService.ULW_ALPHA);
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} finally
{
49
PInvokeService.ReleaseDC(IntPtr.Zero, screenDc);
50![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (hBitmap != IntPtr.Zero)
{
51
PInvokeService.SelectObject(memDc, hOldBitmap);
52
PInvokeService.DeleteObject(hBitmap);
53
}
54
PInvokeService.DeleteDC(memDc);
55
bitmap.Dispose();
56
}
57
}
58![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
59![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected virtual void Dispose(bool disposing)
{
60![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (disposing)
{
61
this.mainForm.SizeChanged -= MainFormOnSizeChanged;
62
}
63
}
64![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
65![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void Draw(Graphics g)
{
66
StringFormat format = new StringFormat();
67
format.Alignment = StringAlignment.Center;
68
format.LineAlignment = StringAlignment.Center;
69
int width = mainForm.ClientSize.Width;
70
int height = mainForm.ClientSize.Height / 7;
71![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
72![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.ShowDate)
{
73
Rectangle rect = new Rectangle(0, height * 2, width, height);
74
g.DrawString(DateTime.Now.ToString("yyyy-MM-dd"), mainForm.Font, Brushes.Black, rect, format);
75
}
76![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
77![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.ShowAmPm)
{
78
float fltX = clockHand.CentrePointF.X - 8;
79
float fltY = clockHand.CentrePointF.Y + 18;
80
string strAMPM = string.Empty;
81![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (DateTime.Now.Hour > 12)
{
82
strAMPM = "PM";
83![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
84
strAMPM = "AM";
85
}
86![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
87
Rectangle rect = new Rectangle(0, height * 4, width, height);
88
g.DrawString(strAMPM, mainForm.Font, Brushes.Black, rect, format);
89
}
90![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
91
clockHand[0] = (int)(ClockHand.SECONDLENTH * clockOpt.SizeFactor);
92
clockHand[1] = (int)(ClockHand.MIMUTELENTH * clockOpt.SizeFactor);
93
clockHand[2] = (int)(ClockHand.HOURLENTH * clockOpt.SizeFactor);
94
clockHand.DrawClockHand(g);
95
}
96![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
97![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void MainFormOnSizeChanged(object sender, EventArgs args)
{
98
this.clockHand.CentrePointF = new PointF(mainForm.ClientSize.Width / 2f, mainForm.ClientSize.Height / 2f);
99
}
100![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
101![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IDisposable Members#region IDisposable Members
102![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
103![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void Dispose()
{
104
Dispose(true);
105
}
106![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
107
#endregion
108
}
上面用到的 ClockHand 类专门负责绘制时钟的指针,我们假定所有的背景图片都是对称图形,也就是中心一定在图片中心。
指针一般要启用反锯齿,因为除了水平或者垂直的线段外,不启用反锯齿的话效果是相当差的。如下:
1
public
class
ClockHand
{
2
private float[] handLength;
3
private PointF centrePointF;
4![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
public static readonly float SECONDLENTH = 48f;
6
public static readonly float MIMUTELENTH = 40f;
7
public static readonly float HOURLENTH = 30f;
8
public static readonly int Size = 128;
9![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public ClockHand(Size clientSize)
{
11
double factor = (double)clientSize.Width / Size;
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
handLength = new float[]
{
13
(int)(SECONDLENTH * factor),
14
(int)(MIMUTELENTH * factor),
15
(int)(HOURLENTH * factor)
16
};
17
centrePointF = new PointF(clientSize.Width / 2f, clientSize.Height / 2f);
18
}
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void DrawClockHand(Graphics graphics)
{
21
float handAngle;
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
using (Pen pen = new Pen(Color.Red, 0.1f))
{
23
pen.EndCap = LineCap.Round;
24![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
25
SmoothingMode savedMode = graphics.SmoothingMode;
26
graphics.SmoothingMode = SmoothingMode.AntiAlias; // Antialias
27![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
28
// Draw second hand
29
handAngle = (float)(DateTime.Now.Second * Math.PI / 30f); // Angle
30
PointF endPointF = new PointF(CentrePointF.X + (float)(handLength[0] * Math.Sin(handAngle)),
31
CentrePointF.Y - (float)(handLength[0] * Math.Cos(handAngle)));
32![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
33
graphics.DrawLine(pen, CentrePointF, endPointF);
34![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
35
// Draw minute hand
36
handAngle = (float)(DateTime.Now.Minute * Math.PI / 30f);
37
endPointF = new PointF(CentrePointF.X + (float)(handLength[1] * Math.Sin(handAngle)),
38
CentrePointF.Y - (float)(handLength[1] * Math.Cos(handAngle)));
39![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
40
pen.Color = Color.Blue;
41
pen.Width = 1.2f;
42
graphics.DrawLine(pen, CentrePointF, endPointF);
43![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
44
// Draw hour hand
45
handAngle = (float)((DateTime.Now.Hour + DateTime.Now.Minute / 60f) * Math.PI / 6f);
46
endPointF = new PointF(CentrePointF.X + (float)(handLength[2] * Math.Sin(handAngle)),
47
CentrePointF.Y - (float)(handLength[2] * Math.Cos(handAngle)));
48![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
49
pen.Width = 2f;
50
graphics.DrawLine(pen, CentrePointF, endPointF);
51![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
52
graphics.SmoothingMode = savedMode;
53
}
54
}
55![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
56![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public float this[int index]
{
57![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
58![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (index >= 0 && index <= 2)
{
59
return handLength[index];
60
}
61![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
62
return -1;
63
}
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
65![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (index >= 0 && index <= 2)
{
66
handLength[index] = value;
67![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
68
throw new IndexOutOfRangeException();
69
}
70
}
71
}
72![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
73![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public PointF CentrePointF
{
74![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
75
return centrePointF;
76
}
77![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
78
centrePointF = value;
79
}
80
}
81
}
上面的 ClockOption 类保存的是应用程序的设置,如下:
1
[Serializable()]
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public
class
ClockOption : IMementoCapable
{
3
[NonSerialized()]
4
public static readonly string AppPath;
5![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
private bool canMove = true;
7
private bool showAmPm = false;
8
private bool showDate = false;
9
private bool penetrate = false;
10
private bool haveRemind = false;
11
private bool checkBounds = true;
12
private string filename = "default.bmp";
13
private byte mouseEnterOpacity = 255;
14
private byte opacity = 255;
15
private double sizeFactor = 1.0;
16
private Point location = new Point(100, 100);
17
private byte previewOpacity = 255;
18
private string language = "en-US";
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
static ClockOption()
{
21
AppPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
22
}
23![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool CanMove
{
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
26
return this.canMove;
27
}
28![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
29
this.canMove = value;
30
}
31
}
32![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
33![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool ShowAmPm
{
34![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
35
return this.showAmPm;
36
}
37![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
38
this.showAmPm = value;
39
}
40![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
41
}
42![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool ShowDate
{
44![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
45
return this.showDate;
46
}
47![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
48
this.showDate = value;
49
}
50
}
51![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
52![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool Penetrate
{
53![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
54
return this.penetrate;
55
}
56![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
57
this.penetrate = value;
58
}
59
}
60![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
61![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool HaveRemind
{
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
63
return this.haveRemind;
64
}
65![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
66
this.haveRemind = value;
67
}
68
}
69![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
70![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public bool CheckBounds
{
71![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
72
return this.checkBounds;
73
}
74![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
75
this.checkBounds = value;
76
}
77
}
78![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
79![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string Filename
{
80![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
81
return this.filename;
82
}
83![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
84
this.filename = value;
85
}
86
}
87![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
88![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte MouseEnterOpacity
{
89![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
90
return this.mouseEnterOpacity;
91
}
92![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
93
this.mouseEnterOpacity = value;
94
}
95
}
96![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
97![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte Opacity
{
98![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
99
return this.opacity;
100
}
101![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
102
this.opacity = value;
103
}
104
}
105![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
106![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public double SizeFactor
{
107![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
108
return this.sizeFactor;
109
}
110![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
111
this.sizeFactor = value;
112
}
113
}
114![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
115
// This two properties are not saved
116![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Point Location
{
117![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
118
return this.location;
119
}
120![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
121
this.location = value;
122
}
123
}
124![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
125![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte PreviewOpacity
{
126![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
127
return this.previewOpacity;
128
}
129![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
130
this.previewOpacity = value;
131
}
132
}
133![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
134![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string Language
{
135![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
136
return this.language;
137
}
138![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
139
this.language = value;
140
}
141
}
142![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
143![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public ClockOption()
{
144
}
145![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
146![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IMementoCapable Members#region IMementoCapable Members
147![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
148![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Properties CreateMemento()
{
149
Properties properties = new Properties();
150
properties.Set("CanMove", this.canMove);
151
properties.Set("Filename", this.filename);
152
properties.Set("HaveRemind", this.haveRemind);
153
properties.Set("CheckBounds", this.checkBounds);
154
properties.Set("MouseEnterOpacity", this.mouseEnterOpacity);
155
properties.Set("Opacity", this.opacity);
156
properties.Set("Penetrate", this.penetrate);
157
properties.Set("ShowAmPm", this.showAmPm);
158
properties.Set("ShowDate", this.showDate);
159
properties.Set("SizeFactor", this.sizeFactor);
160
properties.Set("Language", this.language);
161![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
162
return properties;
163
}
164![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
165![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void SetMemento(Properties properties)
{
166![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (properties != null)
{
167
this.canMove = properties.Get("CanMove", true);
168
this.filename = properties.Get("Filename", "default.bmp");
169
this.haveRemind = properties.Get("HaveRemind", false);
170
this.checkBounds = properties.Get("CheckBounds", true);
171
this.mouseEnterOpacity = properties.Get("MouseEnterOpacity", (byte)255);
172
this.opacity = properties.Get("Opacity", (byte)255);
173
this.penetrate = properties.Get("Penetrate", false);
174
this.showAmPm = properties.Get("ShowAmPm", false);
175
this.showDate = properties.Get("ShowDate", false);
176
this.sizeFactor = properties.Get("SizeFactor", 1.0);
177
this.language = properties.Get("Language", "en-US");
178
}
179
}
180![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
181
#endregion
182
}
IMementoCapable 接口是很明显是一个备忘录,有 CreateMemento() 和 SetMemento(Properties properties)
两个方法,这次先不讲这个内容。Properties 类也有些复杂,它和IMementoCapable合起来是持久化存储的基础,
实现比.Net序列化更为灵活的持久化存储方式。熟悉SharpDevelop的朋友可能比较清楚,这也不是本次要讨论
的内容。
2. 总在最前
这个比较简单,直接设置 Form 的 TopMost 属性即可。
3.使用鼠标移动钟面。
方法一:消息方式:
1
public
class
MainForm : Form
{
2
private static readonly int WM_SYSCOMMAND = 0x112;
3
private static readonly int SC_MOVE = 0xF010;
4
private static readonly int HTCAPTION = 0x2;
5![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseDown(MouseEventArgs args)
{
7
this.Capture = false;
8
MoveTheWindow();
9
10
base.OnMouseDown(args);
11
}
12![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void MoveTheWindow()
{
14
Message m = new Message();
15
m.HWnd = this.Handle;
16
m.Msg = WM_SYSCOMMAND;
17
m.WParam = new IntPtr(SC_MOVE | HTCAPTION);
18
this.WndProc(ref m);
19
}
20
21
// Other code
22
}
缺点是不易控制窗体移动的范围,因此不能提供钟面只在屏幕范围内活动的选项。没有采用这种方法。
方法二:重载 OnMouseDown 和 OnMouseMove(这是最后采用的方法):
1
public
class
MainForm : Form, IMementoCapable
{
2
private ClockOption clockOpt;
3
private Point mousePosition;
4
// Other fileds
5
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public MainForm(ClockOption clockOpt)
{
7
this.clockOpt = clockOpt;
8
this.mousePosition = Point.Empty;
9
// Other code
10
}
11
12
// Other code
13![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
14![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal void CheckBounds(ref Point location)
{
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.CheckBounds)
{
16
Rectangle rectScreen = Screen.GetWorkingArea(this);
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (location.X < rectScreen.Left)
{
18
location.X = rectScreen.Left;
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else if (location.X + this.ClientSize.Width > rectScreen.Right)
{
20
location.X = rectScreen.Right - this.ClientSize.Width;
21
}
22![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
23![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (location.Y < rectScreen.Top)
{
24
location.Y = rectScreen.Top;
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else if (location.Y + this.ClientSize.Height > rectScreen.Bottom)
{
26
location.Y = rectScreen.Bottom - this.ClientSize.Height;
27
}
28
}
29
}
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseMove(MouseEventArgs e)
{
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (e.Button == MouseButtons.Left)
{
33
// The clock is fixed up on the desktop
34
if (!clockOpt.CanMove)
35
return;
36![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
37
int left = this.Location.X + e.Location.X - this.mousePosition.X;
38
int top = this.Location.Y + e.Location.Y - this.mousePosition.Y;
39
Point location = new Point(left, top);
40
CheckBounds(ref location);
41
this.SetBounds(location.X, location.Y, this.ClientSize.Width, this.ClientSize.Height);
42
clockOpt.Location = this.Location;
43
}
44![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
45
base.OnMouseMove(e);
46
}
47![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseDown(MouseEventArgs e)
{
49![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (e.Button == MouseButtons.Left)
{
50
this.mousePosition = e.Location;
51
}
52![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
53
base.OnMouseDown(e);
54
}
55
56
// Other code
57
}
clockOpt.CheckBounds 表示是否要检查屏幕边界,即是否只允许在屏幕范围内移动钟面。
4.鼠标穿透
1
//
PenetrateService.cs
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public
static
class
PenetrateService
{
3
private static readonly uint WS_EX_LAYERED = 0x80000;
4
private static readonly uint WS_EX_TRANSPARENT = 0x20;
5
private static readonly int GWL_EXSTYLE = -20;
6
//private static readonly int LWA_ALPHA = 0x2;
7![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
[DllImport("user32", EntryPoint = "SetLayeredWindowAttributes")]
9
private static extern int SetLayeredWindowAttributes(
10
IntPtr hwnd,
11
int crKey,
12
int bAlpha,
13
int dwFlags
14
);
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void MousePenetrate(Form mainForm, byte alpha)
{
17
uint intExTemp = PInvokeService.GetWindowLong(mainForm.Handle, GWL_EXSTYLE);
18
PInvokeService.SetWindowLong(mainForm.Handle, GWL_EXSTYLE, intExTemp | WS_EX_TRANSPARENT | WS_EX_LAYERED);
19
//SetLayeredWindowAttributes(mainForm.Handle, 0, alpha, LWA_ALPHA);
20
}
21![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void MouseNotPenetrate(Form mainForm, byte alpha)
{
23
PInvokeService.SetWindowLong(mainForm.Handle, GWL_EXSTYLE, WS_EX_LAYERED);
24
//SetLayeredWindowAttributes(mainForm.Handle, 0, alpha, LWA_ALPHA);
25
}
26
}
注释掉的几行代码是有原因的,在设置了窗体的 WS_EX_LAYERED Style 以后,不能再要这两句,否则这个 Style 失去作用。
如果没有采用这种方式,则需要加上这两句代码。
5. 窗体透明度
你可能最快想到的是直接设置 Form的 Opacity 属性,但是在这里他失效了,不但不起作用,还会使WS_EX_LAYERED失效。
其实在 UpdateLayeredWindow 的调用中,就有透明度的选项的。那句
blend.SourceConstantAlpha = clockOpt.PreviewOpacity;
正是这个作用。由于要支持鼠标经过时的透明度和 正常的透明度,所以ClockOption 里面还有 PreviewOpacity 这个属性。
最后补充一点,今天对源代码做了一些修改,今天添加了多国语言支持, 添加了中文资源,修正了农历算法问题. 添加了对允许
拖动到屏幕以外的选项. Fix了一些小的Bug. 如果你感兴趣,可以重新下载。
好了,至此这次写的也差不多了,好累, 不知道有没有漏写什么东西,唉, 时间也不早了,休息吧^_^。
参考资料:
C# winform中不规则窗体制作的解决方案(已经解决24位色以上不能正常显示问题)
用PNG透明图片和GDI+做不规则透明窗体
这是微软技术的一贯特点,使用简单。但是如果要深入的话,还是要投入不少精力的