时隔上篇文章又是很长时间了,其实自己总是想着多纪录点东西,但是总是懒得去写。今天抽个空,分享一下我刚刚做的一个东东,欢迎波友们批评指正。。。
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 。。。