We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

Custom controls inherited from Microsoft textbox and cell renderers/models

Hi all. I am having a problem with the standard GridControl (SyncFusion 3.0.1.0) and was wondering if anyone else had seen this, or if someone could point out what I''m doing wrong. I am using a C# windows application with a GridControl on it. I have a custom control that inherits from the Microsoft TextBox control. The only thing I have changed is to add a public method called Init that puts the string "test" into the Text property of the textbox. I have created a CellModel (called rTextBoxCellModel) that is based on GridGenericControlCellModel. I have a CellRenderer (rTextBoxCellRenderer) that is based on GridGenericControlCellRenderer. (I used whichever example it is that uses the GridGenericControlCellRenderer). Inside the renderer class I create a private variable for my rTextBox: rTextBox rt = new rTextBox(); In the constructor of the renderer class I do this: SetControl(rt); Back out in my Form, I add that CellModel to the grid: gridControl1.CellModels.Add("rTextBox", new rTextBoxCellModel(gridControl1.Model)); Now the fun starts. I set the entire of row 1 to be rTextBoxes: gridControl1.RowStyles[1].CellType = "rTextBox"; That seems to work fine, in that when I run my app and click in a cell on row 1, a textbox appears. I have a button on my form, that when clicked checks the current cell to see if there is a rTextBoxCellRenderer in it. If there is, I create a new instance of a rTextBox and set it equal to the CurrentCell.Renderer.Control. I then create an AutoCompleteStringCollection, add some strings to it prefixed with the column number, and assign it to the rTextBox. (Example: In cell (1, 3) the autocomplete entries are 3ABC, 3DEF. 3GHI). In cell (1, 4) the autocomplete entries are 4ABC, 4DEF. 4GHI). And now the problem: I run my app, click in cell (1,1) and click my button. If I then click in my cell, my textbox appears, and the autocomplete drops down with entries 1ABC, 1DEF, 1GHI. This is correct. I now click in cell (1,2) and click my button, and then go to cell (1,3) and click my button. If I click back into cell (1,1), the autocomplete entries are listed as being 3ABC, 3DEF and 3GHI! Every rTextBox in the grid has the same autocomplete source! I was expecting that each cell effectively contains its own rTextBox control, and so setting properties on one does not affect another. Sorry for the long post, but this is bugging me. I feel that I have missed the point somewhere, and am doing something wrong. There does not seem to be any developer documentation about how CellModels and CellRenderers work, short of the examples. And none of the examples go into anywhere near the details I need. I have found many answers on this forum as SyncFusion support is excellent, so I am holding out hope that I can get an answer for this. I have included my rText classes and my form code as an attachment. Thanks in advance, -Stephen Trinder

14 Replies

ST Stephen Trinder June 23, 2005 04:05 AM UTC

Seems I get a permission denied error when uploading my attachment. I will keep trying.


AD Administrator Syncfusion Team June 23, 2005 08:49 AM UTC

Try this. In your style properties for each one of your special cells, set style.Tag to be the instance of your textbox that your want your special cell control to use for that particular cell. Then at the start of the renderer.Initialize override, get this textbox from the Grid[rowIndex, colIndex].Tag, and set it into the special TextBox member your renderer class is using for its active control. This way, when you move from cell to cell, the the active control for your cell will be initialized to be the desired control. The problem with doing it in abutton click after the cell has become ciurrent is that the state of the renderer has already been set at that point. I think you will need to do the work of setting up teh proper textbox in Initialize so the process ''fits'' into the normal cell architecture.


ST Stephen Trinder June 23, 2005 09:46 PM UTC

Hi Clay. Thanks for that! I''ll give that a go. The button example above is a basic example of what I''m doing so I could explain it easily. In my actual app, I am loading all the cells before the user is able to click anywhere. So am I using the cell model and cell renderer classes correctly? It feels like I''m missing something, but I don''t know what. Thanks, -Stephen PS: I''ll post the code example below.


ST Stephen Trinder June 23, 2005 09:47 PM UTC

Form code: There is a standard SyncFusion gridControl on the form. private void Form1_Load(object sender, EventArgs e) { if (!gridControl1.CellModels.ContainsKey("rText")) gridControl1.CellModels.Add("rText", new rTextCellModel(gridControl1.Model)); gridControl1.RowStyles[1].CellType = "rText"; } rText class code: #region rText /// /// public partial class rText : System.Windows.Forms.TextBox //, IDataGridViewEditingControl { private string m_MyVal = ""; #region Constructors /// /// Default constructor. /// public rText() { this.Validating -= new CancelEventHandler(rText_Validating); this.Validating += new CancelEventHandler(rText_Validating); } void rText_Validating(object sender, CancelEventArgs e) { System.Console.WriteLine("Validating"); } //End of the rText() constructor. public void Init() { this.Text = "12"; } public string MyVal { get { return m_MyVal; } set { m_MyVal = value; } } #endregion } //End of rText class #endregion rText #region rTextCellModel /// /// Implements the data / model part for n rText cell. /// /// /// You typically access cell models through the /// property of the class. /// [Serializable] public class rTextCellModel : GridGenericControlCellModel { /// /// Initializes a new object. /// /// /// Initializes a new object /// and stores a reference to the this cell belongs to. /// /// The for this cell model. /// /// You typically access cell models through the /// property of the class. /// public rTextCellModel(GridModel grid) : base(grid) { } /// /// Initializes a new from a serialization stream. /// /// An object that holds all the data needed to serialize or deserialize this instance. /// Describes the source and destination of the serialized stream specified by info. protected rTextCellModel(SerializationInfo info, StreamingContext context) : base(info, context) { } /// public override GridCellRendererBase CreateRenderer(GridControlBase control) { return new rTextCellRenderer(control, this); } static Size m_rTextControlSize = Size.Empty; /// /// Gets and Sets the rText control size. /// /// Size object public static Size rTextControlSize { get { if (m_rTextControlSize.IsEmpty) { //determine default size of rText when shown in a form TopLevelWindow f = new TopLevelWindow(); f.Location = new Point(10000, 10000); f.Size = new Size(500, 500); f.ShowWindowTopMost(); rText drawrText = new rText(); f.Controls.Add(drawrText); f.Visible = true; m_rTextControlSize = drawrText.Size; } return m_rTextControlSize; } } /// protected override Size OnQueryPrefferedClientSize(Graphics g, int rowIndex, int colIndex, GridStyleInfo style, GridQueryBounds queryBounds) { Size size = rTextControlSize; size.Width += 8; size.Height += 8; return size; } /// /// Sets the default text in the rText box from a GridStyleInfo object. /// /// An rText /// A GridStyleInfo object for the current cell public static void InitalizerText(rText t, GridStyleInfo style) { InitalizerText(t, style.CellValue); } /// /// Sets the default text in the rText box from an object that returns the text in the ToString() method. /// /// An rText /// An object that returns the text in the ToString() method public static void InitalizerText(rText t, object controlValue) { t.Text = controlValue.ToString(); } } #endregion rTextCellModel #region rTextCellRenderer /// /// Implements the renderer part of a rich text cell. /// public class rTextCellRenderer : GridGenericControlCellRenderer { rText drawrText = new rText(); rText editrText = new rText(); rTextCellModel cm; /// /// Initializes a new rTextCellRenderer object for the given GridControlBase /// and GridCellModelBase. /// /// The that displays this cell renderer. /// The that holds data for this cell renderer that should /// be shared among views. /// References to GridControlBase /// and GridCellModelBase will be saved. public rTextCellRenderer(GridControlBase grid, GridCellModelBase cellModel) : base(grid, cellModel) { cm = (rTextCellModel)cellModel; SupportsFocusControl = true; FixControlParent(drawrText); SetControl(editrText); editrText.TextChanged += new EventHandler(editrText_TextChanged); } /// protected override void Dispose(bool disposing) { if (disposing) { editrText.TextChanged -= new EventHandler(editrText_TextChanged); editrText.Dispose(); editrText = null; drawrText.Dispose(); drawrText = null; cm = null; } base.Dispose(disposing); } // protected /*internal*/ override bool OnSaveChanges() /// /// Overridden OnSaveChanges method. /// /// Boolean, true if successful. protected override bool OnSaveChanges() { this.Grid.Model[RowIndex, ColIndex].CellValue = editrText.Text; return true; } /// protected override void OnActivated() { // works around some interaction problem with PropertyGrid specific to this sample; // force redrawing of rText after property grid was refreshed Timer t = new Timer(); t.Tick += new EventHandler(t_Tick); t.Interval = 200; t.Start(); base.OnActivated(); } private void t_Tick(object sender, EventArgs e) { Timer t = (Timer)sender; t.Tick += new EventHandler(t_Tick); t.Dispose(); //this.grid.InvalidateRange(gridRangeInfo.cell(RowIndex, ColIndex)); editrText.Refresh(); } /// protected override void OnDraw(Graphics g, Rectangle clientRectangle, int rowIndex, int colIndex, GridStyleInfo style) { //clientRectangle.Size = GridUtil.Max(clientRectangle.Size, rTextCellModel.rTextControlSize); if (this.ShouldDrawFocused(rowIndex, colIndex)) { style.Control = editrText; editrText.BackColor = style.BackColor; editrText.BorderStyle = BorderStyle.None; editrText.Size = clientRectangle.Size; // editrText.ForeColor = style.Interior.ForeColor; } else { style.Control = drawrText; drawrText.BackColor = style.BackColor; drawrText.BorderStyle = BorderStyle.None; // drawrText.ForeColor = style.Interior.ForeColor; drawrText.Size = clientRectangle.Size; rTextCellModel.InitalizerText(drawrText, style); } base.OnDraw(g, clientRectangle, rowIndex, colIndex, style); } /// protected override void InitializeControlText(object controlValue) { rTextCellModel.InitalizerText(this.editrText, controlValue); } /// protected override Rectangle OnLayout(int rowIndex, int colIndex, GridStyleInfo style, Rectangle innerBounds, Rectangle[] buttonsBounds) { //Rectangle r = base.OnLayout(rowIndex, colIndex, style, innerBounds, buttonsBounds); //r.Size = innerBounds.Size; Rectangle r = innerBounds; //r.Inflate(-1, -1); return r; } private void editrText_TextChanged(object sender, EventArgs e) { //raise CurrentCellChanging event and set CurrentCell.Modified = true if (base.NotifyCurrentCellChanging()) { ControlValue = editrText.Text; base.NotifyCurrentCellChanged(); } } } #endregion rTextCellRenderer


