捕获 TableLayoutPanel 特定区域的图像
Capture image of specific area of a TableLayoutPanel
我有一个使用 TableLayoutPanel
控件的项目。
我想捕获那个 TableLayoutPanel
.
的特定区域的图像
我有一个图像(捕获的)宽度和高度信息。我也有start-end个cell indexes信息,我抓到成功了,但是抓到的区域不对。
这是我的代码:
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();
}
出于某种原因,TableLayoutPanel 不会通过 属性 或方法直接公开单元格边界。但它当然会计算这些值供内部使用,因为它需要绘制其单元格的边框。
它的 OnPaintBackground 方法负责这个。
当控件需要重新绘制自身时,将重新计算单元格边界,并且对于每个单元格,控件都会引发 CellPaint event. Its TableLayoutCellPaintEventArgs 参数 returns 当前单元格的边界和 Column/Row 坐标。
由于每次 TableLayoutPanel 为 modified/resized 时都会引发此事件,我们可以使用 CellPaint
事件处理程序来存储这些引用。
在这里,我使用 Dictionary<TableLayoutPanelCellPosition, Rectangle>
来存储每个 Cell 的坐标并将其映射到它的边界。
Layout 事件处理程序设置了一个布尔字段,该字段导致字典仅在必要时重建(因为 TableLayoutPanel 的布局已更改)。
▶ 字典键是 TableLayoutPanelCellPosition,因为它允许进行快速简单的相等比较。当然,如果需要,您可以使用不同类型的对象。
▶ 假设 TableLayoutPanel 命名为 tlp1
.
▶ 在 运行 时添加 and/or 删除 Columns/Rows 未被处理。如果需要,请在 Layout
事件处理程序中重新定义 Dictionary 而不是 Form Constructor。
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;
}
}
}
要从当前鼠标位置获取 Cell,调用 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;
现在,要从一系列单元格生成位图,您只需指定开始(左、上)和结束(右、下)单元格并将这些值引用传递给 TlpCellRangeToBitmap()
方法(也可以作为扩展方法)
如果 includeBorders
参数设置为 true
,它将包括单元格的外部边框(参见可视示例)。添加检查 TableLayoutPanel 是否真的有边框的代码 (BorderStyle
属性).
GetCellRangeBounds()
返回包含单元格范围的Rectangle:此方法是独立的,可用于其他目的。
例如将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));
}
这是它的工作原理:
我有一个使用 TableLayoutPanel
控件的项目。
我想捕获那个 TableLayoutPanel
.
的特定区域的图像
我有一个图像(捕获的)宽度和高度信息。我也有start-end个cell indexes信息,我抓到成功了,但是抓到的区域不对。
这是我的代码:
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();
}
出于某种原因,TableLayoutPanel 不会通过 属性 或方法直接公开单元格边界。但它当然会计算这些值供内部使用,因为它需要绘制其单元格的边框。
它的 OnPaintBackground 方法负责这个。
当控件需要重新绘制自身时,将重新计算单元格边界,并且对于每个单元格,控件都会引发 CellPaint event. Its TableLayoutCellPaintEventArgs 参数 returns 当前单元格的边界和 Column/Row 坐标。
由于每次 TableLayoutPanel 为 modified/resized 时都会引发此事件,我们可以使用 CellPaint
事件处理程序来存储这些引用。
在这里,我使用 Dictionary<TableLayoutPanelCellPosition, Rectangle>
来存储每个 Cell 的坐标并将其映射到它的边界。
Layout 事件处理程序设置了一个布尔字段,该字段导致字典仅在必要时重建(因为 TableLayoutPanel 的布局已更改)。
▶ 字典键是 TableLayoutPanelCellPosition,因为它允许进行快速简单的相等比较。当然,如果需要,您可以使用不同类型的对象。
▶ 假设 TableLayoutPanel 命名为 tlp1
.
▶ 在 运行 时添加 and/or 删除 Columns/Rows 未被处理。如果需要,请在 Layout
事件处理程序中重新定义 Dictionary 而不是 Form Constructor。
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;
}
}
}
要从当前鼠标位置获取 Cell,调用 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;
现在,要从一系列单元格生成位图,您只需指定开始(左、上)和结束(右、下)单元格并将这些值引用传递给 TlpCellRangeToBitmap()
方法(也可以作为扩展方法)
如果 includeBorders
参数设置为 true
,它将包括单元格的外部边框(参见可视示例)。添加检查 TableLayoutPanel 是否真的有边框的代码 (BorderStyle
属性).
GetCellRangeBounds()
返回包含单元格范围的Rectangle:此方法是独立的,可用于其他目的。
例如将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));
}
这是它的工作原理: