工作中需要用到DataGridView控件,就花了点时间学习了一下,记录下来方便将来查阅。
DataGridView是在原来的DataGrid控件的基础上增加了更多的特性,更方便用户的拓展和定制使用。在学习这个控件的过程中,我自己比较关注的是以下的一些功能:
1.“非绑定的方式填充数据”:这种方式一般用于数据比较少且数据仅用于显示,在数据量比较大或者需要保存用户的修改和输入的情景,这种方式就不是很实用,但作为一种基本的填充数据的方式,还是需要来了解一下,示例的代码如下:
class TestClass
{
public enum Gender
{
Male=0,
Female
}
private Gender m_gender = Gender.Male;
public Gender GetGender
{
get { return m_gender; }
set { m_gender = value; }
}
private int m_nValue = -1;
public int Value
{
get { return m_nValue; }
}
private String m_nName = "Default";
public String Name
{
get { return m_nName; }
}
private int m_nIndex = 1;
public int Index
{
get { return m_nIndex; }
set { m_nIndex = value; }
}
private AnotherClass m_nClass = new AnotherClass();
public AnotherClass ClassValue
{
get { return m_nClass; }
}
}
class AnotherClass
{
public AnotherClass()
{
Random random = new Random();
int nRandom = random.Next(1000);
for (int i = 0; i < nRandom; i++)
{
m_nValueList.Add(i);
}
}
private List<int> m_nValueList = new List<int>();
public int TotalValue
{
get
{
int nTotal=0;
for (int i = 0; i < m_nValueList.Count; i++)
nTotal += m_nValueList[i];
return nTotal;
}
}
}
List<TestClass> m_testClass = new List<TestClass>();
public Form1()
{
InitializeComponent();
//this.dataGridView1.Dock = DockStyle.Fill;
this.Controls.Add(this.dataGridView1);
//this.Load += new EventHandler(Form1_Load);
this.Text = "DataGridView calendar column demo";
int nSize=100;
for (int i = 0; i < nSize; i++)
{
TestClass tmpClass = new TestClass();
tmpClass.Index = i;
m_testClass.Add(tmpClass);
}
}
private void bindData()
{
int nCount = m_testClass.Count;
//dataGridView1.RowCount = nCount;
dataGridView1.ColumnCount = 5;
dataGridView1.Columns[0].Name = "Gender";
dataGridView1.Columns[1].Name = "Name";
dataGridView1.Columns[2].Name = "Value";
dataGridView1.Columns[3].Name = "Index";
dataGridView1.Columns[4].Name = "ClassValue";
for (int i = 0; i < nCount; i++)
{
DataGridViewRow row = new DataGridViewRow();
//Gender cell
DataGridViewComboBoxCell genderCell = new DataGridViewComboBoxCell();
Array genders = Enum.GetValues(typeof(TestClass.Gender));
foreach(object obj in genders)
genderCell.Items.Add(obj);
genderCell.Value = m_testClass[i].GetGender;
row.Cells.Add(genderCell);
//name cell
DataGridViewCell nameCell = new DataGridViewTextBoxCell();
nameCell.Value = m_testClass[i].Name;
row.Cells.Add(nameCell);
//value cell
DataGridViewCell valueCell = new DataGridViewTextBoxCell();
valueCell.Value = m_testClass[i].Value.ToString();
row.Cells.Add(valueCell);
//index cell
DataGridViewCell indexCell = new DataGridViewTextBoxCell();
indexCell.Value = m_testClass[i].Index.ToString();
row.Cells.Add(indexCell);
//classvalue cell
DataGridViewCell classValueCell = new DataGridViewTextBoxCell();
classValueCell.Value = m_testClass[i].ClassValue;
row.Cells.Add(classValueCell);
dataGridView1.Rows.Add(row);
}
}
private void Form1_Load(object sender, EventArgs e)
{
bindData();
}
在这个例子中,我定义了TestClass类,并在这个类中声明了几个属性,这些属性包括枚举类型、类类型、内置类型等,之所以这么设置,也是为了能够更全面地学习这个功能,在窗口加载的函数中,我为每个一个实例对象声明了一个行对象,并利用实例对象的值来填充该实例对象,然后把它显示在DataGridView控件上,值得一提的是,在这里我选用了DataGridViewComboBoxCell类型的单元格来显示枚举类型,查阅MSDN可以发现,框架中已经为我们实现了很多种单元类型,通常情况下我们可以很方便地利用这些类型来填充我们的数据,当然如果我们想用自己定义的控件,就得通过定制的方式来实现,这是后话,我们一会儿再说。另外,这种“非绑定的数据填充方式”在数据对DataGridView上的单元值进行了修改之后,控件并不会自动地把这些值反馈给内部对象(在这里就是m_testClass),这或许也是为什么MSDN建议我们仅在不需要修改内部数据的情况下才使用这种方式来填充数据的原因吧。
2. 绑定数据源的方式来填充数据:这种方式相比于第一种方式,最大的好处就是简化了数据填充和更新的操作,我们只需要利用DataGridView的DataSource属性就可以很方便地把一个数据源和一个DataGridView对象绑定在一起,并在发生修改时自动实现更新,二话不说,先上示例代码:
private void Form1_Load(object sender, EventArgs e)
{
//bindData();
this.dataGridView1.DataSource = bindingSource1;
bindingSource1.DataSource = m_testClass;
}
其它的地方和第一例相同。可以看到,在这里我们首先通过DataSource 属性将一个bindingSource1对象和控件联系起来,然后再把bindingSource1的数据源设置m_testClass,就这么简单地两句话,系统就会自动地把m_testClass中的公有属性填充到表格中,完成操作,当然MSDN中提醒我们,用于充当数据源的对象只有是一些实现了特殊接口的类的实例才行(IList, IBindingList, etc.) 当然这样自动填充的功能方便是方便,但有时候并非完全是我们需要的样子,比如说,m_testClass中的ClassValue属性是自定义的一个类,在自动填充的时候,就会把这个类的信息填充到表格中,在这里,我们假设我们并不希望填充这个信息,而是希望填充AnotherClass类的TotalValue这个属性,因此我们在单元格的格式化函数中对自动填充的过程进行修改,如下所示:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
if (dataGridView1.Columns[e.ColumnIndex].HeaderText == "ClassValue")
{
if (e.Value != null)
e.Value = (e.Value as AnotherClass).TotalValue;
}
}
通过这样的设置,我们就可以修改任意一列显示的数据类型及内容。当然,更进一步地,可能我们还注意到m_testClass中的GetGender属性是个枚举类型,如果可以,我们当然希望利用ComboBox这个控件来显示这个属性值,于是我们进一步修改代码成下面这个样子:
private void Form1_Load(object sender, EventArgs e)
{
//bindData();
this.dataGridView1.DataSource = bindingSource1;
bindingSource1.DataSource = m_testClass;
//首先我们先移除系统自动生成的列
dataGridView1.AutoGenerateColumns = true;
dataGridView1.Columns.Remove("GetGender");
//dataGridView1.Columns["GetGender"].Visible = false;
//定义我们希望的控件类型
DataGridViewComboBoxColumn column = new DataGridViewComboBoxColumn();
column.DataSource = Enum.GetValues(typeof(TestClass.Gender)); //这里设置该控件的数据源,枚举类型的所有值
column.DataPropertyName = "GetGender";//这里设置该控件的值所关联的对象,表示我们希望这个控件的值和“GetGender”这个属性关联
column.Name = "Gender";
dataGridView1.Columns.Add(column);
//connect combo box to mygender
this.comboBox1.DataSource = Enum.GetValues(typeof(TestClass.Gender));
this.comboBox1.SelectedIndex = 0;
}
在这里我们首先移除系统为我们自动填充的列对象,然后手动创建了ComboBox的列实例,并通过设置其数据源和关系的对象完成控件的定制操作。这里值得注意的是DataPropertyName 属性的设置,通常我们通过DataSource来设置整个控件全部的数据源,它可以是表,可以是list等等,但有时候我们希望控件中的某一列和表中的某一个列进行关联,这时候就可以通过DataPropertyName进行设置,如果说DataSource是全局性的数据源定义,DataPropertyName就可以认为是更精细的控制。
在这里我们只是简单地介绍了如何手动和自动地实现DataGridView控件中数据的填充,用的也都是内置的控件类型,有时候我们拓展了某种控件类型,并希望在表格中能够使用它,这时候就需要更复杂的方式来实现,这部分内容会在下篇文章中有详细的记录。
参考自:http://msdn.microsoft.com/zh-cn/library/k39d6s23%28v=vs.110%29.aspx