C#在Canvas中拖放图像

时间:2021-03-23 09:45:00

I tried to Google how to make drag & drop for UIElements on a Canvas, but couldn't find anything that I'm looking for.

我试图谷歌如何在画布上拖放UIElements,但找不到我正在寻找的东西。

I got a C# WPF application with a Window. Inside the Window I got a Canvas where I can add Images to. What I want is to be able to Drag & Drop the Images, while staying within the Canvas' borders. I also want this to be in code, so not in the xaml.

我有一个带有Window的C#WPF应用程序。在窗口内我有一个Canvas,我可以在其中添加图像。我想要的是能够拖放图像,同时保持在画布的边界内。我也希望它在代码中,所以不在xaml中。

I got this in the function where I add/update the Images to the Canvas. The TODO's should be replaced for the Drag & Drop events.

我在我添加/更新图像到画布的功能中得到了这个。 TODO应该替换为拖放事件。

Image img = ImageList[i].Image;
img.Name = "Image" + i;

// TODO: Drag and Drop event for Image

// TODO: Check if Left and Top are within Canvas (minus width / height of Image) 

Canvas.SetLeft(img, Left); // Default Left when adding the image = 0
Canvas.SetTop(img, Top); // Default Top when adding the image = 0

MyCanvas.Children.Add(img);
OnPropertyChanged("MyCanvas");

PS: Though this is for later, if someone has code to drag and drop multiple images at once as an additional bonus, I would appreciate it.

PS:虽然这是为了以后,如果有人有代码一次拖放多个图像作为额外的奖励,我会很感激。

Thanks in advance for the help.

先谢谢您的帮助。

2 个解决方案

#1


5  

Fixed my problem below, by using the following code:

通过使用以下代码修复了我的问题:

img.AllowDrop = true;
img.PreviewMouseLeftButtonDown += this.MouseLeftButtonDown;
img.PreviewMouseMove += this.MouseMove;
img.PreviewMouseLeftButtonUp += this.PreviewMouseLeftButtonUp;


private object movingObject;
private double firstXPos, firstYPos;
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
    // In this event, we get the current mouse position on the control to use it in the MouseMove event.
    Image img = sender as Image;
    Canvas canvas = img.Parent as Canvas;

    firstXPos = e.GetPosition(img).X;
    firstYPos = e.GetPosition(img).Y;

    movingObject = sender;

    // Put the image currently being dragged on top of the others
    int top = Canvas.GetZIndex(img);
    foreach (Image child in canvas.Children)
        if (top < Canvas.GetZIndex(child))
            top = Canvas.GetZIndex(child);
    Canvas.SetZIndex(img, top + 1);
}
private void PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    Image img = sender as Image;
    Canvas canvas = img.Parent as Canvas;

    movingObject = null;

    // Put the image currently being dragged on top of the others
    int top = Canvas.GetZIndex(img);
    foreach (Image child in canvas.Children)
        if (top > Canvas.GetZIndex(child))
            top = Canvas.GetZIndex(child);
    Canvas.SetZIndex(img, top + 1);
}
private void MouseMove(object sender, MouseEventArgs e) {
    if (e.LeftButton == MouseButtonState.Pressed && sender == movingObject) {
        Image img = sender as Image;
        Canvas canvas = img.Parent as Canvas;

        double newLeft = e.GetPosition(canvas).X - firstXPos - canvas.Margin.Left;
        // newLeft inside canvas right-border?
        if (newLeft > canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth)
            newLeft = canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth;
        // newLeft inside canvas left-border?
        else if (newLeft < canvas.Margin.Left)
            newLeft = canvas.Margin.Left;
        img.SetValue(Canvas.LeftProperty, newLeft);

        double newTop = e.GetPosition(canvas).Y - firstYPos - canvas.Margin.Top;
        // newTop inside canvas bottom-border?
        if (newTop > canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight)
            newTop = canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight;
        // newTop inside canvas top-border?
        else if (newTop < canvas.Margin.Top)
            newTop = canvas.Margin.Top;
        img.SetValue(Canvas.TopProperty, newTop);
    }
}

This code allows me to drag-and-drop the Images inside the Canvas, without leaving the Canvas itself.

