WPF:显示和隐藏项一个ItemsControl与效果 [英] WPF: Showing and hiding items in an ItemsControl with effects

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

问题描述

我一直在使用<一个href=\"http://blogs.microsoft.co.il/blogs/arik/archive/2010/02/08/wpf-how-to-animate-visibility-property.aspx\">this伟大的文章作为显示和隐藏一个过渡效果元素的基础。它的工作原理很整齐的,因为它可以让你绑定能见度属性一样正常,则定义会发生什么,当能见度变化(如动画不透明度或触发一个故事板)。当您隐藏一个元素,它使用值强制使其保持可见,直到过渡完毕。

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.

我在寻找一个类似的解决方案,以与的ItemsControl 的ObservableCollection 使用。换句话说,我要绑定的的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 正常(添加/删除等),那么我们就必须创建一个镜像集合,是不断地在同步与源收集与删除的延迟,直到动画完成。这可以用 Col​​lectionChanged 事件来完成。

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.

下面是我做的这一点,使用一个附加行为的实现。它可用于的ItemsControl 的ListBox 的DataGrid 或别的,从的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.

而不是绑定的ItemsSource 的,绑定的附加属性 ItemsSourceBehavior.ItemsSource 。这将创建一个镜像的ObservableCollection 使用反射,使用镜像为的ItemsSource 代替并办理淡入/淡出动画。结果
请注意,我没有测试过这种广泛而有可能是错误和可以作出一些改进,但它在我的情况已经伟大的工作。

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天全站免登陆