简体   繁体   English

捕获 TableLayoutPanel 特定区域的图像

[英]Capture image of specific area of a TableLayoutPanel

I have a Project that uses a TableLayoutPanel control.我有一个使用TableLayoutPanel控件的项目。
I want capture image of specific area of that TableLayoutPanel .我想要捕获该TableLayoutPanel特定区域的图像。
I have an image (which is captured) width and height info.我有一个图像(已捕获)宽度和高度信息。 I also have start-end cell indexes info, I succeeded to capture but the area that captured is wrong.我也有开始结束单元格索引信息,我成功捕获但捕获的区域是错误的。

在此处输入图片说明

This is my code:这是我的代码:

private void getImage()
{
    int totalWidth = 250 // image width size;
    int totalHeight = 200 // image height size;

    int LeftBottomCellColumnIndex = 3
    int LeftBottomCellRowIndex = 4

    int LeftTopCellColumnIndex = 3
    int LeftBottomCellRowIndex = 2 // I also have all cells index info

    Bitmap bm = new Bitmap(totalWidth, totalHeight);
    tableLayoutPanel2.DrawToBitmap(bm, new Rectangle(0, 0, totalWidth, totalHeight)); // 0,0 problem is here. I do not know what I have to put there

    string path = @"C:\Users\prdn5\TestDrawToBitmap.bmp";
    FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
    bm.Save(fs, ImageFormat.Bmp);
    fs.Flush();
    fs.Close();
}

The TableLayoutPanel, for some reason, doesn't expose the Cells bounds directly, through a Property or Method.由于某种原因,TableLayoutPanel 不会通过属性或方法直接公开单元格边界。 But of course it calculates these values for internal use, since it needs to paint the borders of its Cells.但当然它会计算这些值供内部使用,因为它需要绘制其单元格的边界。

Its OnPaintBackground method is responsible for this.它的OnPaintBackground方法对此负责。
When the Control needs to repaint itself, the Cells bounds are recalculated and, for each Cell, the Control raises the CellPaint event.当 Control 需要重新绘制自身时,会重新计算 Cells 边界,并且对于每个 Cell,Control 会引发CellPaint事件。 Its TableLayoutCellPaintEventArgs argument returns both the current Cell's bounds and Column/Row coordinates.它的TableLayoutCellPaintEventArgs参数返回当前单元格的边界和列/行坐标。
Since this event is raised each time the TableLayoutPanel is modified/resized, we can use the CellPaint event handler to store these references.由于每次修改/调整 TableLayoutPanel 大小时都会引发此事件,因此我们可以使用CellPaint事件处理程序来存储这些引用。

Here, I'm using a Dictionary<TableLayoutPanelCellPosition, Rectangle> to store the coordinates of each Cell and map it to its bounds.在这里,我使用Dictionary<TableLayoutPanelCellPosition, Rectangle>来存储每个单元格的坐标并将其映射到其边界。

