Windows Forms数据控件和数据绑定常见问题

文章内容来自:http://social.microsoft.com/Forums/zh-CN/2212/thread/bffbfe12-ecd3-4854-956c-f02a0ec696c3

 

这些材料没有覆盖所有常见问题,它只是尽量涉及到在windows Forms数据控件和数据绑定论坛上经常问到的功能。

  

1. 如何限制用户将焦点设置到特定的单元格上?

2. 如何在单元格中显示竖排文字?

3. 如何改变默认的排序模式?

4. 可以动态的改变DataGridViewCell的类型吗?

5. 如何让DataGridViewComboBoxColumn在没有编辑的状态下显示为TextBox列?

6. 如何实现列标题的多层显示?

7. 如何在一个单元格内显示多个控件?

8. 如何在DataGridView中实现归类的数据的显示?

9. 如何在DataGridView中使按下Enter键达到与按下Tab键一样的效果?

10.如何实现在文本列中只显示数字的输入?

11.如何使DataGridViewCell中的‘/t’控制符起作用?

12.如何让DataGridViewTextBoxCell在编辑模式下接受Enter键和Tab键的输入?

13.如何在DataGridView的全部区域内显示网格线?

14.如何禁止编辑特定的单元格?

15.如何禁用一个单元格?

16.如何使所有的单元格无论是否处于编辑状态都显示控件?

17.如何处理DataGridViewComboBoxCellSelectedIndexChanged事件?

18.如何在一个单元格中同时显示图标和文字?

19.如何隐藏一列?

20.如何阻止用户对一列排序?

21.如何实现多重排序?

22.如何实现行的拖拽和拖放来重新排序行?

23.如何让最后一列足够宽以覆盖余下的DataGridView的工作区?

24.如何让单元格中的文字转行?

25.如何让image列不显示任何图片?

26.如何让DataGridViewComboBoxCell可以编辑?

27.如何实现根据DataGridViewComboBoxColumn列中单元格所选的值,在同一行的另一个DataGridViewComboBoxColumn列中单元格显示该值所对应的子集?

28.如何在用户编辑单元格时显示错误图标?

29.如何同时显示绑定和非绑定数据?

30.如何在同一个DataGridView中显示两张表上的数据?

31.如何显示主从信息?

32.如何在同一个DataGridView中显示主从信息?

33.如何禁止列排序?

34.如何在点击toolstrip button时将数据提交到数据源?

35.如何当用户试图删除一行时显示再次确认的对话框?

 

1. 如何限制用户将焦点设置到特定的单元格上?

 

 DataGridView 默认的定位模式没有禁止用户将焦点设定到特定的单元格上的功能。我们可以通过重写恰当的键盘、导航和鼠标的方法,比如DataGridView.OnKeyDown, DataGridView.ProcessDataGridViewKey, DataGridView.SetCurrentCellAddressCore, DataGridView.SetSelectedCellCore, DataGridView.OnMouseDown 来实现所需的定位逻辑。

 

例如,假设我们需要禁止用户将焦点设在第二列,我们可以从DataGridView 类派生一个类,并重写该类的SetCurrentCellAddressCoreSetSelectedCellCore 方法来实现我们所需要的定位逻辑。详见以下示例:

 

代码:

Public class myDataGridView : DataGridView

{

    private int columnToSkip = -1;

 

    public int ColumnToSkip

    {

        get { return columnToSkip; }

        set { columnToSkip = value ; }

    }

 

    protected override bool SetCurrentCellAddressCore(int columnIndex, int rowIndex,

        bool setAnchorCellAddress, bool validateCurrentCell, bool throughMouseClick)

    {

        if (columnIndex == this .columnToSkip && this .columnToSkip != -1)

        {

            if (this .columnToSkip == this .ColumnCount - 1)

            {

                return base .SetCurrentCellAddressCore(0, rowIndex + 1,

                    setAnchorCellAddress, validateCurrentCell, throughMouseClick);

            }

            else

            {

                if (this .ColumnCount != 0)

                {

                     return base .SetCurrentCellAddressCore(columnIndex + 1, rowIndex,

                        setAnchorCellAddress, validateCurrentCell, throughMouseClick);

                }

            }

        }

        return base .SetCurrentCellAddressCore(columnIndex, rowIndex,

            setAnchorCellAddress, validateCurrentCell, throughMouseClick);

    }

 

    protected override void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected)

    {

        if (columnIndex == this .columnToSkip)

        {

             if (this .columnToSkip == this .ColumnCount - 1)

            {

                base .SetSelectedCellCore(0, rowIndex + 1, selected);

            }

            else

            {

                if (this .ColumnCount != 0)

                {

                     base .SetSelectedCellCore(columnIndex + 1, rowIndex, selected);

                }

            }

        }

        else

        {

            base .SetSelectedCellCore(columnIndex, rowIndex, selected);

        }

    }

}

 

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/f0a36d52-5b3f-4cf3-a18d-a2a849e34ad8/

 

2.如何在单元格中显示竖排文字?   [回到顶端]

 

我们可以在CellPainting事件中使用StringFormatFlag.DirectionVertical标记来绘制竖排文字。详见以下示例:

  

代码:

private void Form1_Load(object sender, EventArgs e)

{

    DataTable dt = new DataTable();

    dt.Columns.Add("c1");

    dt.Columns.Add("c2");

    for (int j = 0; j < 10; j++)

    {

        dt.Rows.Add("aaaaaaaaa", "bbbb");

    }

    this.dataGridView1.DataSource = dt;

    for (int j = 0; j < 10; j++)

    {

        int height = TextRenderer.MeasureText(

            this.dataGridView1[0, j].Value.ToString(),

            this.dataGridView1.DefaultCellStyle.Font).Width;

        this.dataGridView1.Rows[j].Height = height;

    }

    this.dataGridView1.CellPainting += new

         DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting); ;

}

 

void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

