protected override void OnDetachingCore(LayoutContext context)
        {
            base.OnDetachingCore(context);

            // clear any state
            context.LayoutState = null;
        }
        protected override void OnAttachedCore(LayoutContext context)
        {
            base.OnAttachedCore(context);

            var state = context.LayoutState as ActivityFeedLayoutState;

            if (state == null)
            {
                // Store any state we might need since (in theory) the layout could be in use by multiple elements simultaneously
                // In reality for the Xbox Activity Feed there's probably only a single instance.
                context.LayoutState = new ActivityFeedLayoutState();
            }
        }
        protected override Size ArrangeLayoutCore(Size finalSize, LayoutContext context)
        {
            // walk through the cache of containers and arrange
            var state = context.LayoutState as ActivityFeedLayoutState;

            if (state == null)
            {
                throw new InvalidOperationException();
            }

            foreach (var key in state.Containers.Keys)
            {
                var container = state.Containers[key].Item1;
                var rect      = state.Containers[key].Item2;
                container.Arrange(rect);
            }

            return(finalSize);
        }
        protected override Size MeasureLayoutCore(Size availableSize, LayoutContext context)
        {
            // In the Xbox scenario there isn't a user grabbing the window and resizing it.  Otherwise, it
            // might be useful to skip a full layout here and return a cached size until some condition is met
            // such as having enough room to fit another column.  That would require extra work like
            // tracking some additional state, comparing the virtualizing rect with the cached value, or setting
            // a timeout to invalidate the layout and resetting the timer while the available width is changing.

            var virtualizingContext = context as VirtualizingLayoutContext;

            if (virtualizingContext == null)
            {
                throw new InvalidOperationException("This layout requires a virtualizing host like Repeater");
            }

            if (this.MinItemSize == Size.Empty)
            {
                var firstElement = virtualizingContext.GetElementAt(0);
                firstElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

                // setting the member value directly to skip invalidating layout
                this._minItemSize = firstElement.DesiredSize;
            }

            // ideal item width that will expand/shrink to fill available space
            double desiredItemWidth = Math.Max(this.MinItemSize.Width, (availableSize.Width - this.ItemSpacing * 3) / 4);

            var state = context.LayoutState as ActivityFeedLayoutState;

            var realizationRect = virtualizingContext.GetRealizationRect();

            // estimate the extent by finding the last item and getting its bottom/right position
            var extentHeight = ((int)(context.ItemCount / 3) - 1) * (this.MinItemSize.Height + this.RowSpacing) + this.MinItemSize.Height;

            // Determine which rows need to be realized
            var firstRow = Math.Max((int)(realizationRect.Y / (this.MinItemSize.Height + this.RowSpacing)) - 1, 0);
            var lastRow  = Math.Min((int)(realizationRect.Bottom / (this.MinItemSize.Height + this.RowSpacing)) + 1, (int)(context.ItemCount / 3));

            // Determine which items fall on those rows and determine the rect for each item
            SortedDictionary <int, Tuple <UIElement, Rect> > positions = new SortedDictionary <int, Tuple <UIElement, Rect> >();

            Rect[] rectArray = new Rect[3];
            rectArray[0].Height = rectArray[1].Height = rectArray[2].Height = this.MinItemSize.Height;

            for (int rowIndex = firstRow; rowIndex < lastRow; rowIndex++)
            {
                var yoffset = rowIndex * (this.MinItemSize.Height + this.RowSpacing);

                // initialize Y positions of the rects
                rectArray[0].Y = rectArray[1].Y = rectArray[2].Y = yoffset;

                var firstItemIndex = rowIndex * 3;
                var wnn            = firstItemIndex % 3 == 0 && firstItemIndex % 6 != 0;
                var nnw            = firstItemIndex % 3 == 0 && firstItemIndex % 6 == 0;

                // Update the rects to what is required for a given row
                if (wnn)
                {
                    // Left tile (wide)
                    rectArray[0].X     = 0;
                    rectArray[0].Width = (desiredItemWidth * 2 + this.ItemSpacing);
                    // Middle tile (narrow)
                    rectArray[1].X     = rectArray[0].Right + this.ItemSpacing;
                    rectArray[1].Width = desiredItemWidth;
                    // Right tile (narrow)
                    rectArray[2].X     = rectArray[1].Right + this.ItemSpacing;
                    rectArray[2].Width = desiredItemWidth;
                }
                else if (nnw)
                {
                    // Left tile (narrow)
                    rectArray[0].X     = 0;
                    rectArray[0].Width = desiredItemWidth;
                    // Middle tile (narrow)
                    rectArray[1].X     = rectArray[0].Right + this.ItemSpacing;
                    rectArray[1].Width = desiredItemWidth;
                    // Right tile (wide)
                    rectArray[2].X     = rectArray[1].Right + this.ItemSpacing;
                    rectArray[2].Width = desiredItemWidth * 2 + this.ItemSpacing;
                }


                UIElement container = null;
                for (int i = 0; i < 3; i++)
                {
                    var index = firstItemIndex + i;
                    if (state.Containers.ContainsKey(index))
                    {
                        container = state.Containers[index].Item1;
                        var rect = rectArray[index % 3];
                        positions.Add(index, new Tuple <UIElement, Rect>(container, rect));

                        state.Containers.Remove(index);
                    }
                    else
                    {
                        // GetElementAt will return a new element each time it is called in a virtualizing
                        // context so in any given layout pass it should only be called once for a given
                        // index and only for those that need to be measured again.  In this layout we
                        // only call measure on the items that are being realized for the first time or
                        // have just been recycled.
                        container = virtualizingContext.GetElementAt(index);
                        container.Measure(new Size(rectArray[i].Width, rectArray[i].Height));
                        positions.Add(index, new Tuple <UIElement, Rect>(container, rectArray[i]));
                    }
                }
            }

            // clear anything that wasn't used
            foreach (var item in state.Containers)
            {
                virtualizingContext.ClearElement(state.Containers[item.Key].Item1);
            }

            // store the containers we used for use on the next layout pass
            state.Containers = positions;

            // Report a desired size for the layout
            return(new Size(desiredItemWidth * 4 + this.ItemSpacing * 2, extentHeight));
        }