using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Tango.Visuals;
namespace Tango.Visuals
{
///
///
/// Represents a knob control.
///
public partial class Knob : UserControl
{
private List lines;
private Line markLine;
private Brush oldTicksHighlightBrush;
private double initialAngle;
private bool _showSmallMarkers;
#region Events
///
/// Occurs when the knob's value has changed.
///
public event EventHandler ValueChanged;
#endregion
#region Private Methods
private void SetValueFromMouse(double angle)
{
if (angle < -140)
{
angle = -140;
}
if (angle > 140)
{
angle = 140;
}
double value = angle + 140;
value = ((value) * 100) / 280;
value = (value * (Maximum - Minimum)) / 100;
value += Minimum;
if (value < Minimum)
{
Value = Minimum;
}
else if (value > Maximum)
{
Value = Maximum;
}
else
{
Value = value;
}
}
private void SetAngle(double angle)
{
if (ellipseGrid != null)
{
ellipseGrid.RenderTransformOrigin = new Point(0.5, 0.5);
ellipseGrid.RenderTransform = new RotateTransform(angle);
ellipseGrid.InvalidateMeasure();
}
}
private void LightMarkers()
{
if (ellipseGrid != null)
{
RotateTransform rotate = ellipseGrid.RenderTransform as RotateTransform;
double currentAngle = rotate.Angle;
if (lines != null)
{
foreach (Line m in lines)
{
RotateTransform t = m.RenderTransform as RotateTransform;
if (currentAngle >= t.Angle)
{
m.Stroke = TicksHighlightBrush;
}
else
{
m.Stroke = TicksBrush;
}
}
}
}
}
private void RenderMarkers()
{
lines = new List();
double angleChange = 280 / 20;
double markChange = 5;
double counter = 0;
int marksCounter = 0;
mainGrid.Children.OfType().ToList().ForEach(x => mainGrid.Children.Remove(x));
ellipseGrid.Children.OfType().ToList().ForEach(x => mainGrid.Children.Remove(x));
if (_showSmallMarkers)
{
for (int i = 0; i < 21; i++)
{
Line line = new Line();
line.StrokeThickness = TicksWidth;
line.Stroke = TicksBrush;
line.X1 = this.ActualWidth / 2;
if ((marksCounter == markChange || marksCounter == 0))
{
line.Y1 = 0;
marksCounter = 0;
}
else
{
line.Y1 = 3;
}
line.X2 = this.ActualWidth / 2;
line.Y2 = TicksHeight;
mainGrid.Children.Add(line);
line.RenderTransform = new RotateTransform(counter - 140, this.ActualWidth / 2, this.ActualHeight / 2);
counter += angleChange;
marksCounter++;
lines.Add(line);
}
}
ellipseGrid.Children.Remove(markLine);
markLine = new Line();
markLine.StrokeThickness = 2;
markLine.Stroke = TicksHighlightBrush;
markLine.X1 = ellipseGrid.ActualWidth / 2;
markLine.Y1 = 5;
markLine.X2 = markLine.X1;
markLine.Y2 = (ellipseGrid.ActualWidth / 2) * 0.8;
ellipseGrid.Children.Add(markLine);
}
private static BitmapSource GetImageFromApplicationResource(String resourcePath)
{
String callingAssembly = Assembly.GetCallingAssembly().GetName().Name;
Uri uri = new Uri("/" + callingAssembly + ";component/" + resourcePath, UriKind.Relative);
System.Windows.Resources.StreamResourceInfo info = Application.GetResourceStream(uri);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = info.Stream;
bitmapImage.EndInit();
return bitmapImage;
}
#endregion
#region Virtual Methods
///
/// Called when the value changed.
///
protected virtual void OnValueChanged()
{
try
{
if (Value < Minimum)
{
Value = Minimum;
}
if (Value > Maximum)
{
Value = Maximum;
}
double angle = ((Value - Minimum) * 100) / (Maximum - Minimum);
angle = ((angle * 280) / 100) - 140;
SetAngle(angle);
LightMarkers();
}
catch { }
if (ValueChanged != null) ValueChanged(this, new DoubleValueChangedEventArgs(Value));
}
///
/// Invoked before the value has changed.
///
/// The value.
///
protected virtual object OnCoerceValue(double value)
{
var v = SnapToValues ? Math.Round(value, SnapPercision) : value;
return v;
}
///
/// Draws the knob.
///
protected virtual void OnDrawKnob()
{
try
{
RenderMarkers();
LightMarkers();
if (KnobType == KnobType.MetroDark)
{
img.Source = GetImageFromApplicationResource("Knob/volumeKnobMetroDark.png");
}
else if (KnobType == KnobType.MetroLight)
{
img.Source = GetImageFromApplicationResource("Knob/volumeKnobMetroLight.png");
}
else if (KnobType == KnobType.Dark)
{
img.Source = GetImageFromApplicationResource("Knob/volumeKnobDark.png");
}
else if (KnobType == KnobType.Light)
{
img.Source = GetImageFromApplicationResource("Knob/volumeKnobLight.png");
}
else if (KnobType == KnobType.Complex)
{
img.Source = GetImageFromApplicationResource("Knob/volumeKnobComplex.png");
}
}
catch { }
}
#endregion
#region Properties
///
/// Gets or sets the ticks highlight brush.
///
public Brush TicksHighlightBrush
{
get { return (Brush)GetValue(TicksHighlightBrushProperty); }
set { SetValue(TicksHighlightBrushProperty, value); }
}
public static readonly DependencyProperty TicksHighlightBrushProperty =
DependencyProperty.Register("TicksHighlightBrush", typeof(Brush), typeof(Knob), new PropertyMetadata(Brushes.Red, OnPropertiesChanged));
///
/// Gets or sets the ticks brush.
///
public Brush TicksBrush
{
get { return (Brush)GetValue(TicksBrushProperty); }
set { SetValue(TicksBrushProperty, value); }
}
public static readonly DependencyProperty TicksBrushProperty =
DependencyProperty.Register("TicksBrush", typeof(Brush), typeof(Knob), new PropertyMetadata(Brushes.DimGray, OnPropertiesChanged));
///
/// Gets or sets the type of the knob.
///
public KnobType KnobType
{
get { return (KnobType)GetValue(KnobTypeProperty); }
set { SetValue(KnobTypeProperty, value); }
}
public static readonly DependencyProperty KnobTypeProperty =
DependencyProperty.Register("KnobType", typeof(KnobType), typeof(Knob), new PropertyMetadata(KnobType.Dark, OnPropertiesChanged));
///
/// Gets or sets amount of change when using keyboard arrows.
///
public double Change
{
get { return (double)GetValue(ChangeProperty); }
set { SetValue(ChangeProperty, value); }
}
public static readonly DependencyProperty ChangeProperty =
DependencyProperty.Register("Change", typeof(double), typeof(Knob), new PropertyMetadata(1.0));
///
/// Gets or sets the width of the ticks.
///
public double TicksWidth
{
get { return (double)GetValue(TicksWidthProperty); }
set { SetValue(TicksWidthProperty, value); }
}
public static readonly DependencyProperty TicksWidthProperty =
DependencyProperty.Register("TicksWidth", typeof(double), typeof(Knob), new PropertyMetadata(3.0, OnPropertiesChanged));
///
/// Gets or sets the height of the ticks.
///
public double TicksHeight
{
get { return (double)GetValue(TicksHeightProperty); }
set { SetValue(TicksHeightProperty, value); }
}
public static readonly DependencyProperty TicksHeightProperty =
DependencyProperty.Register("TicksHeight", typeof(double), typeof(Knob), new PropertyMetadata(10.0, OnPropertiesChanged));
///
/// Gets or sets the value.
///
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, SnapToValues ? Math.Round(value, SnapPercision) : value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(Knob), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => (d as Knob).OnValueChanged(), (d, value) => (d as Knob).OnCoerceValue((double)value)));
///
/// Gets or sets the minimum value.
///
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(Knob), new PropertyMetadata(0.0, OnPropertiesChanged));
public bool SnapToValues
{
get { return (bool)GetValue(SnapToValuesProperty); }
set { SetValue(SnapToValuesProperty, value); }
}
public static readonly DependencyProperty SnapToValuesProperty =
DependencyProperty.Register("SnapToValues", typeof(bool), typeof(Knob), new PropertyMetadata(false));
public int SnapPercision
{
get { return (int)GetValue(SnapPercisionProperty); }
set { SetValue(SnapPercisionProperty, value); }
}
public static readonly DependencyProperty SnapPercisionProperty =
DependencyProperty.Register("SnapPercision", typeof(int), typeof(Knob), new PropertyMetadata(1));
///
/// Gets or sets the maximum value.
///
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(Knob), new PropertyMetadata(1.0, OnPropertiesChanged));
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Knob).OnDrawKnob();
(d as Knob).OnValueChanged();
}
#endregion
#region Constructors
public Knob()
{
_showSmallMarkers = true;
InitializeComponent();
this.Loaded += Knob_Loaded;
moveThumb.AddHandler(Thumb.MouseEnterEvent, new MouseEventHandler(ellipseGrid_MouseEnter), true);
moveThumb.AddHandler(Thumb.MouseLeaveEvent, new MouseEventHandler(ellipseGrid_MouseLeave), true);
this.AddHandler(UserControl.PreviewKeyDownEvent, new KeyEventHandler(Knob_PreviewKeyDown), true);
this.PreviewMouseDown += Knob_PreviewMouseDown;
this.Focusable = true;
KnobType = KnobType.MetroLight;
this.IsEnabledChanged += Knob_IsEnabledChanged;
moveThumb.DragStarted += moveThumb_DragStarted;
moveThumb.DragDelta += moveThumb_DragDelta;
this.SizeChanged += VolumeKnob_SizeChanged;
}
#endregion
#region Event Handlers
///
/// Handles the SizeChanged event of the VolumeKnob control.
///
/// The source of the event.
/// The instance containing the event data.
private void VolumeKnob_SizeChanged(object sender, SizeChangedEventArgs e)
{
OnDrawKnob();
}
///
/// Handles the DragDelta event of the moveThumb control.
///
/// The source of the event.
/// The instance containing the event data.
private void moveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
double angle = 0;
angle = e.VerticalChange * -1;
angle = this.initialAngle + angle;
this.SetValueFromMouse(angle);
}
///
/// Handles the DragStarted event of the moveThumb control.
///
/// The source of the event.
/// The instance containing the event data.
private void moveThumb_DragStarted(object sender, DragStartedEventArgs e)
{
Point startPoint = Mouse.GetPosition(ellipseGrid);
RotateTransform t = ellipseGrid.RenderTransform as RotateTransform;
if (t != null)
{
this.initialAngle = t.Angle;
}
}
///
/// Handles the IsEnabledChanged event of the Knob control.
///
/// The source of the event.
/// The instance containing the event data.
private void Knob_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == e.OldValue) return;
if (IsEnabled)
{
TicksHighlightBrush = oldTicksHighlightBrush;
RenderMarkers();
}
else
{
oldTicksHighlightBrush = TicksHighlightBrush;
TicksHighlightBrush = Brushes.Gray;
RenderMarkers();
}
}
///
/// Handles the PreviewMouseDown event of the Knob control.
///
/// The source of the event.
/// The instance containing the event data.
private void Knob_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
this.FocusVisualStyle = null;
this.Focusable = true;
this.Focus();
}
///
/// Handles the PreviewKeyDown event of the Knob control.
///
/// The source of the event.
/// The instance containing the event data.
private void Knob_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up && Value < Maximum)
{
Value += Change;
}
else if (e.Key == Key.Down && Value > Minimum)
{
Value -= Change;
}
this.Focusable = true;
this.Focus();
e.Handled = true;
}
///
/// Handles the Loaded event of the Knob control.
///
/// The source of the event.
/// The instance containing the event data.
private void Knob_Loaded(object sender, RoutedEventArgs e)
{
OnDrawKnob();
}
///
/// Handles the MouseLeave event of the ellipseGrid control.
///
/// The source of the event.
/// The instance containing the event data.
private void ellipseGrid_MouseLeave(object sender, MouseEventArgs e)
{
DoubleAnimation maskAni = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
maskEllipse.Fill.BeginAnimation(RadialGradientBrush.OpacityProperty, maskAni);
DoubleAnimation glowAni = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
glowEllipse.BeginAnimation(OpacityProperty, glowAni);
//DoubleAnimation efxAni = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
//glowEffect.BeginAnimation(OpacityProperty, efxAni);
DoubleAnimation glow = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
imgGlow.BeginAnimation(RadialGradientBrush.OpacityProperty, glow);
}
///
/// Handles the MouseEnter event of the ellipseGrid control.
///
/// The source of the event.
/// The instance containing the event data.
private void ellipseGrid_MouseEnter(object sender, MouseEventArgs e)
{
DoubleAnimation maskAni = new DoubleAnimation(0.5, new Duration(TimeSpan.FromMilliseconds(200)));
maskEllipse.Fill.BeginAnimation(RadialGradientBrush.OpacityProperty, maskAni);
DoubleAnimation glowAni = new DoubleAnimation(1, new Duration(TimeSpan.FromMilliseconds(200)));
glowEllipse.BeginAnimation(OpacityProperty, glowAni);
//DoubleAnimation efxAni = new DoubleAnimation(1, new Duration(TimeSpan.FromMilliseconds(200)));
//glowEffect.BeginAnimation(OpacityProperty, efxAni);
DoubleAnimation glow = new DoubleAnimation(0.5, new Duration(TimeSpan.FromMilliseconds(200)));
imgGlow.BeginAnimation(RadialGradientBrush.OpacityProperty, glow);
}
#endregion
}
}