如何在DataTemplate子级折叠后隐藏ListView项占位符?

时间:2015-08-02 04:53:09

标签: c# xaml listview winrt-xaml win-universal-app

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>

enter image description here

在上图中,有三个CarViewControls已折叠,其次是一个未折叠。一个突出显示。当内容被折叠时,我希望它们完全不可见。

我尝试过的事情:

  • DataTemplate控件的高度设置为0(只是为了查看它是否隐藏了无效的占位符
  • 根据此文档将ShowsScrollingPlaceholders设置为FalseMSDN ListView Placeholders

崩溃要求的原因

在每个CarViewControl中,存在一个包含安全令牌(维护WebView登录到特定网站)的WebView。如果您尝试通过引用传递WebView,由于我只能假设是安全措施,您将丢失该安全令牌并且必须重新登录该站点。这就是为什么在我的情况下添加/删除ObservableCollection中的控件不起作用的原因。

2 个答案:

答案 0 :(得分:6)

我会说你的设计存在缺陷,但我无法解决这个问题。所以我会提供一个&#34;解决方法。&#34;

问题是你的DataTemplate正在崩溃,这很好,但显然它所处的容器并没有崩溃。这不会发生,因为父母不会从孩子那里继承。第一个实现是每个项目都包含在ListViewItem中,您可以通过设置ItemContainerStyle来观察。这为您提供了两种解决方案(解决方法)。您可以在ListViewItem上设置一些触发器,也可以像我一样轻松做事 - 如果您不介意用户界面影响。

我的完整工作申请如下。重点是您必须编辑ListViewItem的布局/行为。在我的示例中,默认值不是BorderThickenessPadding不是&#39; 0,0,0,0&#34; ...将这些设置为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分钟之后,我对此并不满意。我不满意的原因是,如果您可以访问键盘,仍然可以选择该项目。在上面的示例中,单击第一个项目,&#34;隐藏&#34;项目,然后使用鼠标,ListView.SelectedItem仍然会改变。

以下是一个快速解决方案(解决方法:D)实际从列表中删除项目并防止它们获得焦点。将ListView.ItemContainerStyle替换为此ActualHeight并更改ListViewItem.DataContext触发器值以匹配您看到的值。这将根据我认为的操作系统主题进行更改 - 我会让您接受测试。最后,请记住ItemsSource将是DataTrigger中项目的IsVisible。这意味着绑定到Car.IsVisible的{​​{1}}绑定到<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> 属性。

CarViewControl

编辑2 (在我发布第一次编辑之前编辑。

拧紧它,不要绑定IsTabStop的可见性;你不需要。您只需要专注于删除项目本身,一旦项目被删除,包含的控件也将被删除(尽管您应该自己进行测试,如果仍然可以更改IsFocusableCarViewControl选项卡到ActualHeight)中的项目。此外,由于使用IsVisible绑定的任意数字并非非常安全,因此只需直接绑定到HideCar属性(或在您的情况下为ListViewItem)并触发可见性<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> 就足够了。

最后,这是我的最终XAML:

shuffle

答案 1 :(得分:3)

我不会尝试通过UI隐藏项目,而是从ViewSource绑定的View或Collection中删除该项目。这是一种更清洁的方法,UI永远不必关心项目的可见性。

编辑1

  

当用户从快速选择菜单中选择特定的汽车时,它会显示该汽车并隐藏其他汽车。

有道理;让我们调用该视图&#34; UserCars&#34;,并假设ItemsSource绑定到UserCars。

这个怎么样:将ItemsSource更改为绑定到&#34; SelectedCollection&#34;。如果要显示UserCars,请将SelectedCollection指向UserCars集合。

要限制集合,您只需将SelectedCollection指向仅使用UserCars填充的新SingleCar.SelectedItem

<强> XAML:

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

<强>视图模型:

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假装成一个&#34; Car Details&#34;通过隐藏这些其他项来查看。这本质上是一个坏主意;绑定到Listview的选定Car项的不同UI元素将提供更好的解决方案。由于新的详细信息面板只是查看已生成的Car实例,因此您不会遇到任何数据。即使你现在能够采用这种方法,我也很担心你将来会让自己更加悲伤。