{

    if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.Value != null)

    {

        e.Paint(e.CellBounds, DataGridViewPaintParts.All

             & ~DataGridViewPaintParts.ContentForeground);

        StringFormat sf = new StringFormat();

        sf.Alignment = StringAlignment.Center;

        sf.LineAlignment = StringAlignment.Center;

        sf.FormatFlags = StringFormatFlags.DirectionVertical;

        e.Graphics.DrawString(e.Value.ToString(), e.CellStyle.Font,

            new SolidBrush(e.CellStyle.ForeColor), e.CellBounds, sf);

        e.Handled = true;

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdesigner/thread/df986ca1-b230-4018-ac2f-55fa1349fe36/

 

3.如何改变列的默认排序模式?  [回到顶端]

  

DataGridView默认的列排序模式为DataGridViewColumnSortMode.Automatic。在某些情况下,开发者希望改变这种默认的排序,但又不希望通过在整个列中循环来实现,例如这个帖子http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/9c45f3ed-695f-4254-b2a2-be58832de8d3/

  

我们可以从DataGridView类派生一个类并重写该类的OnColumnAdded方法来改变这种默认的模式。详见以下示例:

  

代码:

public class mydgv : DataGridView

{

    protected override void OnColumnAdded(DataGridViewColumnEventArgs e)

    {

        base.OnColumnAdded(e);

 

        e.Column.SortMode = DataGridViewColumnSortMode.NotSortable;

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/9c45f3ed-695f-4254-b2a2-be58832de8d3/

  

4.可以动态的改变DataGridViewCell的类型吗?  [回到顶端]

  

可以。例如这个帖子:http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/549ddfcf-f1ad-4f39-acf9-e168369ccc76/

开发者希望在保持ComboBox所选择的值的基础上动态的将DataGridViewComboBoxCell改变为DataGridViewTextBoxCell。单元格(cell)的类型实际上是可以被改变的,只需要重新创建一个所需要类型的单元格(cell),然后将新建的单元格(cell)赋给我们所需要改变的单元格(cell)。详见以下示例:

 

代码:

private void Form1_Load(object sender, EventArgs e)

{

    DataTable dt = new DataTable("b");

    dt.Columns.Add("col");

    dt.Rows.Add("bb1");

    dt.Rows.Add("bb2");

    dt.Rows.Add("bb3");

 

    this.dataGridView1.DataSource = dt;

 

    DataGridViewComboBoxColumn cmb = new DataGridViewComboBoxColumn();

    cmb.Items.Add("111");

    cmb.Items.Add("222");

    cmb.Items.Add("333");

    this.dataGridView1.Columns.Add(cmb);

}

 

private void button1_Click(object sender, EventArgs e)

{

    DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();

    cell.Value = "bb1";

    this.dataGridView1[1, 1] = cell;

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/549ddfcf-f1ad-4f39-acf9-e168369ccc76/

http://social.msdn.microsoft.com/forums/en-US/Vsexpressvcs/thread/b6af6eeb-d676-4592-b64d-9fe2fd66b3a0/

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/deabb9fb-16cb-4f73-be5c-ff67696bf792/

 

5.如何让DataGridViewComboBoxColumn在没有编辑的状态下显示为TextBox列?  [回到顶端]

  

我们可以通过将“DataGridViewComboBoxColumn.DisplayStyle”属性设置为“DataGridViewComboBoxDisplayStyle.Nothing”来实现。

 

 

6.如何实现列标题的多层显示?  [回到顶端]

  

像在这个帖子中问到的:http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/531577e8-0be3-406d-a81b-48f8ed02e8df/,开发者希望将DataGridView的列标题显示多层。如下图所示:

 

-------------------------------------------------------------

             |   January       |    February     |     March       |

             |  Win  | Loss   |  Win   | Loss   |  Win  | Loss   |

-------------------------------------------------------------

Team1   |         |          |           |         |          |         |

Team2   |         |          |           |         |          |         |

TeamN   |         |          |           |         |          |         |

-------------------------------------------------------------

  

要实现这种功能,我们需要通过处理DataGridView.PaintingDataGridView.CellPainting事件来绘制我们所希望的标题样式。详见以下示例:

  

代码:

private void Form1_Load(object sender, EventArgs e)

{

    this.dataGridView1.Columns.Add("JanWin", "Win");

    this.dataGridView1.Columns.Add("JanLoss", "Loss");

    this.dataGridView1.Columns.Add("FebWin", "Win");

    this.dataGridView1.Columns.Add("FebLoss", "Loss");

    this.dataGridView1.Columns.Add("MarWin", "Win");

    this.dataGridView1.Columns.Add("MarLoss", "Loss");

 

    for (int j = 0; j < this.dataGridView1.ColumnCount; j++)

    {

        this.dataGridView1.Columns[j].Width = 45;

    }

 

    this.dataGridView1.ColumnHeadersHeightSizeMode =

         DataGridViewColumnHeadersHeightSizeMode.EnableResizing;

 

    this.dataGridView1.ColumnHeadersHeight =

                this.dataGridView1.ColumnHeadersHeight * 2;

 

    this.dataGridView1.ColumnHeadersDefaultCellStyle.Alignment =

         DataGridViewContentAlignment.BottomCenter;

 

    this.dataGridView1.CellPainting += new

         DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting);

 

    this.dataGridView1.Paint += new PaintEventHandler(dataGridView1_Paint);

}

 

void dataGridView1_Paint(object sender, PaintEventArgs e)

{

    string[] monthes = { "January", "February", "March" };

 

    for (int j = 0; j < 6; )

    {

        //获取列标题单元格

        Rectangle r1 = this.dataGridView1.GetCellDisplayRectangle(j, -1, true);

 

        r1.X += 1;

        r1.Y += 1;

        r1.Width = r1.Width * 2 - 2;

        r1.Height = r1.Height / 2 - 2;

        e.Graphics.FillRectangle(new

           SolidBrush(this.dataGridView1.ColumnHeadersDefaultCellStyle.BackColor), r1);

        StringFormat format = new StringFormat();

        format.Alignment = StringAlignment.Center;

        format.LineAlignment = StringAlignment.Center;

        e.Graphics.DrawString(monthes[j / 2],

            this.dataGridView1.ColumnHeadersDefaultCellStyle.Font,

            new SolidBrush(this.dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor),

            r1,

            format);

        j += 2;

    }

 

}

 

void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

{

    if (e.RowIndex == -1 && e.ColumnIndex > -1)

    {

        e.PaintBackground(e.CellBounds, false);

 

        Rectangle r2 = e.CellBounds;

        r2.Y += e.CellBounds.Height / 2;

        r2.Height = e.CellBounds.Height / 2;

        e.PaintContent(r2);

        e.Handled = true;

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/531577e8-0be3-406d-a81b-48f8ed02e8df/

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/87004d70-482a-4b86-ba18-371670254b6a/

 

 

7.如何在一个单元格内显示多个控件?  [回到顶端]

  

在这个帖子中http://social.msdn.microsoft.com/forums/en-US/winformsdesigner/thread/c28399bb-9d50-4a1e-b671-3dbaebb5cc69/,开发者希望将Button控件和TextBox控件显示在一个单元格内。这种功能的实现可以通过创建一个承载TextBoxButtonUserControl,将UserControl添加到DataGridView的控件集合中,开始让它隐藏,然后通过处理CellBeginEdit事件在当前单元格内显示UserControl。我们也需要考虑通过处理Scroll事件来调整UserControl的位置让其正确显示,和处理CellEndEdit事件将单元格的值设置为从UserControl中获取的值。请点击帖子的链接来查看整个实例。

 

另一个相似的帖子,开发者希望在编辑单元格内容时显示一个下拉文本编辑器。

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/b55be13d-b77e-4af8-b03b-b684c7f758cd/

 

8.如何在DataGridView中实现归类的数据的显示?  [回到顶端]

 

如下图所示:

 

要实现这种功能的关键就是处理CellPainting事件来绘制出我们所需要样式的单元格。详见以下示例:

 

代码:

public class GroupByGrid : DataGridView

{

 

    protected override void OnCellFormatting(

       DataGridViewCellFormattingEventArgs args)

    {

        // 回调父类

        base.OnCellFormatting(args);

 

        // 始终显示第一行

        if (args.RowIndex == 0)

            return;

 

        if (IsRepeatedCellValue(args.RowIndex, args.ColumnIndex))

        {

            args.Value = string.Empty;

            args.FormattingApplied = true;

        }

    }

 

    private bool IsRepeatedCellValue(int rowIndex, int colIndex)

    {

        DataGridViewCell currCell =

           Rows[rowIndex].Cells[colIndex];

 

        DataGridViewCell prevCell =

           Rows[rowIndex - 1].Cells[colIndex];

 

        if ((currCell.Value == prevCell.Value) ||

           (currCell.Value != null && prevCell.Value != null &&

           currCell.Value.ToString() == prevCell.Value.ToString()))

        {

            return true;

        }

        else

        {

            return false;

        }

    }

 

    protected override void OnCellPainting(

       DataGridViewCellPaintingEventArgs args)

    {

        base.OnCellPainting(args);

 

        args.AdvancedBorderStyle.Bottom =

           DataGridViewAdvancedCellBorderStyle.None;

 

        // 忽略列标头和行标头以及第一行

        if (args.RowIndex < 1 || args.ColumnIndex < 0)

            return;

 

        if (IsRepeatedCellValue(args.RowIndex, args.ColumnIndex))

        {

            args.AdvancedBorderStyle.Top =

               DataGridViewAdvancedCellBorderStyle.None;

        }

        else

        {

            args.AdvancedBorderStyle.Top = AdvancedCellBorderStyle.Top;

        }

    }

}

 

 

9.如何在DataGridView中使按下Enter键达到与按下Tab键一样的效果?  [回到顶端]

 

要使按下Enter键达到与按下Tab键一样的效果,我们需要从DataGridView中派生出一个类,写一个自定义的DataGridView控件。这里有两个方面需要考虑。一方面,当DataGridView不处于编辑状态:在这种情况下,我们需要重写OnKeyDown事件来实现我们所需要的定位逻辑。另一方面,当DataGridView处于编辑的状态下:在这种情况下,Enter键是在ProcessDialogKey事件中被处理,因此我们需要重写该事件。详见以下示例:

  

代码:

class myDataGridView : DataGridView

{

    protected override bool ProcessDialogKey(Keys keyData)

    {

        if (keyData == Keys.Enter)

        {

            int col = this.CurrentCell.ColumnIndex;

            int row = this.CurrentCell.RowIndex;

            if (row != this.NewRowIndex)

            {

                if (col == (this.Columns.Count - 1))

                {

                    col = -1;

                    row++;

                }

                this.CurrentCell = this[col + 1, row];

            }

            return true;

        }

        return base.ProcessDialogKey(keyData);

    }

 

    protected override void OnKeyDown(KeyEventArgs e)

    {

        if (e.KeyData == Keys.Enter)

        {

            int col = this.CurrentCell.ColumnIndex;

            int row = this.CurrentCell.RowIndex;

            if (row != this.NewRowIndex)

            {

                if (col == (this.Columns.Count - 1))

                {

                    col = -1;

                    row++;

                }

                this.CurrentCell = this[col + 1, row];

            }

            e.Handled = true;

        }

        base.OnKeyDown(e);

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/bab211e2-8ba1-44e6-b660-9598913f68a0/

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/907f84f6-1c27-491b-a088-e6fd12e47709/

 

10.如何实现在文本列中只显示数字的输入?  [回到顶端]

 

textbox单元格进入编辑状态时,EditingControl被显示在单元格中。我们可以在TextBox.KeyPress事件中,通过char.IsDigit()方法判断键盘输入是否为数字来决定是否需要过滤。详见以下示例:

 

代码:

public partial class DgvNumberOnlyColumn : Form

{

    public DgvNumberOnlyColumn()

    {

        InitializeComponent();

    }

 

    private void DgvNumberOnlyColumn_Load(object sender, EventArgs e)

    {

        this.dataGridView1.Columns.Add("col1", "col1");

        this.dataGridView1.Columns.Add("col2", "col2");

        this.dataGridView1.Rows.Add();

        this.dataGridView1.EditingControlShowing += new

             DataGridViewEditingControlShowingEventHandler(

             dataGridView1_EditingControlShowing);

    }

 

    void dataGridView1_EditingControlShowing(object sender,

         DataGridViewEditingControlShowingEventArgs e)

    {

        if (this.dataGridView1.CurrentCell.ColumnIndex == 0)

        {

            if (e.Control is TextBox)

            {

                TextBox tb = e.Control as TextBox;

                tb.KeyPress -= new KeyPressEventHandler(tb_KeyPress);

                tb.KeyPress += new KeyPressEventHandler(tb_KeyPress);

            }

        }

    }

 

    void tb_KeyPress(object sender, KeyPressEventArgs e)

    {

        if (!(char.IsDigit(e.KeyChar)))

        {

            Keys key = (Keys)e.KeyChar;

 

            if (!(key == Keys.Back || key == Keys.Delete))

            {

                e.Handled = true;

            }

        }

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/ea4f5fce-7363-4bab-962b-7469a3886311/

 

11.如何使DataGridViewCell中的‘/t’控制符起作用?  [回到顶端]

 

DataGridView在绘制单元格的值的时候,‘/t’控制符默认会被忽略。要让‘/t’控制符起作用,我们需要处理CellPainting事件来重新绘制单元格的值。

 

代码:

void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

{

    e.Paint(e.CellBounds,

        DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground);

 

    if (e.Value != null)

    {

        e.Graphics.DrawString(e.Value.ToString(),

            e.CellStyle.Font,

            new SolidBrush(e.CellStyle.ForeColor),

            e.CellBounds.X, e.CellBounds.Y);

    }

    e.Handled = true;

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/988a688f-9585-4205-84ed-c5ad6a5fcf10/

 

 

12.如何让DataGridViewTextBoxCell在编辑模式下接受Enter键和Tab键的输入?  [回到顶端]

 

DataGridViewTextBoxCell在编辑模式下接受Enter键和Tab键输入,我们需要从DataGridView类中派生一个类,重写该类的ProcessDataGridViewKey事件来处理所接收的Enter键和Tab键。

  

代码:

class zxyDataGridView : DataGridView

{

    protected override bool ProcessDataGridViewKey(KeyEventArgs e)

    {

        switch (e.KeyCode)

        {

            case Keys.Tab:

                return false;

            case Keys.Enter:

                {

                    if (this.EditingControl != null)

                    {

                        if (this.EditingControl is TextBox)

                        {

                            TextBox tx = this.EditingControl as TextBox;

                            int tmp = tx.SelectionStart;

                            tx.Text = tx.Text.Insert(tx.SelectionStart,

                                                Environment.NewLine);

                            tx.SelectionStart = tmp + Environment.NewLine.Length;

                            return true;

                        }

                    }

                }

                return false;

        }

        return base.ProcessDataGridViewKey(e);

    }

}

 

我们也需要将TextBoxAcceptsTab属性设置为true使得DataGridViewTextBoxCell在编辑模式下接受Tab键输入。

  

代码:

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)

{

    if (e.Control is TextBox)

    {

        TextBox tb = e.Control as TextBox;

        tb.AcceptsTab = true;

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/988a688f-9585-4205-84ed-c5ad6a5fcf10/

 

13.如何在DataGridView的全部区域内显示网格线?  [回到顶端]

  

默认的情况下,当DataGridView的区域大于所需要显示数据的区域时,DataGridView将多余的部分显示灰色背景。要在DataGridView的所有区域内都显示网格线,我们可以通过继承DataGridView类,重写该类的OnPaint事件,在没有数据的部分也同样绘制网格线。详见以下示例:

  

代码:

public class GridLineDataGridView : DataGridView

{

    public GridLineDataGridView()

    {

        this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

    }

 

    protected override void OnPaint(PaintEventArgs e)

    {

        base.OnPaint(e);

 

        int rowHeight = this.RowTemplate.Height;

 

        int h = this.ColumnHeadersHeight + rowHeight * this.RowCount;

        int imgWidth = this.Width - 2;

        Rectangle rFrame = new Rectangle(0, 0, imgWidth, rowHeight);

        Rectangle rFill = new Rectangle(1, 1, imgWidth - 2, rowHeight);

        Rectangle rowHeader = new Rectangle(2, 2, this.RowHeadersWidth - 3, rowHeight);

 

        Pen pen = new Pen(this.GridColor, 1);

 

        Bitmap rowImg = new Bitmap(imgWidth, rowHeight);

        Graphics g = Graphics.FromImage(rowImg);

        g.DrawRectangle(pen, rFrame);

        g.FillRectangle(new SolidBrush(this.DefaultCellStyle.BackColor), rFill);

        g.FillRectangle(new SolidBrush

           (this.RowHeadersDefaultCellStyle.BackColor), rowHeader);

 

        int w = this.RowHeadersWidth - 1;

        for (int j = 0; j < this.ColumnCount; j++)

        {

            g.DrawLine(pen, new Point(w, 0), new Point(w, rowHeight));

            w += this.Columns[j].Width;

        }

 

        int loop = (this.Height - h) / rowHeight;

        for (int j = 0; j < loop + 1; j++)

        {

            e.Graphics.DrawImage(rowImg, 1, h + j * rowHeight);

        }

    }

}

  

相关链接:

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/141aef69-b7c4-412f-a067-bc4bce011167/

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/d39e565e-cf33-45b9-993c-99d39813fd15/

 

 

14.如何禁止编辑特定的单元格?  [回到顶端]

  

ReadOnly属性指示在单元格中的数据能否被修改。我们可以将单个单元格设置成只读,也可以通过设置DataGridViewRow.ReadOnly或者DataGridViewColumn.Readonly属性将整个列或者整行的单元格设置为只读。默认情况下,如果单元格所处在的行或者列被设置为只读,这些单元格也将会是只读的。

 

我们也可以动态的设置只读的单元格,例如将当前单元格设置为只读,那么表中所有单元格的所有内容都将不能被用户编辑。注意ReadOnly属性不能限制用户通过编程来修改单元格中的内容,也不会影响用户删除行。

 

 

15.如何禁用一个单元格?  [回到顶端]

  

 只读可以阻止单元格被编辑,但是DataGridView没有内置功能禁用一个单元格。通常“禁用”表示用户不能定位到它和视觉上的禁用。禁止动态的定位到禁用的单元格是比较难做到的,但是可以做到视觉上的禁用。由于内置的单元格没有禁用的属性,接下来的示例扩展了DataGridViewButtonCell,让它可以通过设置disable属性来实现视觉上的“禁用”。

  

代码:

public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn

{

    public DataGridViewDisableButtonColumn()

    {

        this.CellTemplate = new DataGridViewDisableButtonCell();

    }

}

 

public class DataGridViewDisableButtonCell : DataGridViewButtonCell

{

    private bool enabledValue;

    public bool Enabled

    {

        get

        {

            return enabledValue;

        }

        set

        {

            enabledValue = value;

        }

    }

 

    // 重写Clone方法使得被修改的属性可以被复制

    public override object Clone()

    {

        DataGridViewDisableButtonCell cell =

            (DataGridViewDisableButtonCell)base.Clone();

        cell.Enabled = this.Enabled;

        return cell;

    }

 

    // 默认的情况下,button cell 为可用的

    public DataGridViewDisableButtonCell()

    {

        this.enabledValue = true;

    }

 

    protected override void Paint(Graphics graphics,

        Rectangle clipBounds, Rectangle cellBounds, int rowIndex,

        DataGridViewElementStates elementState, object value,

        object formattedValue, string errorText,

        DataGridViewCellStyle cellStyle,

        DataGridViewAdvancedBorderStyle advancedBorderStyle,

        DataGridViewPaintParts paintParts)

    {

        // 如果是禁用的,绘制边框、背景和单元格中禁用的button

        if (!this.enabledValue)

        {

            // 绘制被指定的单元格的背景

            if ((paintParts & DataGridViewPaintParts.Background) ==

                DataGridViewPaintParts.Background)

            {

                SolidBrush cellBackground =

                    new SolidBrush(cellStyle.BackColor);

                graphics.FillRectangle(cellBackground, cellBounds);

                cellBackground.Dispose();

            }

 

            // 绘出指定的单元格的边框

            if ((paintParts & DataGridViewPaintParts.Border) ==

 

                DataGridViewPaintParts.Border)

            {

                PaintBorder(graphics, clipBounds, cellBounds, cellStyle,

                    advancedBorderStyle);

            }

 

            // 计算绘制button的区域

            Rectangle buttonArea = cellBounds;

            Rectangle buttonAdjustment =

                this.BorderWidths(advancedBorderStyle);

            buttonArea.X += buttonAdjustment.X;

            buttonArea.Y += buttonAdjustment.Y;

            buttonArea.Height -= buttonAdjustment.Height;

            buttonArea.Width -= buttonAdjustment.Width;

 

            // 绘制禁用button

            ButtonRenderer.DrawButton(graphics, buttonArea,

                PushButtonState.Disabled);

 

            // 绘制禁用button的文本

            if (this.FormattedValue is String)

            {

                TextRenderer.DrawText(graphics,

                    (string)this.FormattedValue,

                    this.DataGridView.Font,

                    buttonArea, SystemColors.GrayText);

            }

        }

        else

        {

            // 如果button cell为可用的,执行基类的Paint方法

            base.Paint(graphics, clipBounds, cellBounds, rowIndex,

                elementState, value, formattedValue, errorText,

                cellStyle, advancedBorderStyle, paintParts);

        }

    }

}

 

16.如何使所有的单元格无论是否处于编辑状态都显示控件?  [回到顶端]

  

DataGridView控件只支持在单元格处于编辑状态时显示一个真实的控件。DataGridView不支持在一行中显示多个控件。当单元格不处于编辑状态时,DataGridView只绘制控件的外观。

 

我们也可以通过DataGridView.Controls.Add()方法将控件添加到DataGridView,设置他们的位置和大小让他们寄放在单元格中,但是在非编辑状态下所有的单元格中都显示控件是毫无意义的。

  

  

17.如何处理DataGridViewComboBoxCellSelectedIndexChanged事件?  [回到顶端]

  

有时,获知用户在ComboBox编辑控件中已经选中一项是很有必要的。我们可以在DataGridView.EditingControlShowing事件中对DataGridViewComboBox进行一些处理来实现。接下来的示例演示了如何实现这种功能。注意这个示例同时也演示了如何防止引发多重SelectedIndexChanged事件。

  

代码:

private void dataGridView1_EditingControlShowing(object sender,

            DataGridViewEditingControlShowingEventArgs e)

{

    ComboBox cb = e.Control as ComboBox;

    if (cb != null)

    {

        // 首先移除事件处理程序以防止多重触发附加事件

        cb.SelectedIndexChanged -= new

        EventHandler(cb_SelectedIndexChanged);

 

        // 附加事件处理程序

        cb.SelectedIndexChanged += new

        EventHandler(cb_SelectedIndexChanged);

    }

}

 

void cb_SelectedIndexChanged(object sender, EventArgs e)

{

    MessageBox.Show("Selected index changed");

}

 

18.如何在一个单元格中同时显示图标和文字?  [回到顶端]

  

DataGridView控件没有内置的功能实现在一个单元格中显示图标和文字。我们可以通过处理相关的绘制来得到我们想要的效果。

  

接下来的示例演示了扩展DataGridViewTextColumncell来同时绘制文字和图像。该示例通过设置DataGridViewCellStyle.Padding属性来调整文本的位置,重写Paint事件来绘制图像。这个示例也可以简化为在CellPainting事件中进行处理。

  

代码:

public class TextAndImageColumn : DataGridViewTextBoxColumn

{

    private Image imageValue;

    private Size imageSize;

 

    public TextAndImageColumn()

    {

        this.CellTemplate = new TextAndImageCell();

    }

 

    public override object Clone()

    {

        TextAndImageColumn c = base.Clone() as TextAndImageColumn;

        c.imageValue = this.imageValue;

        c.imageSize = this.imageSize;

        return c;

    }

 

    public Image Image

    {

        get { return this.imageValue; }

        set

        {

            if (this.Image != value)

            {

                this.imageValue = value;

                this.imageSize = value.Size;

 

                if (this.InheritedStyle != null)

                {

                    Padding inheritedPadding = this.InheritedStyle.Padding;

                    this.DefaultCellStyle.Padding = new Padding(imageSize.Width,

                 inheritedPadding.Top, inheritedPadding.Right,

                 inheritedPadding.Bottom);

                }

            }

        }

    }

 

    private TextAndImageCell TextAndImageCellTemplate

    {

        get { return this.CellTemplate as TextAndImageCell; }

    }

    internal Size ImageSize

    {

        get { return imageSize; }

    }

}

 

public class TextAndImageCell : DataGridViewTextBoxCell

{

    private Image imageValue;

    private Size imageSize;

 

    public override object Clone()

    {

        TextAndImageCell c = base.Clone() as TextAndImageCell;

        c.imageValue = this.imageValue;

        c.imageSize = this.imageSize;

        return c;

    }

 

    public Image Image

    {

        get

        {

            if (this.OwningColumn == null ||

        this.OwningTextAndImageColumn == null)

            {

                return imageValue;

            }

            else if (this.imageValue != null)

            {

                return this.imageValue;

            }

            else

            {

                return this.OwningTextAndImageColumn.Image;

            }

        }

        set

        {

            if (this.imageValue != value)

            {

                this.imageValue = value;

                this.imageSize = value.Size;

                Padding inheritedPadding = this.InheritedStyle.Padding;

                this.Style.Padding = new Padding(imageSize.Width,

                inheritedPadding.Top, inheritedPadding.Right,

                inheritedPadding.Bottom);

            }

        }

    }

 

    protected override void Paint(Graphics graphics, Rectangle clipBounds,

    Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState,

    object value, object formattedValue, string errorText,

    DataGridViewCellStyle cellStyle,

    DataGridViewAdvancedBorderStyle advancedBorderStyle,

    DataGridViewPaintParts paintParts)

    {

        // 绘制基本内容

        base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,

           value, formattedValue, errorText, cellStyle,

           advancedBorderStyle, paintParts);

 

        if (this.Image != null)

        {

            // 绘制单元格上的图片

            System.Drawing.Drawing2D.GraphicsContainer container =

            graphics.BeginContainer();

            graphics.SetClip(cellBounds);

            graphics.DrawImageUnscaled(this.Image, cellBounds.Location);

            graphics.EndContainer(container);

        }

    }

 

    private TextAndImageColumn OwningTextAndImageColumn

    {

        get { return this.OwningColumn as TextAndImageColumn; }

    }

}

 

 

19.如何隐藏一列?  [回到顶端]

  

在一些情况下开发者不希望显示DataGridView中的一些列。例如,开发者希望对特定用户显示员工薪水,而对其他的用户不显示。我们可以通过编程或者是设计器来实现。

 

通过编程实现隐藏一列:

DataGridView控件中,设置列的Visible属性为false来隐藏列。

 

通过设计器实现隐藏一列:

1)在点击DataGridView的智能标记,选择编辑列。

2)在选择列的列表中,选择一列。

3)在列属性的表单中,设置Visible属性为false

 

20.如何阻止用户对一列排序?  [回到顶端]

  

DataGridView控件中,文本列默认是自动排序,其他类型的列不支持自动排序。在一些情况下开发者希望修改这些默认设置。

  

DataGridView控件中,一个列的SortMode属性决定这列的排序方式,因此我们可以通过修改列的SortModeDataGridViewColumnSortMode.NotSortable来阻止用户对这列排序。

 

 

21.如何实现多重排序?  [回到顶端]

  

默认情况下,DataGridView控件没有在多个列上的排序的功能。我们可以通过编程来实现多列排序,这里根据DataGridView是否为数据绑定而有所不同。

 

21.1 数据绑定的DataGridView

  

如果DataGridView是绑定到数据源的,是可以多行排序的,但是只有排序的第一列显示排序的标志符号。另外,SortedColumn属性只会返回第一个排序的列的信息。

  

有些数据源本身就支持多列排序。如果数据源实现了IBindingListView接口并且提供了对Sort属性的支持,那么通过设置Sort属性就能实现多列排序。我们可以手动的设置列的SortGlyhDirection来指示排序的列。

  

接下来的示例使用一个张数据表,设置列的Sort属性使第二列和第三列排序。这个示例也同样演示了如何设置列的SortGlyhDirection。这个示例是假设你窗体上添加了DataGridViewBindingSource组件:

  

代码:

DataTable dt = new DataTable();

dt.Columns.Add("C1", typeof(int));

dt.Columns.Add("C2", typeof(string));

dt.Columns.Add("C3", typeof(string));

 

dt.Rows.Add(1, "1", "Test1");

dt.Rows.Add(2, "2", "Test2");

dt.Rows.Add(2, "2", "Test1");

dt.Rows.Add(3, "3", "Test3");

dt.Rows.Add(4, "4", "Test4");

dt.Rows.Add(4, "4", "Test3");

 

DataView view = dt.DefaultView;

view.Sort = "C2 ASC, C3 ASC";

bindingSource.DataSource = view;

 

DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn();

col0.DataPropertyName = "C1";

dataGridView1.Columns.Add(col0);

col0.SortMode = DataGridViewColumnSortMode.Programmatic;

col0.HeaderCell.SortGlyphDirection = SortOrder.None;

 

DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();

col1.DataPropertyName = "C2";

dataGridView1.Columns.Add(col1);

col1.SortMode = DataGridViewColumnSortMode.Programmatic;

col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending;

 

DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();

col2.DataPropertyName = "C3";

dataGridView1.Columns.Add(col2);

col2.SortMode = DataGridViewColumnSortMode.Programmatic;

col2.HeaderCell.SortGlyphDirection = SortOrder.Ascending;

 

.2未绑定的DataGridView

  

在未绑定的DataGridView的情况下实现多行排序,我们可以处理SortCompare事件或者通过重载Sort方法(IComparer)来实现更加复杂、灵活的排序。

  

21.2.1使用SortCompare事件实现自定义的排序

  

接下来的示例演示了使用SortCompare处理程序来实现自定义的排序。被选择的DataGridViewColumn将会被排序,如果在这一列中有相同的值,ID列将决定最终的排序顺序。

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

class Form1 : Form

{

    private DataGridView dataGridView1 = new DataGridView();

 

    [STAThreadAttribute()]

    static void Main()

    {

        System.Net.Mime.MediaTypeNames.Application.EnableVisualStyles();

        Application.Run(new Form1());

    }

 

    public Form1()

    {

        // 窗体初始化

        dataGridView1.AllowUserToAddRows = false;

        dataGridView1.Dock = DockStyle.Fill;

        dataGridView1.SortCompare += new DataGridViewSortCompareEventHandler(

            this.dataGridView1_SortCompare);

        Controls.Add(this.dataGridView1);

        this.Text = "DataGridView.SortCompare demo";

 

        PopulateDataGridView();

    }

 

    // 可以替换DataGridView部署的代码

    public void PopulateDataGridView()

    {

        // DataGridView中添加列

        dataGridView1.ColumnCount = 3;

 

        // 设置DataGridView的属性

        dataGridView1.Columns[0].Name = "ID";

        dataGridView1.Columns[1].Name = "Name";

        dataGridView1.Columns[2].Name = "City";

        dataGridView1.Columns["ID"].HeaderText = "ID";

        dataGridView1.Columns["Name"].HeaderText = "Name";

        dataGridView1.Columns["City"].HeaderText = "City";

 

        // DataGridView中添加行

        dataGridView1.Rows.Add(new string[] { "1", "Parker", "Seattle" });

        dataGridView1.Rows.Add(new string[] { "2", "Parker", "New York" });

        dataGridView1.Rows.Add(new string[] { "3", "Watson", "Seattle" });

        dataGridView1.Rows.Add(new string[] { "4", "Jameson", "New Jersey" });

        dataGridView1.Rows.Add(new string[] { "5", "Brock", "New York" });

        dataGridView1.Rows.Add(new string[] { "6", "Conner", "Portland" });

 

        // 自动调整列的大小

        dataGridView1.AutoResizeColumns();

    }

 

    private void dataGridView1_SortCompare(object sender,

        DataGridViewSortCompareEventArgs e)

    {

        // 在当前列的排序

        e.SortResult = System.String.Compare(

            e.CellValue1.ToString(), e.CellValue2.ToString());

 

        // 如果单元格值相等,根据ID列排序

        if (e.SortResult == 0 && e.Column.Name != "ID")

        {

            e.SortResult = System.String.Compare(

                dataGridView1.Rows[e.RowIndex1].Cells["ID"].Value.ToString(),

                dataGridView1.Rows[e.RowIndex2].Cells["ID"].Value.ToString());

        }

        e.Handled = true;

    }

}

 

21.2.2使用IComparer接口实现自定义的排序

 

接下来的示例演示了如何通过实现IComparer接口重写Sort方法来实现多重排序。

 

using System;

using System.Drawing;

using System.Windows.Forms;

 

class Form1 : Form

{

    private DataGridView DataGridView1 = new DataGridView();

    private FlowLayoutPanel FlowLayoutPanel1 = new FlowLayoutPanel();

    private Button Button1 = new Button();

    private RadioButton RadioButton1 = new RadioButton();

    private RadioButton RadioButton2 = new RadioButton();

 

    [STAThreadAttribute()]

    public static void Main()

    {

        Application.Run(new Form1());

    }

 

    public Form1()

    {

        // 窗体初始化

        AutoSize = true;

        Text = "DataGridView IComparer sort demo";

 

        FlowLayoutPanel1.FlowDirection = FlowDirection.TopDown;

        FlowLayoutPanel1.Location = new System.Drawing.Point(304, 0);

        FlowLayoutPanel1.AutoSize = true;

 

        FlowLayoutPanel1.Controls.Add(RadioButton1);

        FlowLayoutPanel1.Controls.Add(RadioButton2);

        FlowLayoutPanel1.Controls.Add(Button1);

 

        Button1.Text = "Sort";

        RadioButton1.Text = "Ascending";

        RadioButton2.Text = "Descending";

        RadioButton1.Checked = true;

 

        Controls.Add(FlowLayoutPanel1);

        Controls.Add(DataGridView1);

    }

 

    protected override void OnLoad(EventArgs e)

    {

        PopulateDataGridView();

        Button1.Click += new EventHandler(Button1_Click);

 

        base.OnLoad(e);

    }

 

    // 可以替换DataGridView部署的代码

    private void PopulateDataGridView()

    {

        DataGridView1.Size = new Size(300, 300);

 

        // DataGridView中添加列

        DataGridView1.ColumnCount = 2;

 

        //设置DataGridView列的属性

        DataGridView1.Columns[0].Name = "First";

        DataGridView1.Columns[1].Name = "Last";

        DataGridView1.Columns["First"].HeaderText = "First Name";

        DataGridView1.Columns["Last"].HeaderText = "Last Name";

        DataGridView1.Columns["First"].SortMode =

            DataGridViewColumnSortMode.Programmatic;

        DataGridView1.Columns["Last"].SortMode =

            DataGridViewColumnSortMode.Programmatic;

 

        // DataGridView中添加行

        DataGridView1.Rows.Add(new string[] { "Peter", "Parker" });

        DataGridView1.Rows.Add(new string[] { "James", "Jameson" });

        DataGridView1.Rows.Add(new string[] { "May", "Parker" });

        DataGridView1.Rows.Add(new string[] { "Mary", "Watson" });

        DataGridView1.Rows.Add(new string[] { "Eddie", "Brock" });

    }

 

    private void Button1_Click(object sender, EventArgs e)

    {

        if (RadioButton1.Checked == true)

        {

            DataGridView1.Sort(new RowComparer(SortOrder.Ascending));

        }

        else if (RadioButton2.Checked == true)

        {

            DataGridView1.Sort(new RowComparer(SortOrder.Descending));

        }

    }

 

    private class RowComparer : System.Collections.IComparer

    {

        private static int sortOrderModifier = 1;

 

        public RowComparer(SortOrder sortOrder)

        {

            if (sortOrder == SortOrder.Descending)

            {

                sortOrderModifier = -1;

            }

            else if (sortOrder == SortOrder.Ascending)

            {

                sortOrderModifier = 1;

            }

        }

 

        public int Compare(object x, object y)

        {

            DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x;

            DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y;

 

            // 根据Last Name列排序

            int CompareResult = System.String.Compare(

                DataGridViewRow1.Cells[1].Value.ToString(),

                DataGridViewRow2.Cells[1].Value.ToString());

 

            // 如果Last Name值相等,则根据First Name的值排序

            if (CompareResult == 0)

            {

                CompareResult = System.String.Compare(

                    DataGridViewRow1.Cells[0].Value.ToString(),

                    DataGridViewRow2.Cells[0].Value.ToString());

            }

            return CompareResult * sortOrderModifier;

        }

    }

}

 

 

22.如何实现行的拖拽和拖放来重新排序行?  [回到顶端]

  

DataGridView没有内置的功能实现拖拽和拖放来重新排序行。但是使用以下的拖拽和拖放的代码可以很容易让DataGridView实现这种功能。请在窗体上添加namedataGridView1DataGridView控件,DataGridViewAllowDrop属性为true,并确保所需的事件与对应的事件处理程序相关联。

  

代码:

private Rectangle dragBoxFromMouseDown;

private int rowIndexFromMouseDown;

private int rowIndexOfItemUnderMouseToDrop;

private void dataGridView1_MouseMove(object sender, MouseEventArgs e)

{

    if ((e.Button & MouseButtons.Left) == MouseButtons.Left)

    {

        // 如果鼠标移动到矩形框外面,开始拖拽.

        if (dragBoxFromMouseDown != Rectangle.Empty &&

            !dragBoxFromMouseDown.Contains(e.X, e.Y))

        {

            // 执行拖拽,传入数据

            DragDropEffects dropEffect = dataGridView1.DoDragDrop(

            dataGridView1.Rows[rowIndexFromMouseDown],

            DragDropEffects.Move);

        }

    }

}

 

private void dataGridView1_MouseDown(object sender, MouseEventArgs e)

{

    // 获取鼠标按下时行的索引值.

    rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;

 

    if (rowIndexFromMouseDown != -1)

    {

        // 记录按下鼠标的点

        // DragSize指示在开始拖动操作前鼠标可以移动的范围

        Size dragSize = SystemInformation.DragSize;

 

        // DragSize创建一个以鼠标为中心的矩形

        dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),

                                                       e.Y - (dragSize.Height / 2)),

                            dragSize);

    }

    else

        // 如果鼠标不在ListBox上,重置矩形

        dragBoxFromMouseDown = Rectangle.Empty;

}

 

private void dataGridView1_DragOver(object sender, DragEventArgs e)

{

    e.Effect = DragDropEffects.Move;

}

 

private void dataGridView1_DragDrop(object sender, DragEventArgs e)

{

    // 由于鼠标的位置是与屏幕相关的,它必须转换为与客户端相匹配的点

    Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));

 

    // 获取鼠标按下时的行的索引值

    rowIndexOfItemUnderMouseToDrop =

        dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;

 

// 如果拖放的行为为从将某一行移动然后移除、插入到某一行

    if (e.Effect == DragDropEffects.Move)

    {

        DataGridViewRow rowToMove = e.Data.GetData(

            typeof(DataGridViewRow)) as DataGridViewRow;

        dataGridView1.Rows.RemoveAt(rowIndexFromMouseDown);

        dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop, rowToMove);

    }

}

 

 

 

23.如何让最后一列足够宽以覆盖余下的DataGridView的工作区?  [回到顶端]

  

将最后一列的AutoSizeMode设置为Fill,最后一列将会调整自己的大小填充到DataGridView余下的工作区域。另外,我们也可以通过设置最后一列的MinimumWidth以避免列的宽度太窄。

 

24.如何让单元格中的文字转行?  [回到顶端]

  

默认情况下,DataGridViewTextBoxCell中的文字不转行。这个可以通过设置单元格的WrapMode属性来控制。例如,将DataGridViewCellStyle.WrapMode 属性设置为 DataGridViewTriState.True 便可实现单元格中文本的自动换行效果。

  

代码:

this.dataGridView1.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

 

 

25.如何让image列不显示任何图片?  [回到顶端]

  

默认的情况下,image列或单元格将null值显示为红色的“X”图标。我们可以通过设置列的NullValue属性为null让其不显示任何图片。

  

代码:

this.dataGridViewImageColumn1.DefaultCellStyle.NullValue = null;

 

 

26.如何让DataGridViewComboBoxCell可以编辑?  [回到顶端]

  

默认情况下,DataGridViewComboBoxCell是不支持在单元格中输入的。然而在实际中常常有这种需求。要实现这种功能,有两个方面需要考虑。一方面,为了使用户可以在组合框中输入需要将ComboBox editing controlDropDownStyle设置为DropDown。另一方面,我们需要确认用户输入的值是否存在于ComboBox的项集合中。这是因为组合框单元格的值必须存在于项集合中,否则就会抛出异常。最恰当的做法是,在CellValidating时间处理函数中将值添加到项集合中去。详见以下代码:

  

代码:

private void dataGridView1_CellValidating(object sender,

 DataGridViewCellValidatingEventArgs e)

{

    if (e.ColumnIndex == comboBoxColumn.DisplayIndex)

    {

        if (!this.comboBoxColumn.Items.Contains(e.FormattedValue))

        {

            this.comboBoxColumn.Items.Add(e.FormattedValue);

        }

    }

}

 

private void dataGridView1_EditingControlShowing(object sender,

        DataGridViewEditingControlShowingEventArgs e)

{

    if (this.dataGridView1.CurrentCellAddress.X == comboBoxColumn.DisplayIndex)

    {

        ComboBox cb = e.Control as ComboBox;

        if (cb != null)

        {

            cb.DropDownStyle = ComboBoxStyle.DropDown;

        }

    }

}

 

 

27.如何实现根据DataGridViewComboBoxColumn列中单元格所选的值,在同一行的另一个DataGridViewComboBoxColumn列中单元格显示该值所对应的子集?  [回到顶端]

  

有时我们需要在DataGridView中显示两张有联系的表,比如一个类别和子类别。当用户选择类别时同时筛选子类别的数据。这个功能可以通过两个DataGridViewComboBoxColumn来实现。要实现这种功能,需要创建两个不同的筛选列表。一张列表没有经过筛选,另一张表在用户编辑子类别时被筛选。这是因为DataGridViewComboBoxCell的值必须存在于项集合中,否则会抛出异常。即然这样,由于ComboBoxCell都使用同样的数据源,如果我们对某个ComboBoxCell筛选数据源,另一个ComboBoxCell可能会在经过筛选数据源中搜索不到它所显示的数据而引发DataError事件。

 

接下来的示例使用Northwind数据库中两张有关联的数据表Territory表和Region表(一个Region属于一个特定Territory)。使用类别和子类别的概念,Region是类别,Territory是子类别。

  

代码:

private void Form1_Load(object sender, EventArgs e)

{

    this.territoriesTableAdapter.Fill(this.northwindDataSet.Territories);

    this.regionTableAdapter.Fill(this.northwindDataSet.Region);

 

    // 为筛选的查看创建BindingSource

    filteredTerritoriesBS = new BindingSource();

    DataView dv = new DataView(northwindDataSet.Tables["Territories"]);

    filteredTerritoriesBS.DataSource = dv;

}

 

private void dataGridView1_CellBeginEdit(object sender,

         DataGridViewCellCancelEventArgs e)

{

    if (e.ColumnIndex == territoryComboBoxColumn.Index)

    {

        // ComboBox单元格的数据源设定为筛选后的数据源

        DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1

                        [e.ColumnIndex, e.RowIndex];

        dgcb.DataSource = filteredTerritoriesBS;

 

        // 根据region的选择筛选数据源

        this.filteredTerritoriesBS.Filter = "RegionID = " +

            this.dataGridView1[e.ColumnIndex - 1, e.RowIndex].Value.ToString();

    }

}

 

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

{

    if (e.ColumnIndex == this.territoryComboBoxColumn.Index)

    {

        // 重新设置combobox单元格的数据源为未经筛选的数据源

        DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1

                        [e.ColumnIndex, e.RowIndex];

        dgcb.DataSource = territoriesBindingSource; //未筛选的数据源

        this.filteredTerritoriesBS.RemoveFilter();

    }

}

 

 

 

 

28.如何在用户编辑单元格时显示错误图标?  [回到顶端]

  

当用户在单元格中输入不符合规范的数据时,您或许会希望显示一些错误信息或图标来提示用户更正输入。然而即使设置了ErrorText属性,在编辑状态下,如果用户输入不符合规范的数据,错误提示图标也不会显示。

 

接下来的示例演示了通过在CellValidating事件中设置单元格的边距来给错误图标提供显示区域。由于边距默认情况下会影响错误图标(error icon)的定位,示例中通过CellPainting事件来处理绘制图标的位置。最后当鼠标移动到单元格时,用tooltip显示出错信息。示例同样也可以作为重写GetErrorIconBounds事件来实现与边距无关的错误图标定位的自定义的单元格。

  

代码:

private ToolTip errorTooltip;

private Point cellInError = new Point(-2, -2);

public Form1()

{

    InitializeComponent();

    dataGridView1.ColumnCount = 3;

    dataGridView1.RowCount = 10;

}

 

private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)

