Hi,
Is there a way that the encompassing border for a group of rows sharing the same merge cell data be drawn differently?
To explain we have rows of data representing bookings. To make things easier for the user the cell data for the day of the week is set as a merge cell. This means that if there are three bookings for Monday then the user sees once nice Merged cell block for Monday and the three records. There would then be more data records for each day in the month etc..
This is very nice, however, users have asked is it possible to draw a thick line border around the whole 3 row blocks of data (The rows that share the same merge cell value) as it is difficult to distinguish between the day breaks...
Any thoughts would be appreciated. Ta
Cells in the grid do not draw all four side of their borders. If they did, you would end up with a double-thick border between each cell. So it's very hard to change the border of a single cell or a single merged cell.
What I would recommend is that you use a DrawFilter and draw an extra border inside the existng cell border.
If you are not familiar with DrawFilters, check out the Infragistics Knowledge Base which has lots of articles and sample DrawFilter code. Also, get the Infragistics UIElementViewer Utility. It's a huge help when dealing with UIElements.
What you want to do here is a pretty simple drawFilter, but if you get stuck, just post again here and I will try to help.
K just to make sure I dont get the wrong idea. What you are suggesting is to create a drawfilter that dynamically modifies itself. Thinking out loud it would need to retrieve the current row then determine if the row data has a merged value. Then it would need to determine the index of the row to find out if it is the top or bottom row of the merge block and then draw a fake border line appropriately?Remember it is the block of rows that I wish the border to go around. Is the above pseudo the right approach then?
K I have had a look and I have implemented my own IUIElementDrawFilter
In GetPhasesToFilter I have the following:
if (drawParams.Element is RowColRegionIntersectionUIElement) return Infragistics.Win.DrawPhase.AfterDrawElement; else return Infragistics.Win.DrawPhase.None;
In DrawElement I have :if (drawParams.Element is RowColRegionIntersectionUIElement) { UltraGridRow activeRow = _grid.ActiveRow; if (drawParams.DrawPhase == Infragistics.Win.DrawPhase.AfterDrawElement) { // The ActiveRow may be displayed in multiple scroll regions. So // we need to loop through all of them. foreach (RowScrollRegion rsr in _grid.DisplayLayout.RowScrollRegions) { foreach (ColScrollRegion csr in _grid.DisplayLayout.ColScrollRegions) { UIElement activeRowUIElement = activeRow.GetUIElement(rsr, csr); if (activeRowUIElement != null) { Rectangle activeRowRect = activeRowUIElement.Rect; drawParams.DrawBorders ( UIElementBorderStyle.Solid, System.Windows.Forms.Border3DSide.All, Color.Red, Color.Blue, activeRowRect, drawParams.InvalidRect); } } } return true; } }
The areas I have problems with are :
1. How can I get the list of rows that are currently being displayed (I.e. for performance reasons I do not wish to draw items that are not visible).2. Can I assume that if i get the bottom and top rows of similar date that I can use the expansion of their rects to then use for the DrawBorders function?btw I'm already using a pre existing Draw filter hence why I am performing the check for the type of element.
I think you are going about this the hard way. If you want to draw a border around the merged cell, then you don't want to use the RowColRegionIntersectionUIElement. You can use the MergeCellUIElement and simply draw a border inside it. This will be very easy using the RectInsideBorders property of the element to figure out the rect to draw.
Here's a quick and dirty example of how you might do it:
public class MergedCellBorderDrawFilter : IUIElementDrawFilter { #region IUIElementDrawFilter Members bool IUIElementDrawFilter.DrawElement(DrawPhase drawPhase, ref UIElementDrawParams drawParams) { // This switch is really unneccessary, it's just a sanity check switch (drawPhase) { case DrawPhase.AfterDrawElement: // Another sanity check if (drawParams.Element is MergedCellUIElement) { Rectangle rect = drawParams.Element.RectInsideBorders; using (Pen p = new Pen(Color.Red, 3)) drawParams.Graphics.DrawRectangle(p, rect); } break; } return false; } DrawPhase IUIElementDrawFilter.GetPhasesToFilter(ref UIElementDrawParams drawParams) { if (drawParams.Element is MergedCellUIElement) { return DrawPhase.AfterDrawElement; } return DrawPhase.None; } #endregion }
Thanks, not at my computer atm to check, however, its not the merge cell itself I am trying to draw a border around - its a single thick line border around the whole group of rows that share the same merge cell value.
I.e
merge cell / Date || Person name || Cost
Monday || Fred || 100
Monday || John || 200
Tuesday || Ben || 210
The first column is the merge cell so I am trying to draw a thick line border around both the rows for Monday and a border seperately for Tuesday.
K, given the last code stuff a bunch of testing, unfortunately, I ran into two problems :- The lines overwrite the vertical scrollbar if present
- The lines disappear if you scroll the grid data horizontally
So in a nutshell I'm back to my original attempt which so far touch wood works in all scenarios. I havent got any more time to invest working the weekend atm so I'm going to leave it at this. Thanks to everyone for the help, it was very informative.
Nice job with this draw filter. I will have to file this away just in case I need this later on. Or perhaps Infragistics can write this up in a KB article??
Thank you very much that was a big help I made some changes to account for rows where there are no merged cell records - ending up with :private void ProcessRowColRegionIntersectionUIElement(RowColRegionIntersectionUIElement rowColRegionIntersectionUIElement, UIElementDrawParams drawParams) { Dictionary<UIElement, Rectangle> borderRects = new Dictionary<UIElement, Rectangle>(); foreach (UIElement element in rowColRegionIntersectionUIElement.ChildElements) { MergedCellUIElement mergedCellUIElement = element as MergedCellUIElement; if (mergedCellUIElement != null) { Rectangle rect; UltraGridCell cell = mergedCellUIElement.GetContext(typeof(UltraGridCell)) as UltraGridCell; if (cell != null && cell.Column.Key == "ShiftDate") { rect = Rectangle.Empty; UltraGridCell[ mergedCells = cell.GetMergedCells(); foreach (UltraGridCell mergedCell in mergedCells) { UIElement rowElement = mergedCell.Row.GetUIElement(); if (rowElement != null) { if (rect.IsEmpty) rect = rowElement.Rect; else rect = Rectangle.Union(rect, rowElement.Rect); } } borderRects[mergedCellUIElement] = rect; } } else { RowUIElement rowUi = element as RowUIElement; if (rowUi != null) { // Account for rows that are not merged with any others UltraGridCell[ mergedCells = rowUi.Row.Cells["ShiftDate"].GetMergedCells(); if (mergedCells == null || mergedCells.Length < 1) { UIElement rowElement = rowUi.Row.GetUIElement(); if (rowElement != null) borderRects[rowUi] = rowElement.Rect; } } } } foreach (Rectangle rect in borderRects.Values) { using (Pen pen = new Pen(Color.CadetBlue , 1)) drawParams.Graphics.DrawRectangle(pen, rect); } }
I would like to say a big thanks for the support, cheers.
Very interesting will be able to try out in 2 hours - thank you very much for the input and continued response - its appreciated.
Okay, then how about this approach:
public class MergedCellBorderDrawFilter : IUIElementDrawFilter { #region IUIElementDrawFilter Members bool IUIElementDrawFilter.DrawElement(DrawPhase drawPhase, ref UIElementDrawParams drawParams) { // This switch is really unneccessary, it's just a sanity check switch (drawPhase) { case DrawPhase.AfterDrawElement: // Another sanity check if (drawParams.Element is UltraGridUIElement) { UIElement dataAreaUIElement = drawParams.Element.GetDescendant(typeof(DataAreaUIElement)); foreach (UIElement element in dataAreaUIElement.ChildElements) { RowColRegionIntersectionUIElement rowColRegionIntersectionUIElement = element as RowColRegionIntersectionUIElement; if (rowColRegionIntersectionUIElement != null) this.ProcessRowColRegionIntersectionUIElement(rowColRegionIntersectionUIElement, drawParams); } } break; } return false; } private void ProcessRowColRegionIntersectionUIElement(RowColRegionIntersectionUIElement rowColRegionIntersectionUIElement, UIElementDrawParams drawParams) { Dictionary<MergedCellUIElement, Rectangle> borderRects = new Dictionary<MergedCellUIElement, Rectangle>(); foreach (UIElement element in rowColRegionIntersectionUIElement.ChildElements) { MergedCellUIElement mergedCellUIElement = element as MergedCellUIElement; if (mergedCellUIElement != null) { Rectangle rect; UltraGridCell cell = mergedCellUIElement.GetContext(typeof(UltraGridCell)) as UltraGridCell; if (cell != null && cell.Column.Key == "Int32 1") { rect = Rectangle.Empty; UltraGridCell[ mergedCells = cell.GetMergedCells(); foreach (UltraGridCell mergedCell in mergedCells) { UIElement rowElement = mergedCell.Row.GetUIElement(); if (rowElement != null) { if (rect.IsEmpty) rect = rowElement.Rect; else rect = Rectangle.Union(rect, rowElement.Rect); } } borderRects[mergedCellUIElement] = rect; } } } foreach (Rectangle rect in borderRects.Values) { using (Pen pen = new Pen(Color.Red, 3)) drawParams.Graphics.DrawRectangle(pen, rect); } } DrawPhase IUIElementDrawFilter.GetPhasesToFilter(ref UIElementDrawParams drawParams) { if (drawParams.Element is UltraGridUIElement) { return DrawPhase.AfterDrawElement; } return DrawPhase.None; } #endregion }