Search code examples
c#wpflistviewwpf-controls

How can I control wrapanel so if I removed an item the order doesn't change


In a listview, I am using Wrappanel as ItemsPanelTemplate

<ListView ItemsSource="{Binding TodoItemViewModels}"
    ScrollViewer.VerticalScrollBarVisibility="Disabled"
    Drop="Card_Drop"
    AllowDrop="True"
    DragLeave="Card_DragLeave"
    IsEnabled="{Binding Disable, ElementName=root}"
    Background="Green">

    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Margin="0" Orientation="Vertical" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>


    <!-- Displaying Cards in CardCollection View -->
    <ListView.ItemTemplate>
        <DataTemplate>            
            <Border BorderThickness="2"
                BorderBrush="BlanchedAlmond" 
                Opacity="1"
                Background="BlanchedAlmond"
                Width="25" Height="30"
                MouseMove="Card_MouseMove"
                CornerRadius="5"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch">
                
                <TextBlock Text="{Binding Cardstr}"
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    Foreground="{Binding Cardclr}"
                    Background="BlanchedAlmond" />
            </Border>
        </DataTemplate>
    </ListView.ItemTemplate>

</ListView>

before removing an item

How can I fix the Listview, so if I removed an item there would be an empty slot in its place instead of the list getting rearranged like in the following img: after removing an item

I tried Wrapanel and Gridview both are not working properly. I don't want to insert cell by cell in a grid as it will complicate the program


Solution

  • Instead of removing the card from the source collection, you should set a property of it to indicate that you want to display another template for that particular card.

    You could for example add an IsEmpty property to your class that contains the Cardstr and Cardclr properties and then use ContentControl with a DataTrigger to dynamically change the template:

    <ListView ItemsSource="{Binding TodoItemViewModels}"
        ScrollViewer.VerticalScrollBarVisibility="Disabled"
        Drop="Card_Drop"
        AllowDrop="True"
        DragLeave="Card_DragLeave"
        IsEnabled="{Binding Disable, ElementName=root}"
        Background="Green">
    
        <ListView.Resources>
            <DataTemplate x:Key="cardTemplate">
                <Border BorderThickness="2"
                     BorderBrush="BlanchedAlmond" 
                     Opacity="1"
                     Background="BlanchedAlmond"
                     Width="25" Height="30"
                     MouseMove="Card_MouseMove"
                     CornerRadius="5"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch">
    
                    <TextBlock Text="{Binding Cardstr}"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch"
                         Foreground="{Binding Cardclr}"
                         Background="BlanchedAlmond" />
                </Border>
            </DataTemplate>
            <DataTemplate x:Key="emptyTemplate">
                <Border>
                    <TextBlock>empty...</TextBlock>
                </Border>
            </DataTemplate>
        </ListView.Resources>
    
        <ListView.ItemTemplate>
            <DataTemplate>
                <ContentControl Content="{Binding}">
                    <ContentControl.Style>
                        <Style TargetType="ContentControl">
                            <Setter Property="ContentTemplate" Value="{StaticResource cardTemplate}" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsEmpty}" Value="True">
                                    <Setter Property="ContentTemplate" Value="{StaticResource emptyTemplate}" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </DataTemplate>
        </ListView.ItemTemplate>
    
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Margin="0" Orientation="Vertical" />
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
    
    </ListView>
    

    Make sure that your class implements the INotifyPropertyChanged interface:

    public class Card : INotifyPropertyChanged
    {
        ...
    
        private bool _isEmpty;
        public bool IsEmpty
        {
            get { return _isEmpty; }
            set { _isEmpty = value; NotifyPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged; 
        private void NotifyPropertyChanged(string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    And then you simply set the IsEmpty property of the item that you want to "remove".