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 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.
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; }
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 }