Move control horizontally or vertically, not a combination of both

I've been burning midnight oil here around some lines of code trying to fix the problem.

In the code below, customize the control so that it can float anywhere in its parent control.

However this code allows it to move in either direction, I would like to constrain its movement to either the X or Y axis, which means the user can move it horizontally or vertically without ever combining both.

Point lastPosition = Point.Empty;

control.MouseDown += (sender, evt) =>
{
   lastPosition = evt.Location;
};

control.MouseMove += (sender, evt) =>
{
   // This moves the control anywhere.
   // I only want to move in one direction (left<->right  or  top <-> down) never diagonally
   // Not sure how to first find which direction the user wants to move, 
   // nor how to restrict the movement to only one of the directions mentioned
   int x = last.X + movingPiece.Left - mouseDownLocation.X;
   int y = last.Y + movingPiece.Top - mouseDownLocation.Y;
   movingPiece.Left = x;
   movingPiece.Top = y;
};

      

thank

Thanks to Hans Passant and TaW for the answers, from which I drew ideas from which I built a small 2D engine capable of delimiting areas where multiple controls can be moved within the pane container taking into account collision detection, directional avoidance and restriction overlaid side by side necessary user factors. I would like to accept both answers, but since this is not possible, I accepted the answer that gave the most insight into the question in question.

0


source to share


2 answers


Here's an example. I add a small Panel

"part" for the script to the larger panel Panel

.

I check the minimum delta so that the wobbly hand won't start moving.

One flag is tracking movement, the other is heading, and "0" is "not yet".



bool pieceMoving = false;
byte pieceDirection = 0;
Point startPosition = Point.Empty;

private void AddPieceButton_Click(object sender, EventArgs e)
{
    Panel newPiece = new Panel();
    newPiece.Size = new Size(16, 16);
    newPiece.BackColor = Color.Blue;

    pan_board.Controls.Add(newPiece);

    newPiece.MouseDown += (sender2, evt) => 
           { pieceMoving = true;  pieceDirection = 0; startPosition = evt.Location; };
    newPiece.MouseUp += (sender2, evt) => 
           { pieceMoving = false; pieceDirection = 0;};
    newPiece.MouseMove += (sender2, evt) =>
    {
        int delta = 0;
        if (!pieceMoving) return;
        if (pieceDirection == 0)
        {
            int deltaX = Math.Abs(startPosition.X - evt.X);
            int deltaY = Math.Abs(startPosition.Y - evt.Y);
            delta = deltaX + deltaY;
            if (deltaX == deltaY) return;
            if (delta  < 6) return;  // some minimum movement value
            if (deltaX > deltaY) pieceDirection = 1; else pieceDirection = 2;
        }   
        // else if (delta == 0) { pieceDirection = 0; return; }  // if you like!
        Panel piece = (Panel) sender2;
        if (pieceDirection == 1) piece.Left += evt.X; else piece.Top += evt.Y;

    };

      

Since I put the code in click Button

I named sender 'sender2' and I am using it to enable the same code for many parts.

+3


source


You cannot make it reliable as intended. A common solution to this UI problem is to provide help to the user, which he can turn on and off at will. To do this, use the Shift key. Allow free movement when off, but bind in the dominant direction when held.

You use the Control.ModifierKeys property to check if the key is in the MouseMove event handler. You need the KeyDown and KeyUp event handlers so that you can see the pressed and released Shift key. Significant changes are needed to properly follow the mouse position, it doesn't necessarily hover over the control anymore when you hold down the Shift key. Enough moving parts to be encapsulated in a helper class:

class ControlMover {
    private Control control;
    private Point downPos;
    private Point startPos;
    enum Constrains { None, Hor, Ver };
    private Constrains constraint;

    public ControlMover(Control ctl) {
        control = ctl;
        startPos = control.Location;
        downPos = Cursor.Position;
        control.Capture = true;
        control.MouseMove += control_MouseMove;
        control.MouseUp += control_MouseUp;
        control.MouseCaptureChanged += control_MouseCaptureChanged;
        control.KeyDown += control_KeyDown;
        control.KeyUp += control_KeyUp;
    }

    void handleKey(Keys key, bool down) {
        Console.WriteLine((int)key);
        if (key == Keys.Escape) {
            control.Capture = false;
            control.Location = startPos;
        }
        else if ((key & Keys.KeyCode) == Keys.ShiftKey) {
            if (!down) constraint = Constrains.None;
            else if (constraint == Constrains.None) {
                var curPos = Cursor.Position;
                if (Math.Abs(curPos.X - downPos.X) >= Math.Abs(curPos.Y - downPos.Y))
                     constraint = Constrains.Hor;
                else constraint = Constrains.Ver;
            }
            moveControl();
        }
    }

    void control_MouseCaptureChanged(object sender, EventArgs e) {
        // This ends it
        if (control.Capture) return;
        control.MouseMove -= control_MouseMove;
        control.MouseUp -= control_MouseUp;
        control.MouseCaptureChanged -= control_MouseCaptureChanged;
        control.KeyDown -= control_KeyDown;
        control.KeyUp -= control_KeyUp;

    }
    private void moveControl() {
        var curPos = Cursor.Position;
        if (constraint == Constrains.Hor) curPos.Y = downPos.Y;
        if (constraint == Constrains.Ver) curPos.X = downPos.X;
        curPos = control.Parent.PointToClient(curPos);
        // Keep it inside the parent
        curPos.X = Math.Max(0, curPos.X);
        curPos.Y = Math.Max(0, curPos.Y);
        curPos.X = Math.Min(control.Parent.ClientSize.Width - control.Width, curPos.X);
        curPos.Y = Math.Min(control.Parent.ClientSize.Height - control.Height, curPos.Y);
        control.Location = curPos;
    }

    void control_MouseUp(object sender, MouseEventArgs e) { control.Capture = false; }
    void control_MouseMove(object sender, MouseEventArgs e) { moveControl(); }
    void control_KeyDown(object sender, KeyEventArgs e) { handleKey(e.KeyData, true); }
    void control_KeyUp(object sender, KeyEventArgs e) { handleKey(e.KeyData, false); }
}

      



Sample usage:

    private void button1_MouseDown(object sender, MouseEventArgs e) {
        new ControlMover(button1);
    }

      

+3


source







All Articles