如何在WinForms的DataGridView中添加椭圆形按钮和文本框到当前单元格?

4
我想在我的DataGridView中的当前单元格中添加一个椭圆形按钮和文本框控件。点击椭圆形按钮后,我想要打开一个自定义计算器,其结果将显示在文本框中。我已经开发了自定义计算器。我只想在当前选定的单元格中显示椭圆形按钮和文本框控件。如果我离开一个单元格,则文本框控件的值应分配给已经离开的单元格。以下是屏幕截图。
2个回答

13

您需要按照此处描述的方式创建自定义的EditingControlCellColumn类:http://msdn.microsoft.com/en-us/library/aa730881(v=vs.80).aspx

我已经为您创建了示例应用程序。请查看下面的下载链接。
内容如下:

  • TextButton 控件
    包含没有边框和简单按钮的文本框的用户控件。
    enter image description here

  • 简单的Edit Form
    任何简单的对话框,返回DialogResult
    enter image description here

  • DataGridViewTextButtonEditingControl
    我们需要从我们的TextButton控件继承,并在此处实现IDataGridViewEditingControl接口。

 internal class DataGridViewTextButtonEditingControl : TextButton, IDataGridViewEditingControl
 {
     public DataGridViewTextButtonEditingControl()
     {
         InnerTextBox.TextChanged += (o, e) => NotifyDataGridViewOfValueChange();
     }

     public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
     {
         Font = dataGridViewCellStyle.Font;
         if (dataGridViewCellStyle.BackColor.A < 255)
         {
             Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor);
             BackColor = opaqueBackColor;
             EditingControlDataGridView.EditingPanel.BackColor = opaqueBackColor;
         }
         else
         {
             BackColor = dataGridViewCellStyle.BackColor;
         }
         ForeColor = dataGridViewCellStyle.ForeColor;
     }

     public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
     {
         TextBox textBox = InnerTextBox;
         switch (keyData & Keys.KeyCode)
         {
             case Keys.Right:
                 {
                     if (textBox != null)
                     {
                         // If the end of the selection is at the end of the string,
                         // let the DataGridView treat the key message
                         if ((RightToLeft == RightToLeft.No && !(textBox.SelectionLength == 0 && textBox.SelectionStart == textBox.Text.Length)) ||
                             (RightToLeft == RightToLeft.Yes && !(textBox.SelectionLength == 0 && textBox.SelectionStart == 0)))
                         {
                             return true;
                         }
                     }
                     break;
                 }

             case Keys.Left:
                 {
                     if (textBox != null)
                     {
                         // If the end of the selection is at the begining of the string
                         // or if the entire text is selected and we did not start editing,
                         // send this character to the dataGridView, else process the key message
                         if ((RightToLeft == RightToLeft.No && !(textBox.SelectionLength == 0 && textBox.SelectionStart == 0)) ||
                             (RightToLeft == RightToLeft.Yes && !(textBox.SelectionLength == 0 && textBox.SelectionStart == textBox.Text.Length)))
                         {
                             return true;
                         }
                     }
                     break;
                 }

             case Keys.Home:
             case Keys.End:
                 {
                     // Let the grid handle the key if the entire text is selected.
                     if (textBox != null)
                     {
                         if (textBox.SelectionLength != textBox.Text.Length)
                         {
                             return true;
                         }
                     }
                     break;
                 }

             case Keys.Delete:
                 {
                     // Let the grid handle the key if the carret is at the end of the text.
                     if (textBox != null)
                     {
                         if (textBox.SelectionLength > 0 ||
                             textBox.SelectionStart < textBox.Text.Length)
                         {
                             return true;
                         }
                     }
                     break;
                 }
         }
         return !dataGridViewWantsInputKey;
     }

     public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
     {
         return Text; // Convert.ChangeType(Text, typeof(int));
     }

     public void PrepareEditingControlForEdit(bool selectAll)
     {
         if (selectAll)
         {
             InnerTextBox.SelectAll();
         }
         else
         {
             // Do not select all the text, but
             // position the caret at the end of the text
             InnerTextBox.SelectionStart = InnerTextBox.Text.Length;
         }
     }

     public DataGridView EditingControlDataGridView { get; set; }
     public object EditingControlFormattedValue { get; set; }
     public int EditingControlRowIndex { get; set; }
     public bool EditingControlValueChanged { get; set; }
     public Cursor EditingPanelCursor { get; private set; }
     public bool RepositionEditingControlOnValueChange { get; private set; }

     protected override void OnTextChanged(EventArgs e)
     {
         base.OnTextChanged(e);
         NotifyDataGridViewOfValueChange();
     }

     private void NotifyDataGridViewOfValueChange()
     {
         if (!EditingControlValueChanged)
         {
             EditingControlValueChanged = true;
             EditingControlDataGridView.NotifyCurrentCellDirty(true);
         }
     }
 }
  • DataGridViewTextButtonCell
    我们需要继承自DataGridViewCell以实现DataGridViewTextButtonEditingControl的初始化、单元格绘制和(重要的!)克隆。
    如果没有重写Clone()方法,我们将无法设置新创建实例的属性。

  •  internal sealed class DataGridViewTextButtonCell : DataGridViewCell
     {
         private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetLeft = 3;
         private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetRight = 4;
         private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft = 0;
         private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight = 0;
         private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetTop = 2;
         private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetBottom = 1;
         private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping = 1;
         private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithoutWrapping = 2;
         private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom = 1;
    
         // Type of this cell's editing control
         private static readonly Type defaultEditType = typeof(DataGridViewTextButtonEditingControl);
         // Type of this cell's value. The formatted value type is string, the same as the base class DataGridViewTextBoxCell
         private static readonly Type defaultValueType = typeof(string);
    
         public override object Clone()
         {
             DataGridViewTextButtonCell cell = base.Clone() as DataGridViewTextButtonCell;
             if (cell != null)
             {
                 cell.ButtonClickHandler = ButtonClickHandler;
             }
             return cell;
         }
    
         /// <summary>
         /// Adjusts the location and size of the editing control given the alignment characteristics of the cell
         /// </summary>
         private Rectangle GetAdjustedEditingControlBounds(Rectangle editingControlBounds, DataGridViewCellStyle cellStyle)
         {
             // Add a 1 pixel padding on the left and right of the editing control
             editingControlBounds.X += 1;
             editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - 2);
    
             // Adjust the vertical location of the editing control:
             int preferredHeight = cellStyle.Font.Height + 3;
             if (preferredHeight < editingControlBounds.Height)
             {
                 switch (cellStyle.Alignment)
                 {
                     case DataGridViewContentAlignment.MiddleLeft:
                     case DataGridViewContentAlignment.MiddleCenter:
                     case DataGridViewContentAlignment.MiddleRight:
                         editingControlBounds.Y += (editingControlBounds.Height - preferredHeight) / 2;
                         break;
                     case DataGridViewContentAlignment.BottomLeft:
                     case DataGridViewContentAlignment.BottomCenter:
                     case DataGridViewContentAlignment.BottomRight:
                         editingControlBounds.Y += editingControlBounds.Height - preferredHeight;
                         break;
                 }
             }
    
             return editingControlBounds;
         }
    
         public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
         {
             base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
             TextButton textButton = DataGridView.EditingControl as TextButton;
             if (textButton != null)
             {
                 //textButton.BorderStyle = BorderStyle.None;
                 string initialFormattedValueStr = initialFormattedValue as string;
                 textButton.Text = initialFormattedValueStr;
                 if (ButtonClickHandler != null)
                     textButton.ButtonClick += ButtonClickHandler;
             }
         }
    
         public override void DetachEditingControl()
         {
             base.DetachEditingControl();
             TextButton textButton = DataGridView.EditingControl as TextButton;
             if (textButton != null)
             {
                 textButton.ClearUndo();
                 if (ButtonClickHandler != null)
                     textButton.ButtonClick -= ButtonClickHandler;
             }
         }
    
         public override void PositionEditingControl(bool setLocation, bool setSize, Rectangle cellBounds, Rectangle cellClip, DataGridViewCellStyle cellStyle, bool singleVerticalBorderAdded, bool singleHorizontalBorderAdded, bool isFirstDisplayedColumn, bool isFirstDisplayedRow)
         {
             Rectangle editingControlBounds = PositionEditingPanel(cellBounds,
                                                         cellClip,
                                                         cellStyle,
                                                         singleVerticalBorderAdded,
                                                         singleHorizontalBorderAdded,
                                                         isFirstDisplayedColumn,
                                                         isFirstDisplayedRow);
             editingControlBounds = GetAdjustedEditingControlBounds(editingControlBounds, cellStyle);
             DataGridView.EditingControl.Location = new Point(editingControlBounds.X, editingControlBounds.Y);
             DataGridView.EditingControl.Size = new Size(editingControlBounds.Width, editingControlBounds.Height);
         }
    
         public DataGridViewTextButtonEditingControl EditingControl
         {
             get { return DataGridView == null ? null : DataGridView.EditingControl as DataGridViewTextButtonEditingControl; }
         }
    
         public override Type EditType
         {
             get { return defaultEditType; }
         }
    
         public override Type ValueType
         {
             get { return base.ValueType ?? defaultValueType; }
         }
    
         public override Type FormattedValueType
         {
             get { return defaultValueType; }
         }
    
         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)
         {
             if (DataGridView == null)
             {
                 return;
             }
    
             // First paint the borders and background of the cell.
             base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle,
                        paintParts & ~(DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.ContentForeground));
    
             //if (PartPainted(paintParts, DataGridViewPaintParts.Border))
             //    PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
    
             Point ptCurrentCell = DataGridView.CurrentCellAddress;
             bool cellCurrent = ptCurrentCell.X == ColumnIndex && ptCurrentCell.Y == rowIndex;
             bool cellEdited = cellCurrent && DataGridView.EditingControl != null;
    
             // If the cell is in editing mode, there is nothing else to paint
             if (cellEdited)
             {
                 if (PartPainted(paintParts, DataGridViewPaintParts.Background))
                 {
                     //graphics.FillRectangle(br, cellBounds);
                     PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
                 }
             }
             else
             {
                 if (PartPainted(paintParts, DataGridViewPaintParts.ContentForeground))
                 {
                     // Take the borders into account
                     Rectangle borderWidths = BorderWidths(advancedBorderStyle);
                     Rectangle valBounds = cellBounds;
                     valBounds.Offset(borderWidths.X, borderWidths.Y);
                     valBounds.Width -= borderWidths.Right;
                     valBounds.Height -= borderWidths.Bottom;
                     // Also take the padding into account
                     if (cellStyle.Padding != Padding.Empty)
                     {
                         if (DataGridView.RightToLeft == RightToLeft.Yes)
                         {
                             valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top);
                         }
                         else
                         {
                             valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top);
                         }
                         valBounds.Width -= cellStyle.Padding.Horizontal;
                         valBounds.Height -= cellStyle.Padding.Vertical;
                     }
                     valBounds = GetAdjustedEditingControlBounds(valBounds, cellStyle);
    
                     TextFormatFlags horAlign = TextFormatFlags.Left;
                     switch (cellStyle.Alignment)
                     {
                         case DataGridViewContentAlignment.BottomLeft:
                         case DataGridViewContentAlignment.MiddleLeft:
                         case DataGridViewContentAlignment.TopLeft:
                             horAlign = TextFormatFlags.Left;
                             break;
                         case DataGridViewContentAlignment.BottomCenter:
                         case DataGridViewContentAlignment.MiddleCenter:
                         case DataGridViewContentAlignment.TopCenter:
                             horAlign = TextFormatFlags.HorizontalCenter;
                             break;
                         case DataGridViewContentAlignment.BottomRight:
                         case DataGridViewContentAlignment.MiddleRight:
                         case DataGridViewContentAlignment.TopRight:
                             horAlign = TextFormatFlags.Right;
                             break;
                     }
    
                     bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0;
    
                     SolidBrush br = new SolidBrush(cellSelected ? cellStyle.SelectionBackColor : cellStyle.BackColor);
    
                     if (PartPainted(paintParts, DataGridViewPaintParts.Background))
                     {
                         graphics.FillRectangle(br, cellBounds);
                         PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
                     }
    
                     if (cellStyle.Padding != Padding.Empty) 
                     { 
                         valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top);
                         valBounds.Width -= cellStyle.Padding.Horizontal; 
                         valBounds.Height -= cellStyle.Padding.Vertical;
                     }
    
                     if (cellCurrent)
                     {
                         // Draw focus rectangle 
                         if (DataGridView.Focused && valBounds.Width > 0 && valBounds.Height > 0)
                         {
                             ControlPaint.DrawFocusRectangle(graphics, valBounds, Color.Empty, br.Color);
                         }
                     }
    
                     int verticalTextMarginTop = cellStyle.WrapMode == DataGridViewTriState.True ? DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping : DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithoutWrapping;
                     valBounds.Offset(DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft, verticalTextMarginTop);
                     valBounds.Width -= DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft + DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight;
                     valBounds.Height -= verticalTextMarginTop + DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom;
    
                     TextRenderer.DrawText(graphics, formattedValue as string, cellStyle.Font,
                                           valBounds,
                                           cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor, TextFormatFlags.Default | horAlign | TextFormatFlags.Top);
                 }
                 if (PartPainted(paintParts, DataGridViewPaintParts.ErrorIcon))
                 {
                     // Paint the potential error icon on top of the NumericUpDown control
                     base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText,
                                cellStyle, advancedBorderStyle, DataGridViewPaintParts.ErrorIcon);
                 }
             }
         }
    
         /// <summary>
         /// Little utility function called by the Paint function to see if a particular part needs to be painted. 
         /// </summary>
         private static bool PartPainted(DataGridViewPaintParts paintParts, DataGridViewPaintParts paintPart)
         {
             return (paintParts & paintPart) != 0;
         }
    
         public EventHandler<TextButton.TextButtonEventArgs> ButtonClickHandler { get; set; }
     }
    
  • DataGridViewTextButtonColumn
    只需从DataGridViewColumn派生,提供应传递给我们的底层TextButton控件的几个属性。

  •  internal sealed class DataGridViewTextButtonColumn : DataGridViewColumn
     {
         private EventHandler<TextButton.TextButtonEventArgs> buttonClickHandler;
    
         public DataGridViewTextButtonColumn()
             : base(new DataGridViewTextButtonCell())
         {
         }
    
         public EventHandler<TextButton.TextButtonEventArgs> ButtonClickHandler
         {
             get
             {
                 return buttonClickHandler;
             }
             set
             {
                 DataGridViewTextButtonCell cell = CellTemplate as DataGridViewTextButtonCell;
                 if (cell != null)
                 {
                     if (value != null)
                         cell.ButtonClickHandler += value;
                     else if (buttonClickHandler != null)
                         cell.ButtonClickHandler -= buttonClickHandler;
                 }
                 buttonClickHandler = value;
             }
         }
    
         public override DataGridViewCell CellTemplate
         {
             get
             {
                 return base.CellTemplate;
             }
             set
             {
                 base.CellTemplate = value;
                 DataGridViewTextButtonCell cell = CellTemplate as DataGridViewTextButtonCell;
                 if (cell != null)
                     cell.ButtonClickHandler = ButtonClickHandler;
             }
         }
     }
    
  • 使用示例
    假设gridDataGridView

  •  grid.Columns.AddRange(new DataGridViewColumn[]
         {
             new DataGridViewTextBoxColumn
                 {
                     ValueType = typeof (string),
                     HeaderText = "Name"
                 },
             new DataGridViewTextButtonColumn
                 {
                     ValueType = typeof (int),
                     HeaderText = "Count",
                     ButtonClickHandler = (o, e) =>
                         {
                             grid.EndEdit();
                             using (EditForm frm = new EditForm { Value = e.Text })
                                 if (frm.ShowDialog(this) == DialogResult.OK)
                                 {
                                     e.Text = frm.Value;
                                     e.Handled = true;
                                 }
                             grid.BeginEdit(false);
                         }
                 }
         });
    

    下载链接:完整项目(Zip-Archive,目标框架:v.3.5)
    更新 (10月21日):修复了链接。


    我通过添加示例应用程序来更新了我的答案。 - Dmitry
    非常感谢,您解决了我的问题并提供了非常好的示例。 - Tikam Sangwani

    0

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接