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?
I havent tried this but you may be able to do something like this. This does not take into account multiple columns of merged cells.
MergedCellUIElement elem = drawParams.Element as MergedCellUIElement;
currentRow = lastRow =elem.Row;
UltraGridCell firstCell = elem.Cell;
currentRow = currentRow.GetSibling(SiblingRow.Next, false, true, false);
{
// draw left, right border on currentRow
lastRow = currentRow;
}
// draw left, bottom, right border on last row
Thanks IGDrewS it prompted me with an alternative process which works. However, the performance suxxx big time. If the folks at Infragistics could help in pointing out anything obvious I.e. you shouldnt be doing it that way or anything it would be appreciated. I am guessing that I should not be processing the entire contents in one hit either way, any details would be good.
If you are wondering the performance hit is evident when you try and scroll up or down - it is significantly slower.
Here is my code in full.
public DrawPhase GetPhasesToFilter(ref UIElementDrawParams drawParams) { if (drawParams.Element is EmbeddableUIElementBase) return Infragistics.Win.DrawPhase.AfterDrawBackColor; else if (drawParams.Element is RowColRegionIntersectionUIElement) return Infragistics.Win.DrawPhase.AfterDrawElement; else return Infragistics.Win.DrawPhase.None; } public bool DrawElement(DrawPhase drawPhase, ref UIElementDrawParams drawParams) { // This part of the code draws a nice background in one of my cells if (drawParams.Element is EmbeddableUIElementBase && drawParams.DrawPhase == Infragistics.Win.DrawPhase.AfterDrawBackColor) { CellUIElement oCellUIElement = (CellUIElement)drawParams.Element.GetAncestor(typeof(CellUIElement)); if (oCellUIElement != null && oCellUIElement.Cell != null) { UltraGridCell oCell = oCellUIElement.Cell; if (oCell == null || oCell.Column.Key != _CellColumn) return false; _factory.ShiftTimeStart = (DateTime)oCell.Row.Cells[_CellStartTime].Value; _factory.ShiftTimeEnd = (DateTime)oCell.Row.Cells[_CellEndTime].Value; _factory.PaintShift((RectangleF)drawParams.Element.RectInsideBorders, drawParams.Graphics); return true; } } // This part draws the border that encompasses single or multiple rows that share a merged cell value else if (drawParams.Element is RowColRegionIntersectionUIElement && drawParams.DrawPhase == Infragistics.Win.DrawPhase.AfterDrawElement) { RowColRegionIntersectionUIElement rowColelem = drawParams.Element as RowColRegionIntersectionUIElement; UltraGridRow currentRow, prevRow, nextRow; foreach (UIElement elem in rowColelem.ChildElements) { if (elem.SelectableItem is UltraGridRow) { currentRow = (UltraGridRow) elem.SelectableItem; if (currentRow.VisibleIndex < 0) continue; // 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 = currentRow.GetUIElement(rsr, csr); if (activeRowUIElement != null) { Border3DSide borders = Border3DSide.Left | Border3DSide.Right; prevRow = currentRow.GetSibling(SiblingRow.Previous , false, true, false); if (prevRow == null || currentRow.VisibleIndex == 0) borders |= Border3DSide.Top; nextRow = currentRow.GetSibling(SiblingRow.Next, false, true, false); // ShiftDate is the merge cell column key if (nextRow == null || nextRow.Cells["ShiftDate"].IsMergedWith(currentRow.Cells["ShiftDate"]) == false) borders |= Border3DSide.Bottom; Rectangle activeRowRect = activeRowUIElement.Rect; drawParams.DrawBorders( UIElementBorderStyle.Rounded1Etched , borders , Color.Sienna , Color.Blue , activeRowRect , drawParams.InvalidRect); } } } } } return true; } return false; }
It's really hard to tell from a code snippet, but you do seem to be looping a lot more than I would think would be neccessary, For one thing, each ColScrollRegion has it's own set of UIElements, so you should not need to loop through them.
I also don't know what you _factory class is doing or why you are handling AfterDrawBackColor.
Tbh you could ignore the _factory bit all it does is render the background of a cell similar to an appointment schedule view. But yes you could ignore that bit. As for :
For one thing, each ColScrollRegion has it's own set of UIElements, so you should not need to loop through them
I'm open to any ideas - I'm free to admit it is a bit dirty atm, just the documentation is a bit sketchy or more likely this is a bit more advanced than the usual.
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 }
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.