在表单中分别拖放不同的图形形状

huangapple go评论76阅读模式
英文:

Drag and Drop different graphic shapes within a Form individually

问题

以下是您提供的代码的中文翻译部分:

我试图能够选择、垂直移动并放置两个不同的矩形(可能获取它们的位置,但这是另一天的问题)。

以下是我为一个形状编写的代码,但我找不到一种方法,使得可以通过点击一个而不影响另一个来执行操作。

使用 System.Collections.Generic;
使用 System.Drawing;
使用 System.Linq;

public 部分类 Form1 : Form
{
    矩形 topMarker = new 矩形(50, 125, 50, 2);
    矩形 bottomMarker = new 矩形(50, 200, 50, 2);
   
    布尔值 isMouseDown = ;
    
    public Form1()
    {
        初始化组件();
    }

    私有  pictureBox1_绘制(对象 sender, PaintEventArgs e)
    {
        e.Graphics.FillRectangle(new SolidBrush(Color.Green), topMarker);
        e.Graphics.FillRectangle(new SolidBrush(Color.Red), bottomMarker);
    }

    
    私有  pictureBox1_鼠标按下(对象 sender, MouseEventArgs e)
    {
        isMouseDown = ;
    }

    私有  pictureBox1_鼠标移动(对象 sender, MouseEventArgs e)
    {
        如果 (isMouseDown == )
        {
            topMarker.Location = e.Location;
            topMarker.X = 50;

             如果 (topMarker.Y < 0)
             {
                topMarker.Y = 0;
             }

             如果 (topMarker.Y > pictureBox1.Height)
             {
                 topMarker.Y = pictureBox1.Height - topMarker.Height;
             } 
            刷新();
        }
    }

    私有  pictureBox1_鼠标抬起(对象 sender, MouseEventArgs e)
    {
        isMouseDown = ;
    }
}

希望这可以帮助您理解代码的功能。如果您有任何进一步的问题,请随时提出。

英文:

I am trying to be able to pick, move vertically and drop 2 different rectangles (potentially getting their positions, but that's a problem for another day).

Here's the code that I managed to put together for one shape, but couldn't I find a way to make it possible to perform the action by clicking on one without affecting the other.

using System.Collections.Generic;
using System.Drawing;
using System.Linq;

public partial class Form1 : Form
{
    Rectangle topMarker = new Rectangle(50, 125, 50, 2);
    Rectangle bottomMarker = new Rectangle(50, 200, 50, 2);
   
    bool isMouseDown = false;
    
    public Form1()
    {
        InitializeComponent();
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.FillRectangle(new SolidBrush(Color.Green), topMarker);
        e.Graphics.FillRectangle(new SolidBrush(Color.Red), bottomMarker);
    }

    
    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        isMouseDown = true;
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (isMouseDown == true)
        {
            topMarker.Location = e.Location;
            topMarker.X = 50;

             if (topMarker.Y < 0)
             {
                topMarker.Y = 0;
             }

             if (topMarker.Y > pictureBox1.Height)
             {
                 topMarker.Y = pictureBox1.Height - topMarker.Height;
             } 
            Refresh();
        }
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        isMouseDown = false;
    }
}

答案1

得分: 1

以下是您提供的内容的翻译:

我建议创建一个描述形状的类对象。
在需要与其对象互动时,处理这些对象的集合要容易得多。

在我的观点中,类对象应该实现一个接口,允许调用方法和使用属性,这些方法和属性对您绘制的所有形状都是通用的(假设您可以添加不同的形状,而不仅仅是矩形)。
在这个示例中,Marker类对象实现了一个IShape接口,该接口定义了所有对象必须公开的方法和属性。
它还实现了IDisposable,因为它包含未托管资源,以这种情况下的GraphicsPath的形式。
请在窗体关闭时调用Dispose()来释放集合中所有元素。

  • Marker类的Move()方法允许指定偏移量(与前一个位置的差异)来移动形状。
  • 它的Draw()方法接受一个Graphics对象作为参数,您只需要传递由画布的Paint事件提供的e.Graphics对象即可。Marker在此设备上下文中绘制自己。
  • IsMouseOver()在鼠标位置落在形状边界内时返回true或false。它使用GraphicsPath的IsVisible属性。

为了避免在拖动形状时丢失形状(因为鼠标指针可能移动到其边界之外),当Marker对象接收到点击事件时,我们预先定义了一个“选定的形状”,并存储了指针的当前位置。然后,当鼠标移动时,它会提前更新。当前选定的形状也会更新(请参阅mouseTrackLocationselectedMarker字段)。

这差不多就是全部内容了,除非您可以向识别鼠标指针下的形状的代码添加一些“容忍度”(可以通过将GraphicsPath膨胀为一些不可见内容来实现)。

注意:此代码假定您的目标是.NET 6+和启用了可空性。如果不是这种情况,只需将所有的object?更改为object,并在using块中更改using声明。

以下是Marker类和IShape接口的翻译:

public interface IShape {
    RectangleF Shape { get; }
    GraphicsPath Path { get; }
    void Move(PointF position);
    void Draw(Graphics graphics);
    bool IsMouseOver(PointF mousePosition);
}

public class Marker : IShape, IDisposable {
    private bool disposed = false;
    private GraphicsPath path = new();
    public Marker(RectangleF shape, Color borderColor, Color fillColor) {
        BorderColor = borderColor;
        FillColor = fillColor;
        path.AddRectangle(shape);
        Path = path;
    }

    public RectangleF Shape => path.GetBounds();
    public GraphicsPath Path { get; }
    public Color BorderColor { get; set; }
    public Color FillColor { get; set; }
    public bool IsMouseOver(PointF mousePosition) => path.IsVisible(mousePosition);

