【问题标题】:Creating a resizable grid to overlay on a image using c#使用 c# 创建一个可调整大小的网格以覆盖在图像上
【发布时间】:2018-08-27 13:30:24
【问题描述】:

我正在尝试找出一种方法,用户可以在该方法中将网格拖到图像上,然后调整列和行的大小以适合图像。我将如何去创造这样的东西?我在网上找不到类似的东西。

我想让用户在图像上拖动一个网格并调整其大小以适合图像。

他们将下面的网格拖到图像上(即数字)

在调整表格大小后会变成这样

所以本质上,我们在表单中有一个图像,然后在图像上使用一个可拖动和可调整大小的网格。

最后,我想让用户在图像中的数据上拖动一个网格,然后我想使用 OCR 读取与图像上的单元格对应的每个区域中的数据。 这样我就可以在第 2 行中选择 col 2 并专门读取该数据。

【问题讨论】:

  • 如果您的应用正在显示图像,则其大小是已知的,因此用户无需拖动或调整任何内容。不知道你在追求什么
  • 我想让用户在图像中的数据上拖动一个网格,然后我想使用 OCR 读取与图像上的单元格对应的每个区域中的数据
  • 您的目标是什么:Winforms、WPF、ASP..?您应该始终正确标记您的问题,以便人们可以在问题页面上看到它!
  • 你不需要网格。只画矩形。比较容易
  • 没有开箱即用的控件可以让您做到这一点。 DGV 让您创建网格,但不会显示图像或允许透明度; TableLayoutPanel 将允许这样做并显示一个漂亮的网格。但它不允许直接更改网格。您可以添加一个可移动控件来拖动网格线。不是很难..

标签: c# image winforms


【解决方案1】:

这是一个网格类,可以覆盖在任何Control 上,并将绘制一个N x M 线网格。

您可以用鼠标移动线条,用鼠标右键移动网格。您可以访问两个List<int> XsYs 中的当前x 和y 值。

它是一个Panel 子类,您应该确保它具有正确的大小和行数和列数。

让我们看看它的实际效果:

要设置它,请使用Init 函数..

代码如下:

public partial class Grid : Panel
{
    public Grid()
    {
        InitializeComponent();
        GridColor = Color.DarkMagenta;
        HandleSize = 4;
        BackColor = Color.Transparent;
        DoubleBuffered = true;
    }

    int RowCount { get; set; }
    int ColCount { get; set; }
    Color GridColor { get; set; }
    int HandleSize { get; set; }

    List<int> Xs { get; set; }
    List<int> Ys { get; set; }

    public void Init(int cols, int rows)
    {
        RowCount = rows;
        ColCount = cols;
        Xs = new List<int>();
        Ys = new  List<int>();
        float w = 1f * Width / cols;
        float h = 1f * Height / rows;

        for (int i = 0; i <= cols; i++) Xs.Add((int)(i * w));
        for (int i = 0; i <= rows; i++) Ys.Add((int)(i * h));
        // draw inside the panel only
        if (Xs[cols] == Width) Xs[cols]--;
        if (Ys[rows] == Height) Ys[cols]--;
    }

    public void Init(int cols, int rows, Size sz)
    {
        Size = sz;
        Init(cols, rows);
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);

        using (Pen pen = new Pen(GridColor))
        {
            foreach (int x in Xs) pe.Graphics.DrawLine(pen, x, 0, x, Height);
            foreach (int y in Ys) pe.Graphics.DrawLine(pen, 0, y, Width, y);
        }
    }

    private Point mDown = Point.Empty;

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (Cursor != Cursors.Default) mDown = e.Location;
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);

        // distances
        var dx = Xs.Select(x => Math.Abs(x - e.X));
        var dy = Ys.Select(y => Math.Abs(y - e.Y));
        // smallest distance
        int mx = dx.Min();
        int my = dy.Min();
        // grid index
        int ix = dx.ToList().IndexOf(mx);
        int iy = dy.ToList().IndexOf(my);

        if (e.Button.HasFlag(MouseButtons.Right))
        {   // move the grid with the right mouse button
            Location = new Point(Left + e.X - mDown.X, Top + e.Y - mDown.Y);
        }
        else if (!e.Button.HasFlag(MouseButtons.Left))
        {   // if we are close enough set cursor
            Cursor = Cursors.Default;
            if (mx < HandleSize) Cursor = Cursors.SizeWE;
            if (my < HandleSize) Cursor = Cursors.SizeNS;
            if (mx < HandleSize && my < HandleSize) Cursor = Cursors.SizeAll;
        }
        else
        {   // else move grid line(s)
            if (Cursor == Cursors.SizeWE  || Cursor == Cursors.SizeAll)
               Xs[ix] += e.X - mDown.X;
            if (Cursor == Cursors.SizeNS  || Cursor == Cursors.SizeAll) 
               Ys[iy] +=  e.Y - mDown.Y;
            Invalidate();
            mDown = e.Location;
            // restore order in case we overshot
            Xs = Xs.OrderBy(x => x).ToList();
            Ys = Ys.OrderBy(x => x).ToList();
        }
    }
}

这只是一个快速的镜头,很多事情可以而且可能应该改进,比如添加和删除列和行、验证等。

我将它设置为像这样覆盖Panel panel1

Grid grid1 = new Grid();
panel1.Controls.Add(grid1);
//grid1.Size = panel1.ClientSize;    // overlay full area..or..
grid1.Init(4, 3, new Size(99, 44));  // .. use the overload with size
grid1.Invalidate();

要让用户将其放置和调整大小,您可以使用通常的鼠标事件来代替..

更新:在重新阅读时,我看到您也想让用户调整网格大小。这是一个如何扩展代码以允许从左边缘或右边缘调整大小的示例:

        {   // else move gridline or size grid
            if (Cursor == Cursors.SizeWE  || Cursor == Cursors.SizeAll)
            {
                int delta = mDown.X - e.X;
                if (ix == 0)  // left edge: resize
                {
                    Width += delta;
                    Left -= delta;
                    Xs[Xs.Count - 1] = Width - 1;
                }
                else if (ix == Xs.Count - 1)  // right edge resize
                {
                    Width -= delta;
                    Xs[Xs.Count - 1] = Width - 1;
                }
                else Xs[ix] -= delta;  // move gridline
            }

顶部和底部边缘的工作方式完全相同。就像线交叉调整大小也可以从角落工作..


更新: 可以使用 PictureboxLabel(与 Autosize=false );两者都具有开箱即用的DoubleBuffered 属性,并且比Panels 更支持绘图。

【讨论】:

  • 请注意,InitializeComponent(); 方法没有“初始化”(是的,我看到了 partial 关键字),我认为应该从 costructor 调用 Init(x, y, s)(或 InitializeComponent() ) 具有默认值,因此您可以将其用作自定义控件(从工具箱中拖动它,因为有人会想尝试一下)。细节。我对其进行了测试,效果很好。
  • 是的,如果想从工具箱中添加它,默认值是有意义的。不过,我猜仍然可以使用这些属性,但没有进一步测试。这只是一个快速的,我喜欢它比我之前尝试过的 dgv 加 FLP 的黑客要好得多..
  • 感谢您的客气话。我只是添加了几行来实现调整大小。
猜你喜欢
  • 1970-01-01
  • 2019-10-19
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
相关资源
最近更新 更多