WPF:使用效果显示和隐藏 ItemsControl 中的项目 [英] WPF: Showing and hiding items in an ItemsControl with effects

查看:71
本文介绍了WPF:使用效果显示和隐藏 ItemsControl 中的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用 这篇很棒的文章 作为显示和隐藏具有过渡效果的元素的基础.它的工作非常巧妙,因为它可以让您像往常一样绑定 Visibility 属性,然后定义当可见性改变时会发生什么(例如,为其不透明度设置动画或触发故事板).当你隐藏一个元素时,它使用值强制来保持它可见,直到过渡完成.

I've been using this great article as a basis for showing and hiding elements with a transition effect. It works very neatly in that it lets you bind the Visibility property just as normal, then define what happens when the visibility changes (e.g. animate its opacity or trigger a storyboard). When you hide an element, it uses value coercion to keep it visible until the transition is finished.

我正在寻找与 ItemsControlObservableCollection 一起使用的类似解决方案.换句话说,我想像往常一样将 ItemsSource 绑定到 ObservableCollection,但控制添加和删除项目时发生的情况并触发动画.我不认为使用值强制在这里会起作用,但很明显,项目仍然需要保留在列表中,直到它们的转换完成.有没有人知道任何现有的解决方案可以使这变得容易?

I'm looking for a similar solution to use with an ItemsControl and an ObservableCollection. In other words, I want to bind the ItemsSource to an ObservableCollection as normal, but control what happens when items are added and removed and trigger animations. I don't think using value coercion will work here, but obviously, items still need to stay in the list until their transitions finish. Does anyone know of any existing solutions that would make this easy?

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

I'd like any solution to be reasonably generic and easy to apply to lists of any kind of items. Ideally the style and animation behaviour would be separate, and applying it to a particular list would be a simple task such as giving it an attached property.

推荐答案

淡入很容易,但对于淡出,项目需要保留在源列表中,直到动画完成(如您所说).

Fade-in is easy, but for fade-out the items will need to stay in the source list until the animation is completed (like you said).

如果我们仍然希望能够正常使用源ObservableCollection(添加/删除等),那么我们将不得不创建一个与源集合不断同步的镜像集合延迟删除,直到动画完成.这可以通过 CollectionChanged 事件来完成.

If we still want to be able to use the source ObservableCollection normally (Add/Remove etc.) then we would have to create a mirror collection that is constantly in sync with the source collection with a delay for delete until the animation is completed. This can be done with the CollectionChanged event.

这是我使用附加行为制作的一个实现.它可以用于 ItemsControlListBoxDataGrid 或任何其他源自 ItemsControl 的东西.

Here is an implementation I made of this, using an attached behavior. It can be used for ItemsControl, ListBox, DataGrid or anything else that derives from ItemsControl.

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

Instead of Binding ItemsSource, bind the attached property ItemsSourceBehavior.ItemsSource. It will create a mirror ObservableCollection using Reflection, use the mirror as ItemsSource instead and handle the FadeIn/FadeOut animations.
Note that I haven't tested this extensively and there might be bugs and several improvements that can be made but it has worked great in my scenarios.

示例用法

<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>

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);
    }
}

这篇关于WPF:使用效果显示和隐藏 ItemsControl 中的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