{

    if (dataGridView1.IsCurrentCellDirty)

    {

        if (e.FormattedValue.ToString() == "BAD")

        {

            DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];

            cell.ErrorText = "Invalid data entered in cell";

 

            // 增加边距以便显示图标。这将在编辑控件时发生。

            if (cell.Tag == null)

            {

                cell.Tag = cell.Style.Padding;

                cell.Style.Padding = new Padding(0, 0, 18, 0);

                cellInError = new Point(e.ColumnIndex, e.RowIndex);

            }

 

            if (errorTooltip == null)

            {

                errorTooltip = new ToolTip();

                errorTooltip.InitialDelay = 0;

                errorTooltip.ReshowDelay = 0;

                errorTooltip.Active = false;

            }

            e.Cancel = true;

        }

    }

}

 

private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)

{

    if (dataGridView1.IsCurrentCellDirty && !String.IsNullOrEmpty(e.ErrorText))

    {

        // 绘制出错误图标外的所有图像

        e.Paint(e.ClipBounds, DataGridViewPaintParts.All &

                        ~(DataGridViewPaintParts.ErrorIcon));

 

        // 移动错误图标来填充空白边距

        GraphicsContainer container = e.Graphics.BeginContainer();

        e.Graphics.TranslateTransform(18, 0);

        e.Paint(this.ClientRectangle, DataGridViewPaintParts.ErrorIcon);

        e.Graphics.EndContainer(container);

        e.Handled = true;

    }

}

 

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)

