Click here to Skip to main content
Click here to Skip to main content

Extending basic WinForms Controls and the DataGrid

By , 26 Jul 2005
 

Figure 1 - Noogen.WinForms.dll added to the Toolbox.

Introduction

This article is for those who are still working in .NET 1.1 and are in need of:

  • An auto-complete ComboBox.
  • A working DataGridComboBoxColumn, not the one that is inherited from the existing buggy DataGridTextBoxColumn.
  • A DataGridCheckBoxColumn that supports single click.
  • Bindable CheckBox.
  • Bindable DateTimePicker.
  • To be able to make any control ReadOnly like the existing property on a TextBox without having to inherit every single existing control (Decorator pattern).
  • Designer support for the newly created DataGridColumnStyles.

First of all, I understand that .NET 2.0 will most likely make 90% of these controls obsolete. I was motivated to code this because my company is in immediate need of these controls in .NET 1.1, they won't run production materials on .NET 2.0 beta, and would rather wait for the final release of .NET 2.0 than purchase third party products.

Features

  • NCheckBox- is a bindable CheckBox.
  • NComboBox
    • AutoComplete - enable hint during input.
    • ShowDropDownDuringInput - force DropDown to display during input.
    • ClearSelection - Clear text selection when the control is not in focus.
    • DisableEntryNotInList - disable entry if the value is not in list. (Including disable of paste - by ContextMenu and Ctrl+V.)
    • CharacterCasing - provide the missing CharacterCasing property as seen in existing TextBox control.
    • Bug fix - allow reset SelectionIndex to -1 when the Text is empty.
  • NDateTimePicker - provide a BindableValue property and allow null to bind as MinDate.
  • ReadOnlyGroup
    • Simulate ReadOnly property on any existing control. This is because the Enabled property makes it hard to read with gray text.
    • Allow Color to indicate control in ReadOnly state.
  • NDataGrid
    • provide rich designer support for the newly created DataGridColumnStyles columns.
    • Allow for double-click with a key combination (default Keys.Control).
    • DataGridTextBoxColumnEx - Extended the existing DataGridTextBoxColumn for bug fixing and custom Color formatting.
    • DataGridComboBoxColumn - Add ComboBox and auto-complete support by implementing NComboBox. It was also re-written from scratch by deriving directly from DataGridColumnStyles.
    • DataGridCheckBoxColumn - Allow for single click to toggle CheckState.

Using the code

You can use these controls like any other control. Download the source, compile, create a Windows application project; browse and add the component to your Toolbox. Figure 1 is the result, after the assembly is added to the Toolbox. I suggest adding it to an empty category such as 'General' or 'My User Controls' so that the controls don't get mixed up with the existing framework controls.

Figure 2 below shows how Noogen.WinForms components would look on a form. You can recognize the ReadOnlyGroup control by the icon which looks like a square-faced guy wearing eye-glasses. ReadOnlyGroup implements IExtenderProvider to provide IsInGroup property (Figure 3). ReadOnlyGroup instance contains ReadOnly property to allow user to dynamically change ReadOnly states.

Figure 2 - Noogen.WinForms components on a form in design mode:

Notice in Figure 3 that Noogen.WinForms.NCheckBox allows you to set TrueValue, FalseValue, and NullValue. In this case, it is default to "Y", "N", and DBNull.Value. Other common values are "0", "1", "True", "False" etc... User can set AllowNull to false for NCheckBox to substitute FalseValue for null.

Figure 3 - Noogen.WinForms.NCheckBox properties:

The attached demo starts by generating states, its abbreviations, and some test data (see DemoForm.GenerateData() method). Strong-typed data binding is made possible through automatic DataSet generation with the XSD designer.

Figure 4 - Strong-typed definition of DataSet used in the demo:

Noogen.WinForms.DataGridComboBoxColumn exposes both DisplayMember and ValueMember as seen in Figure 5. I've also set MappingName to "state_code" with CityStates table.

Figure 5 - What's new with the DataGridColumnStyle collection editor?

The demo in action

Figure 6 - AutoComplete ComboBox demo.

Figure 6 demonstrates the Noogen.WinForms.NComboBox auto-complete function. Notice that the button has been clicked, which causes a change from "- Disable" to "+ Enable". The button allows you to toggle the ReadOnly state of the Details group. ReadOnly BackColor of ReadOnlyGroup can also be configured (gray as seen in Figure 6).

/// <summary>
/// ReadOnly toggle button handler.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnEnableToggle_Click(object sender, 
                               System.EventArgs e)
{
  if (this.readOnlyGroup1.ReadOnly)
  {
    this.btnEnableToggle.Text = "- Disable";
    this.readOnlyGroup1.ReadOnly = false;
  }
  else
  {
    this.btnEnableToggle.Text = "+ Enable";
    this.readOnlyGroup1.ReadOnly = true;
  }
}

Figure 7 - ContextMenu on NDateTimePicker

Figure 7 shows how you can quickly set the value of Noogen.WinForms.NDateTimePicker control with the help of ContextMenu. NDateTimePicker exposes BindableValue property to return DBNull.Value if current value is a MinDate.

Figure 8 - Single click DataGridCheckBoxColumn and CellPainting event.

Notice that the CheckBox on the DataGrid is checked, but it is not so in the Details group. The DataGrid cell is active and so it's in edit mode. The data wouldn't change until the cell looses focus. Also notice the yellow background of HOUSTON. This is possible with the CellPainting event explained in the code below. I have also added CellEditing and CellCommitting events allowing you to dynamically cancel/accept edit or commit. All three DataGridComboBoxColumn, DataGridTextBoxColumnEx, and DataGridCheckBoxColumn contain all the events just mentioned.

this.dataGridTextBoxColumnEx1.CellPainting += 
   new Noogen.WinForms.DataGridCellPaintEventHandler(
                   dataGridTextBoxColumnEx1_CellPainting);
private void dataGridTextBoxColumnEx1_CellPainting(
  object sender, Noogen.WinForms.DataGridCellPaintEventArgs e)
{
  if (e.CellValue.Equals("HOUSTON"))
    e.BackBrush = new SolidBrush(Color.Yellow);
}

You've got to try it to really get the feel of it. If you haven't downloaded it yet, go check out the demo. Also take a look at its source to see how little code you are required to write. Almost everything is designable.

Points of interest

ReadOnlyGroup was made possible with the use of [DllImport] SetWindowLong and WS_DISABLED. This may not be the best way in .NET, but it's the easiest. Another way is to use NativeWindow to subclass WndProc and filter out all Keyboard and mouse input there. If that doesn't work, there is always the [DllImport] of SetWindowHook.

Known problem(s)

  • Sometimes, when the UI is slow, you must keep the mouse in the area of DataGridCheckBoxColumn for about half a second after the first click in-order for the cell to recognize the click. Inspect the DataGridCheckBoxColumn.Edit and IsCursorInside(bounds) and you will see what I mean.

History

  • 26th July - Version 1.0.0 uploaded.
  • 28th July - Article updated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Noogen
Web Developer
United States United States
Member
If at first you don't succeed, refract and refract again...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralProblem with booleansmemberMember 373376212 Jan '10 - 13:18 
Hi,
 
First of all thanks for this wonderful job.
I use Windows XP in Italian and your sample works well.
However if I bind a checkbox column on a grid to a datatable using a BOOLEAN column it does not work anymore.
I tried both a test that I wrote and changing the "Picked" field on your XSD to boolean.
 
I am going to use your grid in an existing project therefore I cannot change the data type on my data tables, but I would like to know if you know a suitable workaround.
 
I tried this
 