AD Administrator Syncfusion Team June 24, 2005 07:56 AM UTC

The general idea of our cell architecture is that the GridStyleInfo object for the cell holds all the ''state'' information pertaining to a cell. The renderer class handles the drawing of a the cell and the user interaction with a cell. The drawing support must draw both a non-currentcell (generally referred to as static drawing) and the currentcell. With the currentcell drawing support, you generally have access to a ''cell control'' that is used to support both drawing and user interaction with the active current cell. So, with that background, here is what generally happens. When a cell becomes the current cell, the renderer.Initialize methpod is called. In that method, you take the state information stored in the style for the cell (renderer.Grid.Model[rowIndex, colIndex]), and use it to initialize the active control to reflect the proper state based on the information stored in the style. Then as the user leaves this cell, if any state information has changed (known by testing renderer.CurrentCell.IsModified), then renderer.OnSaveChanges is called, and code that you provide there will move the state information from the cell control back into the style for the cell. it is the renderer''s respnsibility to set the CurrentCell.IsModified flag when the state changes. I think maybe the problem you are seeing, trying to dynamically swap out the active cell control from a button click off the grid, is that this does not follow the procedure of the call torenderer.Initialize to properly initialize the control as soon as the cell became current. Then when you try to use the new control, the control is essentially the old control as the currentcell has not really been initialzed with it. That is why I suggested trying to store your state information in the style somehow, and then use renderer.Initialize to move it into your cell control. Normally, you do not use different control instances for each cell that uses the same celltype. Instead, there is a single control that is shared by all cells, and it is just the state information that is stored in the style and used in renderer.Initialize and renderer.OnSaveChanges. So, in your case, you can try storing your cell specific autocomplete information in the cell style, and use a single instance of the textbox, just setting the autocomplete to the textbox in renderer.Initialize.


ST Stephen Trinder June 27, 2005 05:02 AM UTC