{

    if (dataGridView1[e.ColumnIndex, e.RowIndex].ErrorText != String.Empty)

    {

        DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];

        cell.ErrorText = String.Empty;

        cellInError = new Point(-2, -2);

 

        // 还原单元格的边距。这将在编辑控件时发生。

        cell.Style.Padding = (Padding)cell.Tag;

 

        // 隐藏和释放tooltip

        if (errorTooltip != null)

        {

            errorTooltip.Hide(dataGridView1);

            errorTooltip.Dispose();

            errorTooltip = null;

        }

    }

}

 

// 显示或隐藏说明错误的tootip

private void dataGridView1_CellMouseMove(object sender,

                        DataGridViewCellMouseEventArgs e)

{

    if (cellInError.X == e.ColumnIndex &&

        cellInError.Y == e.RowIndex)

    {

        DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];

 

        if (cell.ErrorText != String.Empty)

        {

            if (!errorTooltip.Active)

            {

                errorTooltip.Show(cell.ErrorText, dataGridView1, 1000);

            }

            errorTooltip.Active = true;

        }

    }

}

 

private void dataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e)

{

    if (cellInError.X == e.ColumnIndex &&

        cellInError.Y == e.RowIndex)

    {

        if (errorTooltip.Active)

        {

            errorTooltip.Hide(dataGridView1);

            errorTooltip.Active = false;

        }

    }

}

 

 

 

 

