当 DataTemplate 子项折叠时如何隐藏 ListView 项目占位符?
How do you hide a ListView Item placeholder when it's DataTemplate child is collapsed?
当 CarViewControl
的可见性设置为折叠时,它仍会在原来的位置显示一个占位符(请参见下面的屏幕截图)。
有什么方法可以在 ListViewItem
折叠时完全隐藏它?
XAML代码
<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>
在上图中,有 3 个 CarViewControls
已折叠,其后有 1 个未折叠。一个是突出显示的。我希望它们在内容折叠时完全不可见。
我尝试过的:
- 将
DataTemplate
控件的高度设置为 0(只是为了查看它是否隐藏了无效的占位符
- 根据此文档将
ShowsScrollingPlaceholders
设置为 False
:MSDN ListView Placeholders
折叠要求的原因
在每个 CarViewControl 中,存在一个包含安全令牌的 WebView(它维护 WebView 已登录到特定网站)。如果您尝试通过引用传递 WebView,由于我只能假设是安全措施,您将丢失该安全令牌并且必须重新登录该站点。这就是为什么 adding/removing 来自 ObservableCollection
的控件在我的情况下不起作用。
我不会尝试通过 UI 隐藏项目,而是从 ItemsSource 绑定的视图或集合中删除项目。这是一种更简洁的方法,UI 永远不必担心项目的可见性。
编辑 1
When a user selects a specific car from the quick select menu, it shows that car and hides the others.
有道理;让我们调用该视图 "UserCars",并假设 ItemsSource 绑定到 UserCars。
怎么样:将 ItemsSource 更改为绑定到 "SelectedCollection"。当您想显示 UserCars 时,将 SelectedCollection 指向 UserCars 集合。
要限制集合,您只需将 SelectedCollection 指向您仅填充 UserCars.SelectedItem
的新 SingleCar
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;
}
编辑 2
您似乎试图通过隐藏这些其他项来使 ListView 伪装成 "Car Details" 视图。这本质上是个坏主意;一个不同的 UI 元素绑定到 Listview 的 Selected Car
Item 将是一个更好的解决方案。由于新的详细信息面板只会查看已生成的 Car
实例,因此您不会招致任何数据命中。即使你现在可以让这种方法奏效,我担心你将来会给自己带来更多的悲伤。
我会说你的设计有缺陷,但我无法修复;所以我会提供一个 "workaround."
问题是您的 DataTemplate
正在倒塌,这很好,但显然它所在的容器不会倒塌。这不会自然发生,因为 parent 不会继承自 child。第一个认识是每个项目都包含在 ListViewItem
中,您可以通过设置 ItemContainerStyle
观察到这一点。这给您留下了两个解决方案(解决方法)。您可以在 ListViewItem
上设置一些触发器,也可以像我一样做一些更简单的事情——如果您不介意 UI 的影响。
我的完整工作申请如下。要点是您必须编辑 ListViewItem
的 layout/behavior。在我的示例中,默认值不是 BorderThickeness
并且 Padding
不是“0, 0, 0, 0”...将它们设置为 0 将完全隐藏您的项目。
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));
}
}
}
}
编辑
老实说,以上是一个非常便宜的解决方案,我又考虑了 3 分钟后对它不满意。我不满意的原因是,如果您可以使用键盘,您仍然可以 select 该项目。在上面的示例中,单击第一项,"hide" 项,然后使用鼠标,ListView.SelectedItem
仍然会发生变化。
下面是一个快速解决方案(解决方法 :D),可以实际从列表中删除项目并防止它们获得焦点。用这个替换 ListView.ItemContainerStyle
并更改 ActualHeight
触发器值以匹配您看到的值。这将根据我相信的 OS 个主题而改变——我会把它留给你来测试。最后,请记住 ListViewItem.DataContext
将成为 ItemsSource
中的一项。这意味着绑定到 IsVisible
的 DataTrigger
绑定到 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>
编辑 2(在我发布第一个编辑之前编辑。
去他的,不要绑定你的可见性CarViewControl
;你不需要。您只需要专注于删除项目本身,一旦项目被删除,包含的控件也将被删除(尽管您应该自己测试并更改 IsTabStop
和 IsFocusable
如果您仍然可以CarViewControl
中的项目)。此外,由于使用带有 ActualHeight
绑定的任意数字不是很安全,只需直接绑定到 IsVisible
属性 (或 HideCar
在您的情况下)并触发可见性ListViewItem
应该足够了。
最后,这是我的决赛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>
当 CarViewControl
的可见性设置为折叠时,它仍会在原来的位置显示一个占位符(请参见下面的屏幕截图)。
有什么方法可以在 ListViewItem
折叠时完全隐藏它?
XAML代码
<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>
在上图中,有 3 个 CarViewControls
已折叠,其后有 1 个未折叠。一个是突出显示的。我希望它们在内容折叠时完全不可见。
我尝试过的:
- 将
DataTemplate
控件的高度设置为 0(只是为了查看它是否隐藏了无效的占位符 - 根据此文档将
ShowsScrollingPlaceholders
设置为False
:MSDN ListView Placeholders
折叠要求的原因
在每个 CarViewControl 中,存在一个包含安全令牌的 WebView(它维护 WebView 已登录到特定网站)。如果您尝试通过引用传递 WebView,由于我只能假设是安全措施,您将丢失该安全令牌并且必须重新登录该站点。这就是为什么 adding/removing 来自 ObservableCollection
的控件在我的情况下不起作用。
我不会尝试通过 UI 隐藏项目,而是从 ItemsSource 绑定的视图或集合中删除项目。这是一种更简洁的方法,UI 永远不必担心项目的可见性。
编辑 1
When a user selects a specific car from the quick select menu, it shows that car and hides the others.
有道理;让我们调用该视图 "UserCars",并假设 ItemsSource 绑定到 UserCars。
怎么样:将 ItemsSource 更改为绑定到 "SelectedCollection"。当您想显示 UserCars 时,将 SelectedCollection 指向 UserCars 集合。
要限制集合,您只需将 SelectedCollection 指向您仅填充 UserCars.SelectedItem
的新 SingleCarXAML:
<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;
}
编辑 2
您似乎试图通过隐藏这些其他项来使 ListView 伪装成 "Car Details" 视图。这本质上是个坏主意;一个不同的 UI 元素绑定到 Listview 的 Selected Car
Item 将是一个更好的解决方案。由于新的详细信息面板只会查看已生成的 Car
实例,因此您不会招致任何数据命中。即使你现在可以让这种方法奏效,我担心你将来会给自己带来更多的悲伤。
我会说你的设计有缺陷,但我无法修复;所以我会提供一个 "workaround."
问题是您的 DataTemplate
正在倒塌,这很好,但显然它所在的容器不会倒塌。这不会自然发生,因为 parent 不会继承自 child。第一个认识是每个项目都包含在 ListViewItem
中,您可以通过设置 ItemContainerStyle
观察到这一点。这给您留下了两个解决方案(解决方法)。您可以在 ListViewItem
上设置一些触发器,也可以像我一样做一些更简单的事情——如果您不介意 UI 的影响。
我的完整工作申请如下。要点是您必须编辑 ListViewItem
的 layout/behavior。在我的示例中,默认值不是 BorderThickeness
并且 Padding
不是“0, 0, 0, 0”...将它们设置为 0 将完全隐藏您的项目。
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));
}
}
}
}
编辑
老实说,以上是一个非常便宜的解决方案,我又考虑了 3 分钟后对它不满意。我不满意的原因是,如果您可以使用键盘,您仍然可以 select 该项目。在上面的示例中,单击第一项,"hide" 项,然后使用鼠标,ListView.SelectedItem
仍然会发生变化。
下面是一个快速解决方案(解决方法 :D),可以实际从列表中删除项目并防止它们获得焦点。用这个替换 ListView.ItemContainerStyle
并更改 ActualHeight
触发器值以匹配您看到的值。这将根据我相信的 OS 个主题而改变——我会把它留给你来测试。最后,请记住 ListViewItem.DataContext
将成为 ItemsSource
中的一项。这意味着绑定到 IsVisible
的 DataTrigger
绑定到 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>
编辑 2(在我发布第一个编辑之前编辑。
去他的,不要绑定你的可见性CarViewControl
;你不需要。您只需要专注于删除项目本身,一旦项目被删除,包含的控件也将被删除(尽管您应该自己测试并更改 IsTabStop
和 IsFocusable
如果您仍然可以CarViewControl
中的项目)。此外,由于使用带有 ActualHeight
绑定的任意数字不是很安全,只需直接绑定到 IsVisible
属性 (或 HideCar
在您的情况下)并触发可见性ListViewItem
应该足够了。
最后,这是我的决赛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>