如何在重用之前清理 ListViewItem 的状态?

Posted

技术标签:

【中文标题】如何在重用之前清理 ListViewItem 的状态?【英文标题】:How to Cleanup the ListViewItem's state before they get reused? 【发布时间】:2021-11-22 12:19:14 【问题描述】:

问题是listview使用了虚拟化,当item改变外观时,list刷新后仍然存在。这是微软工程师的回答

这是由 ListView 的默认面板的虚拟化特性引起的 (项目堆栈面板)。它本质上重用了相同的 ListViewItem (容器),当它重用它们时,它可能处于 不正确。当您使用堆栈面板时,没有虚拟化和 每个项目都有一个容器(不可重复使用),您可以想象 如果您的列表中有很多项目,则会非常耗时。你可以 尝试使用 PrepareContainerForItemOverride, ClearContainerForItemOverride 清理 ListViewItem 的状态 在它们被重用之前。更好的是使用 ContainerContentChanging 事件并在那里执行,因此您不需要 派生类型。

很遗憾,我找不到使用 PrepareContainerForItemOverrideClearContainerForItemOverrideContainerContentChanging 方法的示例。

如何清除 ContainerContentChanging 中 ListViewItem 的状态?

更新:

用户控制:

<Grid>
        <SwipeControl x:Name="ListViewSwipeContainer">
            <Grid VerticalAlignment="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                </Grid.RowDefinitions>
                <TextBlock
                    Margin="10,5,200,5"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Center"
                    FontSize="18"
                    Text="Binding ElementName=subsceneView, Path=Title"
                    TextWrapping="Wrap"/>
                <AppBarButton
                    Name="OpenFolderButton"
                    Grid.RowSpan="2"
                    MinWidth="75"
                    Margin="10,0,10,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Click="OpenFolderButton_Click"
                    Icon="OpenLocal"
                    IsTabStop="False"
                    Label="Open Folder"
                    Visibility="Collapsed"/>

                <AppBarButton
                    Name="DownloadHoverButton"
                    Grid.RowSpan="2"
                    Margin="10,0,10,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Click="DownloadHoverButton_Click"
                    Icon="Download"
                    IsTabStop="False"
                    Label="Download"
                    Visibility="Collapsed"/>

            </Grid>
        </SwipeControl>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="HoveringStates">
                <VisualState x:Name="HoverButtonsHidden"/>
                <VisualState x:Name="HoverButtonsShown">
                    <VisualState.Setters>
                        <Setter Target="DownloadHoverButton.Visibility" Value="Visible"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>

后面的代码:

public sealed partial class SubsceneUserControl : UserControl
    
        #region DependencyProperty
       

        public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register("Title", typeof(string), typeof(SubsceneUserControl),
            new PropertyMetadata(string.Empty));

        public string Title
        
            get  return (string)GetValue(TitleProperty); 
            set  SetValue(TitleProperty, value); 
        

        
        #endregion
        public SubsceneUserControl()
        
            this.InitializeComponent();
        
        private void UserControl_PointerEntered(object sender, PointerRoutedEventArgs e)
        
            if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse ||
                e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
            
                VisualStateManager.GoToState(sender as Control, "HoverButtonsShown", true);
            
        

        private void UserControl_PointerExited(object sender, PointerRoutedEventArgs e)
        
            VisualStateManager.GoToState(sender as Control, "HoverButtonsHidden", true);
        

        private void OpenFolderButton_Click(object sender, RoutedEventArgs e)
        

        

        private void DownloadHoverButton_Click(object sender, RoutedEventArgs e)
        
            OpenFolderButton.Visibility = Visibility.Visible;
            DownloadHoverButton.Visibility = Visibility.Collapsed;
        
    

这是我的列表视图

<ListView
            x:Name="listv"
            ItemsSource="x:Bind Subtitles, Mode=OneWay">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:SubsceneDownloadModel">
                    <local:SubsceneUserControl Title="x:Bind Title"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

private void AddItems()
        
            Subtitles?.Clear();
            for (int i = 0; i < 10; i++)
            
                Subtitles.Add(new SubsceneDownloadModel  Title = "Test " + i );

            
        

【问题讨论】:

为什么不为类实现 INotifyPropertyChanged 以禁用错误重用行为? @NicoZhu-MSFT 我不知道怎么做? 【参考方案1】:

ListViewItem 在被重用之前的状态

对于这种情况,更好的方法是使用 mvvm 绑定并为模型类实现INotifyPropertyChanged。更多详情请参考Data binding in depth。

例如

模型类

public class Model : INotifyPropertyChanged

    public Model(string name)
    
        this.Name = name;
    
   private string _name;

    public string Name
    
        get
        
            return _name;
        
        set
        
            _name = value;
            NotifyPropertyChanged();
        
    

    private bool _visible;
    public bool Visible
    

        get => _visible;
        set
        
            _visible = value;
            NotifyPropertyChanged();
        
    

    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    
        if (PropertyChanged != null)
        
            // PropertyChanged is always null.
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        
    

Xaml

<ListView x:Name="MyList">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Binding Name" />
                <SymbolIcon
            x:Name="MySbl"
            HorizontalAlignment="Right"
            Symbol="Accept"
            Visibility="Binding Visible" />
            </StackPanel>

        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

用法

MyList.ItemsSource = new ObservableCollection<Model> 
        new Model("hello"),
          new Model("hello1"),
            new Model("hello2"),
              new Model("hello3"),
                new Model("hello4"),
                  new Model("hello5"),
                    new Model("hello6"),
        ;

【讨论】:

谢谢,但是我的应用如下 在列表视图中点击按钮后会执行操作,如果成功,按钮必须隐藏。由于不容易访问数据模板的组件或以后更改数据集合,所以我在 usercontrol 中定义了数据模板在您的方法中,如何更新所需的记录并在操作后隐藏或显示按钮?为了更好地了解程序的状态,我会更新第一个帖子请看它 建议你绑定按钮的Visibility view bool值,如果你想隐藏这个按钮,请调用按钮命令方法并将当前项的bool值设置为false。这是类似的案例reply你可以参考

以上是关于如何在重用之前清理 ListViewItem 的状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 Listviewitem 边距以使用 Listviewitem 的背景颜色?

如何在 C# 中将模板设置为 ListViewItem

单击ListViewItem时如何将焦点设置在TextBox上?

ListViewItem中的图片不能动态改变的解决方法

如何设置ListViewItem被选中时的颜色?

更新单个 ListViewItem 的文本时如何防止 ListView 闪烁?