29.如何同时显示绑定和非绑定数据?  [回到顶端]

  

DataGridView中显示的数据通常来自于数据源或其他数据类型,您或许会希望显示一列不是从数据源中获得的数据。这样的列被称为非绑定列。非绑定列有多种形式。我们可以使用虚拟模式(virtual mode)来同时显示绑定和非绑定数据。

  

接下来的示例演示了怎样让一个非绑定的DataGridViewCheckBoxCloumn记录用户选择数据情况。DataGridView处于虚拟模式(virtual mode)而且处理一些相关的事件。勾选的记录通过ID保存在Dictionary中,这样用户对内容排序就不会丢失CheckBox的选择状态。

  

代码:

private System.Collections.Generic.Dictionary<int, bool> checkState;

private void Form1_Load(object sender, EventArgs e)

 

{

    dataGridView1.AutoGenerateColumns = false;

    dataGridView1.DataSource = customerOrdersBindingSource;

 

    // 虚拟化DataGridViewCheckBoxColumn

    dataGridView1.VirtualMode = true;

    dataGridView1.Columns.Insert(0, new DataGridViewCheckBoxColumn());

 

    // 初始化dictionary,存储布尔值表示选择状态

    checkState = new Dictionary<int, bool>();

}

 

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)

{

    // 当单元格中数据改变时,更新保存的选择状态

    if (e.ColumnIndex == 0 && e.RowIndex != -1)

    {

        // OrderID列中获取orderID

        int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;

        checkState[orderID] = (bool)dataGridView1.Rows[e.RowIndex].Cells[0].Value;

    }

}

 

