DataGrid的一些使用技巧

有時候聽有些朋友抱怨.NET的DataGrid不是很好用。就我個人的體會,DataGrid的功能非常強大,可以使我們隨心所欲的完成各種各樣的工作,可惜就是實現起來不夠簡單明瞭。我對平時經常碰到的一些問題積累了一些解決的方法,現在把它們總結一下供大家參考。

比較經常碰到的一個問題是:我們希望DataGrid的某一列只能輸入特定的文本,比如:不能輸入數字。下面的例子說明如何實現這種功能。
新建一個Window應用程序,加入一個DataGrid和SqlConnection,連接SQL數據庫NorthWind。

namespace WindowsApplication1
{
public class Form1 : System.Windows.Forms.Form
{
private myDataGrid dataGrid1;
private System.Data.SqlClient.SqlConnection sqlConnection1;
//加入全局變量oldValue,用它表示單元格原來的文本。
private string oldValue;

private void Form1_Load(object sender, System.EventArgs e)
{
oldValue="";
SqlDataAdapter sda=new SqlDataAdapter("select LastName,FirstName from employees",this.sqlConnection1);
DataSet ds=new DataSet();
sda.Fill(ds,"employees");
DataGridTableStyle ats=new DataGridTableStyle();
ats.MappingName="employees";
DataGridColorColumn dcs1=new DataGridColorColumn();
dcs1.HeaderText="lastname";
ats.GridColumnStyles.Add(dcs1);
DataGridTextBoxColumn dcs2=new DataGridTextBoxColumn();
dcs2.HeaderText="firstname";
dcs2.MappingName="FirstName";
dcs2.TextBox.TextChanged+=new EventHandler(DataGridTextChanged);
dcs2.TextBox.Enter+=new EventHandler(DataGridTextBox_Enter);
ats.GridColumnStyles.Add(dcs2);
this.dataGrid1.TableStyles.Add(ats);
this.dataGrid1.DataSource=ds;
this.dataGrid1.DataMember="employees";
}


private void DataGridTextBox_Enter(object sender,EventArgs e)
{
//當某一單元格獲得焦點時,記錄單元格的文本
oldValue=((DataGridTextBoxColumn) this.dataGrid1.TableStyles[0].GridColumnStyles[1]).TextBox.Text;
}


private void DataGridTextChanged(object sender,EventArgs e)
{
int index=0;
string str=((DataGridTextBoxColumn)this.dataGrid1.TableStyles[0].GridColumnStyles[1]).TextBox.Text;
//當單元格的文本改變時,檢驗是否有非法字符
while(index<str.Length)
{
//如果發現數字,顯示錯誤信息並將單元格還原爲原內容
if (Char.IsDigit(str,index))
{
MessageBox.Show("不能輸入數字,請重新輸入");
((DataGridTextBoxColumn)this.dataGrid1.TableStyles[0].GridColumnStyles[1]).TextBox.Text=oldValue;
return;
}
index++;
}
}
}

------------如何實現多行表頭

有時候聽有些朋友抱怨.NET的DataGrid不是很好用。就我個人的體會,DataGrid的功能非常強大,可以使我們隨心所欲的完成各種各樣的工作,可惜就是實現起來不夠簡單明瞭。我對平時經常碰到的一些問題積累了一些解決的方法,現在把它們總結一下供大家參考。
比較經常碰到的一個問題是:我們希望DataGrid的表頭是多行的(圖1)。我在網上找了很久也找不到解決的方法,後來想到了DataGrid的CaptionText和CaptionFont屬性。於是我就想能不能在Caption的顯示區域畫出多行表頭。下面的示例代碼實現了這個想法,結果如圖1所示。


首先需要編寫一個類來表示自畫的表頭,這個類將記錄表頭的顯示文本、圖標和屬於它管轄的列的信息。

//表頭類
public class TopHeaderColumn
{
public TopHeaderColumn()
{
this.columnCollection=new ArrayList();
}
private string caption;
//表頭的顯示文本
public string Caption
{
get {return caption;}
set {caption=value;}
}
private ArrayList columnCollection;
//用來記錄屬於表頭管轄的各列的信息(通過往集合裏添加object)
public ArrayList ColumnCollection
{
get {return this.columnCollection;}
set {this.columnCollection=value;}
}
private int width;
//表頭的寬度
public int Width
{
get {return width;}
set {width=value;}
}
private Image image=null;
//表頭的圖標
public Image Image
{
get {return image;}
set {image=value;}
}

}
另外,因爲以後的代碼需要DataGrid水平滾動條的位置,而DataGrid無法取得水平滾動條的位置,所以需要對DataGrid做一點修改。
public class myDataGrid:DataGrid
{

public ScrollBar HScrollBar
{
get {return this.HorizScrollBar;}
}
}

好了,可以工作了。新建一個Window應用程序,加入一個myDataGrid、SqlConnection和ImageList,連接SQL數據庫NorthWind。當然,還得加入上面的代碼

namespace WindowsApplication1
{
public class Form1 : System.Windows.Forms.Form
{
private System.Data.SqlClient.SqlConnection sqlConnection1;
private myDataGrid dataGrid1;
private ArrayList al;
private System.Windows.Forms.ImageList imageList1;
//在InitializeComponent()里加入這一句:this.dataGrid1 = new myDataGrid(),並根據你的需要設置其他的DataGrid屬性。注意,CaptionVisible必須設爲true,CaptionText=""。
private void Form1_Load(object sender, System.EventArgs e)
{
SqlDataAdapter da=new SqlDataAdapter("select lastname, firstname, Address,City from employees",this.sqlConnection1);
DataSet ds=new DataSet();
da.Fill(ds,"employees");
this.dataGrid1.DataSource=ds;
this.dataGrid1.DataMember="employees";

//設置DataGrid的各列
DataGridTextBoxColumn c1=new DataGridTextBoxColumn();
DataGridTextBoxColumn c2=new DataGridTextBoxColumn();
DataGridTextBoxColumn c3=new DataGridTextBoxColumn();
DataGridTextBoxColumn c4=new DataGridTextBoxColumn();
c1.MappingName="lastname";
c2.MappingName="firstname";
c3.MappingName="Address";
c4.MappingName="City";
c1.HeaderText="lastname";
c2.HeaderText="firstname";
c3.HeaderText="Address";
c4.HeaderText="City";
c1.WidthChanged+=new EventHandler(this.abc);//列的寬變動時調整表頭寬度
c2.WidthChanged+=new EventHandler(this.abc);
c3.WidthChanged+=new EventHandler(this.abc);
c4.WidthChanged+=new EventHandler(this.abc);

DataGridTableStyle dts=new DataGridTableStyle();
dts.GridColumnStyles.Add(c1);
dts.GridColumnStyles.Add(c2);
dts.GridColumnStyles.Add(c3);
dts.GridColumnStyles.Add(c4);

dts.MappingName="employees"; this.dataGrid1.TableStyles.Add(dts);

//建立自畫的表頭類並將它們加入集合al
al=new ArrayList();
TopHeaderColumn tc1=new TopHeaderColumn();
tc1.Caption="Name";
tc1.Image=this.imageList1.Images[0];
tc1.ColumnCollection.Add(0);//記錄它管轄的列的index
tc1.ColumnCollection.Add(1);
tc1.Width=c1.Width+c2.Width;

TopHeaderColumn tc2=new TopHeaderColumn();
tc2.Caption="Address";
tc2.ColumnCollection.Add(2);
tc2.ColumnCollection.Add(3);
tc2.Width=c3.Width+c4.Width;

al.Add(tc1);
al.Add(tc2);
this.dataGrid1.Paint += new System.Windows.Forms.PaintEventHandler(this.dataGrid1_Paint);
}

private void dataGrid1_Paint(object sender, System.Windows.Forms. PaintEventArgs e)
{
int x=0;
//計算出第一個表頭的左邊距
int left=this.dataGrid1.TableStyles[0].RowHeaderWidth-this.dataGrid1.HScrollBar.Value;
//遍歷表頭集合al,畫出表頭
foreach (object o in this.al)
{
//計算出表頭文字(文字居中)的左邊距
x=left+(((TopHeaderColumn)o).Width-Convert.ToInt32(e.Graphics. MeasureString (((TopHeaderColumn)o).Caption, this.dataGrid1.CaptionFont). Width))/2;
//完成表頭繪製
if (((TopHeaderColumn)o).Image!=null)
e.Graphics.DrawImage(((TopHeaderColumn)o).Image,x-imageList1.Images[0].Size.Width,0);

e.Graphics.DrawString(((TopHeaderColumn)o).Caption,this.dataGrid1. CaptionFont,new SolidBrush(this.dataGrid1.CaptionForeColor),x,0);
if (x>0)
e.Graphics.DrawLine(new Pen(Color.Black,2),left-1,0,left-1,this.dataGrid1.Height);
//計算出下一個表頭的左邊距
left+=((TopHeaderColumn)o).Width;
}
if (x<this.dataGrid1.Width)
e.Graphics.DrawLine(new Pen(Color.Black,2),left-1,0,left-1,this.dataGrid1.Height);
}

private void abc(object sender,EventArgs e)
{
//設置表頭的寬度
foreach (object o in this.al)
{
((TopHeaderColumn)o).Width=0;
foreach (int i in ((TopHeaderColumn)o).ColumnCollection)
{ ((TopHeaderColumn)o).Width+=this.dataGrid1.TableStyles[0].GridColumnStyles[i].Width;
}
}
}

private void dataGrid1_Scroll(object sender, System.EventArgs e)
{
this.dataGrid1.Refresh();
}

上面的代碼實現了兩層表頭,至於三層表頭也同理。

關於如何實現DataGrid多層表頭,許多網友都提到,目前好像沒有一種特別好的方便的方法。

--------如何實現下拉列表

有時候聽有些朋友抱怨.NET的DataGrid不是很好用。就我個人的體會,DataGrid的功能非常強大,可以使我們隨心所欲的完成各種各樣的工作,可惜就是實現起來不夠簡單明瞭。我對平時經常碰到的一些問題積累了一些解決的方法,現在把它們總結一下供大家參考。
比較經常碰到的一個問題是:在編輯單元格內容時我們希望出現這樣的下拉列表,如圖1所示:


圖1

思路:
1 寫一個類comboForm表示下拉列表,類包含兩個成員:Form窗體和DataGrid組件。
2 寫一個類NoKeyUpComboBox(繼承ComboBox),目的是屏蔽WM_KEYUP消息,避免在按Tab鍵時出現問題。
3 寫一個繼承於DataGridTextBoxColumn的類,命名爲DataGridComboFormColumn。在類中加入一個ComboBox和一個comboForm,類實現下面幾個功能:
a 編輯單元格內容時顯示組件NoKeyUpComboBox;
b ComboBox下拉時顯示下拉列表comboForm;
c 鼠標點擊下拉列表時,隱藏comboForm並將用戶選定的內容寫入單元格(當然,你也可以設置其他隱藏下拉列表的操作,比如按回車鍵);
d 下拉列表comboForm不具有焦點時隱藏。

代碼:
//comboForm類:
public class comboForm:Form
{
private DataGrid dataGrid;
public DataGrid DataGrid
{
get {return dataGrid;}
set {dataGrid=value;}
}
public comboForm()
{
this.FormBorderStyle=FormBorderStyle.None;
this.StartPosition=FormStartPosition.Manual;
dataGrid=new DataGrid();
this.Controls.Add(dataGrid);
dataGrid.Dock=DockStyle.Fill;
dataGrid.CaptionVisible=false;
}
}

//NoKeyUpComboBox類:
public class NoKeyUpComboBox:ComboBox
{
const int WM_KEYUP=0x101;
protected override void WndProc(ref Message msg)
{
if (msg.Msg==WM_KEYUP)
return;
base.WndProc(ref msg);
}
}

//DataGridComboFormColumn類:
public class DataGridComboFormColumn:DataGridTextBoxColumn
{
private NoKeyUpComboBox comboBox;
private CurrencyManager _source;
private int rowNum;
private comboForm frm;
public comboForm Frm
{
get {return frm;}
}
//我們將使用Index屬性表示單元格內容與下拉列表的第Index列的內容相聯繫
private int index;
public int Index
{
get {return index;}
set {index=value;}
}

public DataGridComboFormColumn()
{
frm=new comboForm();
comboBox=new NoKeyUpComboBox();
frm.Deactivate+=new EventHandler(frm_deactive);
frm.DataGrid.Click+=new EventHandler(grid_click);
this.comboBox.DropDown+=new EventHandler(comboBox_dropDown);
this.comboBox.Leave+=new EventHandler(comboBox_leave);
}
//comboBox不具有焦點時隱藏
private void comboBox_leave(object sender,EventArgs e)
{
comboBox.Visible=false;
}
//下拉列表--Frm不具有焦點時隱藏
private void frm_deactive(object sender,EventArgs e)
{
frm.Hide();
comboBox.Visible=false;
}
//comboBox下拉時顯示下拉列表--Frm
private void comboBox_dropDown(object sender,EventArgs e)
{
//在這裏您還可以根據下拉列表的長與寬是否超出屏幕設置下拉列表的位置
frm.Left=comboBox.PointToScreen(new Point(0,comboBox.Height)).X;
frm.Top=comboBox.PointToScreen(new Point(0,comboBox.Height)).Y;
frm.Show();
frm.BringToFront();
}
//點擊下拉列表的DataGrid時,將選中的內容寫入單元格並隱藏下拉列表--Frm
private void grid_click(object sender,EventArgs e)
{
BindingManagerBase cm=frm.BindingContext[Frm.DataGrid.DataSource, frm.DataGrid.DataMember];
comboBox.Text=((DataRowView)cm.Current)[index].ToString();
this.TextBox.Text=((DataRowView)cm.Current)[index].ToString();
frm.Hide();
comboBox.Visible=false;
this.SetColumnValueAtRow(_source,rowNum,this.TextBox.Text);
}
//重載Edit方法,使用comboBox代替TextBox
protected override void Edit(CurrencyManager dataSource,int rowNum,Rectangle bounds,bool readOnly,string instanttext,bool cellVisible)
{
base.Edit(dataSource,rowNum,bounds,readOnly,instanttext,cellVisible);
comboBox.Parent=this.TextBox.Parent;
comboBox.Left=this.TextBox.Left-2;
comboBox.Top=this.TextBox.Top-2;
comboBox.Size=new Size(this.TextBox.Width,this.comboBox.Height);
comboBox.Text=this.TextBox.Text;

this.TextBox.Visible=false;
comboBox.Visible=true;
comboBox.BringToFront();
comboBox.Focus();
_source=dataSource;
this.rowNum=rowNum;
}
}

下面的例子說明了如何使用DataGridComboFrom類:
新建一個Windows 應用程序,加入SqlConnection,連接SQL數據庫Northwind,加入下面代碼。

private void Form1_Load(object sender, System.EventArgs e)
{
SqlDataAdapter da=new SqlDataAdapter("select ProductName from Products",this.sqlConnection1);
DataSet ds=new DataSet();
da.Fill(ds,"products");
DataSet ds_Combo=new DataSet();
da.SelectCommand=new SqlCommand("select ProductName,QuantityPerUnit,UnitPrice from Products",this.sqlConnection1);
da.Fill(ds_Combo,"products");

DataGridTableStyle dts=new DataGridTableStyle();
dts.MappingName="products";
myDataGridColumn col=new myDataGridColumn();
col.MappingName="ProductName";
col.Width=100;
col.Index=0;
col.HeaderText="ProductName";

col.Frm.DataGrid.DataSource=ds_Combo;//設置下拉列表的數據源
col.Frm.DataGrid.DataMember="products";
dts.GridColumnStyles.Add(col);

this.dataGrid1.TableStyles.Add(dts);
this.dataGrid1.SetDataBinding(ds,"products");
}

------------如何在DataGrid中顯示來自不同DataTable的數據

有時候聽有些朋友抱怨.NET的DataGrid不是很好用。就我個人的體會,DataGrid的功能非常強大,可以使我們隨心所欲的完成各種各樣的工作,可惜就是實現起來不夠簡單明瞭。我對平時經常碰到的一些問題積累了一些解決的方法,現在把它們總結一下供大家參考。
比較經常碰到的一個問題是:如何將來自不同DataTable的字段組合顯示在DataGrid中?當然,可以將數據合併到一個DataTable中,但有時候由於其他限制不能將數據合併,下面提供了一種在DataGrid中顯示來自不同DataTable(同在一個DataSet中)的數據的方法。

數據在DataSet中的結構


我們希望數據在DataGrid中如下圖顯示


下面用一個例子說明如何實現將來自不同DataTable的數據顯示在DataGrid中。

基本思路:很顯然,DataGrid不能同時綁定兩個DataTable,那麼,要使它顯示兩個不同DataTable的數據,就只有在DataGridColumn中下功夫了。以上面的結構爲例,我們建立一個繼承DataGridTextBoxColumn的類DataGridJoinColumn,重載GetColumnValueAtRow方法。這個類要實現的功能是:數據源是DataTable1,返回值是DataTable1.EmployeeID對應的DataTable2.LastName或DataTable2.FirstName。DataTable1.EmployeeID與DataTable2.EmployeeID是1對多聯繫,我們將這個聯繫添加到DataSet中,就可以通過GetParentRow(relation)方法取得DataTable1.EmployeeID對應的DataTable2.LastName和DataTable2.FirstName的值。具體代碼如下:

public class JoinTextBoxColumn : DataGridTextBoxColumn
{
string relation; //表示DataTable1.EmployeeID與DataTable2.EmployeeID的1對多聯繫
DataColumn col; //表示返回DataTable2的列
public JoinTextBoxColumn(string relation,DataColumn col )
{
this.relation=relation;
this.col=col;
base.ReadOnly=true;
}
protected override object GetColumnValueAtRow(CurrencyManager cm,int RowNum)
{
try
{
DataRow dr=((DataView)cm.List)[RowNum].Row;
DataRow parentdr=dr.GetParentRow(relation);
return parentdr[col]; //返回值是DataTable1.EmployeeID對應的LastName或FirstName
}
catch
{
return ""; //避免在添加新行時發生異常
}
}
protected override bool Commit(CurrencyManager cm,int RowNum)
{
return false;
}
public new bool ReadOnly
{
get {return true;}
}
}

下面的代碼說明了如何使用類DataGridJoinColumn。新建一個Windows程序,加入一個DataGrid和一個SqlConnection,連接數據庫NorthWind。 在Form_Load中加入下面代碼:

private void Form1_Load(object sender, System.EventArgs e)
{
SqlDataAdapter sda1=new SqlDataAdapter("select EmployeeID,LastName,FirstName from Employees",this.sqlConn);
SqlDataAdapter sda3=new SqlDataAdapter("select OrderID,EmployeeID,OrderDate,RequiredDate from Orders",this.sqlConn);

ds=new DataSet();
sda1.Fill(ds,"emp");
sda3.Fill(ds,"ord");

ds.Relations.Add("ord_emp",ds.Tables["emp"].Columns["EmployeeID"],ds.Tables["ord"].Columns["EmployeeID"]);

ds.Tables["ord"].Columns.Add("lastName",typeof(string));
ds.Tables["ord"].Columns.Add("firstName",typeof(string));

DataGridTableStyle dt=new DataGridTableStyle();
DataGridColumnStyle dc;

dc=new DataGridTextBoxColumn();
dc.MappingName="OrderID";
dc.HeaderText="OrderID";
dt.GridColumnStyles.Add(dc);

dc=new DataGridTextBoxColumn();
dc.MappingName="EmployeeID";
dc.HeaderText="EmployeeID";
dt.GridColumnStyles.Add(dc);

dc=new JoinTextBoxColumn("ord_emp",ds.Tables["emp"].Columns["firstName"]);
dc.MappingName="firstName";
dc.HeaderText="firstName";
dt.GridColumnStyles.Add(dc);

dc=new JoinTextBoxColumn("ord_emp",ds.Tables["emp"].Columns["lastName"]);
dc.MappingName="lastName";
dc.HeaderText="lastName";
dt.GridColumnStyles.Add(dc);

dc=new DataGridTextBoxColumn();
dc.MappingName="OrderDate";
dc.HeaderText="OrderDate";
dt.GridColumnStyles.Add(dc);

dc=new DataGridTextBoxColumn();
dc.MappingName="RequiredDate";
dc.HeaderText="RequiredDate";
dt.GridColumnStyles.Add(dc);

dt.MappingName="ord";
this.dataGrid1.TableStyles.Add(dt);

this.dataGrid1.DataSource=ds;
this.dataGrid1.DataMember="ord";
}

引用:http://www.newasp.net/tech/net/11139.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章