此代码允许我将图像拖放到Canvas中,而无需离开Canvas本身。

Now I just need to be able to do two more things:

现在我只需要做两件事:

  1. Fix a little bug where my Mouse slips of the Image when I drag them around to fast. This happens quite often, even when I'm not even moving the dragging image around THAT fast.. Fixed by using the solution mentioned in my other question.
  2. 修复一个小小的bug,当我将它们拖动到快速时,我的鼠标滑动了Image。这种情况经常发生,即使我甚至没有快速移动拖动图像。使用我在其他问题中提到的解决方案。

  3. Making it able to drag-and-drop multiple images at once, preferably by selecting multiple first, and then drag-and-drop the whole bunch of them while staying inside the Canvas.
  4. 使其能够一次拖放多个图像,最好先选择多个图像,然后在停留在Canvas中的同时拖放整个图像。

Will make a new Question for this.

将为此提出一个新问题。

#2


0  

I was did a project that uses a chunk of your code and does drag and drop on canvas, look into it and see if there be any difference, dont really have the time to check

我做了一个项目,使用了一大块代码,并在画布上拖放,查看它,看看是否有任何差异,没有真正的时间来检查

private void pinCanvas_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
         //   Point pt = e.GetPosition(pinCanvas);
         //   Curosor.Text = String.Format("You are at ({0}in, {1}in) in window coordinates", (pt.X / (96 / 72)) * 1/72, (pt.Y / (96 / 72)) * 1/72);
        }
        bool captured = false;
        double x_shape, x_canvas, y_shape, y_canvas;
        UIElement source = null;
        string elementName;
        double elementHeight, elementWidth;
        private void pinCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            setCanvasSize();
            source = (UIElement)sender;
            elementName = ((Label)source).Name;
            switch (elementName)
            {
                case "pinTextBox" :
                    elementHeight = pinActualHeight;
                    elementWidth = pinActualWidth;
                    break;
                case "serialTextBox" :
                    elementHeight = serialActualHeight;
                    elementWidth = serialActualWidth;
                    break;
                case "batchTextBox" :
                    elementHeight = batchActualHeight;
                    elementWidth = batchActualWidth;
                    break;
            }
            Mouse.Capture(source);
            captured = true;
            x_shape = Canvas.GetLeft(source);
            x_canvas = e.GetPosition(Maincanvas).X;
            y_shape = Canvas.GetTop(source);
            y_canvas = e.GetPosition(Maincanvas).Y;
        }

        private void pinCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (captured)
            {

                double x = e.GetPosition(Maincanvas).X;
                double y = e.GetPosition(Maincanvas).Y;
                var xCond = Math.Round(appActivities.DIP2Inch(x_shape), 4).ToString();
                var yCond = Math.Round(appActivities.DIP2Inch(y_shape), 4).ToString();
                var name = ((Label)source).Name;
                x_shape += x - x_canvas;
            //    if ((x_shape < Maincanvas.ActualWidth - elementWidth) && x_shape > 0)
           //     {
                    Canvas.SetLeft(source, x_shape);
                    switch (name)
                    {
                        case "pinTextBox" :
                            pinOffsetLeft.Text = xCond;
                            break;
                        case "serialTextBox" :
                            serialOffsetLeft.Text = xCond;
                            break;
                        case "batchTextBox" :
                            batchOffsetLeft.Text = xCond;
                            break;
                    }

         //       }
                x_canvas = x;
                y_shape += y - y_canvas;
            //    if (y_shape < Maincanvas.ActualHeight - elementHeight && y_shape > 0)
             //   {
                    Canvas.SetTop(source, y_shape);
                    switch (name)
                    {
                        case "pinTextBox":
                            pinOffsetTop.Text = yCond;
                            break;
                        case "serialTextBox":
                            serialOffsetTop.Text = yCond;
                            break;
                        case "batchTextBox":
                            batchOffsetTop.Text = yCond;
                            break;
                    }

          //      }
                y_canvas = y;
            }
        }

        private void pinCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Mouse.Capture(null);
            captured = false;
          //  MessageBox.Show((Canvas.GetTop(source)).ToString());
         /*   if (Canvas.GetTop(source) < 0)
            {
                Canvas.SetTop(source, 0);
            }
            if (Canvas.GetLeft(source) < 0)
            {
                Canvas.SetLeft(source, 0);
            }

            if (Canvas.GetLeft(source) > Maincanvas.ActualWidth - elementWidth)
            {
              //  MessageBox.Show("Left Too Much " + (Canvas.GetLeft(source) * 1/96).ToString());
                Canvas.SetLeft(source, Maincanvas.ActualWidth - elementWidth);
            }

            if (Canvas.GetTop(source) > Maincanvas.ActualHeight - elementHeight)
            {
                Canvas.SetTop(source, Maincanvas.ActualHeight - elementHeight);
            } */
            oneElemntTorched = true;
            //MessageBox.Show(this.pinTextBox.ActualHeight.ToString() + ", " + this.pinTextBox.ActualWidth.ToString());
        }

