Using Milos Windows Forms Grids

Milos provides a set of grid control that can be used in Windows Forms applications (and also as hosted web controls if needed). Although you are free to use whatever grid control you want, we encourage you to use the Milos Grid Controls are they provide more control over appearance, interaction, and performance. All Milos Grids are based on a class called "ListBase". However, ListBase itself is not very exciting when used "out of the box" for this reason, we will start our tour of the Milos Grid Controls with a more sophisticated incarnation: The Office Grid!

The Office Grid Control

The Office Grid Control has been created to provide look and feel as well as functionality similar to the grid used by Microsoft Outlook 2003. Here is an example of the Outlook Grid:

And here is an example of the Milos Office Grid Control for comparison:

Note: The Office Grid also supports icons (as we will see below) even though this screen shot does not make use of that feature.

Adding a Data Bound Office Grid Control to a Windows Form

To follow the samples in this paper, first create a new WinForms project (preferrable C# although you should be able to follow the examples using VB.NET... differences will be pointed out). Then, add references to the SimpleControls.dll and OfficeControls.dll assemblies to your project. Then - if you do not already have the office grid in your toolbox - right-click the VS.NET toolbox and pick "Add Tab". Call the new tab "EPS Office Controls". Then, select that category in the toolbox, right click within it, and pick "Add/Remove Items...". In the dialog that appears, click "Browse..." and pick OfficeControls.dll. This automatically selects all controls within that DLL and adds them to your toolbox. You are now ready to drag and drop an office grid control from your toolbox to Form1 (which was automatically created when you started your new Windows Forms project), size it to use up almost the entire form (perhaps with a few pixels of whitespace around the edged) and anchor it to all 4 sides of the form.

Before we can start doing much with our grid control, we need a data source we can use for our first examples. The easiest way to do that is to create a DataSet and populate it with some dummy data. The following code is not the best I've ever written, but it will serve us nicely as a simple generator for some dummy data:

// We use this dataset to hold test data
private DataSet dsDummy;

public Form1()
{
   
InitializeComponent();

   
// We create a dataset with a single table and some columns
   
this.dsDummy = new DataSet();
   
this.dsDummy.Tables.Add();
   
this.dsDummy.Tables[0].Columns.Add("cSender");
   
this.dsDummy.Tables[0].Columns.Add("cSubject");
   
this.dsDummy.Tables[0].Columns.Add("dReceived",typeof(DateTime));
   
this.dsDummy.Tables[0].Columns.Add("iImportance",typeof(int));
   
this.dsDummy.Tables[0].Columns.Add("iIcon",typeof(int));
   
this.dsDummy.Tables[0].Columns.Add("iFlag",typeof(int));
   
this.dsDummy.Tables[0].Columns.Add("bAttachment",typeof(bool));

   
// We add data
   
for (int i1 = 0;i1<10; i1++)
   
{
       
DateTime dReceived = DateTime.Now.AddDays(i1*-1);
       
for (int i2 = 0;i2<10; i2++)
       
{
           
DataRow oRow = this.dsDummy.Tables[0].NewRow();
           
oRow["cSender"] = "John Doe "+i2.ToString();
           
oRow["cSubject"] = "Your Mortgate #"+(i2+i1).ToString()+" has been approved!";
           
oRow["dReceived"] = dReceived;
           
oRow["iIcon"] = (i2 > 4 ? 0 : 1);
           
oRow["iFlag"] = (i2 > 4 ? 0 : 1);
           
oRow["bAttachment"] = (i2 > 4 ? true : false);
           
if (i2 == 3)
           
{ oRow["iImportance"] = 1; }
           
else
           
{
               
if (i2 == 6)
               
{ oRow["iImportance"] = -1; }
               
else
               
{ oRow["iImportance"] = 0; }
           
}
           
this.dsDummy.Tables[0].Rows.Add(oRow);
       
}
   
}
}

This code shows what your Form1() method (the constructor of your form) should look like. Note the private DataSet field that is created outside that method. (Note: Alternatively, you could also query data from a true data source.).

We can now bind this DataSet to our grid by simply assigning the table within the data set as the grid's DataSource. To do so, add the following line of code as the last line within the Form1() method (just before the last } in the example above):

this.officeGrid1.DataSource = this.dsDummy.Tables[0];

If you now run your application, you will see that the grid appears and it attempts to render rows although they are all blank! That's because our grid control by default does not generate columns. So what we need to do is find the Columns property/collection in the property grid, click the "..." button, and add 8 columns to the dialog that is about to appear:

In this dialog, we will set the Caption, Width, and DataField properties of each column we created. The following table shows the values these properties should be set to in each column:

Col. Caption Width DataField
1 25
2 15 iImportance
3 20 iIcon
4 20 bAttachment
5 Sender 100 cSender
6 Subject 250 cSubject
7 Received 140 dReceived
8 25 iFlag

When you now run your application, you should see a form similar to the following:

The grid is now fully functional, although it does not yet look overly appealing (one could argue that it looks better than the native Windows Forms Data Grid). Note that it is possible to select rows (one or more), sort by clicking on column headers, and even resize columns (note that resizing happens in high-fidelity mode with columns resizing immediately, rather than having a vertical line indicating the result of the resize that is about to happen as the outlook grid does it). Note also that the grid is very performance. There is no lag in resizing columns or when you scroll up and down. Performance of the grid does not change even for very large data sets.

Grouping, Sorting, Formatting, and such...

So how can we go about enhancing the looks of our grid? A quick and easy way is adding some grouping. We can add simple grouping by setting the grid's GroupBy property to "dReceived" and the GroupCaption to "Date:". The result of this operation is shown in the following screen shot:

Note that this grouping works well because we auto-created dummy data with several records falling within the same second. This sort of grouping would not work well ina real-world application, since it would result in a new group for each second. We will address this issue further below.

Note: By default, this grouping option will move the latest (newest) date to the bottom. If we want to reverse that order, we can set the MasterSortFields property to "dReceived desc".

Another item we can improve easily is the date format. If you want the date/time field to be formatted more like the Outlook date time format, you can set the Format property of the 7th column to "ddd MM/d/yyyy h:mm tt". (Note: This uses standard .NET format strings. For more information on formatting search for "formatting overview" in the .NET help). The resulting change can be seen here:

Another easy enhancement we can make is assign icons to the columns that do not yet have any headers. This can be done using the HeaderImage property on individual columns. If you use the regular designer interface to set these properties, you can simply pick a file for each column. The selected files will then automatically be compiled into the application you create, so you do not have to worry about the details. Here is our form with assigned header icons (the used files are in the download ZIP that goes with this paper):

Note: The header of the Office Grid has a slight gradient top to bottom to make it look more three dimensional (the Outlook Grid's header is actually plain gray... ugh!). This means that if your icons have a solid background color it will look "dirty". The icons I used in this example really are GIF files with a transparent background. You can easily create such images using almost any graphics program such as PaintShop.

Although it is not truely required in this example, we can also sort the grid by default rather than waiting for the user to click on a column header. For instance, if we wanted to sort by subject, we can change the Sorted property of the 6th column to "Ascending". Also, if we want to prevent a column from being sorted by, we can change its Sortable property to False. This might be advisable for the very first column in our grid, since that column has no data and really only serves as a "spacer". Note that the sorted column is highlighted by a silverish background (sort column highlighting can be turned off using the HighlightSortedColumn property):

It may not be obvious immediately, but note that the overall sorting of the grid is still based on the date field. Whenever a group is set, the group acts as the main sort order and all other sorting happens within a group. Therefore the second group starts with Mortgage #10. This record is not moved up into the first group, even though we chose to sort by subject. It is also possible to force even more of a global sort order by setting the grid's MasterSortFields property. If we wanted to always keep all the items with an attachment together within a group we could set this property to "bAttachment". This would mean that the grid is now sorted by date, attachment, and subject. We could even take this one step further and set MasterSortFields to "iImportance,bAttachment" if we wanted to sort by receive date, importance, attachment, and subject. The user could change the final sort criteria by clicking column headers, but we would have forced the first 3 sort fields manually. (Whether this makes sense depends on the scenario of course). Note that there could be an alternate UI provided allowing the user to also change those master sort orders and group settings.

Note: Sorting works in this example (as it usually does) because our data source was a data set/ data table. The grid can also be bound to other data sources that may not support sorting (such as arrays or collections). We will discuss these data sources later.

There are a number of additional properties both on the grid, as well as on individual columns, that can be used to change the appearance of the data rendered. I encourage you to experiment with those settings. Most of them change things such as colors and fonts. The grid provides a great deal of flexibility, allowing individual columns or headers to have different fonts, colors, alignments, and much more. The control also supports concepts such as odd and even row back colors. You can also specify whether a column can be resized or not. For more details, see the Milos Class Reference (or experiment!).

Interacting With The Grid

Of course, grids one can not interact with are not overly exciting. But don't worry: All Milos Grids are very interactive beasts! All grids support basic interaction like selecting different rows with the mouse, or the keyboard. Concepts like page up/down, home/end, and mouse wheel are supported by all our grids. The user can also select multiple rows at a time assuming the multi-select property is set to True.

An important task when using the grid is to get access to the rows the user selects. There are two ways to get to that information: The grid has a collection called GridRows. This collection object has a property called SelectedRow, which is a direct reference to the selected grid row. It can be accessed like so:

this.officeGrid1.GriwRows.SelectedRow;

A similar result can be achieved through the SelectedRowIndex property, which returns the numeric index of the selected row.

An alternative to these properties is the SelectedRows collection. This is a collection of all rows that are currently selected. This is important in multi-select scenarios. Note that in multi-select scenarios the SelectedRow (singular) property is still useful and functional. It simply refers to the last row that has been selected. For instance, if the user selects row 1 and then row 5 (with row 1 still selected), the SelectedRows collection will contain two items, while SelectedRow points to row 5.

Whenever the user interacts with the grid, the grid fires events. Most of those events are fairly standard and I won't list them all, but I do want to point out the more interesting ones. The standard events include Click and DoubleClick that fire whenever the user clicks/double-clicks anywhere on the grid. More interesting are row-specific events such as RowClick, RowDoubleClick, RightRowClick, and RowChanged (fires when the selected row changes). The beauty of these events is that they make it very easy to react smartly to user interactions. Perhaps you want to react to a row being double-clicked by opening up a new form. To do so, you need to react to the row double click, get access to the underlying data, and then use that information to launch a new form. Here is an example that does just that:

private void officeGrid1_RowDoubleClick(object Sender, EPS.Windows.Forms.AdvancedControls.RowDoubleClickEventArgs e)
{
    MessageBox.Show(e.GridRow.DataRow["cSubject"].ToString());
}

Note that the event receives special event arguments (the second parameter) that provide information specific to the grid. In our case, the grid is bound to a data set (data table). We can use the GridRow property on the event arguments object to get a reference to the row the user double-clicked on. This row then has a DataRow property, which we can use directly to access the required data. (Note: For non data set items, one can use the DataItem property to get a more generic reference to the bound data).

An interesting concept that is somewhat unique to our grids is the concept of event delays. This allows developers to define a short delay that allows some time to pass before certain events are raised. Imagine a scenario where a user arrows down 50 rows with the keyboard, hence selecting 50 records in succession. If there is an event handler that loads other data whenever a new row is selected, this can be computational very expensive and slow. However, the user probably did not want to look at all the records, but instead just wanted to find a record that happened to be 50 rows down. Specifying a RowSelectDelay of 250 milliseconds fixes this scenario by only firing the row-select-event if the user did not move off that record for a quarter of a second. This generally provides for a very smooth and professional user experience. Try it out! Software with high production values - such as Outlook - support this concept, but I have not yet seen it in a third party grid control. Well, we support this right out of the box! Pretty cool, hm?

Other events of interest are related to headers. There is a ColumnHeaderClick that fires whenever the user clicks a column header (duh!). There also is a ColumnResized event that fires after a column has been resized, and there is a ColumnResizing event that fires continuously while the user resized the column.

Changing the Contents of a Cell

Milos Grids provide many ways to influence how each cell or row within the grid gets rendered. One simple way to take advantage of cell-content-customization are "content retrieval events". These are additional events that fire whenever content for a certain cell needs to be retrieved. Consider the 4th column of our grid for instance. It is meant to indicate whether there is an attachment or not. Right now, the grid attempts to show "True" or "False", although it does a poor job at that, since we didn't provide enough room to do so. But that's OK, since we do not want to see "True" or "False" anyway. perhaps we want to see an empty cell if there was no attachment, and perhaps we want to see a "Y" if there was one. We can implement this by first turning on content retrieval events on the 4th column. These events do not fire automatically for performance reasons, but we can turn them on easily by setting the OwnerContentRetrieval property to True on the 4th column. This causes the ContentRetrieval event to fire. We can trap that event, look whether the event fired for the column we are interested in, and if so, create new content based on the underlying data source. Here is some code that does just that:

private void officeGrid1_ContentRetrieval(object Sender,
    EPS.Windows.Forms.OfficeControls.ContentRetrievalEventArguments e)
{
    if (e.ColumnIndex == 3) // 4th column
   
{
       
if ((bool)e.GridRow.DataRow["bAttachment"])
       
{
           
e.Content = "Y";
       
}
       
else
       
{
           
e.Content = string.Empty;
       
}
   
}
}

As you can see, the event gives us a simple way to access underlying data. This allows us to see whether the bAttachment field is True. If so, we want to display a "Y" and we can do so by simply setting the Content property of the event arguments object to the desired value.

Note that this is only a display mechanism. It does not change or influence the underlying data source in any way. This also means that sorting is done on the underlying data values, no matter what you display in the cell. Depending on the scenario, this may or may not be desired.

We can use the same event to retrieve content for multiple columns. Right now, this event only fires when column 4 needs to be rendered. However, we can also set OwnerContentRetrieval to True on Column 7 and add the following change our event handler to the following:

private void officeGrid1_ContentRetrieval(object Sender,
    EPS.Windows.Forms.OfficeControls.ContentRetrievalEventArguments e)
{
    if (e.ColumnIndex == 3)
// 4th column
   
{
        if ((bool)e.GridRow.DataRow["bAttachment"])
        {
            e.Content = "Y";
        }
       
else
       
{
            e.Content = string.Empty;
        }
    }
    if (e.ColumnIndex == 6)
// 7th column
   
{
        if ((DateTime)e.GridRow.DataRow["dReceived"] > DateTime.Now.AddMinutes(-5))
        {
            e.Content = "Last 5 Minutes";
        }
       
else
       
{
            e.Content = ((DateTime)e.GridRow.DataRow["dReceived"]).ToString("ddd MM/d/yyyy h:mm tt");
        }
    }
}

The result of these changes can be seen in the following screen shot:

Note that in this scenario, when we wanted the regular date and time to be displayed, we had to format the result using ToString() and a format string. The format option we set on column 7 using the Format property is not used anymore whenever we decide to owner-retrieve the cell contents.

We can also perform a similar trick with the group header. To do so, we set the OwnerGroupHeaderContentRetrieval property (on the grid itself) to True. This fires a GroupHeaderRetrieval event. Here is some example code we can put into an event handler:

private void officeGrid1_GroupHeaderRetrieval(object Sender,
    EPS.Windows.Forms.OfficeControls.GroupHeaderRetrievalEventArguments e)
{
    if (((DateTime)e.DataRow["dReceived"]).Date == DateTime.Today)
    {
        e.Content = "Today";
        return;
    }
    if (((DateTime)e.DataRow["dReceived"]).Date == DateTime.Today.AddDays(-1))
    {
        e.Content = "Yesterday";
        return;
    }
    e.Content = ((DateTime)e.DataRow["dReceived"]).ToString("ddd MM/d/yyyy h:mm tt");
}

This results in the following header rendering:

Custom Cell Rendering

This was pretty exciting, but it still didn't quite get us where we wanted to go. Displaying a "Y" whenever we have an attachment is nice but certainly not the level of professionalism we are really aiming for. What we want to do instead is display a little paper-clip icon whenever we have an attachment. While row and cell icons are supported natively by the Office Grid, this feature doesn't help us here, since a cell icon is displayed the same way in every row. We really need to display an icon based on changing conditions in the data source. Luckily there is a straightforward way to do that as well.

First, we set the OwnerContentRetrieval back to False on column #4. Instead, we now set the OwnerDraw property to True on that column. This now gives us full control over how we want the cell to render. Then, we need to load a bitmap object into memory. A good way to do this is to do it when the form starts up and then store the bitmap as a field or property on the form. This way, the bitmap only gets loaded once, which is good for performance. There are many ways to load bitmaps. An ideal way would be to use a compiled-in resource. For this example, we will simply load a file. Add the following code to the very top of your form (just before the constructor and after the data set field we created before):

private Bitmap paperClipImage = new Bitmap(@"\Col4.gif");

Change the path to the image if you have to. This assumes the image is in the bin\debug folder of your solution.

Now we can write the event handler. Setting the OwnerDraw property to True activates the RenderCell event. The code we will put there should be somewhat familiar to you by now:

private void officeGrid1_RenderCell(object Sender,
    EPS.Windows.Forms.OfficeControls.RenderCellEventArguments e)
{
   
if (e.ColumnIndex == 3) // 4th cell
   
{
       
if ((bool)e.GridRow.DataRow["bAttachment"])
       
{
           
e.Graphics.DrawImage(this.paperClipImage,
               
e.SuggestedDrawingRectangle.Left+4,
                e.SuggestedDrawingRectangle.Top);
        
}
   
}
}

We once again look for a certain cell to render. This time however we do not just return a string, but instead, we use the Graphics object that is passed to us and use its DrawImage() method to draw the image we loaded before. The event even tells us where it thinks we should draw by passing along a SuggestedDrawingRectangle. We can use the Left and Top properties of that rectangle to position the image we want to draw. Here is the result:

Great, we are getting there! And it wasn't even all that hard. "Owner drawing" generally sounds much harder than it is, especially since the grid class takes care of most of the hard things for you. For instance, our code still works when people resize columns or scroll up or down (although if this were a real-life app, you might want to consider turning column-resizing off on these columns since there is not much of a business case for letting the user resize them).

To be continued here...

Tricks With Enumerable Data Sources

To be continued here...

 

Using Grids for Data Entry

Milos grids also support data editing directly inside the grid. The grid is very flexible in doing this, by allowing the developer to "host" any .NET control inside the grid. For instance, if a simple text field is to be edited, the grid can use a textbox (in fact, it does so automatically whenever the AutoEdit property of a column is set to true), but if a more complex data type, such as a date field, is to be edited, then the grid can host other controls such as a drop down calendar.

Creating a basic editable grid is simple. For instance, if you want to make the first column of a grid editable (read/write), then simply set the AutoEdit property of that column to true. Here's what that can look like:

Note that this will only set the first column to be editable. You can set each column to be editable (or not) individually (by default they are not editable).

For more advanced types, you can assign a specific control. This can not be done in the property grid, but it has to be done in the form's (or whatever container the grid may live in) constructor. The basic idea is simple: Whenever a cell received focus (my clicking in it, or by keyboard-navigating into it), the desired control will be displayed for editing. When the cell is inactive, the control is not displayed and the grid uses its regular rendering algorithms instead.

This approach is extremely flexible, but there are also some things to be aware of. The most important is that a grids cell can be displayed in two completely different ways: 1) using the grids display-only internal rendering mechanism, and 2) through the hosted control. The two approaches can produce completely different results, although this is generally not advisable. For instance, if a column shows a date and uses a date drop down list for editing, then it is advisable for the column font and the font of the edit control to be the same. The same is true for the date format. Otherwise, the resulting appearance may be extremely confusing.