The Layout event handler sets a boolean Field that causes the Dictionary to be rebuild only when necessary (because the TableLayoutPanel's Layout has changed). Layout事件处理程序设置一个布尔字段,导致仅在必要时重建字典(因为 TableLayoutPanel 的布局已更改)。

▶ The Dictionary Key is a TableLayoutPanelCellPosition because it allows a quick and simple equality comparisons. ▶ Dictionary Key 是TableLayoutPanelCellPosition,因为它允许快速简单的相等比较。 Of course you can use a different type of object, if needed.当然,如果需要,您可以使用不同类型的对象。

▶ Assume the TableLayoutPanel is named tlp1 . ▶ 假设 TableLayoutPanel 被命名为tlp1

▶ Adding and/or removing Columns/Rows at run-time is not handled. ▶ 不处理在运行时添加和/或删除列/行。 In case it's needed, re-define the Dictionary in the Layout event handler instead of the Form Constructor.如果需要,请在Layout事件处理程序而不是 Form Constructor 中重新定义 Dictionary。

public partial class SomeForm : Form
{
    Dictionary<TableLayoutPanelCellPosition, Rectangle> tlpCells = null;
    bool calcCells = false;

    public SomeForm() {
        InitializeComponent();

        // Set the DoubleBuffered property via reflection (if needed)
        var flags = BindingFlags.Instance | BindingFlags.NonPublic;
        tlp1.GetType().GetProperty("DoubleBuffered", flags).SetValue(tlp1, true);

        tlpCells = new Dictionary<TableLayoutPanelCellPosition, Rectangle>();
        for (int x = 0; x < tlp1.ColumnCount; x++) {
            for (int y = 0; y < tlp1.RowCount; y++) {
                tlpCells.Add(new TableLayoutPanelCellPosition(x, y), Rectangle.Empty);
            }
        }
    }

    private void tlp1_Layout(object sender, LayoutEventArgs e) => calcCells = true;

    private void tlp1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
    {
        if (calcCells) {
            var cellPos = new TableLayoutPanelCellPosition(e.Column, e.Row);
            tlpCells[cellPos] = e.CellBounds;

            if (cellPos.Column == tlp1.ColumnCount - 1 && 
                cellPos .Row == tlp1.RowCount - 1) calcCells = false;
        }
    }
}

To get the Cell from the current Mouse position, call GetSelectedCell() , passing the current Pointer location, relative to the TableLayoutPanel:要从当前鼠标位置获取单元格,请调用GetSelectedCell() ,传递相对于 TableLayoutPanel 的当前指针位置:

var cell = GetSelectedCell(tlp1.PointToClient(MousePosition));
// If called from the MouseMove, MouseDown etc handlers, use the MouseEventArgs instead
var cell = GetSelectedCell(e.Location);

// [...]

private TableLayoutPanelCellPosition GetSelectedCell(Point position) 
    => tlpCells.FirstOrDefault(c => c.Value.Contains(position)).Key;

Now, to generate a Bitmap from a range of Cells, you just need to specify the starting (Left, Top) and ending (Right, Bottom) Cells and pass these values references to the TlpCellRangeToBitmap() method (it could also be used as an extension method).现在,要从一系列单元格生成位图,您只需要指定开始(左、上)和结束(右、下)单元格并将这些值引用传递给TlpCellRangeToBitmap()方法(它也可以用作扩展方法)。
If the includeBorders argument is set to true , it will include the external borders of the Cells (see the visual example).如果includeBorders参数设置为true ,它将包括单元格的外部边框(请参阅可视化示例)。 Add code that checks whether the TableLayoutPanel actually has borders ( BorderStyle Property).添加代码以检查 TableLayoutPanel 是否实际具有边框( BorderStyle属性)。
The Rectangle that includes the range of Cells is returned by GetCellRangeBounds() : this method is independent and can be used for other purposes.包含单元格范围的 Rectangle 由GetCellRangeBounds()返回:此方法是独立的,可用于其他目的。

For example, set the Image property of a PictureBox to the generated Bitmap:例如,将 PictureBox 的 Image 属性设置为生成的 Bitmap:

var cellStart = new TableLayoutPanelCellPosition(2, 2);
var cellEnd = new TableLayoutPanelCellPosition(3, 5);

somePictureBox.Image?.Dispose();
somePictureBox.Image = TlpCellRangeToBitmap(tlp1, cellStart, cellEnd, false);

// [...]

private Bitmap TlpCellRangeToBitmap(TableLayoutPanel tlp, TableLayoutPanelCellPosition cellStart, TableLayoutPanelCellPosition cellEnd, bool includeBorders)
{
    // The 3rd parameter includes or excludes the external borders
    var selRect = GetCellRangeBounds(cellStart, cellEnd, includeBorders);
    using (var image = new Bitmap(tlp.Width + 1, tlp.Height + 1)) {
        tlp.DrawToBitmap(image, new Rectangle(Point.Empty, tlp.Size));
        return image.Clone(selRect, PixelFormat.Format32bppArgb);
    }
}

private Rectangle GetCellRangeBounds(TableLayoutPanelCellPosition start, TableLayoutPanelCellPosition end, bool extBorders)
{
    var cellStart = tlpCells[start];
    var cellEnd = tlpCells[end];
    if (extBorders) {
        cellStart.Location = new Point(cellStart.X - 1, cellStart.Y - 1);
        cellEnd.Size = new Size(cellEnd.Width + 2, cellEnd.Height + 2);
    }
    return new Rectangle(cellStart.Location, new Size(cellEnd.Right - cellStart.X, cellEnd.Bottom - cellStart.Y));
}

This is how it works:这是它的工作原理:

TableLayoutPanel 单元格范围到位图

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

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