> 文章列表 > Winform下实现Grid布局

Winform下实现Grid布局

Winform下实现Grid布局

很久没写非常代码化的文章了,代码倒是每天都在写,但是前几天看到一个朋友在用一个“只有一个文本框”的窗体来写“控件随窗体大小改变而改变”,他用的方法是在窗体大小变化后,重新计算根据窗体的新Size计算这一个控件的Size。所以我给他评论是:单个控件压根不需要这么做,而多个控件这么做又没有意义,所以最好的办法就是参考CSS的Grid布局方法自定义一套基于WIniform的Grid布局。

Winform的控件都有一个Anchor属性。只不过如果使用Anchor的话,对于一个控件确实是可以的,但是多个控件行列布局的时候用Anchor就不行了,放大缩小后完全乱套。比如我布局得好好的一个页面:

 用Anchor布局得很好的页面

用Anchor布局的话,上下左右控件的位置倒是可以随窗口大小变化,但是大小却不会变。但是对于中间的空件,那么布局后很可能就是乱七八糟的了。

 中间的控件Anchor布局后显然很乱,不好调整

所以刚好周末,就花了点时间描述一下我所说的参考CSS的Grid布局方法来实现一个Winform下的Grid布局,思路如下

  1. 模拟CSS的Grid布局,加一个GridArea的类,用于存储grid-row-start / grid-column-start / grid-row-end / grid-column-end(想简单的话用一个Rectangle来替代也行);
  2. 在窗体的初始化方法中,完成控件初始化后(InitializeComponent),计算每个控件所属的GridArea;
  3. 在窗体Resize后,根据前面计算出来的每个控件GridArea,重新计算每个控件的位置,大小。

就是这么简单:

 Grid布局前是这样的

布局后

Anchor布局后效果

总的代码就100行,如果有朋友需要,欢迎使用。

public partial class GridLayoutForm : Form{private static int GRIDSPACE = 2;private Dictionary<Control, GridArea> ControlGridArea = new Dictionary<Control, GridArea>();public GridLayoutForm(){InitializeComponent();this.InitGridAreas();}private int GridCols { get; set; }private int GridRows { get; set; }private GridArea CalcGridArea(Control ctrl){int top = ctrl.Top;int left = ctrl.Left;int width = ctrl.Width;int height = ctrl.Height;int startRow = top / GRIDSPACE;int startCol = left / GRIDSPACE;if (startRow <= 0) startRow = 1;if (startCol <= 0) startCol = 1;int endRow = (top + height) / GRIDSPACE;int endCol = (left + width) / GRIDSPACE;if ((top + height) % GRIDSPACE > 0){endRow = (top + height + GRIDSPACE) / GRIDSPACE;}if ((left + width) % GRIDSPACE > 0){endCol = (left + width + GRIDSPACE) / GRIDSPACE;}// grid-row-start / grid-column-start / grid-row-end / grid-column-endreturn new GridArea(startRow, startCol, endRow, endCol + 1);}public void RelocationControls(){if (this.GridCols > 0 && this.GridRows > 0){double spaceW = (double)this.Width / this.GridCols;double spaceH = (double)this.Height / this.GridRows;foreach (Control child in this.Controls){if (this.ControlGridArea.ContainsKey(child)){GridArea area = this.ControlGridArea[child];int left = (int)Math.Round((area.GridColumnStart) * spaceW, 0);int top = (int)Math.Round((area.GridRowStart) * spaceH, 0);int width = (int)Math.Round((area.GridColumnEnd - 1) * spaceW - left, 0);int height = (int)Math.Round((area.GridRowEnd - 1) * spaceH - top, 0);child.Location = new Point(left, top);child.Size = new Size(width, height);}}}}public void InitGridAreas(){this.GridCols = this.Width / GRIDSPACE;this.GridRows = this.Height / GRIDSPACE;foreach (Control child in this.Controls){this.ControlGridArea[child] = this.CalcGridArea(child);}this.RelocationControls();}protected override void OnResize(EventArgs e){base.OnResize(e);this.RelocationControls();}private class GridArea{// grid-row-start / grid-column-start / grid-row-end / grid-column-endpublic GridArea(int rowStart, int columnStart, int rowEnd, int columnEnd){this.GridRowStart = rowStart;this.GridColumnStart = columnStart;this.GridRowEnd = rowEnd;this.GridColumnEnd = columnEnd;}public int GridRowStart { get; set; }public int GridColumnStart { get; set; }public int GridRowEnd { get; set; }public int GridColumnEnd { get; set; }}}