The following example shows a grid that hosts a drop down calendar for date editing:

The following code configures the column to show that control:

EPS.Windows.Forms.SimpleControls.EPSDateDropDown oDropDown = new EPS.Windows.Forms.SimpleControls.EPSDateDropDown();
oDropDown.Font = this.officeGrid1.Columns[2].ColumnFont;
this.officeGrid1.Columns[2].Format = oDropDown.DateFormat;
this
.officeGrid1.Columns[2].ColumnControl = oDropDown;

This looks much more complicated than it is. The important line is the first line that instantiates the control we want to use. Then, we simply make sure the control's font is the same as the font used by that column. We also go the other way and make sure the column's format is the same as the date control's date format. (Note: Depending on the control, you may or may not have to worry about formats... more often than not, you can ignore the format. But there could be other properties you may want to consider). Finally, we assign the control to be the columns's edit control. Voila! Your grid is now editable using a date drop down calendar.

It is important to realize that the data type of your control matters. The data type has to be compatible with the bound data type. For instance, you could not use a drop down calendar control to bind to a boolean field. It's a little less obvious, but you also could not use a calendar control to bind to a string, or (least obvious) a textbox to bind to a date field. If you attempt to use a wrong control for the data type, you are likely get an Assert informing you of the problem. (In some cases, you may also get a program error... in any event, the problem will be pretty obvious).

