Ограничить большой палец до эллиптической области

Я довольно новичок в WPF, поэтому изучаю большую часть этого трудного (но забавного) способа. Я создаю HSV-подобный пользовательский контроль цвета, и хотел бы получить такое поведение, если большой палец, который я использую в качестве "селектора", ограничен эллиптической областью (на самом деле круг). При перемещении наружу селектор должен держаться в стороне и не двигаться вообще. Я считаю, что это наиболее распространенное GUI-поведение, так оно и должно себя вести. Не стесняйтесь предлагать лучшее поведение!

Существует ли распространенное, известное и рекомендуемое решение для этого или каждый должен заново изобретать колесо?

Есть хорошие идеи, как это решить?

Код-за:

public partial class HSVColorPicker : UserControl
{
    public HSVColorPicker()
    {
        InitializeComponent();
    }

    void onDragDelta(object sender, DragDeltaEventArgs e)
    {
        Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
        Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
    }
}

XAML:

<Grid>
    <Canvas x:Name="canvas">
        <Image x:Name="wheel" Source="colorwheel.png" Width="300" Margin="5,5,0,0"/>
        <Thumb Name="thumb" DragDelta="onDragDelta" Canvas.Left="104" Canvas.Top="68" Template="{StaticResource thumbTemplate}" />
    </Canvas>
</Grid>

Пока я здесь, большой палец всегда тянет за курсором, есть ли другой способ создать это? Как я уже сказал, я новичок в WPF и создании GUI, так что, возможно, есть очевидные решения, которые мне не приходили в голову;)

2 ответа

Решение

Я немного переосмыслил и полностью опустил большой палец, используя вместо этого фиктивный круг (названный большим пальцем). Теперь я слушаю mousedown, mouseup и mousemove на холсте и определяю, что должно быть возможно, а что нет. Это имеет приятную особенность, заключающуюся в том, что большой палец прилипает к краю цветового колеса, когда мышь выходит за пределы области, но область немного больше, чем цветное колесо, чтобы было легче получить точку на границе. Не завершено, но это решило мой вопрос, поэтому я выкладываю его как есть в данный момент

    private bool mousePressed { get; set; }
    private bool mouseWithinArea { get; set; }
    private Point circleMiddlePoint { get; set; }
    private int margin;
    private double mPX;
    private double mPY;
    private double localXpos;
    private double globalXpos
    {
        get
        {
            return localXpos + mPX;
        }
        set
        {
            localXpos = value - mPX;
            Canvas.SetLeft(thumb, value);
        }
    }
    private double localYpos;
    private double globalYpos
    {
        get
        {
            return mPY - localYpos;
        }
        set
        {
            localYpos = mPY - value;
            Canvas.SetTop(thumb, value);
        }
    }

    public HSVColorPicker()
    {
        InitializeComponent();
        wheel.Width = 300;
        margin = 15;
        mPX = 150+margin;
        mPY = 150+margin;
        circleMiddlePoint = new Point(mPX, mPY);
    }

    private void CalcPosition(double X, double Y)
    {
        double radius = wheel.Width / 2.0;
        double vectorX = X - mPX;
        double vectorY = Y - mPY;
        double distance = Math.Sqrt(vectorX * vectorX + vectorY * vectorY);
        if (distance > radius)
        {
            double factor = radius / distance;
            vectorX *= factor;
            vectorY *= factor;
        }
        globalXpos = vectorX + mPX;
        globalYpos = vectorY + mPY;
    }

    private void wheel_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (mouseWithinArea)
        {
            mousePressed = true;
            Point mousePoint = e.GetPosition(this);
            CalcPosition(mousePoint.X, mousePoint.Y);
        }
    }

    private void wheel_MouseMove(object sender, MouseEventArgs e)
    {
        Point mousePoint = e.GetPosition(this);
        double relX = mousePoint.X - mPX;
        double relY = mPY - mousePoint.Y;
        if (mouseWithinArea)
        {
            if (Math.Sqrt(relX * relX + relY * relY) > 150+margin)
            {
                mouseWithinArea = false;
            }
            else
            {
                if (mousePressed)
                {
                    CalcPosition(mousePoint.X, mousePoint.Y);
                }
            }
        }
        else
        {
            if (Math.Sqrt(relX * relX + relY * relY) < 150+margin)
            {
                mouseWithinArea = true;
                if (mousePressed)
                {
                    CalcPosition(mousePoint.X, mousePoint.Y);
                }
            }
        }
    }

    private void wheel_MouseUp(object sender, MouseButtonEventArgs e)
    {
        mousePressed = false;
    }
}

<Canvas x:Name="canvas" Background="Transparent" MouseDown="wheel_MouseDown" MouseMove="wheel_MouseMove" MouseUp="wheel_MouseUp" Width="330" Height="330">
        <Image x:Name="wheel" Source="colorwheel.png" Width="300" Margin="15,15,0,0"  />
        <Ellipse Margin="0,0,0,0"
                x:Name="outerEll"
                Stroke="Silver"
                StrokeThickness="15" 
                Width="330"
                Height="330"/>
        <Ellipse Name="thumb" Stroke="Black" Fill="Silver" Canvas.Left="150" Canvas.Top="150" Width="15" Height="15" Margin="-12" />
    </Canvas>

Вы хотите, чтобы центр вашего большого пальца лежал внутри вашего цветового круга.

Таким образом, расстояние между центром вашего большого пальца и центром вашего цветового круга (то есть, центром вашего холста) должно быть меньше или равно радиусу вашего цветового круга (то есть, половина стороны вашего холста).).

Непроверенный код C#:

void onDragDelta(object sender, DragDeltaEventArgs e)
{
    double radius = canvas.RenderSize.Width / 2.0;
    double thumbCenterX = Canvas.GetLeft(thumb) - thumb.RenderSize.Width + e.HorizontalChange;
    double thumbCenterY = Canvas.GetTop(thumb) - thumb.RenderSize.Height + e.VerticalChange;
    double colorWheelCenterX = canvas.RenderSize.Width / 2.0;
    double colorWheelCenterY = canvas.RenderSize.Height / 2.0;
    double vectorX = thumbCenterX - colorWheelCenterX;
    double vectorY = thumbCenterY - colorWheelCenterY;
    double distance = Math.Sqrt(vectorX * vectorX + vectorY * vectorY);
    if(distance > radius) {
        double factor = radius / distance;
        vectorX *= factor;
        vectorY *= factor;
    }
    Canvas.SetLeft(thumb, colorWheelCenterX + vectorX - thumb.RenderSize.Width / 2.0);
    Canvas.SetTop(thumb, colorWheelCenterY + vectorY - thumb.RenderSize.Height / 2.0);
}
Другие вопросы по тегам