WPF:使用效果显示和隐藏ItemsControl中的项目

Tim*_*ers 7 wpf user-interface binding effects

我一直在使用这篇伟大的文章作为显示和隐藏具有过渡效果的元素的基础.它非常巧妙地工作,它允许您Visibility像正常一样绑定属性,然后定义当可见性发生变化时发生的事情(例如,为其不透明度设置动画或触发故事板).隐藏元素时,它会使用值强制使其保持可见,直到转换完成.

我正在寻找一个与a ItemsControl和an 一起使用的类似解决方案ObservableCollection.换句话说,我想将其绑定ItemsSourceObservableCollection正常,但控制添加和删除项目时发生的事情并触发动画.我不认为使用价值强制会在这里起作用,但显然,物品仍然需要留在列表中,直到它们的转换结束.有没有人知道任何现有的解决方案,这将使这容易?

我希望任何解决方案都具有合理的通用性,并且易于应用于任何类型的项目列表.理想情况下,样式和动画行为是分开的,将其应用于特定列表将是一项简单的任务,例如为其提供附加属性.

Fre*_*lad 8

淡入很容易,但对于淡出,项目需要保留在源列表中,直到动画完成(就像你说的那样).

如果我们仍然希望能够ObservableCollection正常使用源(添加/删除等),那么我们必须创建一个与源集合持续同步的镜像集合,并在动画完成之前延迟删除.这可以通过活动完成CollectionChanged.

这是我使用附加行为实现的实现.它可用于ItemsControl,ListBox,DataGrid或其他任何派生自ItemsControl.

ItemsSource绑定附加属性而不是Binding ItemsSourceBehavior.ItemsSource.它将ObservableCollection使用Reflection 创建一个镜像,使用镜像ItemsSource代替并处理FadeIn/FadeOut动画.
请注意,我没有对此进行过广泛的测试,可能存在一些错误和一些改进,但它在我的场景中运行良好.

样本用法

<ListBox behaviors:ItemsSourceBehavior.ItemsSource="{Binding MyCollection}">
    <behaviors:ItemsSourceBehavior.FadeInAnimation>
        <Storyboard>
            <DoubleAnimation Storyboard.TargetProperty="Opacity"
                             From="0.0"
                             To="1.0"
                             Duration="0:0:3"/>
        </Storyboard>
    </behaviors:ItemsSourceBehavior.FadeInAnimation>
    <behaviors:ItemsSourceBehavior.FadeOutAnimation>
        <Storyboard>
            <DoubleAnimation Storyboard.TargetProperty="Opacity"
                             To="0.0"
                             Duration="0:0:1"/>
        </Storyboard>
    </behaviors:ItemsSourceBehavior.FadeOutAnimation>
    <!--...-->
</ListBox>
Run Code Online (Sandbox Code Playgroud)

ItemsSourceBehavior

public class ItemsSourceBehavior
{
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.RegisterAttached("ItemsSource",
                                            typeof(IList),
                                            typeof(ItemsSourceBehavior),
                                            new UIPropertyMetadata(null, ItemsSourcePropertyChanged));
    public static void SetItemsSource(DependencyObject element, IList value)
    {
        element.SetValue(ItemsSourceProperty, value);
    }
    public static IList GetItemsSource(DependencyObject element)
    {
        return (IList)element.GetValue(ItemsSourceProperty);
    }

    private static void ItemsSourcePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        ItemsControl itemsControl = source as ItemsControl;
        IList itemsSource = e.NewValue as IList;
        if (itemsControl == null)
        {
            return;
        }
        if (itemsSource == null)
        {
            itemsControl.ItemsSource = null;
            return;
        }

        Type itemsSourceType = itemsSource.GetType();
        Type listType = typeof(ObservableCollection<>).MakeGenericType(itemsSourceType.GetGenericArguments()[0]);
        IList mirrorItemsSource = (IList)Activator.CreateInstance(listType);
        itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, new Binding{ Source = mirrorItemsSource });

        foreach (object item in itemsSource)
        {
            mirrorItemsSource.Add(item);
        }
        FadeInContainers(itemsControl, itemsSource);

        (itemsSource as INotifyCollectionChanged).CollectionChanged += 
            (object sender, NotifyCollectionChangedEventArgs ne) =>
        {
            if (ne.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (object newItem in ne.NewItems)
                {
                    mirrorItemsSource.Add(newItem);
                }
                FadeInContainers(itemsControl, ne.NewItems);
            }
            else if (ne.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (object oldItem in ne.OldItems)
                {
                    UIElement container = itemsControl.ItemContainerGenerator.ContainerFromItem(oldItem) as UIElement;
                    Storyboard fadeOutAnimation = GetFadeOutAnimation(itemsControl);
                    if (container != null && fadeOutAnimation != null)
                    {
                        Storyboard.SetTarget(fadeOutAnimation, container);

                        EventHandler onAnimationCompleted = null;
                        onAnimationCompleted = ((sender2, e2) =>
                        {
                            fadeOutAnimation.Completed -= onAnimationCompleted;
                            mirrorItemsSource.Remove(oldItem);
                        });

                        fadeOutAnimation.Completed += onAnimationCompleted;
                        fadeOutAnimation.Begin();
                    }
                    else
                    {
                        mirrorItemsSource.Remove(oldItem);
                    }
                }
            }
        };
    }

    private static void FadeInContainers(ItemsControl itemsControl, IList newItems)
    {
        EventHandler statusChanged = null;
        statusChanged = new EventHandler(delegate
        {
            if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                itemsControl.ItemContainerGenerator.StatusChanged -= statusChanged;
                foreach (object newItem in newItems)
                {
                    UIElement container = itemsControl.ItemContainerGenerator.ContainerFromItem(newItem) as UIElement;
                    Storyboard fadeInAnimation = GetFadeInAnimation(itemsControl);
                    if (container != null && fadeInAnimation != null)
                    {
                        Storyboard.SetTarget(fadeInAnimation, container);
                        fadeInAnimation.Begin();
                    }
                }
            }
        });
        itemsControl.ItemContainerGenerator.StatusChanged += statusChanged;
    }

    public static readonly DependencyProperty FadeInAnimationProperty =
        DependencyProperty.RegisterAttached("FadeInAnimation",
                                            typeof(Storyboard),
                                            typeof(ItemsSourceBehavior),
                                            new UIPropertyMetadata(null));
    public static void SetFadeInAnimation(DependencyObject element, Storyboard value)
    {
        element.SetValue(FadeInAnimationProperty, value);
    }
    public static Storyboard GetFadeInAnimation(DependencyObject element)
    {
        return (Storyboard)element.GetValue(FadeInAnimationProperty);
    }

    public static readonly DependencyProperty FadeOutAnimationProperty =
        DependencyProperty.RegisterAttached("FadeOutAnimation",
                                            typeof(Storyboard),
                                            typeof(ItemsSourceBehavior),
                                            new UIPropertyMetadata(null));
    public static void SetFadeOutAnimation(DependencyObject element, Storyboard value)
    {
        element.SetValue(FadeOutAnimationProperty, value);
    }
    public static Storyboard GetFadeOutAnimation(DependencyObject element)
    {
        return (Storyboard)element.GetValue(FadeOutAnimationProperty);
    }
}
Run Code Online (Sandbox Code Playgroud)