locked
WPF VirtualizingTilePanel expand detail item RRS feed

  • Question

  • I need some advice. I am trying to create a ListBox with an expanded detail item. I have also created VirtualizingTilePanel but unfortunately items in the ListBox do not move so that the detail of the item is visible. I tried it with ElasticWrapPanel but there is no virtualization. I need to maintain virtualization. I will be glad for any advice.

    Source code: ExpandItemVirtualizingTilePanel

    ListBox with ElasticWrapPanel without virtualization:

    ListBox with ElasticWrapPanel without virtualization

    ListBox with VirtualizingTilePanel with virtualization but without scrolling items you cannot see the item detail:

    ListBox with VirtualizingTilePanel with virtualization but without scrolling items you cannot see the item detail

    <ListBox ItemsSource="{Binding MoviesCvs.View,IsAsync=True}"
                  ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
                  ScrollViewer.CanContentScroll="True"
    
                  VirtualizingPanel.CacheLengthUnit="Pixel"
                  VirtualizingPanel.CacheLength="100,100"
                  VirtualizingPanel.ScrollUnit="Pixel"
                  VirtualizingPanel.VirtualizationMode="Recycling">
            <ListBox.Resources>
                <DataTemplate x:Key="DetailTempalte">
                    <DataTemplate.Resources>
                        <DropShadowEffect x:Key="z-depth3" BlurRadius="14" ShadowDepth="4.5" Direction="270" Opacity="0.6"  Color="Black"/>
                    </DataTemplate.Resources>
                    <Grid Height="300" Effect="{StaticResource z-depth3}" Background="White" Margin="10">
                        <cachedImage:Image Stretch="UniformToFill" ImageUrl="{Binding BackgropPath}">
                        </cachedImage:Image>
                    </Grid>
                </DataTemplate>
    
                <ControlTemplate TargetType="{x:Type ListBoxItem}" x:Key="withDetailTemplate">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                            <ContentPresenter  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
    
                        <!-- **************** -->
                        <Canvas Grid.Row="1" x:Name="detailCanvas" 
                                Width="0"
                                Height="{Binding ElementName=detailGrid,Path=ActualHeight}"
                                HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Collapsed">
                            <Grid x:Name="detailGrid" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ScrollContentPresenter}},Path=ActualWidth}"
                                  Canvas.Left="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=(panelVirtualizing:VirtualizingTilePanel.ItemLocation).LocationN.X}">
                                <ContentPresenter ContentTemplate="{DynamicResource ResourceKey=DetailTempalte}" />
                            </Grid>
                        </Canvas>
                        <!-- **************** -->
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
    
                            <Setter TargetName="detailCanvas" Property="Visibility" Value="Visible"/>
                        </Trigger>
    
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsSelected" Value="true"/>
                                <Condition Property="Selector.IsSelectionActive" Value="false"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
                        </MultiTrigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </ListBox.Resources>
    
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Template" Value="{StaticResource withDetailTemplate}" />
                </Style>
            </ListBox.ItemContainerStyle>
    
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
    
                     <!--I need to leave VirtualizingTilePanel there-->
                     <panelVirtualizing:VirtualizingTilePanel 
                        x:Name="VirtualizingTilePanel"  
    
                        IsVirtualizing="True"
    
                        VirtualizingPanel.CacheLengthUnit="Pixel"
                        VirtualizingPanel.CacheLength="100,100"
                        VirtualizingPanel.ScrollUnit="Pixel"
                        VirtualizingPanel.VirtualizationMode="Recycling"
    
                        ChildWidth="260" ChildHeight="455"/>
    
                    <!--<local:WrapPaneEx Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ScrollContentPresenter}},Path=ActualWidth}"/>-->
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
    
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <DataTemplate.Resources>
                        <DropShadowEffect x:Key="z-depth3" BlurRadius="14" ShadowDepth="4.5" Direction="270" Opacity="0.6"  Color="Black"/>
                    </DataTemplate.Resources>
                    <Grid Opacity="1" Effect="{StaticResource z-depth3}"  Width="250" Height="445" Background="White" Margin="5">
                        <StackPanel VerticalAlignment="Top">
                            <cachedImage:Image Stretch="Uniform" ImageUrl="{Binding PosterPath}" >
    
                            </cachedImage:Image>
                            <TextBlock Margin="5,10,5,0" Text="{Binding MovieTitle}" TextTrimming="CharacterEllipsis" Foreground="Black" TextWrapping="Wrap" MaxHeight="50" FontSize="17"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    VirtualizingTilePanel
     public class VirtualizingTilePanel : System.Windows.Controls.VirtualizingPanel, IScrollInfo
    {
        public VirtualizingTilePanel()
        {
            // For use in the IScrollInfo implementation
            RenderTransform = _trans;
        }
    
        // Dependency property that controls the size of the child elements
        public static readonly DependencyProperty ChildWidthProperty
            = DependencyProperty.RegisterAttached("ChildWidth", typeof(double), typeof(VirtualizingTilePanel),
                new FrameworkPropertyMetadata(250d, FrameworkPropertyMetadataOptions.AffectsMeasure |
                                                    FrameworkPropertyMetadataOptions.AffectsArrange));
    
        public static readonly DependencyProperty ChildHeightProperty
            = DependencyProperty.RegisterAttached("ChildHeight", typeof(double), typeof(VirtualizingTilePanel),
                new FrameworkPropertyMetadata(445d, FrameworkPropertyMetadataOptions.AffectsMeasure |
                                                    FrameworkPropertyMetadataOptions.AffectsArrange));
    
        // Accessor for the child size dependency property
        public double ChildWidth
        {
            get { return (double)GetValue(ChildWidthProperty); }
            set { SetValue(ChildWidthProperty, value); }
        }
    
        public double ChildHeight
        {
            get { return (double)GetValue(ChildHeightProperty); }
            set { SetValue(ChildHeightProperty, value); }
        }
    
        /// <summary>
        /// The panel's number of columns
        /// </summary>
        private int _columns;
    
        /// <summary>
        /// Measure the children
        /// </summary>
        /// <param name="availableSize">Size available</param>
        /// <returns>Size desired</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            try
            {
    
                if (availableSize.Width == double.PositiveInfinity || availableSize.Height == double.PositiveInfinity)
                {
                    return Size.Empty;
                }
    
                _columns = (int)(availableSize.Width / ChildWidth);
    
                UpdateScrollInfo(availableSize);
    
                // Figure out range that's visible based on layout algorithm
                int firstVisibleItemIndex, lastVisibleItemIndex;
                GetVisibleRange(out firstVisibleItemIndex, out lastVisibleItemIndex);
    
                // We need to access InternalChildren before the generator to work around a bug
                var children = InternalChildren;
                var generator = ItemContainerGenerator;
    
                // Get the generator position of the first visible data item
                var startPos = generator.GeneratorPositionFromIndex(firstVisibleItemIndex);
    
                // Get index where we'd insert the child for this position. If the item is realized
                // (position.Offset == 0), it's just position.Index, otherwise we have to add one to
                // insert after the corresponding child
                var childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;
    
                using (generator.StartAt(startPos, GeneratorDirection.Forward, true))
                {
                    for (var itemIndex = firstVisibleItemIndex;
                        itemIndex <= lastVisibleItemIndex;
                        ++itemIndex, ++childIndex)
                    {
                        bool newlyRealized;
    
                        // Get or create the child
                        var child = generator.GenerateNext(out newlyRealized) as UIElement;
                        if (child == null) continue;
                        if (newlyRealized)
                        {
                            // Figure out if we need to insert the child at the end or somewhere in the middle
                            if (childIndex >= children.Count)
                            {
                                AddInternalChild(child);
                            }
                            else
                            {
                                InsertInternalChild(childIndex, child);
                            }
                            generator.PrepareItemContainer(child);
                        }
                        //var itemLocation = GetItemLocation(child);
                        //if (itemLocation == null)
                        //{
                        //    itemLocation = new ItemLocation(this, child);
                        //    SetItemLocation(child, itemLocation);
                        //}
                        //itemLocation.OnLocationPropertyChanged();
                        // Measurements will depend on layout algorithm
                        child.Measure(GetChildSize());
                    }
                }
    
                // Note: this could be deferred to idle time for efficiency
                CleanUpItems(firstVisibleItemIndex, lastVisibleItemIndex);
    
                return availableSize;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    
        /// <summary>
        /// Arrange the children
        /// </summary>
        /// <param name="finalSize">Size available</param>
        /// <returns>Size used</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            try
            {
                //var size = base.ArrangeOverride(finalSize);
    
                var generator = ItemContainerGenerator;
                UpdateScrollInfo(finalSize);
                for (var i = 0; i < Children.Count; i++)
                {
                    var child = Children[i];
    
    
    
    
                    // Map the child offset to an item offset
                    var itemIndex = generator.IndexFromGeneratorPosition(new GeneratorPosition(i, 0));
    
                    ArrangeChild(itemIndex, child, finalSize);
    
    
    
    
                    var itemLocation = GetItemLocation(child);
                    if (itemLocation == null)
                    {
                        itemLocation = new ItemLocation(this, child);
                        SetItemLocation(child, itemLocation);
                    }
                    itemLocation.OnLocationPropertyChanged();
                }
    
                return finalSize;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
        //protected override Size ArrangeOverride(Size finalSize)
        //{
        //    var size = base.ArrangeOverride(finalSize);
        //    var generator = ItemContainerGenerator;
        //    UpdateScrollInfo(finalSize);
        //    foreach (UIElement fe in this.Children)
        //    {
        //        var itemIndex = generator.IndexFromGeneratorPosition(new GeneratorPosition(0, 0));
    
        //        ArrangeChild(itemIndex, fe, finalSize);
    
    
        //        var itemLocation = GetItemLocation(fe);
        //        if (itemLocation == null)
        //        {
        //            itemLocation = new ItemLocation(this, fe);
        //            SetItemLocation(fe, itemLocation);
        //        }
        //        itemLocation.OnLocationPropertyChanged();
        //    }
        //    return size;
        //}
        /// <summary>
        /// Revirtualize items that are no longer visible
        /// </summary>
        /// <param name="minDesiredGenerated">first item index that should be visible</param>
        /// <param name="maxDesiredGenerated">last item index that should be visible</param>
        private void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)
        {
            try
            {
                var children = InternalChildren;
                var generator = ItemContainerGenerator;
    
                for (var i = children.Count - 1; i >= 0; i--)
                {
                    GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);
                    var itemIndex = generator.IndexFromGeneratorPosition(childGeneratorPos);
                    if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)
                    {
                        generator.Remove(childGeneratorPos, 1);
                        RemoveInternalChildRange(i, 1);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    
        /// <summary>
        /// When items are removed, remove the corresponding UI if necessary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)
        {
            try
            {
                switch (args.Action)
                {
                    case NotifyCollectionChangedAction.Remove:
                    case NotifyCollectionChangedAction.Replace:
                    case NotifyCollectionChangedAction.Move:
                        RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
                        break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    
        #region Layout specific code
    
        /// <summary>
        /// Calculate the extent of the view based on the available size
        /// </summary>
        /// <param name="availableSize">available size</param>
        /// <param name="itemCount">number of data items</param>
        /// <returns></returns>
        private Size CalculateExtent(Size availableSize, int itemCount)
        {
            try
            {
                var childrenPerRow = CalculateChildrenPerRow(availableSize);
    
                // See how big we are
                return new Size(childrenPerRow * ChildWidth,
                    ChildHeight * Math.Ceiling((double)itemCount / childrenPerRow));
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    
        /// <summary>
        /// Get the range of children that are visible
        /// </summary>
        /// <param name="firstVisibleItemIndex">The item index of the first visible item</param>
        /// <param name="lastVisibleItemIndex">The item index of the last visible item</param>
        private void GetVisibleRange(out int firstVisibleItemIndex, out int lastVisibleItemIndex)
        {
            try
            {
                var childrenPerRow = CalculateChildrenPerRow(_extent);
    
                firstVisibleItemIndex = (int)Math.Floor(_offset.Y / ChildHeight) * childrenPerRow;
                lastVisibleItemIndex =
                    (int)Math.Ceiling((_offset.Y + _viewport.Height) / ChildHeight) * childrenPerRow - 1;
    
                var itemsControl = ItemsControl.GetItemsOwner(this);
                var itemCount = itemsControl.HasItems ? itemsControl.Items.Count : 0;
                if (lastVisibleItemIndex >= itemCount)
                    lastVisibleItemIndex = itemCount - 1;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
    
        }
    
        /// <summary>
        /// Get the size of the children. We assume they are all the same
        /// </summary>
        /// <returns>The size</returns>
        private Size GetChildSize()
        {
            return new Size(ChildWidth, ChildHeight + 350);
        }
    
        /// <summary>
        /// Position a child
        /// </summary>
        /// <param name="itemIndex">The data item index of the child</param>
        /// <param name="child">The element to position</param>
        /// <param name="finalSize">The size of the panel</param>
        private void ArrangeChild(int itemIndex, UIElement child, Size finalSize)
        {
            try
            {
                int childrenPerRow = CalculateChildrenPerRow(finalSize);
    
                int row = itemIndex / childrenPerRow;
                int column = itemIndex % childrenPerRow;
                var columnWidth = Math.Floor(finalSize.Width / _columns);
    
                child.Arrange(new Rect(columnWidth * column, row * ChildHeight, columnWidth,
                    child.DesiredSize.Height));
    
                //var itemLocation = GetItemLocation(child);
                //if (itemLocation == null)
                //{
                //    itemLocation = new ItemLocation(this, child);
                //    SetItemLocation(child, itemLocation);
                //}
                //itemLocation.OnLocationPropertyChanged();
    
            }
            catch (Exception e)
            {
    
            }
        }
    
        /// <summary>
        /// Helper function for tiling layout
        /// </summary>
        /// <param name="availableSize">Size available</param>
        /// <returns></returns>
        private int CalculateChildrenPerRow(Size availableSize)
        {
            try
            {
                // Figure out how many children fit on each row
                int childrenPerRow;
                if (double.IsPositiveInfinity(availableSize.Width))
                    childrenPerRow = Children.Count;
                else
                    childrenPerRow = Math.Max(1, (int)Math.Floor(availableSize.Width / ChildWidth));
                return childrenPerRow;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    
        #endregion
    
        #region IScrollInfo implementation
    
        // See Ben Constable's series of posts at http://blogs.msdn.com/bencon/
        private void UpdateScrollInfo(Size availableSize)
        {
            try
            {
                // See how many items there are
                ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
                int itemCount = itemsControl.HasItems ? itemsControl.Items.Count : 0;
    
                Size extent = CalculateExtent(availableSize, itemCount);
                // Update extent
                if (extent != _extent)
                {
                    _extent = extent;
                    ScrollOwner?.InvalidateScrollInfo();
                }
    
                // Update viewport
                if (availableSize != _viewport)
                {
                    _viewport = availableSize;
                    ScrollOwner?.InvalidateScrollInfo();
                }
            }
            catch (Exception e)
            {
    
            }
        }
    
        public ScrollViewer ScrollOwner { get; set; }
    
        public bool CanHorizontallyScroll { get; set; } = false;
    
        public bool CanVerticallyScroll { get; set; } = false;
    
        public double HorizontalOffset => _offset.X;
    
        public double VerticalOffset => _offset.Y;
    
        public double ExtentHeight => _extent.Height;
    
        public double ExtentWidth => _extent.Width;
    
        public double ViewportHeight => _viewport.Height;
    
        public double ViewportWidth => _viewport.Width;
    
        public void LineUp()
        {
            SetVerticalOffset(VerticalOffset - 10);
        }
    
        public void LineDown()
        {
            SetVerticalOffset(VerticalOffset + 10);
        }
    
        public void PageUp()
        {
            SetVerticalOffset(VerticalOffset - _viewport.Height);
        }
    
        public void PageDown()
        {
            SetVerticalOffset(VerticalOffset + _viewport.Height);
        }
    
        public void MouseWheelUp()
        {
            SetVerticalOffset(VerticalOffset - 10);
        }
    
        public void MouseWheelDown()
        {
            SetVerticalOffset(VerticalOffset + 10);
        }
    
        public void LineLeft()
        {
            throw new InvalidOperationException();
        }
    
        public void LineRight()
        {
            throw new InvalidOperationException();
        }
    
        public Rect MakeVisible(Visual visual, Rect rectangle)
        {
            return new Rect();
        }
    
        public void MouseWheelLeft()
        {
            throw new InvalidOperationException();
        }
    
        public void MouseWheelRight()
        {
            throw new InvalidOperationException();
        }
    
        public void PageLeft()
        {
            throw new InvalidOperationException();
        }
    
        public void PageRight()
        {
            throw new InvalidOperationException();
        }
    
        public void SetHorizontalOffset(double offset)
        {
            throw new InvalidOperationException();
        }
    
        public void SetVerticalOffset(double offset)
        {
            try
            {
                if (offset < 0 || _viewport.Height >= _extent.Height)
                {
                    offset = 0;
                }
                else
                {
                    if (offset + _viewport.Height >= _extent.Height)
                    {
                        offset = _extent.Height - _viewport.Height;
                    }
                }
    
                _offset.Y = offset;
    
                ScrollOwner?.InvalidateScrollInfo();
    
                _trans.Y = -offset /*- 350*/;
    
                // Force us to realize the correct children
                InvalidateMeasure();
            }
            catch (Exception e)
            {
    
            }
        }
    
        private readonly TranslateTransform _trans = new TranslateTransform();
        private Size _extent = new Size(0, 0);
        private Size _viewport = new Size(0, 0);
        private Point _offset;
    
        #endregion
    
    
    
        public static ItemLocation GetItemLocation(DependencyObject obj)
        {
            return (ItemLocation)obj.GetValue(ItemLocationProperty);
        }
    
        public static void SetItemLocation(DependencyObject obj, ItemLocation value)
        {
            obj.SetValue(ItemLocationProperty, value);
        }
    
        public static readonly DependencyProperty ItemLocationProperty = DependencyProperty.RegisterAttached("ItemLocation", typeof(ItemLocation), typeof(VirtualizingTilePanel), new PropertyMetadata(null));
    
    }
    public class ItemLocation : System.ComponentModel.INotifyPropertyChanged
    {
        public ItemLocation(VirtualizingPanel panel, UIElement itemContainer)
        {
            this._Panel = panel;
            this._ItemContainer = itemContainer;
        }
    
        private UIElement _ItemContainer;
        private VirtualizingPanel _Panel;
    
        public Point? Location
        {
            get
            {
                if (_Location == null && _Panel != null && _ItemContainer != null)
                {
                    _Location = _ItemContainer.TranslatePoint(default(Point), _Panel);
                }
                return _Location;
            }
        }
        private Point? _Location;
    
        public Point? LocationN
        {
            get
            {
                if (_LocationN == null && _Location == null && _Panel != null && _ItemContainer != null)
                {
                    Point? np = Location;
                    if (np != null)
                    {
                        _LocationN = new Point(-np.Value.X, -np.Value.Y - 350);
                    }
                }
                return _LocationN;
            }
        }
        private Point? _LocationN;
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        internal void OnLocationPropertyChanged()
        {
            _Location = null;
            _LocationN = null;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Location)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LocationN)));
        }
    }


    • Edited by ORRNY66 Friday, October 4, 2019 1:34 PM
    Wednesday, October 2, 2019 11:30 AM

Answers

All replies

  • Hi ORRNY66,

    From your code sample, Unfortunately, I can not give you specific suggestion.  You may can consult with your team and find a better way. 

    Besides, I found you want to show detailed information about the move, I suggest you can use the The Popup control to show detailed information.

    The Popup control provides a way to display content in a separate window that floats over the current application window relative to a designated element or screen coordinate.

    Note: before you provide a minimal runnable demo, please include your test material and remove all private information, also please do not include malicious files.

    Thank you for your understanding.

    Best regards

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Thursday, October 3, 2019 7:26 AM
  • Thanks, I'll try.
    Thursday, October 3, 2019 3:32 PM
  • So I tried it but it's still not what I need. Otherwise thanks for the advice.

    Friday, October 4, 2019 1:22 PM
  • Hi  ORRNY66,   

    Currently, I don't have a good suggestion for this issue. You may can consult with your team and find a better way or solution to solve the issue.

    Thank you for your understanding.

    Best regards

    Yong Lu

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Monday, October 7, 2019 1:39 AM
  • For those who have the same problem, I found this VirtualizingWrapPanelWithItemExpansion

    https://i.imgur.com/0DVsZBv.gif

    • Marked as answer by ORRNY66 Saturday, February 29, 2020 2:04 PM
    Saturday, February 29, 2020 2:04 PM