3

ObservableCollection にバインドされた WPF ListBox があります。アイテムを追加するときに、「注目を集める」アニメーションが「新着」に必要です。DataTemplate トリガーと FrameworkElment.Loaded イベントを使用してこれを行う例は数多くあります。うまく機能し、かっこよく見えます。

ただし、この同じアニメーションは、リストをスクロールするときにもトリガーされます。これは、必要に応じて新しいアイテムを「ロード」する ListBox の仮想化機能によるものです。

ObservableCollection が変更されたときにのみ "Loaded" アニメーションをトリガーし、スクロール時にはトリガーしないように ListBox を構築する方法はありますか?

PS仮想化を無効にすることはオプションではありません

4

1 に答える 1

3

アニメーションをトリガーするために、添付の依存関係プロパティ AddNewItem (ブール値) を作成しました。

public class MyDependencyClass : DependencyObject
{
    public static readonly DependencyProperty AddNewItemProperty;

    public static void SetAddNewItem(DependencyObject DepObject, bool value)
    {
        DepObject.SetValue(AddNewItemProperty, value);
    }

    public static bool GetAddNewItem(DependencyObject DepObject)
    {
        return (bool)DepObject.GetValue(AddNewItemProperty);
    }

    static MyDependencyClass()
    {
        PropertyMetadata MyPropertyMetadata = new PropertyMetadata(false);

        AddNewItemProperty = DependencyProperty.RegisterAttached("AddNewItem",
                                                            typeof(bool),
                                                            typeof(MyDependencyClass),
                                                            MyPropertyMetadata);
    }
}

次に、依存関係プロパティの境界線 (新しい項目を追加するための警告) を作成します。

<Border x:Name="WarningBorder" Style="{StaticResource HideBorderStyle}" local:MyDependencyClass.AddNewItem="False" Height="33" Background="#44515B" BorderThickness="0" Margin="65,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Opacity="0">
        <TextBlock x:Name="WarningText" FontFamily="./#Segoe UI" TextAlignment="Center" Margin="0,5,0,0" FontSize="18" Foreground="Gainsboro" Text="You add new item" />
</Border>

スタイル HideBorderStyleには、プロパティ AddNewItem=True のときにトリガーされるDataTriggerが含まれていました。HideBorderStyle のリスト:

    <Style x:Key="HideBorderStyle" TargetType="{x:Type Border}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=WarningBorder, Path=(local:MyDependencyClass.AddNewItem), Mode=OneWay}" Value="True">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="Width" BeginTime="0:0:0" From="0.0" To="427.0" Duration="0:0:0.5" />
                            <DoubleAnimation Storyboard.TargetProperty="Opacity" BeginTime="0:0:0" From="0.0" To="1.0" Duration="0:0:1.0" />
                            <DoubleAnimation Storyboard.TargetProperty="Opacity" BeginTime="0:0:5" From="1.0" To="0.0" Duration="0:0:1.0" />
                            <DoubleAnimation Storyboard.TargetProperty="Width" BeginTime="0:0:5" From="427.0" To="0.0" Duration="0:0:1.0" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>

SampleListBox と AddButton があるとします。

 <Grid>
     <ListBox x:Name="SampleListBox" Width="400" Height="200" BorderThickness="1" BorderBrush="Black" DisplayMemberPath="Name" Background="AliceBlue" Loaded="SampleListBox_Loaded" />

     <Button Content="Add" Width="50" Height="30" VerticalAlignment="Bottom" Click="AddButton_Click" />
</Grid>

AddButton_Click のリスト:

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {            
        PersonListBox.Add(new Person()
        {
            Name = "NewItem",
        });

        SampleListBox.ItemsSource = PersonListBox;            
    }

SampleListBox_Loaded では、SampleListBox のデータを初期化し、コレクションが変更されたときに呼び出されるハンドラNotifyCollectionChangedEventHandlerを割り当てます。SampleListBox_Loaded イベントのリスト:

private void SampleListBox_Loaded(object sender, RoutedEventArgs e)
{
    PersonListBox.Add(new Person()
    {
        Name = "Peter Orange",
        Age = 32,
        Sample = "Sample",
    });

    SampleListBox.ItemsSource = PersonListBox;
    PersonListBox.CollectionChanged += new NotifyCollectionChangedEventHandler(PersonListBox_CollectionChanged);
}

PersonListBox_CollectionChangedで、依存関係プロパティ AddNewItem の値をTrueに設定しました。PersonListBox_CollectionChanged のリスト:

    private void PersonListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            MyDependencyClass.SetAddNewItem(WarningBorder, true);             
        }
    }

