来自 ObservableCollection 的 ListView 项目需要为实现做好准备

ListView items from an ObservableCollection that need to prepare themselves for realization

我有一个 ListView,它应该显示相当多的项目,包括 "Name"、"Thumbnail" 和 "AnimationPosition" 属性。每个项目类型中的后台任务负责切换缩略图以使其具有动画效果。

不用说,这是一个相当繁重的操作,应该限制在尽可能少的项目上,例如到虚拟化 ListView 的 visible/realizing 项。现在我已经将 ListView 的 DataContext 设置为 ObeservableCollection 实例并将其绑定到其类型的属性。这是我的 XAML 代码的一瞥。

<TabControl Grid.Row="0" Grid.Column="2">

  <TabControl.Resources>

    <Style x:Key="MediaItemStyle" TargetType="{x:Type ListViewItem}">

      <Setter Property="Margin" Value="5,5,5,5"/>
      <Setter Property="Padding" Value="0,0,0,0"/>
      <Setter Property="HorizontalAlignment" Value="Left"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate  TargetType="{x:Type ListViewItem}">
            <Grid HorizontalAlignment="Left" VerticalAlignment="Top" Height="Auto" >
              <Border x:Name="border" BorderBrush="{x:Null}" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="2.5"/>
              <StackPanel HorizontalAlignment="Stretch"  VerticalAlignment="Stretch">
                <ContentPresenter/>
              </StackPanel>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>

    </Style>

    <Style TargetType="custom:MediaContainerListView">

      <Setter Property="ItemsSource" Value="{Binding}"/>
      <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
      <Setter Property="ItemContainerStyle" Value="{StaticResource MediaItemStyle}"/>
      <Setter Property="ItemsPanel">
        <Setter.Value>
          <ItemsPanelTemplate>
            <WrapPanel/>
          </ItemsPanelTemplate>
        </Setter.Value>
      </Setter>
      <Setter Property="ItemTemplate">
        <Setter.Value>
          <DataTemplate>
            <DockPanel Width="256">

              <Image DockPanel.Dock="Top" Height="144" StretchDirection="Both" 
                     Stretch="Fill" Source="{Binding Thumbnail.Source,Mode=OneWay}"/>

              <ProgressBar DockPanel.Dock="Top" Height="2"
                           Minimum="0" Maximum="{Binding Thumbnail.AnimationPosition.Length}"
                           Value="{Binding Thumbnail.AnimationPosition.Position}"
                           Visibility="{Binding Thumbnail.AnimationPosition.Visibility}"/>

              <TextBlock DockPanel.Dock="Bottom" Height="40" 
                         TextWrapping="Wrap" TextTrimming="CharacterEllipsis" 
                         TextAlignment="Center" Text="{Binding Name}"/>

            </DockPanel>
          </DataTemplate>
        </Setter.Value>
      </Setter>
    </Style>

  </TabControl.Resources>

  <TabItem Header="">

    <custom:MediaContainerListView x:Name="MediaContainerView"></custom:MediaContainerListView>

  </TabItem>

</TabControl>

基本上,我有两种方法 start/stop 每个单独项目的动画。

      public async void StartAnimation()
      {
         if( Count > 1 )
         {
            Task thumbnailAnimationTask = AnimationTask( AnimationCancellationToken.Token );
            await thumbnailAnimationTask;
         }
      }

      public void StopAnimation()
      {
         AnimationCancellationToken.Cancel();
      }

我这里有两个问题。

  1. ListView似乎实现了所有项目,而不仅仅是那些可见的或实现范围内的项目。我怀疑我的 XAML 以某种方式破坏了虚拟化并且尝试了很多解决方案都没有成功。请注意,我需要我的 ListView 可以缩放到 MainWindow 的尺寸,而不是固定的高度和宽度。
  2. 我需要在项目即将实现时调用 StartAnimation,在项目离开视图时调用 StopAnimation。

即使我的 ListView 没有正确虚拟化,如果我对 ObservableCollections 工作原理的理解是正确的,那么它只是虚拟化管理的项目的 UI 表示,而不是项目本身,即调用Constructor/Destructor 项中的 StartAnimation/StopAnimation 没有多大帮助,因为无论如何在创建时都会为每个项目调用它们。

有没有一种巧妙的方法以某种方式通知每个项目它们即将实现或离开 ListView 视图?

更新: 虚拟化无法正常工作的问题与 WrapPanel 有关,一旦我切换到 VirtualizingStackPanel 它就开始正常工作。不幸的是,它与 WrapPanel 并不完全相同,并且由于 .NET 框架不提供 VirtualizingWrapPanel,我选择使用 [=12] =].它并不完美,但可以胜任。

你说是1/2

查找对 GetHashcode 的调用
我认为它调用它 GetHashcode 只是为了找到它
无意中我发现它是在项目被虚拟化时调用的

有一个终止的动画(不循环)

我终于自己解决了这个问题。我知道它不必如此复杂,事实并非如此。因为我已经创建了自己的 ListView inherited class 称为 MediaContainerListView,所以我可以重写它的一些虚拟方法。事实证明,其中两个正是我想要的。

protected override void PrepareContainerForItemOverride( DependencyObject element, object item )

在项目即将出现之前调用,并且

protected override void ClearContainerForItemOverride( DependencyObject element, Object item )

在项目即将消失之前调用。所以我在第一个中调用了 StartAnimation,在第二个中调用了 StopAnimation,它完美地工作了!