private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)

{

    // 处理需要显示的虚拟列中单元格的数据的通知。

    // 如果key在字典表中存在,获取数据

    if (e.ColumnIndex == 0)

    {

        int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;

        if (checkState.ContainsKey(orderID))

        {

            e.Value = checkState[orderID];

        }

        else

            e.Value = false;

    }

}

 

private void dataGridView1_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)

{

    // 处理虚拟列中单元格的数据需要保存到字典表中的通知

    if (e.ColumnIndex == 0)

    {

        // OrderID列中获取orderID

        int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;

 

        // 如果key(orderID)存在更新选择状态到字典中

        // 如果key(orderID)不存在添加选择状态到字典中

        if (!checkState.ContainsKey(orderID))

        {

            checkState.Add(orderID, (bool)e.Value);

        }

        else

            checkState[orderID] = (bool)e.Value;

    }

}

 

 

 

30. 如何在同一个DataGridView 中显示两张表上的数据?   [回到顶端]

 

我们可以用虚拟模式(Virtual Mode) 来实现DataGridView 的多表显示功能,除此之外,我们还可以使用JoinView 类,通过这个类我们可以将两张表联系在一起,然后绑定到DataGridView

 

JoinView:

http://support.microsoft.com/kb/325682

 

 

 

31.如何显示主从信息?  [回到顶端]

  

