Category / Section
How to edit a RichText cell in GridControl?
7 mins read
By default, the WinForms Grid control in the library allows you to edit richtext via a dropdown panel. It does not allow you to edit the text in place. You can derive the GridRichTextBoxCellModel and GridRichTextBoxCellRenderer classes to use an embedded RichTextBox in the cell to do active editing.
Creating CellModel
Refer to the following code to create the CustomCellModel.
C#
public class InPlaceRichTextCellModel : GridTextBoxCellModel { private RichTextBox rtb; public InPlaceRichTextCellModel(GridModel grid) : base(grid) { rtb = new RichTextBox(); rtb.ScrollBars = RichTextBoxScrollBars.None; rtb.Location = new Point(-1000, -1000); rtb.Visible = false; } private InPlaceRichTextCellRenderer renderer = null; public override GridCellRendererBase CreateRenderer(GridControlBase control) { renderer = new InPlaceRichTextCellRenderer(control, this); return renderer; } protected override Size OnQueryPrefferedClientSize(Graphics g, int rowIndex, int colIndex, GridStyleInfo style, GridQueryBounds queryBounds) { //Do not resize col width... // 'in versions >= 2.0.6.0, use WinFormsUtils.MeasureSampleWString instead of GridCellModelBase.MeasureSampleWString // Size clientSize = new Size(this.Grid.ColWidths[colIndex], WinFormsUtils.MeasureSampleWString(g, style.GdipFont).Height); Size clientSize = new Size(this.Grid.ColWidths[colIndex], GridCellModelBase.MeasureSampleWString(g, style.GdipFont).Height); rtb.Width = clientSize.Width - style.BorderMargins.Left - style.BorderMargins.Right - this.ButtonBarSize.Width; if (Grid.ActiveGridView == null || !Grid.ActiveGridView.CurrentCell.HasCurrentCellAt(rowIndex, colIndex) || Grid.ActiveGridView.CurrentCell.StaticDrawing) { if(style.CellValue != null) { rtb.Rtf = style.CellValue.ToString(); } else { rtb.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033\viewkind4\uc1\pard\f0\fs17\par}"; } } else rtb.Rtf = ((RichTextBox)this.renderer.TextBox).Rtf; //Insert \par to go to the next line. rtb.SelectionLength = 0;//rtb.TextLength; rtb.SelectionLength = 0; rtb.SelectedRtf = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033\viewkind4\uc1\pard\f0\fs17\par}"; int height = rtb.GetPositionFromCharIndex(rtb.TextLength).Y + style.BorderMargins.Top + style.BorderMargins.Bottom + rtb.SelectionFont.Height / 2; return new Size(this.Grid.ColWidths[colIndex], height); } }
VB
Public Class InPlaceRichTextCellModel Inherits GridTextBoxCellModel Private rtb As RichTextBox Public Sub New(ByVal grid As GridModel) MyBase.New(grid) rtb = New RichTextBox() rtb.ScrollBars = RichTextBoxScrollBars.None rtb.Location = New Point(-1000, -1000) rtb.Visible = False End Sub Private renderer As InPlaceRichTextCellRenderer = Nothing Public Overrides Function CreateRenderer(ByVal control As GridControlBase) As GridCellRendererBase renderer = New InPlaceRichTextCellRenderer(control, Me) Return renderer End Function Protected Overrides Function OnQueryPrefferedClientSize(ByVal g As Graphics, ByVal rowIndex As Integer, ByVal colIndex As Integer, ByVal style As GridStyleInfo, ByVal queryBounds As GridQueryBounds) As Size 'Do not resize col width... ' 'in versions >= 2.0.6.0, use WinFormsUtils.MeasureSampleWString instead of GridCellModelBase.MeasureSampleWString ' Size clientSize = new Size(this.Grid.ColWidths[colIndex], WinFormsUtils.MeasureSampleWString(g, style.GdipFont).Height); Dim clientSize As New Size(Me.Grid.ColWidths(colIndex), GridCellModelBase.MeasureSampleWString(g, style.GdipFont).Height) rtb.Width = clientSize.Width - style.BorderMargins.Left - style.BorderMargins.Right - Me.ButtonBarSize.Width If Grid.ActiveGridView Is Nothing OrElse (Not Grid.ActiveGridView.CurrentCell.HasCurrentCellAt(rowIndex, colIndex)) OrElse Grid.ActiveGridView.CurrentCell.StaticDrawing Then If style.CellValue IsNot Nothing Then rtb.Rtf = style.CellValue.ToString() Else rtb.Rtf = "{\rtf1\ansi\ansicpg1252\deff0\deflang1033\viewkind4\uc1\pard\f0\fs17\par}" End If Else rtb.Rtf = (CType(Me.renderer.TextBox, RichTextBox)).Rtf End If 'Insert \par to go to the next line. rtb.SelectionLength = 0 'rtb.TextLength; rtb.SelectionLength = 0 rtb.SelectedRtf = "{\rtf1\ansi\ansicpg1252\deff0\deflang1033\viewkind4\uc1\pard\f0\fs17\par}" Dim height As Integer = rtb.GetPositionFromCharIndex(rtb.TextLength).Y + style.BorderMargins.Top + style.BorderMargins.Bottom + rtb.SelectionFont.Height \ 2 Return New Size(Me.Grid.ColWidths(colIndex), height) End Function End Class
Creating CellRenderer
The following code is used to create the custom CellRenderer.
C#
public class InPlaceRichTextCellRenderer : GridTextBoxCellRenderer { //Used for in-place editing. GridTextBoxControl activeRichTextBox; RichTextBox staticRichTextBox; //Used to display context menu for active edit control. //Public; so you can modify it. public ContextMenu RtbContextMenu; public InPlaceRichTextCellRenderer(GridControlBase grid, GridCellModelBase cellModel) : base(grid, cellModel) { SupportsFocusControl = true; //Removes the button. //this.Model.ButtonBarSize = new Size(1,1); activeRichTextBox = this.TextBox as GridTextBoxControl;//new GridTextBoxControl(this); staticRichTextBox = new RichTextBox(); activeRichTextBox.BorderStyle = BorderStyle.None; staticRichTextBox.BorderStyle = BorderStyle.None; //activeRichTextBox.TextChanged += new EventHandler(activeRichTextBox_TextChanged); RtbContextMenu = new ContextMenu(new MenuItem[] { new MenuItem("Copy", new EventHandler(copyClick)), new MenuItem("Paste", new EventHandler(pasteClick)), new MenuItem("-"), new MenuItem("Change Font", new EventHandler(fontClick)) }); activeRichTextBox.ContextMenu = RtbContextMenu; RtbContextMenu.Popup += new EventHandler(contextMenu_Popup); } #region Context menu handlers . . . #endregion #region helper methods for the renderer class . . . #endregion #region base class overrides . . . #endregion }
VB
Public Class InPlaceRichTextCellRenderer Inherits GridTextBoxCellRenderer 'Used for in-place editing. Private activeRichTextBox As GridTextBoxControl Private staticRichTextBox As RichTextBox 'Used to display context menu for active edit control. 'Public; so you can modify it. Public RtbContextMenu As ContextMenu Public Sub New(ByVal grid As GridControlBase, ByVal cellModel As GridCellModelBase) MyBase.New(grid, cellModel) SupportsFocusControl = True 'Removes the button. 'this.Model.ButtonBarSize = new Size(1,1); activeRichTextBox = TryCast(Me.TextBox, GridTextBoxControl) 'new GridTextBoxControl(this); staticRichTextBox = New RichTextBox() activeRichTextBox.BorderStyle = BorderStyle.None staticRichTextBox.BorderStyle = BorderStyle.None 'activeRichTextBox.TextChanged += new EventHandler(activeRichTextBox_TextChanged); RtbContextMenu = New ContextMenu(New MenuItem() { New MenuItem("Copy", New EventHandler(AddressOf copyClick)), New MenuItem("Paste", New EventHandler(AddressOf pasteClick)), New MenuItem("-"), New MenuItem("Change Font", New EventHandler(AddressOf fontClick)) }) activeRichTextBox.ContextMenu = RtbContextMenu AddHandler RtbContextMenu.Popup, AddressOf contextMenu_Popup End Sub #Region "Context menu handlers . . ." #End Region #Region "helper methods for the renderer class . . . " #End Region #Region "base class overrides . . ." #End Region End Class
Context Menu Handlers
C#
private void contextMenu_Popup(object sender, EventArgs e) { RtbContextMenu.MenuItems[0].Enabled = CanCopy(); RtbContextMenu.MenuItems[1].Enabled = CanPaste(); } private void copyClick(object sender, EventArgs e) { Copy(); } private void pasteClick(object sender, EventArgs e) { Paste(); } private void fontClick(object sender, EventArgs e) { FontDialog fontDialog1 = new FontDialog(); fontDialog1.ShowColor = true; fontDialog1.Font = activeRichTextBox.SelectionFont; fontDialog1.Color = activeRichTextBox.SelectionColor; this.CurrentCell.ConfirmChanges(); if(fontDialog1.ShowDialog() != DialogResult.Cancel ) { activeRichTextBox.Focus(); activeRichTextBox.SelectionFont = fontDialog1.Font; activeRichTextBox.SelectionColor = fontDialog1.Color; } }
VB
Private Sub contextMenu_Popup(ByVal sender As Object, ByVal e As EventArgs) RtbContextMenu.MenuItems(0).Enabled = CanCopy() RtbContextMenu.MenuItems(1).Enabled = CanPaste() End Sub Private Sub copyClick(ByVal sender As Object, ByVal e As EventArgs) Copy() End Sub Private Sub pasteClick(ByVal sender As Object, ByVal e As EventArgs) Paste() End Sub Private Sub fontClick(ByVal sender As Object, ByVal e As EventArgs) Dim fontDialog1 As New FontDialog() fontDialog1.ShowColor = True fontDialog1.Font = activeRichTextBox.SelectionFont fontDialog1.Color = activeRichTextBox.SelectionColor Me.CurrentCell.ConfirmChanges() If fontDialog1.ShowDialog() <> DialogResult.Cancel Then activeRichTextBox.Focus() activeRichTextBox.SelectionFont = fontDialog1.Font activeRichTextBox.SelectionColor = fontDialog1.Color End If End Sub
Helper methods for the renderer class
The following code is used to create helper method.
C#
//Used to set cell changes flag. private void activeRichTextBox_TextChanged(object sender, EventArgs e) { this.Grid.CurrentCell.IsModified = true; } void AssignRtf(RichTextBox rtb, string rtf) { if (Syncfusion.Drawing.RichTextPaint.IsValidRtf(rtf)) rtb.Rtf = rtf; else rtb.Text = rtf; } protected void FixControlParent(Control control) { if (control.Parent != Grid || !control.Visible) { control.Location = new Point(10000, 10000); control.CausesValidation = false; control.Anchor = AnchorStyles.None; control.Dock = DockStyle.None; Grid.GetWindow().Controls.Add(control); control.Visible = true; SetControl(control); } }
VB
'Used to set cell changes flag. Private Sub activeRichTextBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Me.Grid.CurrentCell.IsModified = True End Sub Private Sub AssignRtf(ByVal rtb As RichTextBox, ByVal rtf As String) If Syncfusion.Drawing.RichTextPaint.IsValidRtf(rtf) Then rtb.Rtf = rtf Else rtb.Text = rtf End If End Sub Protected Sub FixControlParent(ByVal control As Control) If control.Parent <> Grid OrElse (Not control.Visible) Then control.Location = New Point(10000, 10000) control.CausesValidation = False control.Anchor = AnchorStyles.None control.Dock = DockStyle.None Grid.GetWindow().Controls.Add(control) control.Visible = True SetControl(control) End If End Sub
C#
//Used to set cell changes flag. private void activeRichTextBox_TextChanged(object sender, EventArgs e) { this.Grid.CurrentCell.IsModified = true; } void AssignRtf(RichTextBox rtb, string rtf) { if (Syncfusion.Drawing.RichTextPaint.IsValidRtf(rtf)) rtb.Rtf = rtf; else rtb.Text = rtf; } protected void FixControlParent(Control control) { if (control.Parent != Grid || !control.Visible) { control.Location = new Point(10000, 10000); control.CausesValidation = false; control.Anchor = AnchorStyles.None; control.Dock = DockStyle.None; Grid.GetWindow().Controls.Add(control); Control.Visible = true; SetControl(control); } }
VB
'Used to set cell changes flag. Private Sub activeRichTextBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Me.Grid.CurrentCell.IsModified = True End Sub Private Sub AssignRtf(ByVal rtb As RichTextBox, ByVal rtf As String) If Syncfusion.Drawing.RichTextPaint.IsValidRtf(rtf) Then rtb.Rtf = rtf Else rtb.Text = rtf End If End Sub Protected Sub FixControlParent(ByVal control As Control) If control.Parent <> Grid OrElse (Not control.Visible) Then control.Location = New Point(10000, 10000) control.CausesValidation = False control.Anchor = AnchorStyles.None control.Dock = DockStyle.None Grid.GetWindow().Controls.Add(control) control.Visible = True SetControl(control) End If End Sub
Event overrides OnDraw
To handle drawing on cell.
C#
protected override void OnDraw(Graphics g, Rectangle clientRectangle, int rowIndex, int colIndex, GridStyleInfo style) { Rectangle textRectangle = RemoveMargins(clientRectangle, style); if (CurrentCell.HasCurrentCellAt(rowIndex, colIndex) && HasControlValue && ShouldDrawFocused(rowIndex, colIndex) && CurrentCell.IsEditing) { // Position current grid. activeRichTextBox.Bounds = textRectangle;//clientRectangle; activeRichTextBox.BackColor = style.BackColor; if(!activeRichTextBox.Focused) activeRichTextBox.Focus(); } else { string rtf; if (CurrentCell.HasCurrentCellAt(rowIndex, colIndex) && CurrentCell.IsModified && HasControlValue) rtf = ControlValue.ToString(); else rtf = style.Text; Rectangle clipBounds = GetCellBoundsCore(rowIndex, colIndex, true); Color backColor = Color.FromArgb(255, style.Interior.BackColor); textRectangle = new Rectangle(textRectangle.X + 1, textRectangle.Y, textRectangle.Width - 3, textRectangle.Height); //in versions >= 2.0.6.0, use //RichTextPaint.DrawRichText(g, staticRichTextBox, rtf, Grid.PrintingMode, Grid.GridBounds, textRectangle, // clipBounds, backColor, style.WrapText, 100, Grid.IsRightToLeft()); RichTextPaint.DrawRichText(g, staticRichTextBox, rtf, Grid.PrintingMode, Grid.GridBounds, textRectangle, clipBounds, backColor, style.WrapText, 100); } }
VB
Protected Overrides Sub OnDraw(ByVal g As Graphics, ByVal clientRectangle As Rectangle, ByVal rowIndex As Integer, ByVal colIndex As Integer, ByVal style As GridStyleInfo) Dim textRectangle As Rectangle = RemoveMargins(clientRectangle, style) If CurrentCell.HasCurrentCellAt(rowIndex, colIndex) AndAlso HasControlValue AndAlso ShouldDrawFocused(rowIndex, colIndex) AndAlso CurrentCell.IsEditing Then ' Position current grid. activeRichTextBox.Bounds = textRectangle 'clientRectangle; activeRichTextBox.BackColor = style.BackColor If Not activeRichTextBox.Focused Then activeRichTextBox.Focus() End If Else Dim rtf As String If CurrentCell.HasCurrentCellAt(rowIndex, colIndex) AndAlso CurrentCell.IsModified AndAlso HasControlValue Then rtf = ControlValue.ToString() Else rtf = style.Text End If Dim clipBounds As Rectangle = GetCellBoundsCore(rowIndex, colIndex, True) Dim backColor As Color = Color.FromArgb(255, style.Interior.BackColor) textRectangle = New Rectangle(textRectangle.X + 1, textRectangle.Y, textRectangle.Width - 3, textRectangle.Height) 'in versions >= 2.0.6.0, use 'RichTextPaint.DrawRichText(g, staticRichTextBox, rtf, Grid.PrintingMode, Grid.GridBounds, textRectangle, ' clipBounds, backColor, style.WrapText, 100, Grid.IsRightToLeft()); RichTextPaint.DrawRichText(g, staticRichTextBox, rtf, Grid.PrintingMode, Grid.GridBounds, textRectangle, clipBounds, backColor, style.WrapText, 100) End If End Sub
OnInitialize
To set value into the cell control and initialize it.
C#
protected override void OnInitialize(int rowIndex, int colIndex) { base.OnInitialize(rowIndex, colIndex); ControlValue = this.Grid.Model[rowIndex, colIndex].CellValue; FixControlParent(activeRichTextBox); if(this.ControlValue == null) this.ControlValue = ""; AssignRtf(activeRichTextBox, this.ControlValue.ToString()); this.Grid.CurrentCell.IsModified = false; }
VB
Protected Overrides Sub OnInitialize(ByVal rowIndex As Integer, ByVal colIndex As Integer) MyBase.OnInitialize(rowIndex, colIndex) ControlValue = Me.Grid.Model(rowIndex, colIndex).CellValue FixControlParent(activeRichTextBox) If Me.ControlValue Is Nothing Then Me.ControlValue = "" End If AssignRtf(activeRichTextBox, Me.ControlValue.ToString()) Me.Grid.CurrentCell.IsModified = False End Sub
OnClick
C#
protected override void OnClick(int rowIndex, int colIndex, MouseEventArgs e) { if (this.Grid.CurrentCell.HasCurrentCellAt(rowIndex, colIndex)) { // Activate textbox and show caret. GridCellLayout layout = GetCellLayout(rowIndex, colIndex, Grid.Model[rowIndex, colIndex]); bool clickOnCell = layout.ClientRectangle.Contains(new Point(e.X, e.Y)); bool beginEdit = false; if (clickOnCell && (Grid.Model.Options.ActivateCurrentCellBehavior & (GridCellActivateAction.ClickOnCell)) != 0 || (Grid.Model.Options.ActivateCurrentCellBehavior & GridCellActivateAction.SetCurrent) != 0) beginEdit = true; if (beginEdit) { CurrentCell.BeginEdit(); if ((Grid.Model.Options.ActivateCurrentCellBehavior & GridCellActivateAction.SelectAll) == 0) { if (e.Button == MouseButtons.Lef&& (Grid.Model.Options.ActivateCurrentCellBehavior & GridCellActivateAction.PositionCaret) != 0) { Grid.Update(); Point loc = layout.TextRectangle.Location; Point p = new Point(e.X-loc.X, e.Y-loc.Y); Syncfusion.Drawing.ActiveXSnapshot.FakeLeftMouseClick(this.activeRichTextBox, p); } else { Point loc = layout.TextRectangle.Location; Point p = new Point(e.X-loc.X, e.Y-loc.Y); ControlMouseDown(this, new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, 0)); } } else { Point loc = layout.TextRectangle.Location; Point p = new Point(e.X-loc.X, e.Y-loc.Y); ControlMouseDown(this, new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, 0)); } } } base.OnClick(rowIndex, colIndex, e); }
VB
Protected Overrides Sub OnClick(ByVal rowIndex As Integer, ByVal colIndex As Integer, ByVal e As MouseEventArgs) If Me.Grid.CurrentCell.HasCurrentCellAt(rowIndex, colIndex) Then ' Activate textbox and show caret. Dim layout As GridCellLayout = GetCellLayout(rowIndex, colIndex, Grid.Model(rowIndex, colIndex)) Dim clickOnCell As Boolean = layout.ClientRectangle.Contains(New Point(e.X, e.Y)) Dim beginEdit As Boolean = False If clickOnCell AndAlso (Grid.Model.Options.ActivateCurrentCellBehavior And (GridCellActivateAction.ClickOnCell)) <> 0 OrElse (Grid.Model.Options.ActivateCurrentCellBehavior And GridCellActivateAction.SetCurrent) <> 0 Then beginEdit = True End If If beginEdit Then CurrentCell.BeginEdit() If (Grid.Model.Options.ActivateCurrentCellBehavior And GridCellActivateAction.SelectAll) = 0 Then If e.Button = MouseButtons.Lef AndAlso (Grid.Model.Options.ActivateCurrentCellBehavior And GridCellActivateAction.PositionCaret) <> 0 Then Grid.Update() Dim loc As Point = layout.TextRectangle.Location Dim p As New Point(e.X-loc.X, e.Y-loc.Y) Syncfusion.Drawing.ActiveXSnapshot.FakeLeftMouseClick(Me.activeRichTextBox, p) Else Dim loc As Point = layout.TextRectangle.Location Dim p As New Point(e.X-loc.X, e.Y-loc.Y) ControlMouseDown(Me, New MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, 0)) End If Else Dim loc As Point = layout.TextRectangle.Location Dim p As New Point(e.X-loc.X, e.Y-loc.Y) ControlMouseDown(Me, New MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, 0)) End If End If End If MyBase.OnClick(rowIndex, colIndex, e) End Sub
Copy Paste overrides
C#
public override bool CanPaste() { bool ok = false; foreach(string s in Clipboard.GetDataObject().GetFormats()) { if(s == "Rich Text Format Without Objects") { ok = true; break; } } return ok; } public override bool CanCopy() { return activeRichTextBox.SelectionLength != 0; } public override bool Copy() { if (this.HasFocusControl && this.activeRichTextBox.SelectionLength == 0) return false; if (CurrentCell.HasCurrentCellAt(RowIndex, ColIndex) && HasControlValue) { this.activeRichTextBox.Copy(); } return true; } public override bool Paste() { if (this.activeRichTextBox.ReadOnly) return false; if (CurrentCell.HasCurrentCellAt(RowIndex, ColIndex) && HasControlValue) { this.activeRichTextBox.Paste(); return true; } return false; }
VB
Public Overrides Function CanPaste() As Boolean Dim ok As Boolean = False For Each s As String In Clipboard.GetDataObject().GetFormats() If s = "Rich Text Format Without Objects" Then ok = True Exit For End If Next s Return ok End Function Public Overrides Function CanCopy() As Boolean Return activeRichTextBox.SelectionLength <> 0 End Function Public Overrides Function Copy() As Boolean If Me.HasFocusControl AndAlso Me.activeRichTextBox.SelectionLength = 0 Then Return False End If If CurrentCell.HasCurrentCellAt(RowIndex, ColIndex) AndAlso HasControlValue Then Me.activeRichTextBox.Copy() End If Return True End Function Public Overrides Function Paste() As Boolean If Me.activeRichTextBox.ReadOnly Then Return False End If If CurrentCell.HasCurrentCellAt(RowIndex, ColIndex) AndAlso HasControlValue Then Me.activeRichTextBox.Paste() Return True End If Return False End Function
Adding CellModel
C#
this.gridControl1.CellModels.Add("InPlaceRTB", new InPlaceRichTextCellModel(this.gridControl1.Model));
VB
Me.gridControl1.CellModels.Add("InPlaceRTB", New InPlaceRichTextCellModel(Me.gridControl1.Model))
Assigning CellType
C#
GridStyleInfo style = this.gridControl1[2,2]; style.CellType = "InPlaceRTB";
VB
Me.gridControl1.CellModels.Add("InPlaceRTB", New InPlaceRichTextCellModel(Me.gridControl1.Model))
Screenshot
Figure 1: In place editing in RichText cell
Sample Links: