多年前寫的時鐘控件,發布出來,供大家參考。 先看效果: 首先,制作一個從UserControl繼承的時鐘控件,使用雙緩沖繪制。由於是幾年前的水平,一些代碼寫到了屬性中,也懶得再改了,感興趣的,可以改得更規范些。 //ClockControl.cs using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace BrawDraw.Com.PhotoFrame.Net.PublicFunctions { public class ClockControl: UserControl { DateTime dt; Pen pen = new Pen(Color.Black, 1); Pen penSecond = new Pen(Color.Black, 6); Brush brush = new SolidBrush(Color.Black); Bitmap m_Bitmap = null; Graphics g = null; private Font ClockFont = new Font("Arial", 48, FontStyle.Bold); public ClockControl() { SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true); this.UpdateStyles(); Enabled = false; m_Bitmap = new Bitmap(100, 100); } public DateTime Time { get { return dt; } set { m_Bitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height); g = Graphics.FromImage(m_Bitmap); g.Clear(Color.White); InitializeCoordinates(g); DrawDots(g, brush); DrawHourHand(g, pen); DrawMinuteHand(g, pen); DrawNumbers(g); // DrawSecondHand(g, pen); // if(g != null) g.Dispose(); if (dt.Hour != value.Hour) { DrawHourHand(g, pen); } if (dt.Minute != value.Minute) { DrawHourHand(g, pen); DrawMinuteHand(g, pen); } if (dt.Second != value.Second) { DrawMinuteHand(g, pen); DrawSecondHand(g, pen); } if (dt.Millisecond != value.Millisecond) { DrawSecondHand(g, pen); } dt = value; Graphics grfx = CreateGraphics(); grfx.DrawImage(m_Bitmap, 0, 0); this.Validate(); if(grfx != null) grfx.Dispose(); } } protected override void OnPaint(PaintEventArgs pea) { g = pea.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; DrawClock(g); base.OnPaint (pea); } private void DrawClock(Graphics gg) { m_Bitmap = new Bitmap(this.Width, this.Height); Graphics grfx = Graphics.FromImage(m_Bitmap); grfx.Clear(Color.White); InitializeCoordinates(grfx); DrawDots(grfx, brush); DrawHourHand(grfx, pen); DrawMinuteHand(grfx, pen); DrawSecondHand(grfx, pen); DrawNumbers(grfx); gg.DrawImage(m_Bitmap, 0, 0); grfx.Dispose(); } void InitializeCoordinates(Graphics grfx) { if (Width == 0 || Height == 0) return; grfx.TranslateTransform(Width / 2, Height / 2); float fInches = Math.Min(Width / grfx.DpiX, Height / grfx.DpiY); grfx.ScaleTransform(fInches * grfx.DpiX / 2000, fInches * grfx.DpiY / 2000); } void DrawDots(Graphics grfx, Brush brush) { for (int i = 0; i < 60; i++) { int iSize = i % 5 == 0 ? 100 : 30; grfx.FillEllipse(brush, 0 - iSize / 2, - 860 - iSize / 2, iSize, iSize); grfx.RotateTransform(6); } } protected virtual void DrawHourHand(Graphics grfx, Pen pen) { GraphicsState gs = grfx.Save(); grfx.RotateTransform(360f * Time.Hour / 12 + 30f * Time.Minute / 60 + 2); grfx.DrawPolygon(pen, new Point[] { new Point(0, 150), new Point( 100, 0), new Point(0, -600), new Point(-100, 0) }); grfx.Restore(gs); } protected virtual void DrawMinuteHand(Graphics grfx, Pen pen) { GraphicsState gs = grfx.Save(); grfx.RotateTransform(360f * Time.Minute / 60 + 6f * Time.Second / 60 + 2); grfx.DrawPolygon(pen, new Point[] { new Point(0, 200), new Point( 50, 0), new Point(0, -800), new Point(-50, 0) }); grfx.Restore(gs); } protected virtual void DrawSecondHand(Graphics grfx, Pen pen) { GraphicsState gs = grfx.Save(); grfx.RotateTransform(360f * Time.Second / 60 + 6f * Time.Millisecond / 1000 + 2.8f); grfx.DrawLine(penSecond, 0, 0, 0, -800); grfx.Restore(gs); } private void InitializeComponent() { // // ClockControl // this.Name = "ClockControl"; this.Size = new System.Drawing.Size(416, 424); } protected void DrawNumbers(Graphics grfx) { int count = 1; int r = 1900; int dx = 25; int dy = 20; int offset = 8; int dr = 300; GraphicsState gs = grfx.Save(); grfx.TranslateTransform(- r/2 - dx, - r/2 - dy); grfx.DrawEllipse(new Pen(Color.Black, 10), dx / 2 + offset, dy / 2 + offset, r + dx - offset, r + dy - offset); //grfx.DrawEllipse(new Pen(Color.Black, 2), dx + offset, dy + offset, r - offset, r - offset); grfx.TranslateTransform(dr/3 + 15, dr/3 + 15); for (double a = 0; a < 2 * Math.PI; a += (2.0* Math.PI/12)) { double x = (r - dr * 1.2)/2 * Math.Cos(a - Math.PI/3) + (r - dr)/2 + 25; double y = (r - dr * 1.2)/2 * Math.Sin(a - Math.PI/3)+ (r - dr)/2 + 20; grfx.DrawString(Convert.ToString(count), ClockFont, Brushes.Black, (float)x,(float)y, new StringFormat()); count++; } grfx.Restore(gs); //grfx.DrawLine(new Pen(Color.Black, 1), 0, -800, 0, 800); } } } 接著再從BezierClockControl再從 ClockControl控件繼承,由於繪制方式不同,因此最後顯示的效果也不同: //BezierClockControl.cs using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace BrawDraw.Com.PhotoFrame.Net.PublicFunctions { public class BezierClockControl: ClockControl { protected override void DrawHourHand(Graphics grfx, Pen pen) { GraphicsState gs = grfx.Save(); grfx.RotateTransform(360f * Time.Hour / 12 + 30f * Time.Minute / 60); GraphicsPath gp = new GraphicsPath(); Point[] points = new Point[] { new Point( 0, -600), new Point( 0, -300), new Point(200, -300), new Point( 50, -200), new Point( 50, -200), new Point( 50, 0), new Point( 50, 0), new Point( 50, 75), new Point(-50, 75), new Point( -50, 0), new Point(-50, 0), new Point( -50, -200), new Point(-50, -200), new Point(-200, -300), new Point( 0, -300), new Point( 0, -600) }; gp.AddBeziers(points); //grfx.DrawBeziers(pen, points); grfx.FillPath(new SolidBrush(Color.Red), gp); grfx.Restore(gs); gp.Dispose(); } private void InitializeComponent() { // // BezierClockControl // this.Name = "BezierClockControl"; this.Size = new System.Drawing.Size(448, 376); } protected override void DrawMinuteHand(Graphics grfx, Pen pen) { GraphicsState gs = grfx.Save(); grfx.RotateTransform(360f * Time.Minute / 60 + 6f * Time.Second / 60); GraphicsPath gp = new GraphicsPath(); Point[] points = new Point[] { new Point( 0, -800), new Point( 0, -750), new Point( 0, -700), new Point( 25, -600), new Point( 25, -600), new Point( 25, 0), new Point( 25, 0), new Point( 25, 50), new Point(-25, 50), new Point( -25, 0), new Point(-25, 0), new Point( -25, -600), new Point(-25, -600), new Point( 0, -700), new Point( 0, -750), new Point( 0, -800) }; gp.AddBeziers(points); grfx.FillPath(new SolidBrush(Color.Green), gp); //grfx.DrawBeziers(pen, points); grfx.Restore(gs); gp.Dispose(); } } } 最後,我們來測試一下: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace BrawDraw.Com.PhotoFrame.Net.PublicFunctions { /// <summary> /// BezierClock 的摘要說明。 /// </summary> public class BezierClock : System.Windows.Forms.Form { /// <summary> /// 必需的設計器變量。 /// </summary> private System.ComponentModel.Container components = null; //ClockControl clkctl; BezierClockControl clkctl; public BezierClock() { InitializeComponent(); this.SetStyle(ControlStyles.DoubleBuffer, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // Redraw when resized this.SetStyle(ControlStyles.ResizeRedraw, true); Text = "Bezier Clock"; //clkctl = new ClockControl(); clkctl = new BezierClockControl(); clkctl.Parent = this; clkctl.Time = DateTime.Now; clkctl.Dock = DockStyle.Fill; clkctl.BackColor = Color.Black; clkctl.ForeColor = Color.White; Timer timer = new Timer(); timer.Interval = 1000; timer.Tick += new EventHandler(OnTimerTick); timer.Start(); } void OnTimerTick(object obj, EventArgs ea) { clkctl.Time = DateTime.Now; } /// <summary> /// 清理所有正在使用的資源。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows 窗體設計器生成的代碼 /// <summary> /// 設計器支持所需的方法 - 不要使用代碼編輯器修改 /// 此方法的內容。 /// </summary> private void InitializeComponent() { // // BezierClock // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(432, 421); this.Name = "BezierClock"; this.Text = "BezierClock"; } #endregion /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { Application.Run(new BezierClock()); } } }