アイテムがコレクションに追加されると、アニメーションがトリガーされるようになりました。別の要素を追加すると、アニメーションはトリガーされません。彼女が働いたので、将来の表示アニメーションのために値を折りたたむ必要があります。

private void ResetButton_Click(object sender, RoutedEventArgs e)
{
    MyDependencyClass.SetAddNewItem(WarningBorder, false);
}

この場合、アニメーションの値をいつリセットするかを決定する必要があります。

編集

追加したアイテムをアニメーション化するには、前の例に基づいています。しかし、DataTrigger を使用したスタイルがListBoxItemに割り当てられるようになりまし

  <ListBox x:Name="SampleListBox" local:MyDependencyClass.AddNewItem="False" local:MyDependencyClass.InitCountOfList="0" Width="400" Height="200" BorderThickness="1" BorderBrush="Black" DisplayMemberPath="Name" Background="AliceBlue" Loaded="SampleListBox_Loaded">
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Style.Triggers>                        
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=SampleListBox, Path=(local:MyDependencyClass.AddNewItem), Mode=OneWay}" Value="True" />
                            <Condition Binding="{Binding ElementName=SampleListBox, Converter={StaticResource BoolToInitItemsConverter}}" Value="True" />
                        </MultiDataTrigger.Conditions>

                        <MultiDataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" BeginTime="0:0:0" From="0.0" To="1.0" Duration="0:0:1.0" />
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiDataTrigger.EnterActions>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

正しい作業のために、アニメーション (特にプログラムの開始時) は別のプロパティ (InitCountOfList (Int)) に従って作成されました。

public class MyDependencyClass : DependencyObject
{
    public static readonly DependencyProperty AddNewItemProperty, InitCountOfListProperty;

    #region AddNewItem

    public static void SetAddNewItem(DependencyObject DepObject, bool value)
    {
        DepObject.SetValue(AddNewItemProperty, value);
    }

    public static bool GetAddNewItem(DependencyObject DepObject)
    {
        return (bool)DepObject.GetValue(AddNewItemProperty);
    }

    #endregion

    #region InitCountOfList

    public static void SetInitCountOfList(DependencyObject DepObject, int value)
    {
        DepObject.SetValue(InitCountOfListProperty, value);
    }

    public static int GetInitCountOfList(DependencyObject DepObject)
    {
        return (int)DepObject.GetValue(InitCountOfListProperty);
    }

    #endregion 

    #region Constructor of DependencyProperty

    static MyDependencyClass()
    {
        PropertyMetadata BoolPropertyMetadata = new PropertyMetadata(false);
        PropertyMetadata IntPropertyMetadata = new PropertyMetadata(0);

        AddNewItemProperty = DependencyProperty.RegisterAttached("AddNewItem",
                                                            typeof(bool),
                                                            typeof(MyDependencyClass),
                                                            BoolPropertyMetadata);

        InitCountOfListProperty = DependencyProperty.RegisterAttached("InitCountOfList",
                                                                 typeof(int),
                                                                 typeof(MyDependencyClass),
                                                                 IntPropertyMetadata);
    }

    #endregion
}

データは Window_ContentRendered イベントで初期化されます。

 private void Window_ContentRendered(object sender, EventArgs e)
 {
     PersonListBox.Add(new Person()
     {
         Name = "Peter Orange",                              
     });

     SampleListBox.ItemsSource = PersonListBox;

     // Set the initial number of collection
     MyDependencyClass.SetInitCountOfList(SampleListBox, SampleListBox.Items.Count);
 }

SampleListBox_Loaded イベントでハンドラNotifyCollectionChangedEventHandlerを割り当てます。

private void SampleListBox_Loaded(object sender, RoutedEventArgs e)
{            
    PersonListBox.CollectionChanged += new NotifyCollectionChangedEventHandler(PersonListBox_CollectionChanged);
}

Handler と AddButton_Click は同じままでした:

private void PersonListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        MyDependencyClass.SetAddNewItem(SampleListBox, true);
    }
}

private void AddButton_Click(object sender, RoutedEventArgs e)
{            
    PersonListBox.Add(new Person()
    {
        Name = "NewItem",
    });
}

コンバーターBoolToInitItemsConverterは、開始時に要素の追加が行われなかった場合に true を返します。

/// <summary>
/// Return true, if count of collection > initial
/// </summary>
public class BoolToInitItemsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ListBox MyListBox = value as ListBox;
        int InitCountOfList = MyDependencyClass.GetInitCountOfList(MyListBox);

        if (MyListBox.Items.Count > InitCountOfList)
        {
            // Added a new element
            return true;
        }

        // First run and we do not want
        // to run the animation
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return DependencyProperty.UnsetValue as object;
    }
}

これでアニメーション作品は追加アイテムのみになりました。

于 2013-06-09T19:12:31.737 に答える