【问题标题】:How can I treat the circle as a control after drawing it? - Moving and selecting shapes绘制后如何将圆圈视为控件? - 移动和选择形状
【发布时间】:2016-11-15 16:42:11
【问题描述】:

实际上,点击每个圆圈后,我希望它的颜色发生变化,例如,我希望它变成红色,总的来说,我想把它当作控制。

当我双击图片框时,我知道如何绘制代表图形节点的圆圈。我正在使用以下代码:

        public Form1()
    {
        InitializeComponent();

        pictureBox1.Paint += new PaintEventHandler(pic_Paint);
    }

    public Point positionCursor { get; set; }
    private List<Point> points = new List<Point>();
    public int circleNumber { get; set; }

    private void pictureBox1_DoubleClick(object sender, EventArgs e)
    {
        positionCursor = this.PointToClient(new Point(Cursor.Position.X - 25, Cursor.Position.Y - 25));

        points.Add(positionCursor);
        Label lbl = new Label();
        lbl.BackColor = Color.Transparent;
        lbl.Font = new Font("Arial", 7);

        lbl.Size = new Size(20, 15);

        if (circleNumber >= 10)
        {
            lbl.Location = new Point(points[circleNumber].X + 3, points[circleNumber].Y + 6);
        }
        else
        {
            lbl.Location = new Point(points[circleNumber].X + 7, points[circleNumber].Y + 7);
        }
        lbl.Text = circleNumber.ToString();
        pictureBox1.Controls.Add(lbl);
        circleNumber++;
        pictureBox1.Invalidate();
    }

    private void pic_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.SmoothingMode = SmoothingMode.AntiAlias;

        using (var pen = new Pen(Color.DimGray, 2))
        {
            foreach (Point pt in points)
            {
                g.FillEllipse(Brushes.White, pt.X, pt.Y, 25, 25);
                g.DrawEllipse(pen, pt.X, pt.Y, 26, 26);
            }
        }
    }

【问题讨论】:

  • 如果你需要它是一个控件,然后制作一个绘制圆圈的控件.. 然后你制作新的控件并将它放在窗体上而不是画一个圆圈
  • 您需要对圆圈进行命中测试,然后如果命中测试点在圆圈中,则将其绘制为红色。
  • @BugFinder,你的意见,我让用户控制。
  • @RezaAghaei,对不起,我没有。

标签: c# .net winforms gdi+ picturebox


【解决方案1】:

您需要执行命中测试来检查一个点是否在一个圆圈中。作为一个选项,您可以向GraphicsPath 添加一个圆圈,并使用路径的IsVisible 方法来检查该点是否在圆圈中。

例如,将 ponit p 作为直径为 d 的圆的左上角位置,您可以通过这种方式检查当前单击的点是否在圆内或 con:

var result = false;
using (var path = new GraphicsPath())
{
    path.AddEllipse(p.X, p.Y, d, d);
    result = path.IsVisible(e.Location);
}

示例代码

我看到您在该主题中提出了多个问题。所以在这里我分享一些代码来帮助你朝着正确的方向前进。

定义填充颜色、选定填充颜色、圆圈大小、边框宽度等变量,以便在需要时轻松更改。

List<Rectangle> Shapes = new List<Rectangle>();
int selectedIndex = -1;
Size size = new Size(25, 25);
Color fillColor = Color.White;
Color selectedfillCOlor = Color.Red;
Color borderColor = Color.Gray;
int borderWidth = 2;

双击

在此处将圈子添加到Shapes 列表。将圆的边界矩形添加到列表中就足够了。

private void pic_MouseDoubleClick(object sender, MouseEventArgs e)
{
    var p = e.Location;
    p.Offset(-size.Width / 2, -size.Height / 2);
    Shapes.Add(new Rectangle(p, size));
    pic.Invalidate();
}

点击

这里执行hit-test来检查点是否在一个圆圈中。检查单击时Ctrl键是否按下,进行选择,然后将找到的索引设置为selectedIndex绘画时使用它。

