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

全部回复

  •  

    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/

     

    2009年7月8日 6:24
  •   

    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/

     

    2009年7月8日 12:04
  •  

    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/

      

    2009年7月8日 12:06
  •   

    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/

     

    2009年7月8日 12:10
  •  

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

      

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

     

    2009年7月8日 12:10
  •  

    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/

     

    2009年7月8日 12:11
  •  

    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/

     

    2009年7月8日 12:12
  •  

    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;

            }

        }

    }

     

    2009年7月8日 12:13
  •  

    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/

     

    2009年7月8日 12:14
  •  

    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/

     

    2009年7月8日 12:15
  •  

    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/

     

    2009年7月8日 12:15
  •  

    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/

     

    2009年7月8日 12:16
  •  

    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/

     

    2009年7月8日 12:17
  •  

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

      

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

     

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

     

    2009年7月8日 12:17
  •  

    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);

            }

        }

    }

     

    2009年7月8日 12:18
  •  

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

      

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

     

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

      

    2009年7月8日 12:18
  •   

    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");

    }

     

    2009年7月8日 12:19
  •  

    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; }

        }

    }

     

    2009年7月8日 12:19
  •  

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

      

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

     

    通过编程实现隐藏一列:

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

     

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

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

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

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

     

    2009年7月8日 12:20
  •  

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

      

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

      

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

     

    2009年7月8日 12:21
  •  

    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;

     

      (接下贴)

     

    2009年7月8日 12:23
  •  

    21.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;

        }

    }

     

      (接下贴)

     

    2009年7月8日 12:25
  •  

    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;

            }

        }

    }

     

    2009年7月8日 12:25
  •  

    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);

        }

    }

     

    2009年7月8日 12:26
  •  

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

      

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

     

    2009年7月8日 12:27
  •  

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

      

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

      

    代码:

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

     

    2009年7月8日 12:28
  •  

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

      

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

      

    代码:

    this.dataGridViewImageColumn1.DefaultCellStyle.NullValue = null;

     

    2009年7月8日 12:28
  •  

    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;

            }

        }

    }

     

    2009年7月8日 12:29
  •  

    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();

        }

    }

     

    2009年7月8日 12:30
  •  

    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;

            }

        }

    }

     

    2009年7月8日 12:31
  •  

    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;

        }

    }

     

    2009年7月8日 12:32
  •  

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

     

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

     

    JoinView:

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

      

    2009年7月8日 12:32
  •   

    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.");

            }

        }

    }

     

    2009年7月8日 12:38
  •  

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

     

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

     

    2009年7月8日 12:38
  •  

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

      

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

     

    2009年7月8日 12:39
  •  

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

      

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

      

    代码:

    this.Validate();

    this.customersBindingSource.EndEdit();

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

     

    2009年7月8日 12:39
  •  

    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;

    }


    2009年7月8日 12:40