Note that in some special cases, it may not be enough to to just have the same fonts and text format. This is the case when the control's data display is not at all based on text. A typical example for this are checkboxes for logical values:

Note that in this case things look a bit "dumb" since the checkbox only shows up whenever a cell is active. All other cells in that column show the string representation of that logical value. At this point, all we can do is owner-draw something that looks like a checkbox, which can be done with the following code (if you're dealing with a grid based on TouchGrid, see HowTo_UseCheckBoxesInMilosTouchGrids):

private void officeGrid1_RenderCell(object Sender, EPS.Windows.Forms.OfficeControls.RenderCellEventArguments e)
{
  
if (e.ColumnIndex == 1)
  
{
     
int iLeft = e.SuggestedDrawingRectangle.Left+2;
     
e.Graphics.DrawRectangle(Pens.Black,iLeft,4,10,10);

     
if ((bool)e.DataRow["Active"] == true)
     
{
        
e.Graphics.DrawLine(Pens.Black,iLeft+2,8,iLeft+4,10);
        
e.Graphics.DrawLine(Pens.Black,iLeft+2,9,iLeft+4,11);
        
e.Graphics.DrawLine(Pens.Black,iLeft+2,10,iLeft+4,12);
        
e.Graphics.DrawLine(Pens.Black,iLeft+4,10,iLeft+8,6);
        
e.Graphics.DrawLine(Pens.Black,iLeft+4,11,iLeft+8,7);
        
e.Graphics.DrawLine(Pens.Black,iLeft+4,12,iLeft+8,8);
      
}
  
}
}

Here is the result:

Note that I also changed the FlatStyle of the checkbox to be flat, so there truely is no visible difference between the checkbox in the active row (which is the actual checkbox control) and the owner-drawn checkboxes in all the other rows (which are generated by the code above). When you host controls in grids, it is generally a good idea not to use 3d borders.

In Milos grids it is not possible to host a control in every row of the grid (such as 14 different checkboxes for every visible row in this example). The Milos grid is highly optimized, and unlike other grid controls, it can show a very large number of rows without much of a performance implication. However, such optimization is not compatible with showing a custom control in every row of the grid. If you absolutely must show a true checkbox control in each grid row, you will have to use a different grid. (Luckily, this is not necessary in most scenarios).

Special Case: Checkboxes in Touch Screen Grids

Coming soon...

 

Advanced Information: Some Control Hosting Details

It is not entirely accurate to say that the grid can host any WinForms control there is. It is more accurate to say that it can host any control there is, but it can only handle controls that fall in several categories. There are two major preconditions that need to be fulfilled for the control to be used in a useful manner. These are based on the fact that the grid has to be able to retrieve the value that is bound to the column and then hand it to the control by setting the control's value property. For instance, if the column is bound to a string and the column hosts a textbox control, then the grid retrieves the string and communicates it to the textbox's Text property (which is of type string). The grid then listens for a TextChanged event, and when that happens, it retrieves the string from the Text property and puts it back into the columns bound source.

But this is only one scenario. Another scenario could have a column bound to a boolean field (true/false) and host a checkbox control. In that case, the value has to be retrieved in its native boolean form and then be put into the Checked property of the checkbox. Subsequently, the grid listens for a CheckedChanged event on the checkbox and whenever that event fires, the new checked value is retrieved and bound to the boolean data source.

As you can see (and imagine), there are many such scenarios. Most of them fall in a few major categories, but one could easily imagine a third party control that uses a completely different combination of field types, property name, and event name. In that case, the grid would be able to host the control, but no meaningful interaction could occur.

So here is what the grid looks for in a contained control:

  1. Is the hosted control derrived from a checkbox? If so, this is handled as a special case.
  2. Is the hosted control derrived from a listbox or drop down list? If so, this is handled as a special case.
  3. Is the hosted control a textbox or a control derrived from a textbox? If so, this is handled as a special case. The Text property is used unless the textbox also have a Value property.
  4. Otherwise: Does the control have a Checked property? If so, this is used.
  5. Otherwise: Does the control have a SelectedIndex property? If so, we use that.
  6. Otherwise: Does the control have a Value property?
  7. Otherwise: We use the Text property which every control has (although it may or may not really be useful).

The grid also listens for ALL these events and whenever any of them fire, the picked property is bound back to the source bound to the grid column:

There are a few other special things that happen when controls are hosted. For instance, keyboard navigation has to be handled differently (compared to read-only grids). Also, different controls require different keyboard handling techniques. Pressing the left key moves the cursor to the left within a textbox (unless the cursor was already at the 1st position within the textbox in which case we would move to the previous column), but it only moves to a previous column if the control was a checkbox. As you can imagine, there are lots of special cases. What happens in different controls when ctrl-a is pressed? Well, it depends.

Anyway: If you want to host a control that does not seem to work, make sure it has one of the properties and one of the events listed here.