I replace the template of the Slider control with a knob template and handle some mouse events to implement rotation. The code snippets below shows the details:
Xaml:
<Window x:Class="WpfApplication16.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="clr-namespace:WpfApplication16" Title="Window1" Height="300" Width="300"> <Window.Resources> <my:ValueAngleConverter x:Key="valueAngleConverter"/> <my:ValueTextConverter x:Key="valueTextConverter"/> </Window.Resources> <Grid> <Slider Name="knob"> <Slider.Template> <ControlTemplate> <Viewbox> <Canvas Width="300" Height="300" Margin="5"> <Ellipse Fill="LightBlue" Width="300" Height="300" Canvas.Left="0" Canvas.Top="0" Stroke="Black" StrokeThickness="10" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/> <Ellipse Fill="Black" Width="60" Height="60" Canvas.Left="120" Canvas.Top="120"/> <Canvas> <Line Stroke="Red" StrokeThickness="5" X1="150" Y1="150" X2="150" Y2="10" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp"/> <Ellipse Fill="Red" Width="20" Height="20" Canvas.Left="140" Canvas.Top="0" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp"> <Ellipse.ToolTip> <ToolTip> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" Converter="{StaticResource valueTextConverter}"/> </ToolTip> </Ellipse.ToolTip> </Ellipse> <Canvas.RenderTransform> <RotateTransform CenterX="150" CenterY="150"> <RotateTransform.Angle> <MultiBinding Converter="{StaticResource valueAngleConverter}"> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value"/> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum"/> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum"/> </MultiBinding> </RotateTransform.Angle> </RotateTransform> </Canvas.RenderTransform> </Canvas> </Canvas> </Viewbox> </ControlTemplate> </Slider.Template> </Slider> </Grid> </Window>
Code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfApplication16 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private bool _isPressed = false; private Canvas _templateCanvas = null; private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { //Enable moving mouse to change the value. _isPressed = true; } private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { //Disable moving mouse to change the value. _isPressed = false; } private void Ellipse_MouseMove(object sender, MouseEventArgs e) { if (_isPressed) { //Find the parent canvas. if (_templateCanvas == null) { _templateCanvas = MyHelper.FindParent<Canvas>(e.Source as Ellipse); if (_templateCanvas == null) return; } //Canculate the current rotation angle and set the value. const double RADIUS = 150; Point newPos = e.GetPosition(_templateCanvas); double angle = MyHelper.GetAngleR(newPos, RADIUS); knob.Value = (knob.Maximum - knob.Minimum) * angle / (2 * Math.PI); } } } //The converter used to convert the value to the rotation angle. public class ValueAngleConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { double value = (double)values[0]; double minimum = (double)values[1]; double maximum = (double)values[2]; return MyHelper.GetAngle(value, maximum, minimum); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } //Convert the value to text. public class ValueTextConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { double v = (double)value; return String.Format("{0:F2}", v); } public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } public static class MyHelper { //Get the parent of an item. public static T FindParent<T>(FrameworkElement current) where T : FrameworkElement { do { current = VisualTreeHelper.GetParent(current) as FrameworkElement; if (current is T) { return (T)current; } } while (current != null); return null; } //Get the rotation angle from the value public static double GetAngle(double value, double maximum, double minimum) { double current = (value / (maximum - minimum)) * 360; if (current == 360) current = 359.999; return current; } //Get the rotation angle from the position of the mouse public static double GetAngleR(Point pos, double radius) { //Calculate out the distance(r) between the center and the position Point center = new Point(radius, radius); double xDiff = center.X - pos.X; double yDiff = center.Y - pos.Y; double r = Math.Sqrt(xDiff * xDiff + yDiff * yDiff); //Calculate the angle double angle = Math.Acos((center.Y - pos.Y) / r); Console.WriteLine("r:{0},y:{1},angle:{2}.", r, pos.Y, angle); if (pos.X < radius) angle = 2 * Math.PI - angle; if (Double.IsNaN(angle)) return 0.0; else return angle; } } }