Элементы ListView из ObservableCollection, которые должны подготовить себя к реализации

У меня есть ListView, который должен отображать довольно большое количество элементов, состоящих из свойств "Name", "Thumbnail" и "AnimationPosition". Фоновая задача каждого типа элемента отвечает за переключение миниатюр для их анимации.

Теперь само собой разумеется, что это довольно тяжелая операция и должна быть ограничена как можно меньшим количеством элементов, например видимыми / реализующими элементами виртуализированного ListView. Теперь я уже установил DataContext моего ListView в экземпляр 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>

По сути, у меня есть два метода, которые запускают / останавливают анимацию для каждого отдельного элемента.

      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, является правильным, это только представление пользовательского интерфейса элементов, которыми управляет виртуализация, а не сами элементы, то есть вызов StartAnimation/StopAnimation из конструктора / деструктора элементы не сильно помогают, так как они все равно вызываются для каждого элемента во время создания.

Есть ли удобный способ как-то проинформировать каждый элемент о том, что он собирается быть реализованным, или покинуть представление ListView?

Обновление: проблема неэффективной виртуализации была связана с WrapPanel, и как только я переключился на VirtualizingStackPanel, он начал работать правильно. К сожалению, это не совсем то же самое, что WrapPanel, и поскольку.NET Framework не предлагает VirtualizingWrapPanel, я выбрал его здесь. Это не идеально, но это делает работу.

2 ответа

Решение

Я наконец решил проблему сам. Я знал, что это не должно быть так сложно, и на самом деле это не так. Поскольку я уже создал свой собственный унаследованный класс ListView с именем MediaContainerListView, я мог переопределить некоторые из его виртуальных методов. Два из них оказались именно тем, что я хотел.

protected override void PrepareContainerForItemOverride( DependencyObject element, object item )

который вызывается непосредственно перед тем, как элемент должен появиться, и

protected override void ClearContainerForItemOverride( DependencyObject element, Object item )

который вызывается как раз перед тем, как предмет собирается исчезнуть. Поэтому я вызываю StartAnimation в первом и StopAnimation во втором, и он работает безупречно!

Вы сказали, что было 1/2

Ищите звонок в GetHashcode
Я думаю, что он называет это GetHashcode просто чтобы найти его
Случайно обнаружил, что он вызывается при виртуализации элемента

Есть анимация, которая заканчивается (не зацикливается)

Другие вопросы по тегам