Skip to content

Commit e7d8350

Browse files
committed
Fix clock hands
1 parent a14140d commit e7d8350

File tree

16 files changed

+214
-154
lines changed

16 files changed

+214
-154
lines changed

BionicCode.Net/BionicCode.Controls.Net.Core.Wpf/AnalogClockFace.cs

Lines changed: 64 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
namespace BionicCode.Controls.Net.Core.Wpf
2222
{
2323
[TemplatePart(Name = "PART_ElementHost", Type = typeof(FrameworkElement))]
24-
public class AnalogClockFace : Control
24+
public class AnalogClockFace : ContentControl
2525
{
2626
#region IsCenterElementOnCircumferenceEnabled attached property
2727

@@ -230,19 +230,7 @@ public class AnalogClockFace : Control
230230
public double SelectedSecond { get => (double) GetValue(AnalogClockFace.SelectedSecondProperty); set => SetValue(AnalogClockFace.SelectedSecondProperty, value); }
231231

232232
#endregion SelectedSecond dependency property
233-
234-
#region Diameter dependency property
235-
236-
//public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
237-
// "Diameter",
238-
// typeof(double),
239-
// typeof(AnalogClockFace),
240-
// new PropertyMetadata(default(double), AnalogClockFace.OnDiameterChanged));
241-
242-
//public double Diameter { get => (double) GetValue(AnalogClockFace.DiameterProperty); set => SetValue(AnalogClockFace.DiameterProperty, value); }
243-
244-
#endregion Diameter dependency property
245-
233+
246234
#region ClockFaceLoadedRoutedEvent
247235

248236
public static readonly RoutedEvent ClockFaceLoadedRoutedEvent = EventManager.RegisterRoutedEvent("ClockFaceLoaded",
@@ -256,21 +244,39 @@ public event RoutedEventHandler ClockFaceLoaded
256244

257245
#endregion
258246

247+
#region Diameter read-only dependecy property
248+
249+
protected static readonly DependencyPropertyKey DiameterPropertyKey = DependencyProperty.RegisterReadOnly(
250+
"Diameter",
251+
typeof(double),
252+
typeof(AnalogClockFace),
253+
new PropertyMetadata(default(double)));
254+
255+
public static readonly DependencyProperty DiameterProperty = AnalogClockFace.DiameterPropertyKey.DependencyProperty;
256+
257+
public double Diameter
258+
{
259+
get => (double)GetValue(AnalogClockFace.DiameterProperty);
260+
private set => SetValue(AnalogClockFace.DiameterPropertyKey, value);
261+
}
262+
263+
#endregion Diameter read-only dependecy property
264+
259265
#region Radius read-only dependecy property
260266

261-
//protected static readonly DependencyPropertyKey RadiusPropertyKey = DependencyProperty.RegisterReadOnly(
262-
// "Radius",
263-
// typeof(double),
264-
// typeof(AnalogClockFace),
265-
// new PropertyMetadata(default(double)));
267+
protected static readonly DependencyPropertyKey RadiusPropertyKey = DependencyProperty.RegisterReadOnly(
268+
"Radius",
269+
typeof(double),
270+
typeof(AnalogClockFace),
271+
new PropertyMetadata(default(double)));
266272

267-
//public static readonly DependencyProperty RadiusProperty = AnalogClockFace.RadiusPropertyKey.DependencyProperty;
273+
public static readonly DependencyProperty RadiusProperty = AnalogClockFace.RadiusPropertyKey.DependencyProperty;
268274

269-
//public double Radius
270-
//{
271-
// get => (double) GetValue(AnalogClockFace.RadiusProperty);
272-
// private set => SetValue(AnalogClockFace.RadiusPropertyKey, value);
273-
//}
275+
public double Radius
276+
{
277+
get => (double)GetValue(AnalogClockFace.RadiusProperty);
278+
private set => SetValue(AnalogClockFace.RadiusPropertyKey, value);
279+
}
274280

275281
#endregion Radius read-only dependecy property
276282

@@ -298,34 +304,29 @@ static AnalogClockFace()
298304
}
299305
public AnalogClockFace()
300306
{
301-
InitializeClockFaceCanvas();
302307
InitializeClockRotateTransforms();
308+
InitializeClockFaceCanvas();
303309
this.IntervalLabelFormatter = value => value.ToString();
304-
//this.Loaded += OnLoaded;
305-
}
306-
307-
private void OnLoaded(object sender, RoutedEventArgs e)
308-
{
309-
AddClockHands();
310310
}
311311

312312
private void InitializeClockFaceCanvas()
313313
{
314314
this.ClockFaceCanvas = new Canvas();
315-
var widthBinding = new Binding(nameof(this.ActualWidth)) {Source = this};
315+
this.Content = this.ClockFaceCanvas;
316+
317+
var widthBinding = new Binding(nameof(this.Diameter)) {Source = this};
316318
this.ClockFaceCanvas.SetBinding(FrameworkElement.WidthProperty, widthBinding);
317319
var heightBinding = new Binding(nameof(this.Height)) {Source = this};
318320
this.ClockFaceCanvas.SetBinding(FrameworkElement.HeightProperty, heightBinding);
319321
}
320322

321323
private void InitializeClockRotateTransforms()
322324
{
323-
var radius = this.ActualWidth / 2;
324-
this.IntervalElementTransform = new RotateTransform(0, radius, radius);
325-
this.HourHandTransform = new RotateTransform(0, radius, radius);
326-
this.MinuteHandTransform = new RotateTransform(0, radius, radius);
327-
this.SecondHandTransform = new RotateTransform(0, radius, radius);
328-
var radiusBinding = new Binding(nameof(this.ActualWidth)) {Source = this, Converter = new DividerValueConverter(), ConverterParameter = 2};
325+
this.IntervalElementTransform = new RotateTransform(0, this.Radius, this.Radius);
326+
this.HourHandTransform = new RotateTransform();
327+
this.MinuteHandTransform = new RotateTransform(0, this.Radius, this.Radius);
328+
this.SecondHandTransform = new RotateTransform(0, this.Radius, this.Radius);
329+
var radiusBinding = new Binding(nameof(this.Radius)) { Source = this };
329330
BindingOperations.SetBinding(this.IntervalElementTransform, RotateTransform.CenterXProperty, radiusBinding);
330331
BindingOperations.SetBinding(this.IntervalElementTransform, RotateTransform.CenterYProperty, radiusBinding);
331332
BindingOperations.SetBinding(this.HourHandTransform, RotateTransform.CenterXProperty, radiusBinding);
@@ -335,31 +336,26 @@ private void InitializeClockRotateTransforms()
335336
BindingOperations.SetBinding(this.SecondHandTransform, RotateTransform.CenterXProperty, radiusBinding);
336337
BindingOperations.SetBinding(this.SecondHandTransform, RotateTransform.CenterYProperty, radiusBinding);
337338
}
338-
339-
#region Overrides of FrameworkElement
340-
341-
/// <inheritdoc />
342-
public override void OnApplyTemplate()
343-
{
344-
base.OnApplyTemplate();
345-
var hostPanel = GetTemplateChild("PART_ElementHost") as FrameworkElement;
346-
347-
hostPanel.TryAssignValueToUnknownElement(this.ClockFaceCanvas);
348-
}
349-
350-
#endregion
351-
339+
352340

353341
#region Overrides of FrameworkElement
354342

355343
/// <inheritdoc />
356344
protected override Size MeasureOverride(Size constraint)
357345
{
358-
DrawAnalogClock();
359346
constraint = base.MeasureOverride(constraint);
347+
DrawAnalogClock();
360348
return constraint;
361349
}
362350

351+
/// <inheritdoc />
352+
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
353+
{
354+
base.OnRenderSizeChanged(sizeInfo);
355+
this.Diameter = this.ActualWidth;
356+
this.Radius = this.Diameter / 2;
357+
}
358+
363359
#endregion
364360

365361

@@ -628,7 +624,7 @@ protected virtual UIElement Create15MinuteIntervalLabel(int labelValue)
628624
return label;
629625
}
630626

631-
return new TextBlock {Text = formattedLabelValue.ToString()};
627+
return new TextBlock { Text = formattedLabelValue.ToString(), Padding = new Thickness(0) };
632628
}
633629

