经过6天的连续奋战,FxGrid组件开发完成,今天正式投入使用。这个我把这个Grid作为一个轻量级组件,以弥补开发上的部分需求。
现在介绍一下这个组件的特点:
1、支持界面设计,能够设计出任意复杂的表头;
2、能够设计每个字段的汇总行为,汇总行为包括:None,Sum,Avg,Count;
3、能够设定每个字段的中文显示名称;
4、能够设定每个字段的数据对其方式;
5、能够设定表头的颜色;
6、能够设定每个字段的只读属性;
7、双击单元格,触发双击事件;
8、提供扩展接口,当某行某个单元格被选中时,通过该接口,能够控制该单元格的只读属性(该能够能够满足复杂表格数据的编辑)
9、支持DataTable和DataView数据源
该组件的缺点:
1、不支持鼠标滚轮;
2、是一个.Net组件,只能运行于.Net环境中。
该组件存在的理由:
1、通过表格作为录入界面时,要求表格的表头能够显示更加复杂的内容;
2、作为表格,能够设定是否需要显示合计值;
3、当有需求要求将查询数据和操作数据放在起一处理时(这样做的好处是减少界面数量),该组件的扩展接口提供了对当前选择单元格的只读属性的控制功能。
总而言之,这个表格没有PowerBuilder的DataWindow那么强大,也没有Crystal Report那么华丽,它只是一个轻量级的Grid(和我自己一样^_^),对于它的诞生理由,我一直都在思考,我也希望各位能够各抒己见。
(有关Grid的设计,我会继续下去,这个控件太具有挑战性了。但是下面的路该怎么走,我有几点想法:
1、参照MFC中的View概念,去研究
2、.Net下有关控件的构造
以上是技术上的考虑,从需求分析上
3、进一步细化开发中对Grid的需求,这种细化方法和商人细化市场的思想差不多)
以下是这个组件的源代码,用C++.Net编写完成,由于本人对C++.Net的使用时间只有2个多月,代码拙劣的地方还请高手指教。
(Afx是微软的开发团队,Fx是我自己的定义,呵呵!
这个组件不足之处,希望各位不吝赐教。
有关组件设计与系统分析,非常希望能够和更多的朋友交流。联系QQ:64528619)
设计视图(上传了半天图片,没有成功,只有等改天了)
#pragma
once

namespace
FxSystem

