ListView - VirtualizingTilePanel с подробным элементом развертки - PullRequest
0 голосов
/ 05 марта 2019

Я создал ListView с подробным элементом expad, я установил ItemsPanelTemplate на ElasticWrapPanel.см. рис. Мне нужно посоветовать, как переделать ElasticWrapPanel на ElasticVirtualizingWrapPanel.

Я пробовал VirtualizingTilePanel, но не вижу развернутого элемента детализации, см. рисунок 2. Кто-нибудь посоветует мне.Спасибо за ответ.

ListView с ElasticWrapPanel

enter image description here

ListView с VirtualizingTilePanel

enter image description here

пример исходного кода

ListView XAML:

<ListView ItemsSource="{Binding}" DataContext="ABCDEFGHIJKLMNOPQRSTUVWXYZ">
        <ListView.Resources>
            <DataTemplate x:Key="DetailTempalte">
                <Grid Height="100" Background="Green" Margin="5">
                    <Button Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                </Grid>
            </DataTemplate>

            <ControlTemplate TargetType="{x:Type ListViewItem}" 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 ListViewItem}},Path=(local:ElasticWrapPanel .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>
        </ListView.Resources>

        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="Template" Value="{StaticResource withDetailTemplate}" />
            </Style>
        </ListView.ItemContainerStyle>

        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <local:WrapPaneEx Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ScrollContentPresenter}},Path=ActualWidth}"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>

        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Width="100" Height="100" Background="Blue" Margin="5">
                    <TextBlock Text="{Binding}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

ElasticWrapPanel без виртуализации:

  public class ElasticWrapPanel : Panel
{
    #region Constructor

    public ElasticWrapPanel()
    {
    }

    #endregion

    #region Properties

    [TypeConverter(typeof(LengthConverter))]
    public double ItemMinWidth
    {
        get
        {
            return (double)GetValue(ItemMinWidthProperty);
        }
        set
        {
            SetValue(ItemMinWidthProperty, value);
        }
    }

    public static readonly DependencyProperty ItemMinWidthProperty = DependencyProperty.Register("ItemMinWidth", typeof(double), typeof(ElasticWrapPanel));

    [TypeConverter(typeof(LengthConverter))]
    public double ColumnWidth
    {
        get
        {
            return (double)GetValue(ColumnWidthProperty);
        }
        private set
        {
            SetValue(ColumnWidthProperty, value);
        }
    }

    public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth", typeof(double), typeof(ElasticWrapPanel));

    #endregion

    #region Protected methods

    protected override Size MeasureOverride(Size availableSize)
    {
        _uiElementsRect.Clear();
        _columnCount = CalculateColumnCount(availableSize);

        if (_columnCount > 0)
        {
            ColumnWidth = (int)Math.Floor(availableSize.Width / _columnCount);
            double rowHeight = 0;
            double lastRowHeight = 0;
            int currentColumn = 0;
            int currentRow = 0;

            foreach (UIElement children in InternalChildren)
            {


                children.Measure(availableSize);

                ((FrameworkElement)children).MaxWidth = ColumnWidth;

                var itemLocation = GetItemLocation(children);
                if (itemLocation == null)
                {
                    itemLocation = new ItemLocation(this, children);
                    SetItemLocation(children, itemLocation);
                }
                itemLocation.OnLocationPropertyChanged();


                children.Measure(availableSize);

                rowHeight = Math.Max(rowHeight, children.DesiredSize.Height);

                Rect childrenRect = new Rect(0, 0, 0, 0);
                childrenRect.X = currentColumn * ColumnWidth;
                childrenRect.Y = lastRowHeight;
                childrenRect.Width = ColumnWidth;
                childrenRect.Height = children.DesiredSize.Height;

                _uiElementsRect.Add(children, childrenRect);

                currentColumn++;

                if (currentColumn == _columnCount)
                {
                    lastRowHeight += rowHeight;
                    currentRow++;
                    rowHeight = 0;
                    currentColumn = 0;
                }
            }
        }

        if (_uiElementsRect.Any())
        {
            double maxBottom = _uiElementsRect.Max(ui => ui.Value.Bottom);

            return new Size(availableSize.Width, maxBottom);
        }
        else
            return base.MeasureOverride(availableSize);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        if (_columnCount > 0)
            foreach (KeyValuePair<UIElement, Rect> keyValuePair in _uiElementsRect)
                keyValuePair.Key.Arrange(keyValuePair.Value);

        return base.ArrangeOverride(finalSize);
    }

    #endregion

    #region Private methods

    private int CalculateColumnCount(Size availableSize)
    {
        if (ItemMinWidth == 0 || Double.IsNaN(ItemMinWidth) || Double.IsInfinity(availableSize.Width))
            return 0;
        else
            return (int)Math.Floor(availableSize.Width / ItemMinWidth);
    }

    #endregion

    #region Private members

    int _columnCount = 0;

    IDictionary<UIElement, Rect> _uiElementsRect = new Dictionary<UIElement, Rect>();

    #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(ElasticWrapPanel), new PropertyMetadata(null));
}
public class ItemLocation : System.ComponentModel.INotifyPropertyChanged
{
    public ItemLocation(Panel panel, UIElement itemContainer)
    {
        this._Panel = panel;
        this._ItemContainer = itemContainer;
    }

    private UIElement _ItemContainer;
    private Panel _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);
                }
            }
            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)));
    }
}

VirtualizingTilePanel без детализации развернуть элемент https://gist.github.com/ORRNY66/7bda3087f9758b892f33c5ceccfb84b8

...