[DefaultValue("N"), TypeConverter(typeof(BooleanConverter))]
public object FalseValue
{
get { return this._falseValue; }
set
{
if (value != null && !this._falseValue.Equals(value) || this._falseValue.Equals(false) )

 
(the same for TrueValue)
 
and this
 
private bool IsValidCheckValue(object checkValue)
{
bool retValue = ( checkValue.Equals(this.NullValue) ||
checkValue.Equals(this._falseValue) ||
checkValue.Equals(this._trueValue)
|| checkValue.Equals(true)
|| checkValue.Equals(false)

);
 

and this seems working, at least on a simple sample.
 
Do you see any problem with my workaround?
 
Yours faithfully
 
Luca
Questionbindable check box - why row is changing ?memberparfilko30 Dec '09 - 10:13 
I am tryig to make the same...
I made form with
master-grid -> bindingSource -> somedataset.table
also I indicate each row when RowState become modified.
 
Add detail-controls for current row which is binded to the same bindingSource
All standard controls, as well as your checkbox is work fine: when I scloll grid detail controls reflect current values and row remain unchanged.
 
now I create my property (simple as possible)
    public class MyCheckBox : CheckBox
    {
        [Bindable(true)]
        public string Val
        {
            get { return this.val; }
            set {
                // this.val = value;
            }
        } string val = "N";
    }
... and bind it to the same datasource (some string field)
(or you may bind property Tag)
 
now, when scrolling datagrid ROWS BECOMES MODIFIED
 
What did I missed?
Even this property does not affect anything - still the same.
 
Please help.
 
Thanks,
Vadim
GeneralMultiple Combo's in GridmemberMember 295308310 Jun '09 - 5:18 
Noogen,
First of all thanx for the nice controls!!
 
Now my question: I have a form with the NdataGrid on it. This grid contains 4 comboboxes. Those comboboxes are 'sort of' linked to eachother. In my backend database I have a table with 5 fields: ID, CODE1, CODE2, CODE3, CODE4. This table contains some combinations e.g.
 
1,G,S,B,N
2,G,S,B,B
3,G,S,S,N
4,F,B,S,S
 
This table serves as input for my four comboboxes (Code1 = combo1, Code2 = Combo2 etc..)
 
In another table the user needs to assign a 'combination' to a record. This means that in my other table I also have (in this example) 4 records. Now in the grid I would like to do the following: When the user selects G in the first column, I would like to 'filter' on CODE1. This means that the other combos (2,3 & 4) only have 2 more possibilities as the third is not valid anymore). No if the user proceeds and selects S for the second, still 3 possibilities stay, when he selects B for the third, the list get filtered again and only 2 possibilities stay. This 'flow' to make sure that the user only selects a combination that is 'possible'.
 
Now when I filter the underlaying datasource of my combo's, this is of course done for all the 'lines' in the grid, the filtering should only happen on 'row' level.
Is there any way to do this in the Grid?
 
Regards
Kurt
QuestionWon't Maintain a Selection in ComboBoxmembernweiher8 Nov '07 - 7:12 
Hi,
I am using your ComboBox on a DataGrid (as per you article) ... However, I am populating the combobox with a StringCollection! When selecting from the combobox - I always revert to "null" - not the latest selection! Any ideas?
 

 
commentCodeList = new StringCollection();
 
dataGridComboBoxColumn1 = new Noogen.WinForms.DataGridComboBoxColumn();
 
nDataGrid1.DataSource = m_Table;
nDataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
nDataGrid1.Location = new System.Drawing.Point(16, 16);
nDataGrid1.Name = "nDataGrid1";
nDataGrid1.Size = new System.Drawing.Size(792, 408);
nDataGrid1.TabIndex = 0;
DataGrid1.TableStyles.AddRange(new System.Windows.Forms.DataGridTableStyle[] {this.nDataGridTableStyle1});
 
nDataGridTableStyle1.DataGrid = this.nDataGrid1;
nDataGridTableStyle1.GridColumnStyles.AddRange(new System.Windows.Forms.DataGridColumnStyle[] { this.dataGridTextBoxColumnEx1, this.dataGridTextBoxColumnEx2,
this.dataGridTextBoxColumnEx3, this.dataGridTextBoxColumnEx4,
this.dataGridComboBoxColumn1);

nDataGridTableStyle1.MappingName = m_Table.TableName;
 

dataGridComboBoxColumn1.BackColor = System.Drawing.SystemColors.Window;
dataGridComboBoxColumn1.DataSource = this.commentCodeList;
 
dataGridComboBoxColumn1.DisplayMember = "CommentCode";
dataGridComboBoxColumn1.DropDownWidth = 200;
dataGridComboBoxColumn1.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridComboBoxColumn1.HeaderText = " Comment Code ";
dataGridComboBoxColumn1.MappingName = " Comment Code ";
dataGridComboBoxColumn1.ReadOnly = false;
dataGridComboBoxColumn1.ValueMember = " Comment Code ";
dataGridComboBoxColumn1.Width = 110;
 

In my GenerateData ...
from a list ... I populate the "dataSource"
 
if (obj.Length > 0)
{
commentCodeList.Add(obj);
}
 
nDataGrid1.DataSource = m_Table;
 

It displays the combo box ok ... but it won't maintain the selection when focus leaves the "cell"!
 

 
Neil
GeneralRe: Won't Maintain a Selection in ComboBoxmemberMember 409621311 Dec '08 - 3:31 
Even i'm facing this issue...:(
GeneralSelecting multiple lines with ctrl key has issuesmembermrstu18 May '07 - 19:52 
Hi,
 
I am using the datagrid, and row select works fine for a single line and when holding down shift and selecting multiple lines, but if I use the ctrl key to multi select it has very odd behavour, is this by design?
GeneralRe: Selecting multiple lines with ctrl key has issuesmembermrstu18 May '07 - 20:00 
Opps,
 
I see that is because the 'AutoSelectKey' property is by default the left ctrl key. I changed it to be the alt key and now the normal ctrl key functionality works a treat! Blush | :O )
GeneralSelectionChangeCommitted event not raisedmemberZoltan Balazs16 Mar '07 - 13:52 
There is a bug in the NComboBox code.
When the user types in the combo using autocomplete and hit enter the combo displays the closest
match properly on the UI, but in the code the combo text as well as the selectionIndex didn't change.
More specifically the bug is that the SelectionChangeCommitted event does not fire when user hits
Enter, and the selectedindex goes back to -1. Selecting items from the list with the mouse works.
 
The bugfix can be applied to the OnKeyPress method as follows:
 

this.Text = this.GetItemText(this.Items[index]);
e.Handled = true;
 
// FIX
this.DroppedDown = false;
this.SelectedIndex = index;
// FIX
}
 
// FIX
this.DroppedDown = true;
this.SelectionStart = sToFind.Length;
// FIX
this.SelectionLength = this.Text.Length - this.SelectionStart;
 

 

GeneralRe: handling event on combobox value selected in a datagridmemberaks_16102 Mar '06 - 18:29 
Which language u r working on?
GeneralImprovement for overlapping combobox on datagrid cellmemberxebbmw12 Oct '05 - 5:53 
Here is a modification for showing the datagrid combox box inside a cell. Modify the code inside DataGridComboBoxColumn class, method Edit:
 
old version:
 
if (cellIsVisible)
{
// set the control bound to the current cell
bounds.Offset(this._widthMargin, 2 * this._heightMargin);
bounds.Width -= this._widthMargin;
bounds.Height -= (2 * this._heightMargin);
this.DataGridComboBox.Bounds = bounds;
 
...
 
new version:
 
if (cellIsVisible)
{
// set the control bound to the current cell
//bounds.Offset(this._widthMargin, 2 * this._heightMargin);
bounds.Offset(0,0);
bounds.Width += this._widthMargin;
bounds.Height -= (2 * this._heightMargin);
this.DataGridComboBox.Bounds = bounds;
 
...
 
Regards,
 
Chrys

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 27 Jul 2005
Article Copyright 2005 by Noogen
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid