繁体   English   中英

如何在 C# 中将鼠标单击与绘制的 object 关联?

[英]How do I associate a mouse click with a drawn object in C#?

我有一个画框,上面画着一堆矩形(突出显示图像的一些特征)。 我想确定我的用户是否在给定的矩形内单击,并添加特定于该矩形的操作(即显示附加信息)。 我该怎么做呢?

如果需要,我可以提供更多信息,我只是不确定此时哪些信息有用。

绘制矩形的当前代码。 rectX、rectY、rectRot、rectColor 目前都是 arrays。 rectW 和 rectH 是常数。

private void pbPicture_Paint(object sender, PaintEventArgs e)
    {
      for(int i = 0; i < rectX.Length; i++)
      {
        e.Graphics.ResetTransform();
        e.Graphics.TranslateTransform(rectX[i], rectY[i]);
        e.Graphics.RotateTransform(rectRot[i]);
        e.Graphics.DrawRectangle(new Pen(rectColor[i], penWidth), 0, 0, rectW, rectH);
      }
      e.Graphics.ResetTransform();
    }

编辑:添加图片链接,附加代码。

如果您应用并保留每个形状的转换数据会更容易,然后您可以在您的实现中使用它来绘制形状,与鼠标输入交互......等等。 无需执行任何额外的转换调用来绘制主要形状,也无需执行数学例程来确定形状/矩形是否包含给定的点。

请考虑此处的Shape class,它封装了您在实现中需要的相关数据和功能。 使用GraphicsPath class 保持形状并应用变换,以及使用GraphicsPath.IsVisible方法确定形状是否包含给定点,以便您可以采取相应的操作。 保留和公开Matrix实例是为了使用它来转换图形,以防您需要在形状上进行更多绘图,例如绘制文本、图像......等。

using System;
using System.Drawing;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Collections.Generic;

public class Shape : IDisposable
{
    private bool disposedValue;
    private Matrix mx;
    private GraphicsPath gp;
    private Size sz;
    private Point loc;
    private float rot;

    public string Text { get; set; }
    public Size Size
    {
        get => sz;
        set
        {
            if (sz != value)
            {
                sz = value;
                CleanUp();
            }
        }
    }
    public Point Location
    {
        get => loc;
        set
        {
            if (loc != value)
            {
                loc = value;
                CleanUp();
            }
        }
    }
    public float Rotation
    {
        get => rot;
        set
        {
            if (rot != value)
            {
                rot = value;
                CleanUp();
            }
        }
    }
    public Matrix Matrix
    {
        get
        {
            if (mx == null)
            {
                mx = new Matrix();
                // According to your code snippet, you don't need to offset here.
                // mx.Translate(Location.X, Location.Y);
                mx.RotateAt(Rotation, Center);
            }
            return mx;
        }
    }
    public GraphicsPath GraphicsPath
    {
        get
        {
            if (gp == null)
            {
                gp = new GraphicsPath();
                gp.AddRectangle(Rectangle);
                gp.Transform(Matrix);
            }
            return gp;
        }
    }
    public Point Center
    {
        get
        {
            var r = Rectangle;
            return new Point(r.X + r.Width / 2, r.Y + r.Height / 2);
        }
    }
    public Rectangle Rectangle => new Rectangle(Location, Size);
    public bool Selected { get; set; }
    public Color BorderColor { get; set; } = Color.Black;
    // Add more, ForeColor, BackColor ...etc.

    public bool Contains(Point point) => GraphicsPath.IsVisible(point);

    private void CleanUp()
    {
        gp?.Dispose();
        gp = null;
        mx?.Dispose();
        mx = null;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing) CleanUp();
            disposedValue = true;
        }
    }

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

有了这个,你的实现应该很简单:

private readonly List<Shape> shapes = new List<Shape>();
private const int recW = 100;
private const int recH = 20;

// A method to create the list...
private void SomeMethod()
{
    shapes.ForEach(s => s.Dispose());
    shapes.Clear();

    // In some loop...
    var shape = new Shape
    {
        Text = "Shape...",
        Size = new Size(recW, recH),
        Location = new Point(some.X, some.Y),
        Rotation = someAngle
    };
    shapes.Add(shape);
    // Add the reset...

    pbox.Invalidate();
}

// And to dispose of them...
protected override void OnFormClosed(FormClosedEventArgs e)
{
    base.OnFormClosed(e);
    shapes.ForEach(x => x.Dispose());
}

绘图部分:

private void pbox_Paint(object sender, PaintEventArgs e)
{
    var g = e.Graphics;

    using (var sf = StringFormat.GenericTypographic)
    {
        sf.Alignment = sf.LineAlignment = StringAlignment.Center;

        shapes.ForEach(s =>
        {
            using (var pnBorder = new Pen(s.BorderColor))
            {                
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.PixelOffsetMode = PixelOffsetMode.Half;
                if (s.Selected) g.FillPath(Brushes.DarkOrange, s.GraphicsPath);
                g.DrawPath(pnBorder, s.GraphicsPath);

                if (!string.IsNullOrEmpty(s.Text))
                {
                    g.SmoothingMode = SmoothingMode.None;
                    g.PixelOffsetMode = PixelOffsetMode.Default;
                    g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                    g.Transform = s.Matrix;
                    g.DrawString(s.Text, Font, Brushes.Black, s.Rectangle, sf);
                    g.ResetTransform();
                }
            }
        });
    }
}

与鼠标事件交互:

private void pbox_MouseDown(object sender, MouseEventArgs e)
{
    foreach (var shape in shapes)
        shape.Selected = shape.Contains(e.Location);

    pbox.Invalidate();
}

我创建了带有随机值的矩形(形状对象)来演示。

SO73294478

注意:此处也应用了一些偏移量( Matrix.Translate(...) )以在形状之间留出一些空间。

\您可以使用事件参数e来获取鼠标坐标

private void pictureBox1_Click(object sender, EventArgs e)
{
    MouseEventArgs me = (MouseEventArgs)e;
    Point coordinates = me.Location;
}

还有一个链接可以帮助您识别形状上的点击事件 -

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/ide/tutorial-3-create-a-matching-game?view=vs-2015&redirectedfrom=MSDN

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM