Анимированный ListView в WPF

03.01.2013 at 21:00

Захотелось мне сделать добавление элемента в ListView анимированным: чтобы элемент выезжал из верхнего края списка.
Вот как я это делал:

1. Классы данных

class DataElement : INotifyPropertyChanged
{
  private string caption;
  public string Caption
  {
    get { return this.caption; }
    set
    {
      this.caption = value;
      DoNotify("Caption");
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void DoNotify(string propertyName)
  {
    if (this.PropertyChanged != null)
      this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
}

Класс, экземпляры которого отображаются в списке. Реализует интерфейс INotifyPropertyChanged и имеет свойство Caption для отображения в списке.

class Data
{
  public ObservableCollection<DataElement> Entities { get; private set; }

  public Data()
  {
    this.Entities = new ObservableCollection<DataElement>();

    this.Entities.Add(new DataElement() { Caption = "First" });
    this.Entities.Add(new DataElement() { Caption = "Second" });
    this.Entities.Add(new DataElement() { Caption = "Third" });
    this.Entities.Add(new DataElement() { Caption = "Fourth" });
  }
}

Класс, экземпляр которого будет являться контекстом данных окна. Содержит свойство — коллекцию отображаемых элементов и в конструкторе заполняет ее тестовыми данными.

2. Разметка

<Storyboard x:Key="NewElementAnimation">
  <DoubleAnimation Duration="0:0:1" From="0" To="40"
                   Storyboard.TargetProperty="Height" />
</Storyboard>

Добавляем в ресурсы окна эту анимацию, увеличивающую высоту элемента с 0 до 40 за 1 секунду.

<Style x:Key="AnimatingElement"
       TargetType="ScrollViewer">
  <Style.Triggers>
    <EventTrigger RoutedEvent="Loaded">
      <BeginStoryboard Storyboard="{StaticResource NewElementAnimation}" />
    </EventTrigger>
  </Style.Triggers>
</Style>

Так же добавляем в ресурсы стиль, который запускает эту анимацию для загруженных элементов. TargetType равен ScrollViewer потому, что элементы списка будут завернуты в ScrollViewer.

<StackPanel>
  <ListBox ItemsSource="{Binding Entities}">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <ScrollViewer Style="{StaticResource AnimatingElement}" 
                      VerticalScrollBarVisibility="Hidden" 
                      Loaded="ScrollViewer_Loaded">
          <Label Content="{Binding Caption}" Height="40"/>
        </ScrollViewer>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
  <Button Name="AddButton" Click="Button_Click">Add Item</Button>
</StackPanel>

Разметка самой формы. ScrollViewer нужен для того, чтобы при нехватке высоты отображался низ содержимого, а не верх. Это достигается прокруткой вниз при загрузке ScrollViewer.

3. Код окна.

public MainWindow()
{
  InitializeComponent();
  this.DataContext = new Data();
}

Конструктор окна. Означивает контекст данных в новый экземпляр класса Data.

private void Button_Click(object sender, RoutedEventArgs e)
{
  var data = (Data)this.DataContext;
  data.Entities.Insert(0, new DataElement() { Caption = "NewElement" });
}

Обработчик нажатия на кнопку. Добавляет новый элемент в список.

private void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
  (sender as ScrollViewer).ScrollToBottom();
}

Прокрутка ScrollViewer вниз при загрузке.

Tags: