[原创](⊙WinForm⊙) 开发自定义的表格控件
时隔上篇文章又是很长时间了,其实自己总是想着多纪录点东西,但是总是懒得去写。今天抽个空,分享一下我刚刚做的一个东东,欢迎波友们批评指正。。。
And Now , Let 's Start! Go Go Go !!!
Before,我们还是先预览一下效果图吧:
First,如果你是一个winform开发人员,而且你们的产品明确要求不能使用三方控件,那么首先你需要蛋定~,至于怎麽蛋定,自己敲去。
我们都晓得,在winform开发中,如果应用.net自带的那些控件的话有时候是很难满足我们的需求的。例如我们想做一个表格,那么我们会去用DataGridView,这个东东是个好东西,功能也特别的强大,其实我们完全可以去定制扩展它(前提是如果你非常熟悉他的话),但是往往有些效果就是那么的不自然,比如如果我想要像网页的表格一样,最后一列的操作项,根据不同的值放不同的按钮,又或者我一个单元格又要文字又要图片,怎么放? 我们需要定制扩展,但是DataGridView很复杂,扩展定制起来还是挺麻烦的,这里我就自定义了一个winform的表格iTable.
Second,设计想法:
1.使用TableLayoutPanel作为扩展对象,因为他提供了很好的表格形式。(其实也可以用ListView来扩展)
2.表格需要表头,表头需要能有排序的功能,而且点击后应该有排序的小箭头。因此定制一种表头单元格控件。
3.对于单元格我们的需要定制,一个单元格内有可能存放很多种内容,比如:纯文本、链接文本、图片、按钮等,这里分为三种:纯文本、链接文本和容器类(只要有容器,不管你放按钮 还是图片都可以)。
4.对于一个表格,如何去设计,可以选择使用一行一行的设计,但这里使用列的形式。一个列中包含表头单元格样式和单元格样式。
5.表格的列需要几种模式:Absolute、Percent、AutoSize。这里就是运用TableLayoutPanel的好处。
6.表格中的按钮,链接文本等能在点击时响应事件,并且能让我们知道触发者和响应的行信息。
7.其他
Third,代码:
@iCellStyle
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; using System.ComponentModel; namespace UserTableControlSurvey.UserComponent { /// <summary> /// 单元格 /// </summary> public class iCellStyle { #region "属性变量" // // 单元格内容类型 // private iCellContentType _contentType = iCellContentType.CONTAINER; // // 单元格内容对齐方式 // private ContentAlignment _textAlign = ContentAlignment.MiddleCenter; // // 单元格字体样式 // private Font _textFont = SystemFonts.DefaultFont; // // 高度 // private int height = 20; #endregion #region "getter/setter" /// <summary> /// 高度 /// </summary> public int Height { get { return this.height; } set { this.height = value; } } /// <summary> /// 内容类型 /// </summary> public iCellContentType ContentType { get { return this._contentType; } set { this._contentType = value; } } /// <summary> /// 文本对齐方式 /// </summary> public ContentAlignment TextAlign { get { return this._textAlign; } set { this._textAlign = value; } } /// <summary> /// 文本字体 /// </summary> public Font TextFont { get { return this._textFont; } set { this._textFont = value; } } #endregion public iCellStyle() { } public iCellStyle(iCellContentType contentType, ContentAlignment textAlign, Font txtFont) : this() { this._contentType = contentType; this._textAlign = textAlign; this._textFont = txtFont; } } }
@iGridStyle
using System; using System.Collections.Generic; using System.Text; namespace UserTableControlSurvey.UserComponent { public class iGridStyle { } /// <summary> /// 单元格内容类型 /// </summary> public enum iCellContentType { // // 纯文本 // PLAIN_TEXT, // // 连接文本 // LINK_TEXT, // // 容器,可以添加控件 // CONTAINER } /// <summary> /// 排序方式 /// </summary> public enum iCellSortArrow { // // 不排序 // NONE, // // 按升序排序 // ASC, // // 按降序排序 // DESC } }
@iHeaderCell
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.Drawing.Drawing2D; namespace UserTableControlSurvey.UserComponent { /// <summary> /// 表头单元格 /// </summary> public class iHeaderCell : UserControl { // // 默认排序方式为不排序 // public iCellSortArrow SortArrow = iCellSortArrow.NONE; // // 文本 // private string text = string.Empty; // // 是否获取焦点 // private bool IsFocusOn = false; // // 鼠标经过颜色 // protected Color MouseOverColor = Color.SkyBlue; // // 是否允许排序 // public bool IsAllowSort = true; /// <summary> /// 文本 /// </summary> public override string Text { set { this.text = value; } get { return this.text; } } /// <summary> /// 排序事件响应 /// </summary> public event EventHandler<iHeaderSortEventArgs> Sorted; public iHeaderCell() { this.Dock = DockStyle.Fill; this.AutoSize = true; this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true); this.MouseHover += new EventHandler(iHeaderCell_MouseHover); this.MouseLeave += new EventHandler(iHeaderCell_MouseLeave); this.MouseClick += new MouseEventHandler(iHeaderCell_MouseClick); } public iHeaderCell(string text, Font font, Color backColor, Color foreColor) : this() { this.Init(text, font, backColor, foreColor); } public iHeaderCell(string text,string name, Font font, Color backColor, Color foreColor) : this() { this.Text = text; this.Name = name; this.Font = font; this.BackColor = backColor; this.ForeColor = foreColor; } public void Init(string text, Font font, Color backColor, Color foreColor) { this.Text = text; this.Font = font; this.BackColor = backColor; this.ForeColor = foreColor; } #region "Events" private void iHeaderCell_MouseClick(object sender, MouseEventArgs e) { if (this.IsAllowSort) { if (this.SortArrow == iCellSortArrow.ASC) this.SortArrow = iCellSortArrow.DESC; else this.SortArrow = iCellSortArrow.ASC; this.Refresh(); this.OnSort(); } } private void iHeaderCell_MouseLeave(object sender, EventArgs e) { this.IsFocusOn = false; this.Refresh(); } private void iHeaderCell_MouseHover(object sender, EventArgs e) { this.IsFocusOn = true; this.Refresh(); } protected void OnSort() { if (Sorted != null) Sorted(this, new iHeaderSortEventArgs(this.Name,this.SortArrow)); } #endregion #region "repaint" protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); Graphics g = e.Graphics; Rectangle rect = e.ClipRectangle; Brush brush = new SolidBrush(this.BackColor); if (IsFocusOn) brush = new LinearGradientBrush(rect, Color.White, this.MouseOverColor, 90, true); g.FillRectangle(brush, rect); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Rectangle rect1 = e.ClipRectangle; Rectangle rect = new Rectangle(rect1.Location.X +(rect1.Width-this.Width), rect1.Location.Y + (rect1.Height-this.Height), rect1.Width, rect1.Height); Graphics g = e.Graphics; SizeF size = g.MeasureString(this.text, this.Font); PointF point = this.GetPreferredSize(size, rect); g.DrawString(this.text, this.Font, new SolidBrush(ForeColor), point); DrawSortArrow(g, size, rect, point); } protected void DrawSortArrow(Graphics g, SizeF size, Rectangle rect, PointF point) { float triangleWidth = 10; float triangleHeight = 6; float offset = 5; float x = point.X + size.Width + 5; float y = point.Y; PointF[] points = new PointF[3]; switch (this.SortArrow) { case iCellSortArrow.ASC: points[0] = new PointF((rect.Location.X + rect.Width - offset - triangleWidth / 2), (rect.Height - triangleHeight) / 2); points[1] = new PointF((rect.Location.X + rect.Width - offset - triangleWidth), (rect.Height + triangleHeight) / 2); points[2] = new PointF((rect.Location.X + rect.Width - offset), (rect.Height + triangleHeight) / 2); break; case iCellSortArrow.DESC: points[0] = new PointF((rect.Location.X + rect.Width - offset - triangleWidth / 2), (rect.Height + triangleHeight) / 2); points[1] = new PointF((rect.Location.X + rect.Width - offset - triangleWidth), (rect.Height - triangleHeight) / 2); points[2] = new PointF((rect.Location.X + rect.Width - offset), (rect.Height - triangleHeight) / 2); break; default: break; } if (points.Length > 0) { g.FillPolygon(Brushes.SlateGray, points); //g.DrawPolygon(Pens.SlateGray, points); } } // // Attention:在计算中间位置的时候使用this.Width 和this.Height,滚动的时候不会出错 // protected PointF GetPreferredSize(SizeF size, Rectangle rect) { PointF point = new PointF(); point.X = rect.Location.X + (this.Width - size.Width) / 2; point.Y = rect.Location.Y + (this.Height - size.Height) / 2; return point; } #endregion } }
.............................................. 有点多,还是把主要上码。
#关键部分,iTable源码
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.ComponentModel; using System.Data; using System.Drawing; namespace UserTableControlSurvey.UserComponent { /// <summary> /// @author : gxjiang /// @date : 2011/4/21 /// @description : 自定义表格控件 /// </summary> public class iTable : TableLayoutPanel { // // 外部添加控件的委托 // public FunctionsAddHandler OuterFunctionsAdd; // // 表头的索引 // private const int INDEX_HEADER = 0; // // 保存表格的数据 // private DataTable _data; // // 列类型 // private iColumn[] _iColumns; /// <summary> /// 绑定的数据 /// </summary> public DataTable Data { get { return this._data; } set { this._data = value; this.ClearRows(); this.PrepareRows(); } } /// <summary> /// 列样式定义集合 /// </summary> [Browsable(true)] public iColumn[] iColumns { get { return this._iColumns; } set { this._iColumns = value; this.InitializeColumnStyle(); this.SortedEventBinder(); } } /// <summary> /// 排序事件响应 /// </summary> public event EventHandler<iHeaderSortEventArgs> Sorted; /// <summary> /// 对于按钮等事件 /// </summary> public event EventHandler<iActionsEventArgs> Actioned; public iTable() { // // 预处理一下 // this.ColumnCount = 1; this.RowCount = 1; // // 设置一些方式 // this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true); // // 默认给委托指向一个默认处理的方法 // this.OuterFunctionsAdd = new FunctionsAddHandler(DefaultOperationAddHandler); //this.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single; } private void iColumns_Sorted(object sender, iHeaderSortEventArgs e) { // // 清除各个表头的排序箭头 // foreach (iColumn icon in this._iColumns) { if (e.Name != icon.Name) icon.HeaderCell.SortArrow = iCellSortArrow.NONE; } // // 刷新,使得其他的表头的箭头去掉 // this.Refresh(); // // 对datatable进行排序 // this._data.DefaultView.Sort = e.Name + " " + e.SortArrow; this._data = this._data.DefaultView.ToTable(); // // 重新将数据刷新到界面 // this.DataRebinding(); // // 向外部抛出排序事件 // //this.OnSort(e.Name, e.SortArrow); } // // 初始化表格的列 // protected void InitializeColumnStyle() { this.ColumnCount = this.iColumns.Length; iColumn iCol; for (int i = 0, len = this.iColumns.Length; i < len; i++) { iCol = this.iColumns[i]; ColumnStyles.Add(new ColumnStyle(iCol.ColumnSizeType, iCol.Width)); } //TODO: 这里还需要跟实际结合起来调试 } // // 初始化表头 // protected void InitializeHeader() { this.RowStyles.Add(new RowStyle(SizeType.Absolute, this.iColumns[INDEX_HEADER].HeaderCell.Height)); for (int i = 0, len = this.ColumnCount; i < len; i++) { this.Controls.Add(this.iColumns[i].HeaderCell, i, 0); this.iColumns[i].HeaderCell.Dock = DockStyle.Fill; } } protected void PrepareRows() { this.RowCount = _data.Rows.Count + 2; int i = 1; foreach (DataRow row in _data.Rows) { i = this.RowStyles.Add(new RowStyle(SizeType.Absolute, this._iColumns[INDEX_HEADER].Cell.Height)); for (int j = 0; j < this._iColumns.Length; j++) { this.PerpareCells(j, i, row); } } this.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); } protected void PerpareCells(int j, int i, DataRow row) { switch (this._iColumns[j].Cell.ContentType) { case iCellContentType.LINK_TEXT: LinkLabel lable = new LinkLabel(); lable.TextAlign = this.iColumns[j].Cell.TextAlign; lable.Text = row[this._iColumns[j].Name].ToString(); lable.BackColor = Color.Transparent; this.Controls.Add(lable, j, i); lable.Dock = DockStyle.Fill; lable.LinkClicked += new LinkLabelLinkClickedEventHandler(FuncButtons_Click); break; case iCellContentType.PLAIN_TEXT: Label label = new Label(); label.TextAlign = this.iColumns[j].Cell.TextAlign; label.Text = row[this._iColumns[j].Name].ToString(); label.BackColor = Color.Transparent; this.Controls.Add(label, j, i); label.Dock = DockStyle.Fill; break; case iCellContentType.CONTAINER: Panel panel = new Panel(); panel.Dock = DockStyle.Fill; panel.BackColor = Color.Transparent; Control[] ctrls = OuterFunctionsAdd(j, i, row[this._iColumns[j].Name]); foreach (Control ctrl in ctrls) { panel.Controls.Add(ctrl); ctrl.MouseClick += new MouseEventHandler(FuncButtons_Click); } this.Controls.Add(panel, j, i); break; default: break; } } private void DataRebinding() { int i = 1; foreach (DataRow row in _data.Rows) { for (int j = 0; j < this._iColumns.Length; j++) { switch (this._iColumns[j].Cell.ContentType) { case iCellContentType.CONTAINER: Panel pnl = this.GetControlFromPosition(j, i) as Panel; if (pnl != null) { pnl.Controls.Clear(); Control[] ctrls = OuterFunctionsAdd(j, i, row[this._iColumns[j].Name]); foreach (Control ctrl in ctrls) { pnl.Controls.Add(ctrl); ctrl.MouseClick += new MouseEventHandler(FuncButtons_Click); } } break; default: Control thisControl = this.GetControlFromPosition(j, i); if (thisControl != null) { thisControl.Text = row[this._iColumns[j].Name].ToString(); } break; } } i++; } } private void FuncButtons_Click(object sender, EventArgs e) { Control ctrl = sender as Control; String name = String.Empty; if (ctrl != null) { TableLayoutPanelCellPosition position = new TableLayoutPanelCellPosition(); if (ctrl is LinkLabel) { position = this.GetPositionFromControl(ctrl); name = "label"; } else if (ctrl is Button) { Control parent = ctrl.Parent; position = this.GetPositionFromControl(parent); name = ctrl.Name; } this.OnActioned( position.Column, position.Row, name, _data.Rows[position.Row - 1] ); } } public void ClearRows() { if (this.Controls.Count > 0) this.Controls.Clear(); if (this.RowStyles.Count > 0) this.RowStyles.Clear(); this.InitializeHeader(); } protected Control[] DefaultOperationAddHandler(int colIndex, int rowIndex, object val) { return new Control[] { }; } protected void SortedEventBinder() { foreach (iColumn icon in this._iColumns) { icon.Sorted += new EventHandler<iHeaderSortEventArgs>(iColumns_Sorted); } } #region " " protected void OnSort(string name, iCellSortArrow sortArrow) { if (Sorted != null) Sorted(this, new iHeaderSortEventArgs(name, sortArrow)); } protected void OnActioned(int colIndex, int rowIndex, string name, object data) { if (this.Actioned != null) Actioned(this, new iActionsEventArgs(colIndex, rowIndex, name, data)); } #endregion #region " inherited " protected override void OnResize(EventArgs eventargs) { base.OnResize(eventargs); this.Refresh(); } protected override void OnScroll(ScrollEventArgs se) { base.OnScroll(se); this.Refresh(); } protected override void OnCellPaint(TableLayoutCellPaintEventArgs e) { base.OnCellPaint(e); Graphics g = e.Graphics; if (e.Row != this.RowCount - 1) g.DrawLine(Pens.Red, new Point(e.CellBounds.Location.X, e.CellBounds.Location.Y + e.CellBounds.Height - 1), new Point(e.CellBounds.Location.X + e.CellBounds.Width - 1, e.CellBounds.Location.Y + e.CellBounds.Height - 1)); if (e.Row == INDEX_HEADER) { g.FillRectangle( Brushes.LightBlue, e.CellBounds.Location.X , e.CellBounds.Location.Y + 1, e.CellBounds.Width-1, e.CellBounds.Height - 2); } } #endregion } }
@Test
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using UserTableControlSurvey.UserComponent; namespace UserTableControlSurvey { public partial class Mainfrm : Form { public Mainfrm() { InitializeComponent(); table = new iTable(); AddiColumns(); this.panel1.Controls.Add(table); table.BackColor = Color.GhostWhite; table.AutoScroll = true; table.Dock = DockStyle.Fill; table.Sorted += new EventHandler<iHeaderSortEventArgs>(header_Sorted); table.Actioned += new EventHandler<iActionsEventArgs>(table_Actioned); table.OuterFunctionsAdd = new FunctionsAddHandler(OperationAdd); DataTable datatable = new DataTable(); datatable.Columns.Add("column1"); datatable.Columns.Add("column2"); datatable.Columns.Add("column3"); datatable.Columns.Add("column4"); datatable.Columns.Add("column5"); for (int i = 0; i < 10; i++) { DataRow row = datatable.NewRow(); row["column1"] = "column1"+i; row["column2"] = "column2" + i; row["column3"] = "column3" + i; row["column4"] = "column4" + i; row["column5"] = "column5" + i; datatable.Rows.Add(row); } table.Data = datatable; } protected Control[] OperationAdd(int colIndex, int rowIndex, object val) { if (rowIndex % 2 == 0) { Button btn3 = new Button(); btn3.Size = new Size(80, 30); btn3.Text = "单个操作"; btn3.Name = "button_Single"; btn3.Location = new Point(40, 10); btn3.BackColor = Color.WhiteSmoke; return new Control[] {btn3 }; } Button btn = new Button(); btn.Size = new Size(60, 30); btn.Text = "操作1"; btn.Name = "button_1"; btn.Location = new Point(10, 10); btn.BackColor = Color.WhiteSmoke; Button btn2 = new Button(); btn2.Size = new Size(60, 30); btn2.Text = "操作2"; btn2.Name = "button_2"; btn2.Location = new Point(80, 10); btn2.BackColor = Color.WhiteSmoke; return new Control[] { btn,btn2 }; } void table_Actioned(object sender, iActionsEventArgs e) { DataRow row = e.AvaliableData as DataRow ; if (row != null) { MessageBox.Show(e.Name + ":" + e.ColIndex + ":" + e.RowIndex+":"+row[1]); } } private void AddiColumns() { // // iHeaderCells // Font font = new Font("微软雅黑", 12, FontStyle.Regular); Color backColor = Color.LightBlue; Color foreColor = Color.Red; Headers = new iHeaderCell[5]; Headers[0] = new iHeaderCell("第一列", font, backColor, foreColor); Headers[0].Height = 60; Headers[1] = new iHeaderCell("第二列", font, backColor, foreColor); Headers[2] = new iHeaderCell("第三列", font, backColor, foreColor); Headers[3] = new iHeaderCell("第四列", font, backColor, foreColor); Headers[4] = new iHeaderCell("第五列", font, backColor, foreColor); // // iCells // Font cellFont = new System.Drawing.Font("宋体",10,FontStyle.Italic); Cells = new iCellStyle[5]; Cells[0] = new iCellStyle(iCellContentType.PLAIN_TEXT, ContentAlignment.MiddleCenter, cellFont); Cells[0].Height = 50; Cells[1] = new iCellStyle(iCellContentType.PLAIN_TEXT, ContentAlignment.MiddleCenter, cellFont); Cells[2] = new iCellStyle(iCellContentType.PLAIN_TEXT, ContentAlignment.MiddleCenter, cellFont); Cells[3] = new iCellStyle(iCellContentType.LINK_TEXT, ContentAlignment.MiddleCenter, cellFont); Cells[4] = new iCellStyle(iCellContentType.CONTAINER, ContentAlignment.MiddleCenter, cellFont); // // iColumns // Columns = new iColumn[5]; Columns[0] = new iColumn(); Columns[0].HeaderCell = Headers[0]; Columns[0].Cell = Cells[0]; Columns[0].Name = "column1"; Columns[0].ColumnSizeType = SizeType.Absolute; Columns[0].Width = 100; Columns[1] = new iColumn(); Columns[1].HeaderCell = Headers[1]; Columns[1].Cell = Cells[1]; Columns[1].Name = "column2"; Columns[1].ColumnSizeType = SizeType.Absolute; Columns[1].Width = 100; Columns[2] = new iColumn(); Columns[2].HeaderCell = Headers[2]; Columns[2].Cell = Cells[2]; Columns[2].Name = "column3"; Columns[2].ColumnSizeType = SizeType.Absolute; Columns[2].Width = 150; Columns[3] = new iColumn(); Columns[3].HeaderCell = Headers[3]; Columns[3].Cell = Cells[3]; Columns[3].Name = "column4"; Columns[3].ColumnSizeType = SizeType.Percent; Columns[3].Width = 60; Columns[4] = new iColumn(); Headers[4].IsAllowSort = false; Columns[4].HeaderCell = Headers[4]; Columns[4].Cell = Cells[4]; Columns[4].Name = "column5"; Columns[4].ColumnSizeType = SizeType.Absolute; Columns[4].Width = 160; table.iColumns = Columns; } void header_Sorted(object sender, iHeaderSortEventArgs e) { MessageBox.Show(e.Name+":"+e.SortArrow); } #region "HeaderCells" // // 列 // private iColumn[] Columns; // // 表头 // private iHeaderCell[] Headers; // // 单元格类型 // private iCellStyle[] Cells; // // 参加表格 // private iTable table; #endregion } }
先到这里吧,波友们先看着,后面再介绍下。
To Be Next 。。。
出处:http://gxjiang.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。