使用DataGridView常见的方案就是显示主/从信息,例如对具有父类/子类关系的两张数据表的显示。在主表中选择一行会触发子表中显示相应的数据。

  

我们可以通过DataGridView控件之间的交互和BindingSource组件来实现主从信息显示。接下来的示例演示了如何显示SQL ServerNorthwind数据库中的有关联的两张表:CustomersOrders。通过在主DataGridView中选择一个顾客,顾客相对应的表单将会显示在从DataGridView中。

  

代码:

using System;

using System.Data;

using System.Data.SqlClient;

using System.Windows.Forms;

 

public class Form1 : System.Windows.Forms.Form

{

    private DataGridView masterDataGridView = new DataGridView();

    private BindingSource masterBindingSource = new BindingSource();

    private DataGridView detailsDataGridView = new DataGridView();

    private BindingSource detailsBindingSource = new BindingSource();

 

    [STAThreadAttribute()]

    public static void Main()

    {

        Application.Run(new Form1());

    }

 

    // 初始化窗体

    public Form1()

    {

        masterDataGridView.Dock = DockStyle.Fill;

        detailsDataGridView.Dock = DockStyle.Fill;

 

        SplitContainer splitContainer1 = new SplitContainer();

        splitContainer1.Dock = DockStyle.Fill;

        splitContainer1.Orientation = Orientation.Horizontal;

        splitContainer1.Panel1.Controls.Add(masterDataGridView);

        splitContainer1.Panel2.Controls.Add(detailsDataGridView);

 

        this.Controls.Add(splitContainer1);

        this.Load += new System.EventHandler(Form1_Load);

        this.Text = "DataGridView master/detail demo";

    }

 

