How to hide a ListView item when its child DataTemplate has collapsed?

When the parameter's visibility is CarViewControl

set to collapse, it still shows the placeholder where it was before (see screenshot below).

Is there a way to completely hide ListViewItem

when it is tailored?

XAML code

<ScrollViewer>
    <ListView ItemsSource="{Binding CarVM.UserCars}" ShowsScrollingPlaceholders="False">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate>
                <ctrl:CarViewControl Car="{Binding}" Visibility="{Binding HideCar, Converter={ThemeResource InverseVisConverter}}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ScrollViewer>

      

enter image description here

In the image above, there are three CarViewControls

that are reset and then one that is not. One is highlighted. I want them to be completely invisible when the content collapsed.

What I have tried:

  • Setting the height of the control DataTemplate

    to 0 (just to see if it hides a placeholder that had no effect
  • Customization ShowsScrollingPlaceholders

    based False

    on this documentation: MSDN ListView Placeholders

Reason for claiming collapse

Within each CarViewControl, there is a WebView that includes a security token (which maintains that the WebView is registered with a specific website). If you try to pass a WebView by reference, due to what I can only assume, these are security measures, you lose this security token and must re-enter the site. So adding / removing a control from ObservableCollection

won't work in my case.

+3


source to share


2 answers


I would say your design is flawed, but I cannot fix it; so I'll provide a "workaround".

The problem is yours is DataTemplate

collapsing, which is great, but obviously the container it is in is not collapsing. This will not happen because the parent will not inherit from the child. The first implementation is each element is wrapped in ListViewItem

, and you can see that from setting ItemContainerStyle

. This leaves you with two solutions (workarounds). You can set up some triggers on yours ListViewItem

, or you can do something lighter like me and if you don't mind the influence of the UI.

Below is my complete working application. The main thing is that you need to edit the layout / behavior ListViewItem

. In my example, the default values โ€‹โ€‹are not BorderThickeness

and are Padding

not "0, 0, 0, 0" ... Setting these parameters to 0 will completely hide your objects.

MainWindow.xaml

<Window x:Class="CollapsingListViewItemContainers.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:CollapsingListViewItemContainers"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Content="Disappear Car 3" Click="Button_Click" />
            <ListView ItemsSource="{Binding Cars}">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                        <Setter Property="MinHeight" Value="0" />
                        <Setter Property="Padding" Value="0 0 0 0" />
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:Car}">
                        <Grid Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisConverter}}">
                            <StackPanel>
                                <TextBlock Text="{Binding Name}" />
                                <TextBlock Text="{Binding Title}" />
                                <TextBlock Text="{Binding Id}" />
                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
    </Grid>
