创建自定义选择器(多开关)/WPF C#

创建自定义选择器(多开关)/WPF C#,wpf,controls,selector,Wpf,Controls,Selector,我想创建一个看起来像的控件。所以我选择从选择器派生它,并将项目放在画布上。 问题是:我应该如何对项目的放置进行编码?(可能为包含坐标的项目创建包装类,或者总是从画布接收坐标?) 另外,您也可以将我发送到已经实现的类似控制(遗憾的是,我没有找到任何类似的控制)。开始时请参阅此。在本例中,包含用于滑块的ControlTemplate 例如: XAML <Window x:Class="SliderControl.MainWindow" xmlns="http://schemas

我想创建一个看起来像的控件。所以我选择从选择器派生它,并将项目放在画布上。 问题是:我应该如何对项目的放置进行编码?(可能为包含坐标的项目创建包装类,或者总是从画布接收坐标?)

另外,您也可以将我发送到已经实现的类似控制(遗憾的是,我没有找到任何类似的控制)。

开始时请参阅此。在本例中,包含用于
滑块的ControlTemplate

例如:

XAML

<Window x:Class="SliderControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:SliderControl"
        Title="MainWindow" Height="350" Width="525">

    <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>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        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;
    }
}
样本筛选:

开始见此。在本例中,包含用于
滑块的ControlTemplate

例如:

XAML

<Window x:Class="SliderControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:SliderControl"
        Title="MainWindow" Height="350" Width="525">

    <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>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        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;
    }
}
样本筛选:


谢谢你的帮助,但你没有找到我。我想创建的不是滑块,而是项目控制(我的意思是不仅可以有数字,还可以有文字/其他控制)您可以将元素放在滑块周围吗?每个元素对应一个数字。例如:如果滑块值1-用户选择按钮1,2-按钮2,等等。谢谢你的帮助,但你没有得到我。我想创建的不是滑块,而是项目控制(我的意思是不仅可以有数字,还可以有文字/其他控制)您可以将元素放在滑块周围吗?每个元素对应一个数字。例如:如果滑块值1-用户选择按钮1,则滑块值2-按钮2,等等。