C# 在 Canvas 中拖放图像

Posted

技术标签:

【中文标题】C# 在 Canvas 中拖放图像【英文标题】:C# Drag and Drop Image within Canvas 【发布时间】:2014-03-16 23:46:05 【问题描述】:

我尝试在 Google 上搜索如何在 Canvas 上拖放 UIElements,但找不到任何我想要的东西。

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

我在将图像添加/更新到画布的函数中得到了这个。应为拖放事件替换 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:虽然这是为了以后,如果有人有代码可以一次拖放多个图像作为额外奖励,我将不胜感激。

提前感谢您的帮助。

【问题讨论】:

与其编辑您的问题,不如将您的解决方案发布为答案并接受它?它以这种方式与网站的其他部分保持一致。 【参考方案1】:

使用以下代码解决了我的问题:

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);
    

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

现在我只需要能够再做两件事:

    修复了当我快速拖动鼠标时鼠标滑过图像的小错误。这种情况经常发生,即使我什至没有快速移动拖动的图像.. Fixed by using the solution mentioned in my other question. 使其能够一次拖放多个图像,最好先选择多个图像,然后在留在画布内的同时拖放所有图像。

将为此提出一个新问题。

【讨论】:

复制和粘贴就可以了。代码简单易懂,点击之间没有奇怪的位置返回。 @KMC 不要忘记添加来自this answer 的代码来修复我在要点 1 中提到的错误。 @KevinCruijssen 我不明白为什么(在 MouseMove 处理程序中)您使用 Canvas.Margin 来评估新位置(X 和 Y)。它只有在删除所有这些后才有效!【参考方案2】:

我做了一个项目,它使用了你的一大段代码并在画布上拖放,查看它是否有任何区别,真的没有时间检查

private void pinCanvas_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        
         //   Point pt = e.GetPosition(pinCanvas);
         //   Curosor.Text = String.Format("You are at (0in, 1in) 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());
        

【讨论】:

以上是关于C# 在 Canvas 中拖放图像的主要内容,如果未能解决你的问题,请参考以下文章

在深度嵌套的视觉树中拖放装饰器

在 C# 托盘中拖放 NotifyIcon

H5 Canvas

C# WinForms - 在同一 TreeViewControl 中拖放

如何使用 jQuery 在 div 中多次拖放图像以及在 drop div 中拖放图像?

Java在列表中拖放图像