Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I only allow dragging in a circular path?

Tags:

wpf

Is it possible to restrict the drag source to only move within the boundaries of a circular path when dragging it?

like image 507
loraderon Avatar asked Oct 22 '10 12:10

loraderon


2 Answers

You don't need the 360-point path. Instead, as you are dragging, compute the current angle using Math.Atan2(Y,X), and then generate the point on the circle. You would still need to compute center and radius on resize and store them, or compute them inside MouseMove.

    private void UserControl_MouseMove(object sender, MouseEventArgs e)
    {
        if (!isDraggingMarker)
            return;
        var position = e.GetPosition(this);

        double angle = Math.Atan2(position.Y - center.Y, position.X - center.X);
        var closest = new Point(center.X + radius*Math.Cos(angle),
                                center.Y + radius*Math.Sin(angle));

        SetMarkerPosition(closest);
    }
like image 68
Justin Avatar answered Sep 29 '22 20:09

Justin


Create a circle of points and then when the mouse moves (and we are dragging) calculate the nearest point and snap to that point.

CircularDrag.xaml

<UserControl x:Class="DraggingBoundaries.CircularDrag"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         SizeChanged="UserControl_SizeChanged"
         MouseMove="UserControl_MouseMove"
         MouseLeave="UserControl_MouseLeave"
         MouseLeftButtonUp="UserControl_MouseLeftButtonUp"
         >
    <Grid Background="White">
        <Border 
            x:Name="Marker"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Width="14" 
            Height="14" 
            Background="CornflowerBlue" 
            CornerRadius="2" 
            BorderThickness="1" 
            BorderBrush="DarkGray"
            MouseLeftButtonDown="Marker_MouseLeftButtonDown"
            />
    </Grid>
</UserControl>

CircularDrag.xaml.cs

using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace DraggingBoundaries
{
    public partial class CircularDrag : UserControl
    {
        List<Point> allowedWheelMarkerPositions;
        bool isDraggingMarker;

        public CircularDrag()
        {
            InitializeComponent();
        }

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var center = new Point(e.NewSize.Width / 2, e.NewSize.Height / 2);

            var radius = (center.X < center.Y ? center.X : center.Y) - 15;

            allowedWheelMarkerPositions = CreateCirclePath(center, radius);
            SetMarkerPosition(allowedWheelMarkerPositions.First());
        }

        private List<Point> CreateCirclePath(Point center, double radius)
        {
            var result = new List<Point>();
            for (double angle = 0; angle <= 360; angle++)
            {
                double angleR = angle * (Math.PI / 180);
                double x = center.X + Math.Cos(angleR) * radius;
                double y = center.Y - Math.Sin(angleR) * radius;

                result.Add(new Point(x, y));
            }
            return result;
        }

        private void UserControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (!isDraggingMarker)
                return;
            var position = e.GetPosition(this);

            var closest = allowedWheelMarkerPositions
                .OrderBy(p => GetDistance(position, p))
                .First();

            SetMarkerPosition(closest);
        }

        private void SetMarkerPosition(Point closest)
        {
            Marker.Margin = new Thickness(closest.X - Marker.Width / 2, closest.Y - Marker.Height / 2, 0, 0);
        }

        private double GetDistance(Point a, Point b)
        {
            var deltaX = a.X - b.X;
            var deltaY = a.Y - b.Y;

            return Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
        }

        private void Marker_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isDraggingMarker = true;
        }

        private void UserControl_MouseLeave(object sender, MouseEventArgs e)
        {
            isDraggingMarker = false;
        }

        private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDraggingMarker = false;
        }
    }
}
like image 45
loraderon Avatar answered Sep 29 '22 21:09

loraderon