</Window>

      

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace CollapsingListViewItemContainers
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<Car> _cars = new ObservableCollection<Car>
                        {
                            new Car("Not yours", "Mine", 1),
                            new Car("Not mine", "Yours", 2),
                            new Car("Not ours", "His", 3),
                            new Car("Not ours", "Hers", 4),
                        };
        public ObservableCollection<Car> Cars
        {
            get
            {
                return _cars;
            }
        }
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Cars[2].IsVisible = !Cars[2].IsVisible;
        }
    }

    public class Car : INotifyPropertyChanged
    {
        private bool _isVisible;
        public bool IsVisible
        {
            get
            {
                return _isVisible;
            }
            set
            {
                _isVisible = value;
                NotifyPropertyChanged("IsVisible");
            }
        }
        public string Name
        {
            get; set;
        }

        public string Title
        {
            get; set;
        }

        public int Id
        {
            get; set;
        }

        public Car(string name, string title, int id)
        {
            Name = name;
            Title = title;
            Id = id;
            IsVisible = true;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged(string propertyName)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

      

Edit



Let's be honest, the above was a pretty cheap solution and I was not happy with it after thinking about it for another 3 minutes. The reason I'm not happy is because you can select an item if you have keyboard access. In the example above, click the first item, "hide" the items, then use the mouse and ListView.SelectedItem

it will still change.

So this is a quick solution (workaround: D) to actually remove the item from the list and prevent them from getting focus. Replace ListView.ItemContainerStyle

with this and change the trigger value ActualHeight

according to the values โ€‹โ€‹you see. This will change depending on which OSes I believe I'll leave for you to check. Finally, remember what ListViewItem.DataContext

will belong to the item in ItemsSource

. This means that DataTrigger

associated with IsVisible

is bound to a property Car.IsVisible

.

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Style.Triggers>
            <Trigger Property="ActualHeight" Value="4">
                <Setter Property="Visibility" Value="Collapsed" />
            </Trigger>
            <DataTrigger Binding="{Binding IsVisible}" Value="True">
                <Setter Property="Visibility" Value="Visible" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ListView.ItemContainerStyle>

      

Edit 2 (edited before I even posted the first edit.

Screw it in, don't bind your visibility CarViewControl

; You do not need. You only need to focus on deleting the element itself, and once the element is deleted, the controls are also deleted (although you should check this yourself and change IsTabStop

and IsFocusable

if you can still insert elements in CarViewControl

). Also, since using an arbitrary number with binding is ActualHeight

not very safe, binding directly to the property IsVisible

(or HideCar

in your case) and triggering the visibility should be sufficient ListViewItem

.

Finally, here is my final XAML:

<Window x:Class="CollapsingListViewItemContainers.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:CollapsingListViewItemContainers"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Content="Disappear Car 3" Click="Button_Click" />
            <ListView ItemsSource="{Binding Cars}">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsVisible}" Value="False">
                                <Setter Property="Visibility" Value="Collapsed" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding IsVisible}" Value="True">
                                <Setter Property="Visibility" Value="Visible" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:Car}">
                        <Grid>
                            <StackPanel>
                                <TextBlock Text="{Binding Name}" />
                                <TextBlock Text="{Binding Title}" />
                                <TextBlock Text="{Binding Id}" />
                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
    </Grid>
</Window>

      

+6


source


Instead of trying to hide the item through the UI, I remove the item from the view or collection that the ItemsSource is associated with. This is a cleaner approach and the UI should never bother with the visibility of the element.

EDIT 1

When the user selects a specific car from the quick select menu, it shows that the car is and hides others.

Has the meaning; let this view "UserCars" and suppose ItemsSource is bound to UserCars.

How about this: change the ItemSource to bind to "SelectedCollection". When you want to show UserCars, select SelectedCollection to the UserCars collection.

To constrain the set, you simply point the SelectedCollection to a new SingleCar, which you fill with only UserCars.SelectedItem



XAML:

<ListView ItemsSource="{Binding SelectedCollection}" SelectedItem="{Binding SelectedCar}">

      

ViewModel:

private Car _selectedCar;
public Car SelectedCar { 
  get { return _selectedCar; } 
  set { _selectedCar = value; OnPropertyChanged("SelectedCar"); }
}

private ObservableCollection<Car> _selectedCollection = CarVM.UserCars;
private ObservableCollection<Car> SelectedCollection { 
  get { return _selectedCollection; } 
  set { _selectedCollection = value; OnPropertyChanged("SelectedCollection"); }
}

private ObservableCollection<Car> _originalCollectionForReferenceKeepingOnly;

// called via RelayCommand
public void UserJustSelectedACar()
{
    ObservableCollection<Car> singleItemCollection =  new ObservableCollection<Car>();
    singleItemCollection.Add(SelectedCar);
    _originalCollectionForReferenceKeepingOnly = SelectedCollection;
    SelectedCollection = singleItemCollection;
}

public void ReturnToFullUsedCarsList()
{
    SelectedCollection = _originalCollectionForReferenceKeepingOnly;
}

      

EDIT 2

It looks like you are trying to make the ListView visible as "Car Parts" by hiding these other items. This is inherently a bad idea; another UI element related to the Listview Selected Car

will provide a better solution. Since the new dashboard will simply look at the already generated instance Car

, you would not be affected by any data. Even though you can make this approach work right now, I'm worried that you're going to just cause yourself more grief in the future.

+3


source







All Articles