locked
WPF WrapPanel expanding selected item RRS feed

  • Question

  • Hello,
    I need some advice. I would like to create Wrapping ListView, where the detail of the selected item has been viewed inline in-between other items.

    see picture:

    I created my own ListView control with ItemDetailTemplatem. Everything works well, except on ItemDetail. I need ItemDetail to be across the width ListView. see picture: Here is the source code: ListViewWrapPanelExpand.zip.

    Here is my current version see picture. I will be happy for any advice. Thanks a lot



    source code:

    <controls:ListViewExpand 
        ItemsSource="{Binding CollectionCvs.View}" 
        ItemDetailTemplate="{StaticResource SelectedDetailTemplate}" 
        ItemMianTemplate="{StaticResource ItemMainTemplate}">
        <controls:ListViewExpand.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </controls:ListViewExpand.ItemsPanel>
    </controls:ListViewExpand>

    ItemDetailTemplate

    <DataTemplate x:Key="SelectedDetailTemplate">
        <Canvas x:Name="Canvas1" ClipToBounds="False" Width="147" Height="320" Panel.ZIndex="100" >
    
            <Grid Margin="0" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"
                  ClipToBounds="False" Panel.ZIndex="100"  x:Name="Grid1" Canvas.Top="5"  Background="Transparent"  Height="320" VerticalAlignment="Stretch" >
                <Grid Background="Green" x:Name="ItemDetail1"  Margin="10" >
                    <Button Width="100" Height="30" VerticalAlignment="Center" HorizontalAlignment="Center" Content="{Binding ItemCount}"></Button>
                    <!--<TextBlock Text="Item detail" Foreground="White" FontSize="25" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>-->
                </Grid>
            </Grid>
        </Canvas>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorLevel=1, AncestorType={x:Type ListViewItem}, Mode=FindAncestor}}" Value="True">
    
    
                <!--<Setter TargetName="Canvas1" Property="Height" Value="320"/>
                <Setter TargetName="Grid1" Property="Height" Value="320"/>-->
    
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    ListViewExpand style
    <DataTemplate x:Key="ItemMainTemplate"  >
        <Grid Width="147" Height="258"  Background="Blue" x:Name="Item">
            <TextBlock Text="{Binding ItemCount}" Foreground="White" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        </Grid>
    </DataTemplate>
    
    <Style TargetType="{x:Type controls:ListViewExpand}">
    
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
        <!--<Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>-->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:ListViewExpand}">
                    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                        <ScrollViewer Focusable="False">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="{TemplateBinding Padding}"/>
                        </ScrollViewer>
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsGrouping" Value="true"/>
                                <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style  TargetType="{x:Type controls:ListViewExpandItem}">
        <!--<Setter Property="Padding" Value="10"/>
            <Setter Property="Margin" Value="10"/>-->
        <!--<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />-->
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:ListViewExpandItem}">
    
                    <Border x:Name="Item"
                            Padding="3" 
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
    
    
    
                        <VirtualizingStackPanel IsVirtualizing="True" Orientation="Vertical">
                            <ContentControl x:Name="PART_Main" 
                                                Content="{Binding}" 
                                                ContentTemplate="{TemplateBinding MainTemplate}"
                                    />
                            <ContentControl Panel.ZIndex="100"  Visibility="Collapsed" Name="PART_Detail" Content="{Binding}"  ContentTemplate="{TemplateBinding DetailTemplate}" />
                        </VirtualizingStackPanel>
    
                    </Border>
    
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter TargetName="PART_Detail" Property="Visibility" Value="Visible"  />
                        </Trigger>
    
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    
    </Style>

    Friday, October 13, 2017 9:50 PM

Answers

  • Hi ORRNY66,

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <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:WrapPaneEx.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>
    </Window>
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        class WrapPaneEx : WrapPanel
        {
            protected override Size ArrangeOverride(Size finalSize)
            {
                var size = base.ArrangeOverride(finalSize);
    
                foreach (UIElement fe in this.Children)
                {
                    var itemLocation = GetItemLocation(fe);
                    if (itemLocation == null)
                    {
                        itemLocation = new ItemLocation(this, fe);
                        SetItemLocation(fe, itemLocation);
                    }
                    itemLocation.OnLocationPropertyChanged();
                }
                return size;
            }
    
            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(WrapPaneEx), 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)));
            }
        }
    }

    • Edited by gekkaMVP Sunday, October 15, 2017 12:36 PM
    • Marked as answer by ORRNY66 Sunday, October 15, 2017 3:59 PM
    Sunday, October 15, 2017 12:36 PM

All replies


  • First off you subclass the ListView.  The ListView is a items presenter with headers.  Since you have no headers I would think a ListBox would do.

    I really don't think you can do what you are trying with just a generic WrapPanel.

    Think of what the WrapPanel does.  It takes your ItemsSource and starts adding items (in your case horizontally).  When the next item to add exceeds the width of the WrapPanel it will create a new row and add that item to the new row.

    Now each item added has a container into which the item is presented.  It has a width and height and if you are trying to expand that item so that the width of the "child" item is greater than the parent item you will have problems.  This would mean that the "details" width could only be as wide as the parent of the details.  

    All that to say I'm not sure you are on the right track.

    Another problem is with the WrapPanel itsself.  You can add items and it will continue to add them in row 1 since there is no width provided for the panel.


    Lloyd Sheen

    Saturday, October 14, 2017 9:25 PM
  • Hi ORRNY66,

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <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:WrapPaneEx.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>
    </Window>
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        class WrapPaneEx : WrapPanel
        {
            protected override Size ArrangeOverride(Size finalSize)
            {
                var size = base.ArrangeOverride(finalSize);
    
                foreach (UIElement fe in this.Children)
                {
                    var itemLocation = GetItemLocation(fe);
                    if (itemLocation == null)
                    {
                        itemLocation = new ItemLocation(this, fe);
                        SetItemLocation(fe, itemLocation);
                    }
                    itemLocation.OnLocationPropertyChanged();
                }
                return size;
            }
    
            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(WrapPaneEx), 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)));
            }
        }
    }

    • Edited by gekkaMVP Sunday, October 15, 2017 12:36 PM
    • Marked as answer by ORRNY66 Sunday, October 15, 2017 3:59 PM
    Sunday, October 15, 2017 12:36 PM
  • Thank you very much. Beautiful work. I did not hope anyone would advise me. Once again, thank you very much.
    Sunday, October 15, 2017 3:59 PM