protected override void InitializeForContextCore(VirtualizingLayoutContext context)
        {
            var state = new GroupedLayoutState(context);

            context.LayoutState = state;
            base.InitializeForContextCore(context);
        }
        private Size ArrangeColumn(VirtualizingLayoutContext context, GroupedLayoutState state, ColumnInfo colInfo, int col, int headerIndex, double startingY, bool widthChanged, out int nextHeaderIndex)
        {
            // Note that you MUST call GetOrCreateElement on any items that are in the realized region... that's how
            // the control tracks which elements it shouldn't dispose.

            double y = startingY;
            double x = col * colInfo.ColumnWidth + ColumnSpacing * col;
            int    i = headerIndex;

            var header   = state.GetItemAt(headerIndex);
            var headerEl = context.GetOrCreateElementAt(headerIndex);

            headerEl.Arrange(new Rect(x, y, colInfo.ColumnWidth, headerEl.DesiredSize.Height));
            y += headerEl.DesiredSize.Height + AfterHeaderSpacing;

            i++;

            while (i < context.ItemCount)
            {
                if (!(context.GetItemAt(i) is ViewItemTaskOrEvent))
                {
                    // End of column
                    break;
                }

                var item = state.GetItemAt(i);

                // Logic technically ignores height of item, but heights are short enough it shouldn't matter
                if (y < context.RealizationRect.Top ||
                    y > context.RealizationRect.Bottom)
                {
                    y += item.Height.GetValueOrDefault(40);
                }
                else
                {
                    var el = context.GetOrCreateElementAt(i);
                    //if (widthChanged || !item.Arranged)
                    {
                        el.Arrange(new Rect(x, y, colInfo.ColumnWidth, el.DesiredSize.Height));
                    }

                    y += el.DesiredSize.Height;
                }

                // Include item spacing
                y += ItemSpacing;

                i++;
            }

            // Remove last item spacing (since no item after it)
            y -= ItemSpacing;

            nextHeaderIndex = i;
            return(new Size(colInfo.ColumnWidth, y - startingY));
        }
        private Size MeasureColumn(VirtualizingLayoutContext context, GroupedLayoutState state, Size availableSizeForElements, int headerIndex, double startingY, bool widthChanged, out int nextHeaderIndex)
        {
            // Note that you MUST call GetOrCreateElement on any items that are in the realized region... that's how
            // the control tracks which elements it shouldn't dispose.

            double y = startingY;
            int    i = headerIndex;

            var header = state.GetItemAt(headerIndex);

            //if (widthChanged || header.Height == null)
            {
                var headerEl = context.GetOrCreateElementAt(headerIndex);
                headerEl.Measure(availableSizeForElements);
                header.Height = headerEl.DesiredSize.Height;
            }

            y += header.Height.Value + AfterHeaderSpacing;

            i++;

            while (i < context.ItemCount)
            {
                if (!(context.GetItemAt(i) is ViewItemTaskOrEvent))
                {
                    // End of column
                    break;
                }

                var item = state.GetItemAt(i);

                // Logic technically ignores height of item, but heights are short enough it shouldn't matter
                if (y < context.RealizationRect.Top ||
                    y > context.RealizationRect.Bottom)
                {
                    y += item.Height.GetValueOrDefault(40);
                }
                else
                {
                    // Theoretically since tasks/events are removed and then added back when edited, we know they can never change size...
                    // Therefore we can store their measured size, and only call measure if their width changed or height unknown...
                    // However this didn't work in reality, not sure why.

                    //if (widthChanged || item.Height == null)
                    {
                        var itemEl = context.GetOrCreateElementAt(i);
                        itemEl.Measure(availableSizeForElements);
                        item.Height = itemEl.DesiredSize.Height;
                    }

                    y += item.Height.Value;
                }

                // Include item spacing
                y += ItemSpacing;

                i++;
            }

            // Remove last item spacing (since no item after it)
            y -= ItemSpacing;

            nextHeaderIndex = i;
            return(new Size(availableSizeForElements.Width, y - startingY));
        }