using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Tango.MachineStudio.Logging.Controls
{
public class TimeRuler : Control
{
private double _smallStep;
private double _bigStep;
#region Private Methods
private static T FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null) return null;
// check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return FindVisualParent<T>(parentObject);
}
}
#endregion
#region Properties
public double RenderWidth
{
get { return (double)GetValue(RenderWidthProperty); }
set { SetValue(RenderWidthProperty, value); }
}
public static readonly DependencyProperty RenderWidthProperty =
DependencyProperty.Register("RenderWidth", typeof(double), typeof(TimeRuler), new FrameworkPropertyMetadata(400.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double HorizontalOffset
{
get { return (double)GetValue(HorizontalOffsetProperty); }
set { SetValue(HorizontalOffsetProperty, value); }
}
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.Register("HorizontalOffset", typeof(double), typeof(TimeRuler), new PropertyMetadata(0.0, HorizontalOffsetChanged));
private static void HorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as TimeRuler;
control.Margin = new Thickness(-control.HorizontalOffset, 0, 0, 0);
control.InvalidateVisual();
}
#region Zoom
/// <summary>
/// Gets or sets the zoom factor for the ruler. The default value is 1.0.
/// </summary>
public double Zoom
{
get
{
return (double)GetValue(ZoomProperty);
}
set
{
SetValue(ZoomProperty, value);
this.InvalidateVisual();
}
}
private void SetSteps(double value)
{
if (value >= 50)
{
_smallStep = 0.25;
_bigStep = 1;
}
else if (value >= 40)
{
_smallStep = 0.25;
_bigStep = 2;
}
else if (value >= 30)
{
_smallStep = 0.5;
_bigStep = 2;
}
else if (value >= 20)
{
_smallStep = 1;
_bigStep = 5;
}
else if (value >= 10)
{
_smallStep = 1;
_bigStep = 10;
}
else if (value >= 5)
{
_smallStep = 2;
_bigStep = 20;
}
else if (value >= 3)
{
_smallStep = 5;
_bigStep = 30;
}
else if (value >= 1)
{
_smallStep = 30;
_bigStep = 300;
}
else if (value >= 0.5)
{
_smallStep = 60;
_bigStep = 600;
}
}
/// <summary>
/// Identifies the Zoom dependency property.
/// </summary>
public static readonly DependencyProperty ZoomProperty =
DependencyProperty.Register("Zoom", typeof(double), typeof(TimeRuler), new FrameworkPropertyMetadata((double)1.0, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(ZoomChanged)));
private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as TimeRuler).SetSteps((double)e.NewValue);
}
#endregion
#endregion
#region Constructor
public TimeRuler()
{
_smallStep = 1;
_bigStep = 10;
FontSize = 10;
this.Loaded += TimeRuler_Loaded;
}
private void TimeRuler_Loaded(object sender, RoutedEventArgs e)
{
this.InvalidateVisual();
}
#endregion
#region Methods
/// <summary>
/// Participates in rendering operations.
/// </summary>
/// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
double xDest = RenderWidth;
int start = (int)(HorizontalOffset / Zoom);
drawingContext.DrawRectangle(Background, new Pen(BorderBrush, 0), new Rect(new Point(start * Zoom, 0.0), new Point(xDest + (start * Zoom) + 100, Height)));
drawingContext.DrawLine(new Pen(BorderBrush, 1), new Point(start * Zoom, Height - 1), new Point(xDest + (start * Zoom) + 100, Height));
double l = (start % _smallStep);
while (l != 0) //I don't know why, but this is needed in order to prevent ruler from flickering on some lower scale factors :/
{
start += 1;
l = (start % _smallStep);
}
for (double dUnit = start; dUnit < (RenderWidth / Zoom) + (start) + 10; dUnit += _smallStep)
{
double d = dUnit * (this.Zoom);
double startHeight;
double endHeight;
startHeight = Height;
endHeight = ((dUnit % _bigStep == 0) ? (this.ActualHeight / 5) * 1.5 : this.ActualHeight / 5);
drawingContext.DrawLine(new Pen(Foreground, 1), new Point(d, startHeight), new Point(d, (startHeight - endHeight)));
double uu = (dUnit % _bigStep);
if ((dUnit != 0.0) && (uu == 0) && (dUnit < (RenderWidth / Zoom) + start + 10))
{
double u = dUnit;
TimeSpan t = TimeSpan.FromSeconds((u));
FormattedText ft = new FormattedText(t.ToString("g"),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(FontFamily.ToString()),
FontSize,
Foreground);
ft.SetFontWeight(FontWeight);
ft.TextAlignment = TextAlignment.Center;
drawingContext.DrawText(ft, new Point(d, (Height / 2) - (ft.Height / 2) - 2));
}
}
}
#endregion
}
}