2019-06-06 | Improving the Infragistics UltraGrid Selection Behavior
Infragistics offers a suite of advanced .NET user controls which are extremely customizable. Most of them work pretty well out of the box, but a few can use a little tweaking.
I've done a lot of work with their UltraGrid control, an amped up version of a DataGridView. It can do some pretty neat things that would be a pain to implement yourself, row banding being the main one, but the default selection behavior and appearances can be confusing.
For example, which row do you think is selected in the image above? If you guessed the first row with the blue highlighting and arrow selector on it, you'd be WRONG. That's because the UltraGrid's .Selected property contains no Rows or Cells when it's first loaded, but the first row does get flagged as Active, which gives it a very selected-looking appearance. This can confuse users when they see the first row highlighted and try to perform an action on it, only to be told that they still need to select a row.
Or, if we take a look at the multi-select appearance, we can find a few more oddities.
Here we're only selecting the 2x3 group of cells, but because we dragged top to bottom, we 'landed' on the bottom row and deceptively highlighted the whole thing. We also get a weird dotted outline on whatever cell we landed on, along with an unusual combination of highlighting and non-highlighting. Not great. So, let's make it better!
- Make our own UserControl that inherits from UltraGrid and give it a cool name. How about HyperGrid!
- Get rid of the dotted outline by overwriting the DrawFilter class with a different version I got off of Infragistics' support site, which skips that part of the drawing phase.
- Disable the active and selected appearances on all bands, so rows won't look selected until they actually are.
public partial class HyperGrid : Infragistics.Win.UltraWinGrid.UltraGrid
{
public HyperGrid()
{
InitializeComponent();
// Prevent the grid from displaying the dotted border around the last selected cell (active cell focus rectangle).
this.DrawFilter = new NoFocusRect();
}
private void HyperGrid_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
foreach (Infragistics.Win.UltraWinGrid.UltraGridBand band in e.Layout.Bands.All)
{
// Prevent the grid from using the active appearance and selected appearance.
band.Override.ActiveAppearancesEnabled = Infragistics.Win.DefaultableBoolean.False;
band.Override.SelectedAppearancesEnabled = Infragistics.Win.DefaultableBoolean.False;
}
}
// This class is used to prevent the grid from drawing the focus rectangle on the selected cell.
// http://devcenter.infragistics.com/Support/KnowledgeBaseArticle.aspx?ArticleID=4791
public class NoFocusRect : Infragistics.Win.IUIElementDrawFilter
{
public bool DrawElement(Infragistics.Win.DrawPhase drawPhase, ref Infragistics.Win.UIElementDrawParams drawParams)
{
return true;
}
public Infragistics.Win.DrawPhase GetPhasesToFilter(ref Infragistics.Win.UIElementDrawParams drawParams)
{
return Infragistics.Win.DrawPhase.BeforeDrawFocus;
}
}
}
Now we can replace any UltraGrid with out custom HyperGrid, and it'll be completely unselected on load. Good start, but we can do better. Next I set up some custom colours and highlighting. Some properties can be set immediately in the constructor, others can only be accessed once we're running InitializeLayout and setting up the individual bands.
public partial class HyperGrid : Infragistics.Win.UltraWinGrid.UltraGrid
{
private Color ReadOnlyBackColor = Color.White;
private Color AllowEditBackColor = Color.LightGoldenrodYellow;
private Color BorderColor = Color.DarkGray;
private Color SelectionBorderColor = Color.Firebrick;
private Color SelectionColor = Color.FromArgb(55, 0, 0, 0); // Transparent black to make a gray.
public HyperGrid()
{
InitializeComponent();
// Prevent the grid from displaying the dotted border around the last selected cell (active cell focus rectangle).
this.DrawFilter = new NoFocusRect();
// Set how the selection overlay is displayed. Using these properties is a lot nicer than dealing with half a dozen different Appearance objects.
this.DisplayLayout.SelectionOverlayBorderColor = SelectionBorderColor;
this.DisplayLayout.SelectionOverlayBorderThickness = 2;
this.DisplayLayout.SelectionOverlayColor = SelectionColor;
}
private void HyperGrid_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
foreach (Infragistics.Win.UltraWinGrid.UltraGridBand band in e.Layout.Bands.All)
{
// Prevent the grid from using the active appearance and selected appearance.
band.Override.ActiveAppearancesEnabled = Infragistics.Win.DefaultableBoolean.False;
band.Override.SelectedAppearancesEnabled = Infragistics.Win.DefaultableBoolean.False;
// The active cell will still have a grey border around the inside which will look weird when the rest are highlighted, so we also need to do this.
band.Override.ActiveCellBorderThickness = 0;
// General display settings which need to be set at the Band level.
band.Override.RowSelectors = Infragistics.Win.DefaultableBoolean.True;
band.Override.RowSelectorWidth = 24;
band.Override.BorderStyleCell = Infragistics.Win.UIElementBorderStyle.Solid;
band.Override.BorderStyleRow = Infragistics.Win.UIElementBorderStyle.Solid;
band.Override.HeaderStyle = Infragistics.Win.HeaderStyle.Standard;
band.Override.RowAppearance.BorderColor = BorderColor;
band.Override.CellAppearance.BackColor = AllowEditBackColor;
band.Override.CellAppearance.BorderColor = BorderColor;
}
}
// This class is used to prevent the grid from drawing the focus rectangle on the selected cell.
// http://devcenter.infragistics.com/Support/KnowledgeBaseArticle.aspx?ArticleID=4791
public class NoFocusRect : Infragistics.Win.IUIElementDrawFilter
{
public bool DrawElement(Infragistics.Win.DrawPhase drawPhase, ref Infragistics.Win.UIElementDrawParams drawParams)
{
return true;
}
public Infragistics.Win.DrawPhase GetPhasesToFilter(ref Infragistics.Win.UIElementDrawParams drawParams)
{
return Infragistics.Win.DrawPhase.BeforeDrawFocus;
}
}
}
Getting this set of properties finely tuned took a lot of rooting around, but I'm pretty happy with the final product. Here's what the HyperGrid looks like when it's first loaded, with no selection. Note the yellow background on editable cells, which makes it very clear which fields you're allowed to change versus which ones are locked.
And here's what (multi) selection behavior looks like. No weird full-row highlighting, all selected cells are coloured the same way, and we get a nice customizable border colour that gives it a little flair.