#1


5  

Fixed my problem below, by using the following code:

通过使用以下代码修复了我的问题:

img.AllowDrop = true;
img.PreviewMouseLeftButtonDown += this.MouseLeftButtonDown;
img.PreviewMouseMove += this.MouseMove;
img.PreviewMouseLeftButtonUp += this.PreviewMouseLeftButtonUp;


private object movingObject;
private double firstXPos, firstYPos;
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
    // In this event, we get the current mouse position on the control to use it in the MouseMove event.
    Image img = sender as Image;
    Canvas canvas = img.Parent as Canvas;

    firstXPos = e.GetPosition(img).X;
    firstYPos = e.GetPosition(img).Y;

    movingObject = sender;

    // Put the image currently being dragged on top of the others
    int top = Canvas.GetZIndex(img);
    foreach (Image child in canvas.Children)
        if (top < Canvas.GetZIndex(child))
            top = Canvas.GetZIndex(child);
    Canvas.SetZIndex(img, top + 1);
}
private void PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    Image img = sender as Image;
    Canvas canvas = img.Parent as Canvas;

    movingObject = null;

    // Put the image currently being dragged on top of the others
    int top = Canvas.GetZIndex(img);
    foreach (Image child in canvas.Children)
        if (top > Canvas.GetZIndex(child))
            top = Canvas.GetZIndex(child);
    Canvas.SetZIndex(img, top + 1);
}
private void MouseMove(object sender, MouseEventArgs e) {
    if (e.LeftButton == MouseButtonState.Pressed && sender == movingObject) {
        Image img = sender as Image;
        Canvas canvas = img.Parent as Canvas;

        double newLeft = e.GetPosition(canvas).X - firstXPos - canvas.Margin.Left;
        // newLeft inside canvas right-border?
        if (newLeft > canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth)
            newLeft = canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth;
        // newLeft inside canvas left-border?
        else if (newLeft < canvas.Margin.Left)
            newLeft = canvas.Margin.Left;
        img.SetValue(Canvas.LeftProperty, newLeft);

        double newTop = e.GetPosition(canvas).Y - firstYPos - canvas.Margin.Top;
        // newTop inside canvas bottom-border?
        if (newTop > canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight)
            newTop = canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight;
        // newTop inside canvas top-border?
        else if (newTop < canvas.Margin.Top)
            newTop = canvas.Margin.Top;
        img.SetValue(Canvas.TopProperty, newTop);
    }
}

This code allows me to drag-and-drop the Images inside the Canvas, without leaving the Canvas itself.

此代码允许我将图像拖放到Canvas中,而无需离开Canvas本身。

Now I just need to be able to do two more things:

现在我只需要做两件事:

  1. Fix a little bug where my Mouse slips of the Image when I drag them around to fast. This happens quite often, even when I'm not even moving the dragging image around THAT fast.. Fixed by using the solution mentioned in my other question.
  2. 修复一个小小的bug,当我将它们拖动到快速时,我的鼠标滑动了Image。这种情况经常发生,即使我甚至没有快速移动拖动图像。使用我在其他问题中提到的解决方案。

  3. Making it able to drag-and-drop multiple images at once, preferably by selecting multiple first, and then drag-and-drop the whole bunch of them while staying inside the Canvas.
  4. 使其能够一次拖放多个图像,最好先选择多个图像,然后在停留在Canvas中的同时拖放整个图像。