    private void Form1_Load(object sender, System.EventArgs e)

    {

        // DataGridView绑定到对应的BindingSource

        // 从数据库中加载数据

        masterDataGridView.DataSource = masterBindingSource;

        detailsDataGridView.DataSource = detailsBindingSource;

        GetData();

 

        // 调整主DataGridView列的大小,以适应新加载的数据

        masterDataGridView.AutoResizeColumns();

 

        // 配置从表可以根据数据的变化自动调整列宽

        detailsDataGridView.AutoSizeColumnsMode =

            DataGridViewAutoSizeColumnsMode.AllCells;

    }

 

    private void GetData()

    {

        try

        {

            // 指定一个链接字符串

            // 用可以连接到本机SQL Server 样板数据库

            // Northwind的链接字符串替换下面的链接字符串

            String connectionString =

                "Integrated Security=SSPI;Persist Security Info=False;" +

                "Initial Catalog=Northwind;Data Source=localhost";

            SqlConnection connection = new SqlConnection(connectionString);

 

            // 创建一个数据集(DataSet

            DataSet data = new DataSet();

            data.Locale = System.Globalization.CultureInfo.InvariantCulture;

 

            // Customers表中的数据填充到数据集(DataSet

            SqlDataAdapter masterDataAdapter = new

                SqlDataAdapter("select * from Customers", connection);

            masterDataAdapter.Fill(data, "Customers");

 

            // Orders表中的数据填充到数据集(DataSet

            SqlDataAdapter detailsDataAdapter = new

                SqlDataAdapter("select * from Orders", connection);

            detailsDataAdapter.Fill(data, "Orders");

 

            // 建立两张表之间的DataRelation

            DataRelation relation = new DataRelation("CustomersOrders",

                data.Tables["Customers"].Columns["CustomerID"],

                data.Tables["Orders"].Columns["CustomerID"]);

            data.Relations.Add(relation);

 

            // 将主表数据连接器绑定到Customers

            masterBindingSource.DataSource = data;

            masterBindingSource.DataMember = "Customers";

 

            // 将子表连接器绑定到主表数据连接器

            // 使用DataRelation名来实现根据主表的当前行来筛选子表中的数据

            detailsBindingSource.DataSource = masterBindingSource;

            detailsBindingSource.DataMember = "CustomersOrders";

        }

        catch (SqlException)

        {

            MessageBox.Show("To run this example, replace the value of the " +

                "connectionString variable with a connection string that is " +

                "valid for your system.");

        }

    }

}

 

 

 

32.如何在同一个DataGridView中显示主从信息?  [回到顶端]

 

DataGridView不支持在同一个DataGridView中显示主从信息。您可以使用Windows Forms的先前版本DataGrid控件来实现这种功能。

 

33.如何禁止列排序?  [回到顶端]

  

我们可以通过设置某一列DataGridViewColumn.SortMode属性来禁止用户排序特定的列。在Visual studion 2005中通过右击DataGridView选择编辑列,选择我们希望禁止用户排序的列,设置其SortMode属性为NotSortable即可。

 

34.如何在点击toolstrip button时将数据提交到数据源?  [回到顶端]

  

默认情况下,toolbarmenu不会触发数据验证。在一个数据绑定的控件中,验证是数据确认和更新的必要环节。一旦窗体和所有绑定的控件验证后,将会提交所有的当前修改。最后,TableAdapter将所有的修改更新到数据库。将下面的三行代码放添加到click事件处理程序中可以实现这种功能:

  

代码:

this.Validate();

this.customersBindingSource.EndEdit();

this.customersTableAdapter.Update(this.northwindDataSet.Customers);

 

35.如何当用户试图删除一行时显示再次确认的对话框?  [回到顶端]

 

当用户在DataGridView中选择一行,点击Del键,就会触发UserDeletingRow事件。我们可以提示用户是否继续删除该行。注意新行不能被删除。将以下代码添加到UserDeletingRow事件处理程序中可以实现这种功能:

  

代码:

if (!e.Row.IsNewRow)

{

    DialogResult response = MessageBox.Show("Are you sure?", "Delete row?",

                         MessageBoxButtons.YesNo,

                         MessageBoxIcon.Question,

                         MessageBoxDefaultButton.Button2);

    if (response == DialogResult.No)

        e.Cancel = true;

}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值