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 } }