Will make a new Question for this.

将为此提出一个新问题。

#2


0  

I was did a project that uses a chunk of your code and does drag and drop on canvas, look into it and see if there be any difference, dont really have the time to check

我做了一个项目,使用了一大块代码,并在画布上拖放,查看它,看看是否有任何差异,没有真正的时间来检查

private void pinCanvas_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
         //   Point pt = e.GetPosition(pinCanvas);
         //   Curosor.Text = String.Format("You are at ({0}in, {1}in) in window coordinates", (pt.X / (96 / 72)) * 1/72, (pt.Y / (96 / 72)) * 1/72);
        }
        bool captured = false;
        double x_shape, x_canvas, y_shape, y_canvas;
        UIElement source = null;
        string elementName;
        double elementHeight, elementWidth;
        private void pinCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            setCanvasSize();
            source = (UIElement)sender;
            elementName = ((Label)source).Name;
            switch (elementName)
            {
                case "pinTextBox" :
                    elementHeight = pinActualHeight;
                    elementWidth = pinActualWidth;
                    break;
                case "serialTextBox" :
                    elementHeight = serialActualHeight;
                    elementWidth = serialActualWidth;
                    break;
                case "batchTextBox" :
                    elementHeight = batchActualHeight;
                    elementWidth = batchActualWidth;
                    break;
            }
            Mouse.Capture(source);
            captured = true;
            x_shape = Canvas.GetLeft(source);
            x_canvas = e.GetPosition(Maincanvas).X;
            y_shape = Canvas.GetTop(source);
            y_canvas = e.GetPosition(Maincanvas).Y;
        }

        private void pinCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (captured)
            {

                double x = e.GetPosition(Maincanvas).X;
                double y = e.GetPosition(Maincanvas).Y;
                var xCond = Math.Round(appActivities.DIP2Inch(x_shape), 4).ToString();
                var yCond = Math.Round(appActivities.DIP2Inch(y_shape), 4).ToString();
                var name = ((Label)source).Name;
                x_shape += x - x_canvas;
            //    if ((x_shape < Maincanvas.ActualWidth - elementWidth) && x_shape > 0)
           //     {
                    Canvas.SetLeft(source, x_shape);
                    switch (name)
                    {
                        case "pinTextBox" :
                            pinOffsetLeft.Text = xCond;
                            break;
                        case "serialTextBox" :
                            serialOffsetLeft.Text = xCond;
                            break;
                        case "batchTextBox" :
                            batchOffsetLeft.Text = xCond;
                            break;
                    }

         //       }
                x_canvas = x;
                y_shape += y - y_canvas;
            //    if (y_shape < Maincanvas.ActualHeight - elementHeight && y_shape > 0)
             //   {
                    Canvas.SetTop(source, y_shape);
                    switch (name)
                    {
                        case "pinTextBox":
                            pinOffsetTop.Text = yCond;
                            break;
                        case "serialTextBox":
                            serialOffsetTop.Text = yCond;
                            break;
                        case "batchTextBox":
                            batchOffsetTop.Text = yCond;
                            break;
                    }

          //      }
                y_canvas = y;
            }
        }

        private void pinCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Mouse.Capture(null);
            captured = false;
          //  MessageBox.Show((Canvas.GetTop(source)).ToString());
         /*   if (Canvas.GetTop(source) < 0)
            {
                Canvas.SetTop(source, 0);
            }
            if (Canvas.GetLeft(source) < 0)
            {
                Canvas.SetLeft(source, 0);
            }

            if (Canvas.GetLeft(source) > Maincanvas.ActualWidth - elementWidth)
            {
              //  MessageBox.Show("Left Too Much " + (Canvas.GetLeft(source) * 1/96).ToString());
                Canvas.SetLeft(source, Maincanvas.ActualWidth - elementWidth);
            }

            if (Canvas.GetTop(source) > Maincanvas.ActualHeight - elementHeight)
            {
                Canvas.SetTop(source, Maincanvas.ActualHeight - elementHeight);
            } */
            oneElemntTorched = true;
            //MessageBox.Show(this.pinTextBox.ActualHeight.ToString() + ", " + this.pinTextBox.ActualWidth.ToString());
        }