private void pic_MouseClick(object sender, MouseEventArgs e)
{
    if (ModifierKeys != Keys.Control)
        return;
    selectedIndex = -1;
    for (int i = 0; i < Shapes.Count; i++)
    {
        using (var path = new GraphicsPath())
        {
            path.AddEllipse(Shapes[i]);
            if (path.IsVisible(e.Location))
                selectedIndex = i;
        }
    }
    pic.Invalidate();
}

绘画

将图形对象的SmoothingMode 设置为AntiAlias 以获得更流畅的绘图。然后在for循环中绘制形状并注意selectedIndex为选定的形状使用不同的填充颜色。

要绘制文本,您不需要使用label,您可以使用TextRenderer 类简单地绘制文本。

private void pic_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    for (int i = 0; i < Shapes.Count; i++)
    {
        var selected = (selectedIndex == i);
        using (var brush = new SolidBrush(selected ? selectedfillCOlor : fillColor))
            e.Graphics.FillEllipse(brush, Shapes[i]);
        using (var pen = new Pen(borderColor, borderWidth))
            e.Graphics.DrawEllipse(pen, Shapes[i]);
        TextRenderer.DrawText(e.Graphics, (i + 1).ToString(),
                this.Font, Shapes[i], Color.Black,
                TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
    }
}

一些注意事项

  • 最好将代码封装在派生自PictureBox或派生Control的新控件中,并将DoubleBuffered设置为true。

  • Circle 封装在执行命中测试和圆形渲染的Circle 类中是一个不错的选择。特别是如果您想稍后移动它们或执行一些其他交互或让每个圆圈都有自己的属性,如颜色等。

示例圆类

这是一个示例 circle 类,可以作为一个很好的起点。

public class Circle
{
    private Color selectedFillColor = Color.Red;
    private Color normalFillColor = Color.Red;
    private Color borderColor = Color.Red;
    private int borderWidth = 2;
    public Point Location { get; set; }
    public int Diameter { get; set; }
    public Rectangle Bounds
    {
        get
        {
            return new Rectangle(Location, new Size(Diameter, Diameter));
        }
    }
    public bool HitTest(Point p)
    {
        var result = false;
        using (var path = new GraphicsPath())
        {
            path.AddEllipse(Bounds);
            result = path.IsVisible(p);
        }
        return result;
    }
    public bool Selected { get; set; }
    public void Draw(Graphics g)
    {
        using (var brush = new SolidBrush(
            Selected ? selectedFillColor : normalFillColor))
            g.FillEllipse(brush, Bounds);
        using (var pen = new Pen(borderColor, 2))
            g.DrawEllipse(pen, Bounds);
    }
}

【讨论】:

  • 非常感谢,您的意见,我读了哪些书?使用 GDI+ 或 Pro .NET 2.0 图形编程进行图形编程。您有更好的建议吗?
  • 不客气 :) - 这是一本好书。但它可能包含许多您在当前任务中不需要的信息。因此,如果您想阅读这本书或任何文章,只需阅读可能与您的任务相关的那些部分。无论如何,我还在答案中添加了一个示例 Circle 类。
  • @Martin System.Drawing.Drawing2D.GraphicsPath。您可以使用using System.Drawing.Drawing2D; 添加。
  • @Martin 你可能想看看Draw multiple freehand Polyline or Curve drawing - Adding Undo FeatureHow to drag and move shapes in C# 特别是最后一个分享使用Shape 类的想法:) 你可能会发现这些答案很有用。跨度>
  • @VisualVincent 在这种情况下,您应该 1) 使您的形状 IDisposable 并在 Dispose 方法中处理缓存路径。然后您应该在合适的时间处理每个形状,例如在绘图表面处理时。此外,当您将形状从Shapes 绘图表面集合中删除时,您不应该忘记处理它。*2)* 如果形状移动或调整大小,请不要忘记使缓存路径无效。 →如果您考虑上述规则,则不会有任何问题。但是这些对于一个例子来说太多了,对于一个不知道它的人来说,它变得很复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多