...
{
namespace FxForms

...{
using namespace System;
using namespace FxSystem::FxData;
using namespace System::Data;
using namespace System::Windows::Forms;


public __value enum FxGridColumnAction

...{
None,//什么也不做
Count,//计算
Sum,//合计
Avg//平均值
};

public __value enum FxGridColumnStyle

...{
Text,//文本显示样式
CheckBox//Checkbox显示样式
};

//字段标题
public __gc class FxGridColumn : public Label

...{
private://字段
String __gc* _name;//字段名称
FxGridColumnAction __value _action;//字段行为
HorizontalAlignment __value _align;//数据的对其方式
FxGridColumnStyle __value _style;//显示样式
bool _readOnly;//是否只读
public:
FxGridColumn(void):Label(),
_action(FxGridColumnAction::None),
_align(HorizontalAlignment::Left),
_style(FxGridColumnStyle::Text),
_readOnly(false)

...{
}
__property void set_ReadOnly(bool __value value)

...{
this->_readOnly = value;
}

__property __value bool get_ReadOnly()

...{
return this->_readOnly;
}

__property __value FxGridColumnAction get_Action()

...{
return this->_action;
}

__property void set_Action(FxGridColumnAction value)

...{
this->_action = value;
}

__property void set_ColName(String __gc* value)

...{
this->_name = value;
}

__property String __gc* get_ColName()

...{
return this->_name;
}
__property void set_DataAlign(HorizontalAlignment __value value)

...{
this->_align = value;
}

__property __value HorizontalAlignment get_DataAlign()

...{
return this->_align;
}
__property void set_ContentsStyle(FxGridColumnStyle __value value)

...{
this->_style = value;
}

__property __value FxGridColumnStyle get_ContentsStyle()

...{
return this->_style;
}
};


//表格汇总字段列表
public __gc class FxGridBottomItem :public Label

...{
private:
FxGridColumnAction __value _action;//处理方式
String __gc* _name;//字段名称

public:
__property void set_Action(FxGridColumnAction __value value)

...{
this->_action = value;
}
__property __value FxGridColumnAction get_Action()

...{
return this->_action;
}
__property void set_ColumnName(String __gc* value)

...{
this->_name = value;
}
__property String __gc* get_ColumnName()

...{
return this->_name;
}
};

//表格
public __gc class FxGrid : public Panel

...{
public:
__delegate void CellModifierHandler(DataRow __gc* row, String __gc* colName, DataGridTextBoxColumn __gc* col);//单元格编辑器
private:
Panel __gc* _pHead;//表头容器
Array __gc* _cols;//数据字段表头列表
DataGridColumnStyle __gc* _colStyles __gc[];//字段列表
Panel __gc* _pBottom;//表格的底部
DataGrid __gc* _theGrid;//表格控件
DataGridTableStyle __gc* _tbStyle;//表格的显示定义
Panel __gc* _theGridContainer;//Grid的容器
HScrollBar __gc* _hScroll;//横向滚动条
VScrollBar __gc* _vScroll;//纵向滚动条
int _leftMost,_leftStart;//最左边的位置,左边开始滚动的位置
int _topMost, _topStart;//最顶端的位置,最顶端开始的位置
FxForms::FxGrid::CellModifierHandler __gc* _cellModifier;//表格编辑函数指针

public:
__event void RowDoubleClick(int rowIndex);//单元格的双击时间


FxGrid():Panel(),_theGrid(0),_leftMost(0),_leftStart(0),_cellModifier(0)

...{
//对添加的控件进行过滤
this->ControlAdded += __gc new System::Windows::Forms::ControlEventHandler(this,ControlEventHandler);
}

void Init()//初始化

...{
this->AutoScroll = false;
this->CreateHeader();
this->CreateTitleList();
this->CreateGridColumnStyle();
this->CreateGridBottom();
this->CreateGrid();
this->CreateHScroll();
this->CreateVScroll();
this->CreateGridContainer();
}


__property void set_CellModifier(FxForms::FxGrid::CellModifierHandler __gc* value)

...{
this->_cellModifier = value;
}
__property FxForms::FxGrid::CellModifierHandler __gc* get_CellModifier()

...{
return this->_cellModifier;
}
__property void set_DataTable(DataTable __gc* value)

...{
if (this->_theGrid ==0)
throw __gc new Exception(S"Grid为空");

this->_tbStyle->MappingName = value->TableName;
this->_theGrid->DataSource = value;

//更新表格
this->UpdateGrid(value->Rows->Count);

//绑定表格的处理事件
value->RowChanged += __gc new DataRowChangeEventHandler(this,DataRowChangeHandler);

}
__property void set_DataView(DataView __gc* value)

...{
if (this->_theGrid ==0)
throw __gc new Exception(S"Grid为空");

this->_tbStyle->MappingName = value->Table->TableName;
this->_theGrid->DataSource = value;

//更新表格
this->UpdateGrid(value->Count);

//绑定表格的处理事件
value->Table->RowChanged += __gc new DataRowChangeEventHandler(this,DataRowChangeHandler);
}

//返回当前被选中的行
__property __value int get_CurrentRowIndex()

...{
return this->_theGrid->CurrentRowIndex;
}
//创建表头
private:
void CreateHeader()

...{
this->_pHead = __gc new Panel;
this->_pHead->BorderStyle = BorderStyle::FixedSingle;
int maxLeft =0,maxTop =0;//最大宽度,最大高度
for (; this->Controls->Count >0; )

...{
if (this->Controls->Item[0]->Left + this->Controls->Item[0]->Width > maxLeft)
maxLeft = this->Controls->Item[0]->Left + this->Controls->Item[0]->Width;

if (this->Controls->Item[0]->Top + this->Controls->Item[0]->Height > maxTop)
maxTop = this->Controls->Item[0]->Top + this->Controls->Item[0]->Height;

this->_pHead->Controls->Add(this->Controls->Item[0]);
}
//清空对原有控件的引用
this->Controls->Clear();
this->Controls->Add(this->_pHead);

//计算最大宽度和最大高度
this->_pHead->Top = 0;
this->_pHead->Left = 0;
this->_pHead->Width = maxLeft;
this->_pHead->Height = maxTop;
}


//创建数据列表
void CreateTitleList()

...{
ArrayList __gc* cols = __gc new ArrayList(32);
for (int i = 0; i<this->_pHead->Controls->Count; i++)

...{
if (this->_pHead->Controls->Item[i]->GetType()->ToString()->CompareTo(S"FxSystem.FxForms.FxGridColumn") == 0)
cols->Add(this->_pHead->Controls->Item[i]);
}

this->_cols = cols->ToArray(__typeof(FxGridColumn));

//根据left属性从新排列
FxGridColumn __gc* buffer;
FxGridColumn __gc* item;//比较缓存,比较指针

for (int i = 0; i < this->_cols->Count; i++)

...{
buffer = static_cast<FxGridColumn __gc*>(this->_cols->Item[i]);
for (int j = i+1;j<this->_cols->Count; j++)

...{
item = static_cast<FxGridColumn __gc*>(this->_cols->Item[j]);

if (buffer ->Left > item->Left)//移动位置

...{
this->_cols->Item[i] = this->_cols->Item[j];
this->_cols->Item[j] = buffer;
buffer = static_cast<FxGridColumn __gc*>(this->_cols->Item[i]);
}
}
}


}

//创建Grid字段定义
void CreateGridColumnStyle()

...{
this->_colStyles = __gc new DataGridColumnStyle __gc* __gc[this->_cols->Count];
FxGridColumn __gc* item;
//设置列属性
for (int i = 0; i< this->_cols->Count; i++)

...{
item = static_cast<FxGridColumn __gc*>(this->_cols->Item[i]);
//创建列样式
if (item->ContentsStyle == FxSystem::FxForms::FxGridColumnStyle::Text)

...{
DataGridTextBoxColumn __gc* col = __gc new DataGridTextBoxColumn;
col->TextBox->DoubleClick += __gc new EventHandler(this,GridDoubleClick);//绑定双击事件
col->NullText = S"";
this->_colStyles[i] = col;
}
else
this->_colStyles[i] = __gc new DataGridBoolColumn;
//设置列宽
this->_colStyles[i]->Width = item->Width;

//设置对其方式
this->_colStyles[i]->Alignment = item->DataAlign;

//设置数据映射名称
this->_colStyles[i]->MappingName = item->ColName;

//确定只读方式
this->_colStyles[i]->ReadOnly = item->ReadOnly;

}
}

//创建表格的底部
void CreateGridBottom()

...{
this->_pBottom = __gc new Panel;
FxGridColumn __gc* item;
FxGridBottomItem __gc* bottomItem;

for (int i = 0; i < this->_cols->Count; i++)

...{
item = static_cast<FxGridColumn __gc*>(this->_cols->Item[i]);
bottomItem = __gc new FxGridBottomItem;
this->_pBottom->Controls->Add(bottomItem);
bottomItem->Top = 0;
bottomItem->Left = item->Left;
bottomItem->Width = item->Width;
bottomItem->Action = item->Action;
bottomItem->Height = 20;
bottomItem->BorderStyle = BorderStyle::FixedSingle;
bottomItem->ColumnName = item->ColName;
}
this->_pBottom->Height = 20;
this->_pBottom->Width = this->_pHead->Width;

}

//创建表格
void CreateGrid()

...{
this->_theGrid = __gc new DataGrid;
this->_theGrid->CaptionVisible = false;
this->_theGrid->BackgroundColor = System::Drawing::Color::White;
this->_theGrid->RowHeaderWidth=0;
this->_theGrid->ColumnHeadersVisible=false;

this->_tbStyle = __gc new DataGridTableStyle;
this->_tbStyle->RowHeadersVisible = false;
this->_tbStyle->RowHeaderWidth = 0;
this->_tbStyle->PreferredRowHeight = 20;

//添加列定义
for (int i = 0; i < this->_colStyles->Count; i++ )

...{
DataGridColumnStyle __gc* item = static_cast<DataGridColumnStyle __gc*>(this->_colStyles->Item[i]);
this->_tbStyle->GridColumnStyles->Add(item);
}
this->_theGrid->TableStyles->Add(this->_tbStyle);

//添加事件
this->_theGrid->CurrentCellChanged += __gc new EventHandler(this, CurrentCellChangedHandler);

}

//创建横向滚动条
void CreateHScroll()

...{
this->_hScroll = __gc new HScrollBar;
this->Controls->Add(this->_hScroll);
this->_hScroll->Visible = false;
this->_hScroll->Height = 20;//高度为20
}

//创建纵向滚动条
void CreateVScroll()

...{
this->_vScroll = __gc new VScrollBar;
this->Controls->Add(this->_vScroll);
this->_vScroll->Visible = false;
this->_vScroll->Width = 20;
this->_vScroll->BringToFront();
}



//创建表格容器
void CreateGridContainer()

...{
this->_theGridContainer = __gc new Panel;
this->Controls->Add(this->_theGridContainer);
this->_theGridContainer->Location = System::Drawing::Point(0,0);

this->_theGridContainer->BorderStyle = BorderStyle::FixedSingle;
this->_theGridContainer->Width = this->Width - this->_vScroll->Width;
this->_theGridContainer->Top = this->_pHead->Height;
this->_theGridContainer->Height = this->Height - this->_pHead->Height - this->_hScroll->Height;
this->_theGridContainer->Controls->Add(this->_theGrid);
this->_theGrid->Location = System::Drawing::Point(0,0);
//调整滚动条的位置
this->_vScroll->Left = this->_theGridContainer->Width;
this->_vScroll->Top = 0;//this->_theGridContainer->Top;
this->_vScroll->Height = this->_theGridContainer->Height + this->_pHead->Height;
this->_vScroll->Visible = true;
this->_hScroll->Left = 0;
this->_hScroll->Width = this->_theGridContainer->Width;
this->_hScroll->Top = this->_theGridContainer->Top + this->_theGridContainer->Height;
this->_hScroll->Visible=true;

//调整Grid的位置
this->_theGrid->Width = this->_pHead->Width + this->_theGrid->RowHeaderWidth + 2;
this->_theGrid->Width += this->_cols->Count;
this->_theGrid->Height = this->_theGridContainer->Height - this->_pBottom->Height;

//调整Head的位置,加上RowHeaderWidth
this->_pHead->Left = this->_theGrid->RowHeaderWidth+2;

//显示表格的底部
this->_theGridContainer->Controls->Add(this->_pBottom);
this->_pBottom->Left = this->_pHead->Left;
this->_pBottom->Top = this->_theGrid->Height ;

//横向滚动条的相关设置
this->_leftStart = this->_pHead->Left;
this->_leftMost -= (this->_pHead->Width - this->_theGridContainer->Width + this->_pHead->Left);
this->_hScroll->Maximum = this->_leftStart - this->_leftMost;
this->_hScroll->SmallChange = 5;
this->_hScroll->Scroll += __gc new ScrollEventHandler(this,HScrollEvent);

//纵向滚动条相关设置
this->_vScroll->Maximum = 0;
this->_vScroll->Scroll += __gc new ScrollEventHandler(this,VScrollEvent);
}

//横向滚动条滚动事件
void HScrollEvent(
Object __gc* sender,
ScrollEventArgs __gc* e

)...{
int newleft = 0;//新位置
newleft = this->_leftStart - e->NewValue;
if (newleft > this->_leftMost)

...{
this->_pHead->Left = newleft;
this->_pBottom->Left = newleft;
this->_theGrid->Left = newleft - this->_theGrid->RowHeaderWidth -2;
}
else

...{
this->_pHead->Left = this->_leftMost;
this->_pBottom->Left = this->_leftMost;
this->_theGrid->Left = this->_leftMost - this->_theGrid->RowHeaderWidth -2;
}
}

//纵向滚动条滚动事件
void VScrollEvent(
Object __gc* sender,
ScrollEventArgs __gc* e

)...{
int newTop = 0;//新位置
newTop = this->_topStart - e->NewValue;
if (newTop > this->_topMost)

...{
this->_theGrid->Top = newTop;
this->_pBottom->Top = newTop + this->_theGrid->Height;
}
else

...{
this->_theGrid->Top = this->_topMost;
this->_pBottom->Top = this->_topMost + this->_theGrid->Height;
}
}

//更新数据

void UpdateGrid(int rows/**//*数据数量*/)

...{
//计算数据行的高度
if (rows < 1)
return;

int height = 0;//当前加载数据后的实际高度
int viewHeight = 0;//表格容器的高度

height = (rows + 4) * this->_tbStyle->PreferredRowHeight ;//数据的实际高度

viewHeight = this->_theGridContainer->Height - this->_pBottom->Height;

if (height > viewHeight)//需要显示滚动条

...{
this->_theGrid->Height = height;
this->_topStart = 0;
this->_topMost = viewHeight - height;
this->_pBottom->Top = height;
this->_vScroll->Maximum = this->_topStart - this->_topMost + this->_pBottom->Height;
this->_vScroll->SmallChange = this->_tbStyle->PreferredRowHeight;
}
else//不需要显示滚动条

...{
this->_topMost = 0;
this->_topStart = 0;
this->_vScroll->Maximum = 0;
this->_theGrid->Height = this->_theGridContainer->Height - this->_pBottom->Height;
}

//重新计算合计
this->CalSum();
}

//Cell的双击处理
void GridDoubleClick(
Object* sender,
EventArgs* e

)...{
if (this->_theGrid->CurrentRowIndex >= 0)
this->RowDoubleClick(this->_theGrid->CurrentRowIndex + 1);
}

//处理合计
void CalSum()

...{
if (this->_theGrid->DataSource == 0)
return;

if (this->_theGrid->DataSource->GetType()->Equals(__typeof(System::Data::DataTable)))//DataTable

...{
System::Data::DataTable __gc* data = static_cast<System::Data::DataTable __gc*>(this->_theGrid->DataSource);
FxGridBottomItem __gc* item;

//计算DataTable的合计
for (int i =0; i< this->_pBottom->Controls->Count;i++)

...{
item = static_cast<FxGridBottomItem __gc*>(this->_pBottom->Controls->Item[i]);
if (item->Action == FxForms::FxGridColumnAction::Sum)//合计计算

...{
item->Text = Convert::ToString(
FxSystem::FxData::DataTableCalculator::Sum(data,item->ColumnName)
);
break;
}
else if (item->Action == FxForms::FxGridColumnAction::Avg)//平均值计算

...{
item->Text = Convert::ToString(
FxSystem::FxData::DataTableCalculator::Avg(data,item->ColumnName)
);

}
else if(item->Action == FxForms::FxGridColumnAction::Count)//计算数量

...{
item->Text = Convert::ToString(
data->Rows->Count
);
}
}
}
else if (this->_theGrid->DataSource->GetType()->Equals(__typeof(System::Data::DataView)))//DataView

...{
System::Data::DataView __gc* data = static_cast<System::Data::DataView __gc*>(this->_theGrid->DataSource);
FxGridBottomItem __gc* item;
//计算DataView的合计
for (int i =0; i<this->_pBottom->Controls->Count;i++)

...{
item = static_cast<FxGridBottomItem __gc*>(this->_pBottom->Controls->Item[i]);
if (item->Action == FxForms::FxGridColumnAction::Sum)//合计计算

...{
item->Text = Convert::ToString(
FxData::DataViewCalculator::Sum(data,item->ColumnName)
);
}
else if (item->Action == FxForms::FxGridColumnAction::Avg)//平均值计算

...{
item->Text = Convert::ToString(
FxData::DataViewCalculator::Avg(data,item->ColumnName)
);

}
else if(item->Action == FxForms::FxGridColumnAction::Count)//计算数量

...{
item->Text = Convert::ToString(
data->Count
);
}
}
}
else

...{
throw __gc new Exception(S"无效的数据源");
}

}
//如果添加的控件不是FxGridColumn,DataGrid,Label,那么抛出异常
void ControlEventHandler(
Object* sender,
ControlEventArgs* e)

...{
if (!(e->Control->GetType()->ToString()->CompareTo(S"System.Windows.Forms.Label") == 0
|| e->Control->GetType()->ToString()->CompareTo(S"FxSystem.FxForms.FxGridColumn") == 0
|| e->Control->GetType()->ToString()->CompareTo(S"System.Windows.Forms.Panel") == 0
|| e->Control->GetType()->ToString()->CompareTo(S"System.Windows.Forms.HScrollBar") == 0
|| e->Control->GetType()->ToString()->CompareTo(S"System.Windows.Forms.VScrollBar") == 0
|| e->Control->GetType()->ToString()->CompareTo(S"FxSystem.FxForms.FxGridBottomItem") == 0 ))

...{
MessageBox::Show(e->Control->GetType()->ToString());
throw __gc new Exception(S"该控件只能允许使用Lable、FxGridColumn、Panel、FxGridBottomItem");
}
}

//处理当前单元格变化事件
void CurrentCellChangedHandler(Object __gc* sender, System::EventArgs __gc* args)

...{
if (this->_cellModifier == 0)
return;

if (this->_theGrid->DataSource->GetType()->Equals(__typeof(System::Data::DataTable)))

...{
//检查是否有效
if (this->_theGrid->CurrentRowIndex<0)
return;

int colIndex = this->_theGrid->CurrentCell.ColumnNumber;
int rowIndex = this->_theGrid->CurrentRowIndex;
System::Data::DataTable __gc* data= static_cast<System::Data::DataTable __gc*>(this->_theGrid->DataSource);
DataGridTextBoxColumn __gc* col = static_cast<DataGridTextBoxColumn __gc*>(this->_tbStyle->GridColumnStyles->Item[colIndex]);
this->_cellModifier->Invoke(data->Rows->Item[rowIndex],col->MappingName, col);

}
else if (this->_theGrid->DataSource->GetType()->Equals(__typeof(System::Data::DataView)))

...{
//检查是否有效
if (this->_theGrid->CurrentRowIndex<0)
return;

int colIndex = this->_theGrid->CurrentCell.ColumnNumber;
int rowIndex = this->_theGrid->CurrentRowIndex;
System::Data::DataView __gc* data= static_cast<System::Data::DataView __gc*>(this->_theGrid->DataSource);
DataGridTextBoxColumn __gc* col = static_cast<DataGridTextBoxColumn __gc*>(this->_tbStyle->GridColumnStyles->Item[colIndex]);
this->_cellModifier->Invoke(data->Item[rowIndex]->Row,col->MappingName, col);
}
else

...{
throw __gc new Exception(S"无效数据源");
}
}

//当数据变化时的处理
void DataRowChangeHandler(
Object __gc* sender,
DataRowChangeEventArgs __gc* e)

...{
//添加行
if (e->Action == DataRowAction::Add)

...{
this->UpdateGrid(e->Row->Table->Rows->Count);
return;
}

//删除行
if (e->Action == DataRowAction::Delete)

...{
this->UpdateGrid(e->Row->Table->Rows->Count);
return;
}

//当前行的数据被成功编辑
if (e->Action == DataRowAction::Change)

...{
this->CalSum();
return;
}
}
};
}
}
http://blog.youkuaiyun.com/firefox1/archive/2006/12/05/1431416.aspx