using System; using System.ComponentModel; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; using System.Drawing.Text; namespace DevComponents.DotNetBar.Controls { /// /// Analog clock control. /// [Description("Analog Clock Control"), ToolboxBitmap(typeof(AnalogClockControl), "AnalogClock.AnalogClockControl.bmp"), DefaultEvent("ValueChanged"), DefaultProperty("Value")] public class AnalogClockControl : Control { #region Private Vars private const float _BaseSize = 100.0f; private Point _EditLoc; private eClockEditStates _EditState; private Size _LastSize; private Timer _Timer; #endregion #region Implementation /// /// Default minimum size. Defaults to 100, 100. /// protected override Size DefaultMinimumSize { get { return new Size(100, 100); } } private bool _AntiAlias; /// /// Gets or sets whether anti-aliasing is used when rendering the control. Default value is true. /// [DefaultValue(true), Category("Appearance"), Description("Indicates whether Anti-aliasing is used when rendering the control.")] public bool AntiAlias { get { return _AntiAlias; } set { _AntiAlias = value; Invalidate(); } } private bool _AutomaticMode; /// /// Gets or sets the state for automatic mode. When true the clock will auto redraw once a second and display the current date/time. Default value is false. /// [DefaultValue(false), Category("Behavior"), Description("Toggles the state for automatic mode. When true the clock will auto redraw once a second and display the current date/time.")] public bool AutomaticMode { get { return _AutomaticMode; } set { _AutomaticMode = value; if (_AutomaticMode) { _Value = DateTime.Now; _IsEditable = false; if (!DesignMode) { if (_Timer != null) _Timer.Dispose(); _Timer = new Timer(); _Timer.Interval = 1000; _Timer.Tick += Timer_Tick; _Timer.Enabled = true; } } else { if (_Timer != null) { _Timer.Enabled = false; _Timer.Dispose(); _Timer = null; } } Invalidate(); } } private eClockStyles _ClockStyle; /// /// Gets or sets clock style for this control. /// [DefaultValue(eClockStyles.Style1), Category("Appearance"), Description("The clock style for the control."), RefreshProperties(RefreshProperties.All)] public eClockStyles ClockStyle { get { return _ClockStyle; } set { _ClockStyle = value; if (_ClockStyle != eClockStyles.Custom) { if (_ClockStyleData != null) { _ClockStyleData.Parent = null; _ClockStyleData.Dispose(); } _ClockStyleData = new ClockStyleData(_ClockStyle, this); } Invalidate(); } } private ClockStyleData _ClockStyleData; /// /// Gets or sets the clock style data elements for this control. /// [Category("Appearance"), Description("The clock style data for the control."), RefreshProperties(RefreshProperties.All)] public ClockStyleData ClockStyleData { get { return _ClockStyleData; } set { if (_ClockStyleData != null) _ClockStyleData.Parent = null; _ClockStyleData = value; _ClockStyleData.Parent = this; _ClockStyleData.Style = eClockStyles.Custom; ClockStyle = eClockStyles.Custom; Invalidate(); } } /// /// Resets the property to default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetClockStyleData() { ClockStyle = eClockStyles.Style1; } private eClockIndicatorStyles _IndicatorStyle; /// /// Gets or sets a the indicator style the clock control. Default value is Ticks. /// [DefaultValue(eClockIndicatorStyles.Ticks), Category("Appearance"), Description("Indicates which indicator style the clock will use.")] public eClockIndicatorStyles IndicatorStyle { get { return _IndicatorStyle; } set { _IndicatorStyle = value; Invalidate(); } } private bool _IsEditable; /// /// Gets or sets whether the time can be changed by moving the clock hands. Default value is false. /// [DefaultValue(false), Category("Behavior"), Description("Indicates whether the time can be changed by moving the clock hands. If AutomaticMode is enabled, this setting is ignored.")] public bool IsEditable { get { return _IsEditable; } set { if (!AutomaticMode) _IsEditable = value; Invalidate(); } } private bool _ShowGlassOverlay; /// /// Gets or sets a value indicating whether to display the glass overlay on the clock control. Default value is true. /// [DefaultValue(true), Category("Appearance"), Description("Indicates whether the glass overlay on the clock control will be displayed or not.")] public bool ShowGlassOverlay { get { return _ShowGlassOverlay; } set { _ShowGlassOverlay = value; Invalidate(); } } private bool _ShowSecondHand; /// /// Gets or sets a value indicating whether to display the second hand on the clock control. Default value is true. /// [DefaultValue(true), Category("Appearance"), Description("Indicates whether the second hand on the clock control will be displayed or not.")] public bool ShowSecondHand { get { return _ShowSecondHand; } set { _ShowSecondHand = value; Invalidate(); } } /// /// Occurs while user is dragging the mouse in order to change time. /// [Description("Occurs while user is dragging the mouse in order to change time.")] public event TimeValueChangingEventHandler ValueChanging; /// /// Raises ValueChanging event. /// /// Provides event arguments. protected virtual void OnValueChanging(TimeValueChangingEventArgs e) { TimeValueChangingEventHandler handler = ValueChanging; if (handler != null) handler(this, e); } /// /// Occurs when Value i.e. time clock is displaying has changed. /// [Description("Occurs when Value i.e. time clock is displaying has changed.")] public event EventHandler ValueChanged; /// /// Raises ValueChanged event. /// /// Provides event arguments. protected virtual void OnValueChanged(EventArgs e) { EventHandler handler = ValueChanged; if (handler != null) handler(this, e); } private DateTime _Value; /// /// Gets or sets the current date/time value for this control. /// [Category("Behavior"), Description("The current date/time value for this control. If AutomaticMode is enabled, assigned values are ignored.")] public DateTime Value { get { return _Value; } set { _Value = value; OnValueChanged(EventArgs.Empty); Invalidate(); } } /// /// Resets the property to default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetValue() { Value = DateTime.Now; } ///// ///// Occurs when subproperty value has changed. ///// //public event DevComponents.Schedule.Model.SubPropertyChangedEventHandler SubPropertyChanged; /// /// Initializes a new instance of the ClockControl class /// public AnalogClockControl() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); _AntiAlias = true; _AutomaticMode = false; _ClockStyle = eClockStyles.Style1; _ClockStyleData = new ClockStyleData(); _ClockStyleData.Parent = this; _EditState = eClockEditStates.None; _IndicatorStyle = eClockIndicatorStyles.Ticks; _IsEditable = false; _ShowGlassOverlay = true; _ShowSecondHand = true; _Value = DateTime.Now; MinimumSize = new Size((int)_BaseSize, (int)_BaseSize); ; Size = MinimumSize; } /// /// Releases all resources used by the class. /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (_ClockStyleData != null) { _ClockStyleData.Parent = null; _ClockStyleData.Dispose(); } if (_Timer != null) { _Timer.Dispose(); _Timer = null; } } } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); RectangleF rect; float bezelWidth, angle; if (e.Button != MouseButtons.Left || !_IsEditable) return; bezelWidth = _ClockStyleData.BezelWidth * (ClientRectangle.Width - 1); rect = new RectangleF(ClientRectangle.X + bezelWidth, ClientRectangle.Y + bezelWidth, (ClientRectangle.Width - 1) - bezelWidth * 2.0f, (ClientRectangle.Height - 1) - bezelWidth * 2.0f); angle = RoundToExactHour((float)_Value.TimeOfDay.TotalHours * 30.0f); if (_ClockStyleData.HourHandStyle.ContainsPoint(rect, angle, e.Location)) { _LastMouseMoveHour = _Value.Hour; _NewMouseMoveHour = _Value.Hour; if (_Value.Hour > 12) _LastMouseMoveHour -= 12; _EditState = eClockEditStates.Hour; } else { angle = (float)_Value.TimeOfDay.TotalMinutes * 6.0f; if (_ClockStyleData.MinuteHandStyle.ContainsPoint(rect, angle, e.Location)) _EditState = eClockEditStates.Minute; else { angle = (float)_Value.TimeOfDay.Seconds * 6.0f; if (_ShowSecondHand && _ClockStyleData.SecondHandStyle.ContainsPoint(rect, angle, e.Location)) _EditState = eClockEditStates.Second; } } if (_EditState != eClockEditStates.None) _EditLoc = e.Location; } private int _LastMouseMoveHour = -1; private int _NewMouseMoveHour = -1; protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); RectangleF rect; float bezelWidth, angle; if (!_IsEditable) return; if (_EditState != eClockEditStates.None) { _EditLoc = e.Location; // We need to record the direction of hour hand movement so AM/PM can be switched if (_EditState == eClockEditStates.Hour) { int hour = GetHourFromPoint(_EditLoc); if (hour == 1 && _LastMouseMoveHour == 12) _LastMouseMoveHour = 0; else if (hour == 12 && _LastMouseMoveHour == 1) _LastMouseMoveHour = 13; _NewMouseMoveHour += (hour - _LastMouseMoveHour); if (_NewMouseMoveHour < 0) _NewMouseMoveHour = 23; else if (_NewMouseMoveHour > 23) _NewMouseMoveHour = 0; _LastMouseMoveHour = hour; } OnValueChanging(new TimeValueChangingEventArgs(GetCurrentMouseTime())); Invalidate(); } else { bezelWidth = _ClockStyleData.BezelWidth * (ClientRectangle.Width - 1); rect = new RectangleF(ClientRectangle.X + bezelWidth, ClientRectangle.Y + bezelWidth, (ClientRectangle.Width - 1) - bezelWidth * 2.0f, (ClientRectangle.Height - 1) - bezelWidth * 2.0f); angle = RoundToExactHour((float)_Value.TimeOfDay.TotalHours * 30.0f); if (_ClockStyleData.HourHandStyle.ContainsPoint(rect, angle, e.Location)) Cursor = Cursors.Hand; else { angle = (float)_Value.TimeOfDay.TotalMinutes * 6.0f; if (_ClockStyleData.MinuteHandStyle.ContainsPoint(rect, angle, e.Location)) Cursor = Cursors.Hand; else { angle = (float)_Value.TimeOfDay.Seconds * 6.0f; if (_ShowSecondHand && _ClockStyleData.SecondHandStyle.ContainsPoint(rect, angle, e.Location)) Cursor = Cursors.Hand; else if (Cursor != Cursors.Default) Cursor = Cursors.Default; } } } } private float GetClockAngleFromPoint(Point p) { float angle = (float)MathHelper.GetDegrees(GetAngleFromPoint(p)); angle += 90.0f; while (angle < 0) angle += 360; while (angle >= 360) angle -= 360; return angle; } private int GetHourFromPoint(Point p) { float angle = RoundToExactHour(GetClockAngleFromPoint(p)); int hour = (int)Math.Round(angle / 30.0, 0); if (hour == 0) hour = 12; return hour; } private int GetMinuteFromPoint(Point p) { float angle = GetClockAngleFromPoint(p); int minute = (int)Math.Round(angle / 6.0, 0); if (minute == 60) minute = 0; return minute; } private int GetSecondFromPoint(Point p) { float angle = GetClockAngleFromPoint(p); int minute = (int)Math.Round(angle / 6.0, 0); if (minute == 60) minute = 0; return minute; } private DateTime GetCurrentMouseTime() { DateTime time = _Value; switch (_EditState) { case eClockEditStates.Hour: time = new DateTime(_Value.Year, _Value.Month, _Value.Day, _NewMouseMoveHour, _Value.Minute, _Value.Second); break; case eClockEditStates.Minute: time = new DateTime(_Value.Year, _Value.Month, _Value.Day, _Value.Hour, GetMinuteFromPoint(_EditLoc), _Value.Second); break; case eClockEditStates.Second: time = new DateTime(_Value.Year, _Value.Month, _Value.Day, _Value.Hour, _Value.Minute, GetSecondFromPoint(_EditLoc)); break; } return time; } protected override void OnMouseUp(MouseEventArgs e) { if (_EditState != eClockEditStates.None) { _EditLoc = e.Location; Value = GetCurrentMouseTime(); _EditLoc = Point.Empty; _EditState = eClockEditStates.None; Cursor = Cursors.Default; } base.OnMouseUp(e); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g; RectangleF rect; float bezelWidth; g = e.Graphics; TextRenderingHint textHint = g.TextRenderingHint; SmoothingMode sm = g.SmoothingMode; if (_AntiAlias) { g.TextRenderingHint = BarUtilities.AntiAliasTextRenderingHint; g.SmoothingMode = SmoothingMode.AntiAlias; } bezelWidth = _ClockStyleData.BezelWidth * (ClientRectangle.Width - 1); rect = new RectangleF(ClientRectangle.X + bezelWidth, ClientRectangle.Y + bezelWidth, (ClientRectangle.Width - 1) - bezelWidth * 2.0f, (ClientRectangle.Height - 1) - bezelWidth * 2.0f); e.Graphics.TranslateTransform(rect.X + rect.Width * 0.5f, rect.Y + rect.Height * 0.5f); switch (_IndicatorStyle) { case eClockIndicatorStyles.Ticks: DrawTicks(g, rect); break; case eClockIndicatorStyles.Numbers: DrawNumbers(g, rect); break; } DrawHands(g, rect, false); DrawCap(g, rect); DrawHands(g, rect, true); if (_ShowGlassOverlay) DrawGlassOverlay(g, rect); g.TextRenderingHint = textHint; g.SmoothingMode = sm; } protected override void OnPaintBackground(PaintEventArgs pevent) { base.OnPaintBackground(pevent); Graphics gfx; RectangleF innerRect, outerRect; GraphicsPath outerPath, innerPath; Brush brush; Pen pen; RectangleF imageRect = new RectangleF(); float aspect, bezelWidth; float scaleFactor; gfx = pevent.Graphics; gfx.SmoothingMode = _AntiAlias ? SmoothingMode.AntiAlias : SmoothingMode.Default; Rectangle clientRectangle = ClientRectangle; if (_ClockStyleData.BezelColor.BorderWidth > 0) { int borderInflateSize = (int)((_ClockStyleData.BezelColor.BorderWidth * Math.Min(clientRectangle.Width, clientRectangle.Height)) / 2); clientRectangle.Inflate(-borderInflateSize, -borderInflateSize); } scaleFactor = Math.Min(clientRectangle.Width, clientRectangle.Height); bezelWidth = _ClockStyleData.BezelWidth * (scaleFactor - 1); innerRect = new RectangleF(clientRectangle.X + bezelWidth, clientRectangle.Y + bezelWidth, (clientRectangle.Width - 1) - bezelWidth * 2.0f, (clientRectangle.Height - 1) - bezelWidth * 2.0f); outerRect = new RectangleF(clientRectangle.X, clientRectangle.Y, clientRectangle.Width - 1, clientRectangle.Height - 1); outerPath = new GraphicsPath(); innerPath = new GraphicsPath(); switch (_ClockStyleData.ClockShape) { case eClockShapes.Round: outerPath.AddEllipse(outerRect); outerPath.CloseAllFigures(); innerPath.AddEllipse(innerRect); innerPath.CloseAllFigures(); break; default: outerPath.AddRectangle(outerRect); innerPath.AddRectangle(innerRect); break; } if (_ClockStyleData.FaceBackgroundImage != null) { gfx.SetClip(outerPath); if (_ClockStyleData.FaceBackgroundImage.Width < _ClockStyleData.FaceBackgroundImage.Height) aspect = (float)innerRect.Width / (float)_ClockStyleData.FaceBackgroundImage.Width; else aspect = (float)innerRect.Height / (float)_ClockStyleData.FaceBackgroundImage.Height; imageRect.X = innerRect.X + ((innerRect.Width / 2.0f) - (aspect * _ClockStyleData.FaceBackgroundImage.Width) / 2.0f); imageRect.Y = innerRect.Y + ((innerRect.Height / 2.0f) - (aspect * _ClockStyleData.FaceBackgroundImage.Height) / 2.0f); imageRect.Width = aspect * _ClockStyleData.FaceBackgroundImage.Width; imageRect.Height = aspect * _ClockStyleData.FaceBackgroundImage.Height; gfx.DrawImage(_ClockStyleData.FaceBackgroundImage, imageRect); } gfx.ResetClip(); gfx.SetClip(innerPath, CombineMode.Exclude); brush = _ClockStyleData.BezelColor.GetBrush(outerPath); gfx.FillPath(brush, outerPath); brush.Dispose(); if (_ClockStyleData.BezelColor.BorderWidth > 0) { pen = _ClockStyleData.BezelColor.GetBorderPen(scaleFactor, PenAlignment.Center); gfx.DrawPath(pen, outerPath); pen.Dispose(); } gfx.ResetClip(); if (_ClockStyleData.FaceBackgroundImage == null) { brush = _ClockStyleData.FaceColor.GetBrush(innerPath, new PointF(0.50f, 0.50f)); gfx.FillPath(brush, innerPath); brush.Dispose(); } if (_ClockStyleData.FaceColor.BorderWidth > 0) { pen = _ClockStyleData.FaceColor.GetBorderPen(scaleFactor, PenAlignment.Center); gfx.DrawPath(pen, innerPath); pen.Dispose(); } innerPath.Dispose(); outerPath.Dispose(); } protected override void OnResize(EventArgs e) { base.OnResize(e); //if (Width < 100) //{ // Width = 100; // Height = 100; //} //else if (_LastSize.Width != Width) Height = Width; else Width = Height; _LastSize = Size; } /// /// Renders the clock's center cap. /// /// Graphics object used for rendering. /// Bounding rectangle. protected virtual void DrawCap(Graphics gfx, RectangleF rect) { float scaleFactor; GraphicsPath path; RectangleF capRect; Pen pen; Brush brush; float capSize; scaleFactor = Math.Min(rect.Width, rect.Height); capSize = _ClockStyleData.CapSize * scaleFactor; capRect = new RectangleF(-capSize / 2.0f, -capSize / 2.0f, capSize, capSize); path = new GraphicsPath(); path.AddEllipse(capRect); brush = _ClockStyleData.CapColor.GetBrush(path); gfx.FillPath(brush, path); if (_ClockStyleData.CapColor.BorderWidth > 0) { pen = _ClockStyleData.CapColor.GetBorderPen(scaleFactor, PenAlignment.Outset); gfx.DrawPath(pen, path); pen.Dispose(); } brush.Dispose(); path.Dispose(); } /// /// Renders the clock's glass overlay. /// /// Graphics object used for rendering. /// Bounding rectangle. protected virtual void DrawGlassOverlay(Graphics gfx, RectangleF rect) { GraphicsState gState; PathGradientBrush brush; GraphicsPath path, brushPath; PointF[] curvePoints; float radius; gState = gfx.Save(); rect.Width *= 0.95f; rect.Height *= 0.95f; rect.X = -rect.Width / 2.0f; rect.Y = -rect.Height / 2.0f; radius = Math.Min(rect.Width, rect.Height) / 2.0f; brushPath = new GraphicsPath(); brushPath.AddEllipse(rect); brush = new PathGradientBrush(brushPath); brush.CenterPoint = new PointF(0.0f, 0.0f); brush.CenterColor = Color.FromArgb(192, Color.White); brush.SurroundColors = new Color[] { Color.FromArgb(64, Color.White) }; path = new GraphicsPath(); path.AddArc(rect, 180, 180); curvePoints = new PointF[5]; curvePoints[0].X = -radius; curvePoints[0].Y = 0.0f; curvePoints[1].X = -radius * 0.5f; curvePoints[1].Y = -radius * 0.175f; curvePoints[2].X = 0.0f; curvePoints[2].Y = -radius * 0.25f; curvePoints[3].X = radius * 0.5f; curvePoints[3].Y = -radius * 0.175f; curvePoints[4].X = radius; curvePoints[4].Y = 0.0f; path.AddCurve(curvePoints); path.CloseAllFigures(); gfx.RotateTransform(_ClockStyleData.GlassAngle); gfx.FillPath(brush, path); gfx.Restore(gState); brush.Dispose(); brushPath.Dispose(); path.Dispose(); } private static float RoundToExactHour(float angle) { return (float)Math.Floor((angle / 30f)) * 30f; } /// /// Renders the clock's hands. /// /// Graphics object used for rendering. /// Bounding rectangle. /// True if this is the rending pass after the cap has been rendered. protected virtual void DrawHands(Graphics gfx, RectangleF rect, bool overCap) { GraphicsState gState; GraphicsPath path; Pen pen; Brush brush; float scaleFactor, angle; scaleFactor = Math.Min(rect.Width, rect.Height); gState = gfx.Save(); gfx.ResetTransform(); //Hour Hand if (_ClockStyleData.HourHandStyle.DrawOverCap == overCap) { if (_EditState == eClockEditStates.Hour) angle = (float)MathHelper.GetDegrees(GetAngleFromPoint(_EditLoc)) + 90.0f; else angle = (float)(_Value.TimeOfDay.TotalHours > 12 ? _Value.TimeOfDay.TotalHours - 12 : _Value.TimeOfDay.TotalHours) * 30.0f; // When clock is editable round up the angle to point at the hour exactly if (_IsEditable && !_AutomaticMode) { angle = RoundToExactHour(angle); } path = _ClockStyleData.HourHandStyle.GenerateHandPath(rect, angle); brush = _ClockStyleData.HourHandStyle.HandColor.GetBrush(path, _ClockStyleData.HourHandStyle.HandColor.BrushAngle + angle); gfx.FillPath(brush, path); if (_ClockStyleData.HourHandStyle.HandColor.BorderWidth > 0) { pen = _ClockStyleData.HourHandStyle.HandColor.GetBorderPen(scaleFactor, PenAlignment.Outset); gfx.DrawPath(pen, path); pen.Dispose(); } brush.Dispose(); path.Dispose(); } //Minute Hand if (_ClockStyleData.MinuteHandStyle.DrawOverCap == overCap) { if (_EditState == eClockEditStates.Minute) angle = (float)MathHelper.GetDegrees(GetAngleFromPoint(_EditLoc)) + 90.0f; else angle = (float)_Value.TimeOfDay.TotalMinutes * 6.0f; path = _ClockStyleData.MinuteHandStyle.GenerateHandPath(rect, angle); brush = _ClockStyleData.MinuteHandStyle.HandColor.GetBrush(path, _ClockStyleData.MinuteHandStyle.HandColor.BrushAngle + angle); gfx.FillPath(brush, path); if (_ClockStyleData.MinuteHandStyle.HandColor.BorderWidth > 0) { pen = _ClockStyleData.MinuteHandStyle.HandColor.GetBorderPen(scaleFactor, PenAlignment.Outset); gfx.DrawPath(pen, path); pen.Dispose(); } brush.Dispose(); path.Dispose(); } //Second Hand if (_ShowSecondHand && _ClockStyleData.SecondHandStyle.DrawOverCap == overCap) { if (_EditState == eClockEditStates.Second) angle = (float)MathHelper.GetDegrees(GetAngleFromPoint(_EditLoc)) + 90.0f; else angle = (float)_Value.TimeOfDay.Seconds * 6.0f; path = _ClockStyleData.SecondHandStyle.GenerateHandPath(rect, angle); brush = _ClockStyleData.SecondHandStyle.HandColor.GetBrush(path, _ClockStyleData.SecondHandStyle.HandColor.BrushAngle + angle); gfx.FillPath(brush, path); if (_ClockStyleData.SecondHandStyle.HandColor.BorderWidth > 0) { pen = _ClockStyleData.SecondHandStyle.HandColor.GetBorderPen(scaleFactor, PenAlignment.Outset); gfx.DrawPath(pen, path); pen.Dispose(); } brush.Dispose(); path.Dispose(); } gfx.Restore(gState); } /// /// Renders the clock's numeric hour indicators. /// /// Graphics object used for rendering. /// Bounding rectangle. protected virtual void DrawNumbers(Graphics gfx, RectangleF rect) { PointF center, txtCenter; float scaleFactor, radius, tickIncrement, angle; Brush brush; StringFormat strFormat; Font fnt; strFormat = new StringFormat(); strFormat.Alignment = StringAlignment.Center; strFormat.LineAlignment = StringAlignment.Center; scaleFactor = Math.Min(rect.Width, rect.Height) / _BaseSize; fnt = new Font(_ClockStyleData.NumberFont.FontFamily, _ClockStyleData.NumberFont.Size * scaleFactor, _ClockStyleData.NumberFont.Style, GraphicsUnit.Pixel); center = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f); radius = (rect.Width / 2.0f) - (gfx.MeasureString("12", fnt).Height) / 1.25f; tickIncrement = (float)MathHelper.GetRadians(30.0f); angle = (float)MathHelper.GetRadians(-60.0f); brush = new SolidBrush(_ClockStyleData.NumberColor); txtCenter = new PointF(); for (int i = 1; i <= 12; i++) { txtCenter.X = (float)(radius * Math.Cos(angle)); txtCenter.Y = (float)(radius * Math.Sin(angle)); gfx.DrawString(i.ToString(), fnt, brush, txtCenter, strFormat); angle += tickIncrement; } brush.Dispose(); fnt.Dispose(); } /// /// Renders the clock's tick hour/minute indicators. /// /// Graphics object used for rendering. /// Bounding rectangle. protected virtual void DrawTicks(Graphics gfx, RectangleF rect) { float scaleFactor; PointF[] largePts, smallPts; GraphicsState gState; Brush largeBrush, smallBrush; Pen largePen = null, smallPen = null; GraphicsPath largePath, smallPath; scaleFactor = Math.Min(rect.Width, rect.Height); largePts = new PointF[4]; largePts[0].X = (-_ClockStyleData.LargeTickWidth * 0.5f) * scaleFactor; largePts[0].Y = -0.45f * scaleFactor; largePts[1].X = (-_ClockStyleData.LargeTickWidth * 0.5f) * scaleFactor; largePts[1].Y = (-0.45f + _ClockStyleData.LargeTickLength) * scaleFactor; largePts[2].X = (_ClockStyleData.LargeTickWidth * 0.5f) * scaleFactor; largePts[2].Y = (-0.45f + _ClockStyleData.LargeTickLength) * scaleFactor; largePts[3].X = (_ClockStyleData.LargeTickWidth * 0.5f) * scaleFactor; largePts[3].Y = -0.45f * scaleFactor; smallPts = new PointF[4]; smallPts[0].X = (-_ClockStyleData.SmallTickWidth * -0.5f) * scaleFactor; smallPts[0].Y = -0.45f * scaleFactor; smallPts[1].X = (-_ClockStyleData.SmallTickWidth * -0.5f) * scaleFactor; smallPts[1].Y = (-0.45f + _ClockStyleData.SmallTickLength) * scaleFactor; smallPts[2].X = (_ClockStyleData.SmallTickWidth * -0.5f) * scaleFactor; smallPts[2].Y = (-0.45f + _ClockStyleData.SmallTickLength) * scaleFactor; smallPts[3].X = (_ClockStyleData.SmallTickWidth * -0.5f) * scaleFactor; smallPts[3].Y = -0.45f * scaleFactor; largePath = new GraphicsPath(); largePath.AddPolygon(largePts); smallPath = new GraphicsPath(); smallPath.AddPolygon(smallPts); largeBrush = _ClockStyleData.LargeTickColor.GetBrush(largePath); if (_ClockStyleData.LargeTickColor.BorderWidth > 0) largePen = _ClockStyleData.LargeTickColor.GetBorderPen(scaleFactor, PenAlignment.Center); smallBrush = _ClockStyleData.LargeTickColor.GetBrush(largePath); if (_ClockStyleData.SmallTickColor.BorderWidth > 0) smallPen = _ClockStyleData.SmallTickColor.GetBorderPen(scaleFactor, PenAlignment.Center); gState = gfx.Save(); for (int i = 0; i < 12; i++) { gfx.FillPolygon(largeBrush, largePts); if (largePen != null) gfx.DrawPolygon(largePen, largePts); for (int j = 0; j < 4; j++) { gfx.RotateTransform(6); gfx.FillPolygon(smallBrush, smallPts); if (smallPen != null) gfx.DrawPolygon(smallPen, smallPts); } gfx.RotateTransform(6); } gfx.Restore(gState); largePath.Dispose(); smallPath.Dispose(); largeBrush.Dispose(); smallBrush.Dispose(); if (largePen != null) largePen.Dispose(); if (smallPen != null) smallPen.Dispose(); } private float GetAngleFromPoint(PointF pt) { float angle; PointF center = new PointF(ClientRectangle.X + ClientRectangle.Width / 2.0f, ClientRectangle.Y + ClientRectangle.Height / 2.0f); pt.X -= center.X; pt.Y -= center.Y; angle = (float)Math.Atan2((double)(pt.Y), (double)(pt.X)); return angle; } private void UpdateAutomaticTime() { if (_TimeZoneInfo != null) Value = DevComponents.Schedule.TimeZoneInfo.ConvertTime(DateTime.Now, _TimeZoneInfo); else Value = DateTime.Now; } private void Timer_Tick(object sender, EventArgs e) { UpdateAutomaticTime(); } private DevComponents.Schedule.TimeZoneInfo _TimeZoneInfo = null; private string _TimeZone = string.Empty; /// /// Gets or sets the time-zone string identifier that is used to display the time when AutomaticMode=true and clock is displaying current time. /// [DefaultValue(""), Editor("DevComponents.DotNetBar.Design.TimeZoneSelectionEditor, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(System.Drawing.Design.UITypeEditor)), Category("Behavior"), Description("Indicates time-zone string identifier that is used to display the time when AutomaticMode=true and clock is displaying current time.")] public string TimeZone { get { return _TimeZone; } set { if (value != _TimeZone) { DevComponents.Schedule.TimeZoneInfo timeZoneInfo = null; if (!string.IsNullOrEmpty(value)) timeZoneInfo = DevComponents.Schedule.TimeZoneInfo.FindSystemTimeZoneById(value); if (timeZoneInfo != null || string.IsNullOrEmpty(value)) { string oldValue = _TimeZone; _TimeZone = value; _TimeZoneInfo = timeZoneInfo; OnTimeZoneChanged(oldValue, value); } } } } /// /// Called when TimeZone property has changed. /// /// Old property value /// New property value protected virtual void OnTimeZoneChanged(string oldValue, string newValue) { if (_AutomaticMode) UpdateAutomaticTime(); //OnPropertyChanged(new PropertyChangedEventArgs("TimeZone")); } ///// ///// Raises the SubPropertyChanged event. ///// ///// Event arguments //public void OnSubPropertyChanged(DevComponents.Schedule.Model.SubPropertyChangedEventArgs e) //{ // if (SubPropertyChanged != null) // SubPropertyChanged(this, e); // Invalidate(); //} #endregion } /// /// Enumeration containing the available hour/minute indicators. /// public enum eClockIndicatorStyles { /// /// Control will use ticks for hour/minute indicators. /// Ticks, /// /// Control will use numbers for hour indicators. /// Numbers } /// /// Enumeration containing the available mouse edit states. /// public enum eClockEditStates { /// /// Control is not currently in an edit state. /// None, /// /// Control is currently in an hour edit state. /// Hour, /// /// Control is currently in an minute edit state. /// Minute, /// /// Control is currently in an second edit state. /// Second } /// /// Provides event arguments for TimeValueChanging event. /// public class TimeValueChangingEventArgs : EventArgs { /// /// Gets the current time represented by the control. /// public readonly DateTime Time; /// /// Initializes a new instance of the TimeValueChangingEventArgs class. /// /// public TimeValueChangingEventArgs(DateTime time) { Time = time; } } /// /// Defines delegate for TimeValueChanging event. /// /// Source of event. /// Event arguments public delegate void TimeValueChangingEventHandler(object sender, TimeValueChangingEventArgs e); }