Hi Clay. Thank you for that information! I see now where my problem is. Now that I know how the code flows, I can fix my problem, and understand what it is I''m doing. Thanks a lot for your help! -Stephen Trinder


ST Stephen Trinder July 7, 2005 01:49 AM UTC

Hello again. I have tried to create a cellModel and cellRenderer to handle my custom textbox, but I can''t quite get it working. When I click into the cell, I do not get a cursor, and so cannot type. Your suggestion is great in theory, but I just can''t seem to get some code written that does what I want it to do. Here is what I currently have: public class rText2CellModel : GridStaticCellModel { protected rText2CellModel(SerializationInfo info, StreamingContext context): base(info, context) { } public rText2CellModel(GridModel grid) : base(grid) { AllowFloating = false; } public override GridCellRendererBase CreateRenderer(GridControlBase control) { return new rText2CellRenderer(control, this); } public static void InitialiseText(rText t, string value) { t.Text = value; } static Size m_rTextControlSize = Size.Empty; /// /// Gets and Sets the rText control size. /// /// Size object public static Size rTextControlSize { get { if (m_rTextControlSize.IsEmpty) { //determine default size of rText when shown in a form TopLevelWindow f = new TopLevelWindow(); f.Location = new Point(10000, 10000); f.Size = new Size(500, 500); f.ShowWindowTopMost(); rText myrText = new rText(); f.Controls.Add(myrText); f.Visible = true; m_rTextControlSize = myrText.Size; } return m_rTextControlSize; } } protected override Size OnQueryPrefferedClientSize(Graphics g, int rowIndex, int colIndex, GridStyleInfo style, GridQueryBounds queryBounds) { Size size = rTextControlSize; size.Width += 8; size.Height += 8; size.Height += 8; return size; } }// of model public class rText2CellRenderer : GridStaticCellRenderer { rText MyrText; public rText2CellRenderer(GridControlBase grid, GridCellModelBase cellModel) : base(grid, cellModel) { MyrText = new rText(); SupportsFocusControl = true; SetControl(MyrText); MyrText.TextChanged += new EventHandler(MyrText_TextChanged); } void MyrText_TextChanged(object sender, EventArgs e) { if (base.NotifyCurrentCellChanging()) { ControlValue = MyrText.Text; Grid.Model.Modified = true; base.NotifyCurrentCellChanged(); } } protected override void OnInitialize(int rowIndex, int colIndex) { if (Grid.Model[rowIndex, colIndex].Tag != null) MyrText = (rText)Grid.Model[rowIndex, colIndex].Tag; } protected override bool OnSaveChanges() { Grid.Model[RowIndex, ColIndex].Tag = MyrText; return true; } protected override void OnDraw(Graphics g, Rectangle clientRectangle, int rowIndex, int colIndex, GridStyleInfo style) { if (this.ShouldDrawFocused(rowIndex, colIndex)) { style.Control = MyrText; MyrText.BackColor = style.BackColor; MyrText.BorderStyle = BorderStyle.None; MyrText.Size = clientRectangle.Size; } else { style.Control = MyrText; MyrText.BackColor = style.BackColor; MyrText.BorderStyle = BorderStyle.None; MyrText.Size = clientRectangle.Size; } base.OnDraw(g, clientRectangle, rowIndex, colIndex, style); } protected override void InitializeControlText(object controlValue) { rText2CellModel.InitialiseText(MyrText, controlValue.ToString()); } protected override Rectangle OnLayout(int rowIndex, int colIndex, GridStyleInfo style, Rectangle innerBounds, Rectangle[] buttonsBounds) { Rectangle r = innerBounds; return r; } }// of Renderer


AD Administrator Syncfusion Team July 7, 2005 08:58 AM UTC

In your OnDraw method, you are using the same textbox, MyrText, for both the static drawing and active editing drawing. This will not work. Take a look at the \Syncfusion\Essential Studio\3.2.1.0\Windows\Grid.Windows\Samples\CellTypes\CalendarCells. There, the renderer uses two controls, one for use in static drawing (drawCalendar) and the other for use in active editing drawing (editCalendar). You could do something similar. But if all you want to do is draw text in teh cell when the cell is not active, you could just call a static grid method to draw the text, and not have to have a second control. Here is a sample that does this. http://www.syncfusion.com/Support/user/uploads/CustomTextBoxCell_c4139e72.zip The sample has a celltype that uses a TextBox defined by the cell''s style.Tag object. This textBox is used for editing in the cell, but the text is just drawn by a static method when the cell is not editing. In the sample, there are 2 cells that use this celltype. One cell has a TextBox with a red backcolor assigned to its Tag and teh other has a blue TextBox. So, when teh grid is initially displayed, the two cells just display the text with whatever backcolor is set via the styles on that cell. But when the cell becomes the active cell, then blue or red textbox appears.


ST Stephen Trinder July 10, 2005 10:50 PM UTC

Hi Clay. Thanks for clearing that up. That sample sounds great - except the zip file is 0 bytes! Can I get this sample from anywhere else? Thanks, -Stephen Trinder.


AD Administrator Syncfusion Team July 11, 2005 07:24 AM UTC

Try this link. http://www.syncfusion.com/Support/user/uploads/CustomTextBoxCell_142cd8df.zip


ST Stephen Trinder July 12, 2005 12:16 AM UTC

Hi Clay. Great, thanks for that! I see now the difference in what I was trying to do and that example is that I wasn''t creating rText objects in the "form code" to assign to the Tag property. Once I did that, it started working. However, I was hoping there was a way to apply a custom control to an entire row, but have the control in each cell able to have different properties set on it. Using the sample code from the zip (replacing setting 2,2 and 4,4): //Apply the ColoTextBox to row 1. TextBox redTextBox = new TextBox(); gridControl1.RowStyles[1].CellType = "ColoTextBox"; gridControl1.RowStyles[1].Tag = redTextBox; //Now try to set the backcolor on the coloTextBox in individual cells. Object Tag = gridControl1[1, 1].Tag; if (Tag.GetType() == typeof(TextBox)) { TextBox t = (TextBox)Tag; t.BackColor = Color.Orange; } Tag = gridControl1[1, 2].Tag; if (Tag.GetType() == typeof(TextBox)) { TextBox t = (TextBox)Tag; t.BackColor = Color.Green; } Now if I click in cell 1,2, the backcolor is green. However, cell 1,1 is also green! Short of creating a new TextBox object in the "form code" for every cell in the row (instead of setting one for the entire row), is there some way I can define an entire row to be of celltype ColoTextBox, but be able to update the controls in each cell seperately (i.e. change the autocomplete entries, or the backcolor)? Thanks, -Stephen


AD Administrator Syncfusion Team July 12, 2005 01:34 AM UTC

If you want to show the same textbox across cells, then you will have to have 2 textboxes in your cellrenderer, one to handle the edits and teh other to handle teh static drawing. This is what is done in the grid\Samples\CellTypes\CalendarCells sample. Here is the sample from above modified to handle this. http://www.syncfusion.com/Support/user/uploads/CustomTextBoxCell_263d00cb.zip The tag now just passes some property (or properties) that you want to use to initilize the two textboxes used in teh renderer.


ST Stephen Trinder July 12, 2005 02:51 AM UTC

Hi Clay. I apologise for my thick skull not being able to absorb this quickly, but I''m getting it now :-) That last example did the trick, except for one last thing: I really want the colour to stay there when the control is being edited. However, the renderer class InitializeControlText override only gets the controlValue as the argument. It would be cool if I could initialise the editColorTextBox the same way the staticColorTextBox is initalised. Is that possible? Thanks a lot for your help, -Stephen Trinder


AD Administrator Syncfusion Team July 12, 2005 08:13 AM UTC

You could do this in the OnDraw.
protected override void OnDraw(Graphics g, Rectangle clientRectangle, int rowIndex, int colIndex, GridStyleInfo style)
{
	if (this.ShouldDrawFocused(rowIndex, colIndex))
	{
		style.Control = editColorTextBox;
		ColorTextBoxCellModel.InitializeTextBox(editColorTextBox, style);
	}
	else
	{
		style.Control = staticColorTextBox;
		ColorTextBoxCellModel.InitializeTextBox(staticColorTextBox, style);
	}
	base.OnDraw (g, clientRectangle, rowIndex, colIndex, style);
}

Loader.
Live Chat Icon For mobile
Up arrow icon