    public void Move(PointF position) {
        // 按偏移量移动,而不是绝对位置
        using var mx = new Matrix(1, 0, 0, 1, position.X, position.Y);
        path.Transform(mx);
    }

    public void Draw(Graphics canvas) {
        using var brush = new SolidBrush(FillColor);
        using var pen = new Pen(BorderColor);
        canvas.FillPath(brush, path);
        canvas.DrawPath(pen, path);
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) {
        if (!disposed) {
            path?.Dispose();
            disposed = true;
        }
    }
}

希望这有助于您理解原文中的内容!如果您有其他问题,请随时提出。

英文:

I suggest to create a class object that describes your shape.
A collection of these object is much easier to handle when you need to interact with one of its objects.

The class object, in my view, should implement an Interface that allows to call methods and use properties that are common to all the shapes you draw (assuming you could add different shapes, not just rectangles).
In the example here, the Marker class objects implements an IShape Interface, which defines which methods and properties all objects must expose.
It also implements IDisposable, since it holds unmanaged resources, in the form of a GraphicsPath, in this case. Remember to call Dispose() on all elements of the collection when the Form closes.

  • The Marker's class Move() method allows to specify an offset (difference from the previous position) to move a shape.
  • Its Draw() method accepts a Graphics object as argument, you just need to pass the e.Graphics object provided by the PaintEventArgs of the Paint event of the canvas where the shapes are drawn. The Marker draws itself in this device context.
  • The IsMouseOver() returns true or false when the Mouse position falls inside the bounds of the shape. It uses the GraphicsPath's IsVisible property

To avoid losing a shape while it's being dragged around (because the Mouse Pointer may move outside it's bounds), we pre-define a selected shape when one of the Marker objects receives a click event and we store the current position of the Pointer. It's then updated in advance while the Mouse is moved. The currently selected shape is also updated (see the mouseTrackLocation and selectedMarker Fields)

Thats' pretty much all about it, except you could add some tolerance to the code that identifies the shape under the Mouse Pointer (could be done inflating the GraphicsPath with some invisible content)

NOTE: This code assumes you're targeting .NET 6+ and nullable enabled. If this is not the case, simply change all object? to object and using declarations in using blocks

<br/>

public partial class SomeForm : Form {

    private List&lt;Marker&gt; markers = new();
    private bool dragShapeStarted = false;
    private Point mouseTrackLocation = Point.Empty;
    private Marker? selectedMarker = null;

    public SomeForm() {
        InitializeComponent();
        markers.AddRange(new[]{
            new Marker(new RectangleF(50, 125, 100, 2), Color.Transparent, Color.Green),
            new Marker(new RectangleF(50, 200, 100, 2), Color.Transparent, Color.Red),
            new Marker(new RectangleF(50, 250, 100, 2), Color.Transparent, Color.Orange),
        });
    }

    private void somePictureBox_MouseDown(object? sender, MouseEventArgs e) {
        if (e.Button == MouseButtons.Left) {
            selectedMarker = markers.FirstOrDefault(m =&gt; m.IsMouseOver(e.Location));
            // Mouse Down on a shape &amp; selectedMarker not null =&gt; pre-emptive positive result
            if (selectedMarker != null) dragShapeStarted = true;
            mouseTrackLocation = e.Location;
        }
    }

    private void somePictureBox_MouseMove(object? sender, MouseEventArgs e) {
        if (!(sender is PictureBox pBox)) return;

        pBox.Cursor = selectedMarker is null ? Cursors.Default : Cursors.HSplit;
        // We need to move the selected shape as quick as possible: see MouseDown
        if (selectedMarker != null &amp;&amp; dragShapeStarted) {
            selectedMarker.Move(new PointF(0, e.Location.Y - mouseTrackLocation.Y));
            mouseTrackLocation = e.Location;
            pBox.Invalidate();
        }
        // Enables curson change while the Mouse Pointer is moved
        selectedMarker = markers.FirstOrDefault(m =&gt; m.IsMouseOver(e.Location));
    }

    private void somePictureBox_MouseUp(object? sender, MouseEventArgs e) {
        dragShapeStarted = false;
        selectedMarker = null;
    }

    private void somePictureBox_Paint(object? sender, PaintEventArgs e) {
        markers.ForEach(m =&gt; m.Draw(e.Graphics));
    }
}

The Marker class and IShape Interface:

public interface IShape {
    RectangleF Shape { get; }
    GraphicsPath Path { get; }
    void Move(PointF position);
    void Draw(Graphics graphics);
    bool IsMouseOver(PointF mousePosition);
}

public class Marker : IShape, IDisposable {
    private bool disposed = false;
    private GraphicsPath path = new();
    public Marker(RectangleF shape, Color borderColor, Color fillColor) {
        BorderColor = borderColor;
        FillColor = fillColor;
        path.AddRectangle(shape);
        Path = path;
    }

    public RectangleF Shape =&gt; path.GetBounds();
    public GraphicsPath Path { get; }
    public Color BorderColor { get; set; }
    public Color FillColor { get; set; }
    public bool IsMouseOver(PointF mousePosition) =&gt; path.IsVisible(mousePosition);

    public void Move(PointF position) {
        // Translates by an offset, not absolute position
        using var mx = new Matrix(1, 0, 0, 1, position.X, position.Y);
        path.Transform(mx);
    }

    public void Draw(Graphics canvas) {
        using var brush = new SolidBrush(FillColor);
        using var pen = new Pen(BorderColor);
        canvas.FillPath(brush, path);
        canvas.DrawPath(pen, path);
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) {
        if (!disposed) {
            path?.Dispose();
            disposed = true;
        }
    }
}

This is how it works:

在表单中分别拖放不同的图形形状

huangapple
  • 本文由 发表于 2023年6月8日 11:01:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76428336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定