634630
protected virtual UIElement Create5MinuteIntervalLabel(int labelValue)
@@ -640,7 +636,7 @@ protected virtual UIElement Create5MinuteIntervalLabel(int labelValue)
640636
return label;
641637
}
642638

643-
return new TextBlock {Text = formattedLabelValue.ToString()};
639+
return new TextBlock {Text = formattedLabelValue.ToString(), Padding = new Thickness(0) };
644640
}
645641

646642
protected virtual UIElement CreateMinuteIntervalLabel(int labelValue)
@@ -652,7 +648,7 @@ protected virtual UIElement CreateMinuteIntervalLabel(int labelValue)
652648
return label;
653649
}
654650

655-
return new TextBlock {Text = formattedLabelValue.ToString()};
651+
return new TextBlock {Text = formattedLabelValue.ToString(), Padding = new Thickness(0) };
656652
}
657653

658654
protected virtual UIElement CloneElement(UIElement elementToClone) => elementToClone.CloneElement();
@@ -691,12 +687,10 @@ protected virtual void DrawAnalogClock()
691687
{
692688
this.ClockFaceCanvas.Children.Clear();
693689

694-
if (this.ActualWidth == 0)
690+
if (this.Diameter == 0)
695691
{
696692
return;
697693
}
698-
699-
double radius = this.ActualWidth / 2;
700694
double steps = 60.0;
701695
double degreeOfStep = 360.0 / steps;
702696
double intervalMarkerCenterPositionRadius = -1;
@@ -718,7 +712,7 @@ protected virtual void DrawAnalogClock()
718712
if (intervalMarkerCenterPositionRadius.Equals(-1))
719713
{
720714
double radiusOffset = CalculateIntervalMarkerCenterRadiusOffset(intervalMarker);
721-
intervalMarkerCenterPositionRadius = radius + radiusOffset;
715+
intervalMarkerCenterPositionRadius = this.Radius + radiusOffset;
722716
}
723717

724718
var stepLabel = GetStepLabel(step);
@@ -735,7 +729,7 @@ protected virtual void DrawAnalogClock()
735729
if (!this.Is15MinuteIntervalEnabled && intervalMarkerCenterPositionRadius.Equals(-1))
736730
{
737731
double radiusOffset = CalculateIntervalMarkerCenterRadiusOffset(intervalMarker);
738-
intervalMarkerCenterPositionRadius = radius + radiusOffset;
732+
intervalMarkerCenterPositionRadius = this.Radius + radiusOffset;
739733
}
740734

741735
var stepLabel = GetStepLabel(step);
@@ -748,7 +742,7 @@ protected virtual void DrawAnalogClock()
748742
if (!this.Is15MinuteIntervalEnabled && !this.Is5MinuteIntervalEnabled && intervalMarkerCenterPositionRadius.Equals(-1))
749743
{
750744
double radiusOffset = CalculateIntervalMarkerCenterRadiusOffset(intervalMarker);
751-
intervalMarkerCenterPositionRadius = radius + radiusOffset;
745+
intervalMarkerCenterPositionRadius = this.Radius + radiusOffset;
752746
}
753747

754748
if (this.Is24HModeEnabled)
@@ -776,7 +770,7 @@ protected virtual void DrawAnalogClock()
776770
}
777771

778772
Point cartesianPoint = GetCartesianPointOfStep(degreesOfCurrentStep, intervalMarkerCenterPositionRadius);
779-
if (GetIsCenterElementOnCircumferenceEnabled(intervalMarker))
773+
if (AnalogClockFace.GetIsCenterElementOnCircumferenceEnabled(intervalMarker))
780774
{
781775
AlignElementCenterPointToRadius(ref cartesianPoint, intervalMarker);
782776
}
@@ -791,14 +785,15 @@ protected virtual void DrawAnalogClock()
791785
double labelRadiusOffset = -24;
792786
double intervalMarkerLabelCenterPositionRadius = intervalMarkerCenterPositionRadius + labelRadiusOffset;
793787
cartesianPoint = GetCartesianPointOfStep(degreesOfCurrentStep, intervalMarkerLabelCenterPositionRadius);
794-
AlignElementCenterPointToRadius(ref cartesianPoint, intervalMarkerLabel);
788+
AlignElementCenterPointToRadius(ref cartesianPoint, intervalMarkerLabel);
789+
cartesianPoint.Offset(0, 4);
795790
AddCartesianElementToClockFace(intervalMarkerLabel, cartesianPoint);
796791
}
797792

798-
double deltaToMiddleRadius = (radius - intervalMarkerCenterPositionRadius) * 2;
793+
double deltaToMiddleRadius = (this.Radius - intervalMarkerCenterPositionRadius) * 2;
799794
var clockFaceBackgroundPosition = new Point();
800795
clockFaceBackgroundPosition.Offset(deltaToMiddleRadius / 2, deltaToMiddleRadius / 2);
801-
AddElementToClockFace(new Ellipse() { Height = this.Height - deltaToMiddleRadius, Width = this.ActualWidth - deltaToMiddleRadius, Fill = Brushes.DarkRed }, clockFaceBackgroundPosition, 0);
796+
AddElementToClockFace(new Ellipse() { Height = this.Diameter - deltaToMiddleRadius, Width = this.Diameter - deltaToMiddleRadius, Fill = Brushes.DarkRed }, clockFaceBackgroundPosition, 0);
802797

803798
AddClockHands();
804799
OnClockFaceLoaded();
@@ -846,7 +841,7 @@ private double CalculateIntervalMarkerCenterRadiusOffset(UIElement intervalMarke
846841

847842
private Point GetCartesianPointOfStep(double degreesOfCurrentStep, double radius)
848843
{
849-
double axisOffset = this.ActualWidth / 2;
844+
double axisOffset = this.Radius;
850845
// Rotate and invert degrees in order to move the 0 clock value to the top
851846
// (instead of the original right -> cartesian based circle)
852847
double rotatedDegrees = degreesOfCurrentStep - 90;
@@ -868,7 +863,7 @@ private Point GetCartesianPointOfStep(double degreesOfCurrentStep, double radius
868863

869864
public void AddCartesianElementToClockFace(UIElement clockElement, Point cartesianPoint)
870865
{
871-
Point screenPoint = ConvertCartesianPointToScreenPoint(cartesianPoint, this.ActualWidth);
866+
Point screenPoint = ConvertCartesianPointToScreenPoint(cartesianPoint, this.Diameter);
872867
AddElementToClockFace(clockElement, screenPoint);
873868
}
874869

BionicCode.Net/BionicCode.Controls.Net.Core.Wpf/BionicCode.Controls.Net.Core.Wpf.xml

Lines changed: 32 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)