WPF: Showing and hiding items in an ItemsControl with effects

前端 未结 3 1814
我在风中等你
我在风中等你 2021-01-02 18:25

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

3条回答
  •  一整个雨季
    2021-01-02 19:11

    @Fredrik Hedblad Nicely done. I do have a few remarks.

    • When adding an item the animation sometimes starts on the previously added item.

    • Inserting items into the list, added them all to the bottom (so no sorted list support)

    • (personal issue: needed separate animation for each item)

    In code below in have an addapted version, which resolves the issues listed above.

    public class ItemsSourceBehavior
    {
        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)
        {
            //If animations need to be run together set this to 'false'.
            const bool separateAnimations = true;
    
            var itemsControl = source as ItemsControl;
            var itemsSource = e.NewValue as IList;
            if (itemsControl == null)
            {
                return;
            }
            if (itemsSource == null)
            {
                itemsControl.ItemsSource = null;
                return;
            }
    
            var itemsSourceType = itemsSource.GetType();
            var listType = typeof (ObservableCollection<>).MakeGenericType(itemsSourceType.GetGenericArguments()[0]);
            var mirrorItemsSource = (IList) Activator.CreateInstance(listType);
            itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, new Binding {Source = mirrorItemsSource});
    
            foreach (var item in itemsSource)
            {
                mirrorItemsSource.Add(item);
                if (separateAnimations)
                    StartFadeInAnimation(itemsControl, new List {item});
            }
    
            if (!separateAnimations)
            {
                StartFadeInAnimation(itemsControl, itemsSource);
            }
    
            (itemsSource as INotifyCollectionChanged).CollectionChanged +=
                (object sender, NotifyCollectionChangedEventArgs ne) =>
                {
                    if (ne.Action == NotifyCollectionChangedAction.Add)
                    {
                        foreach (var newItem in ne.NewItems)
                        {
                            //insert the items instead of just adding them
                            //this brings support for sorted collections
                            mirrorItemsSource.Insert(ne.NewStartingIndex, newItem);
    
                            if (separateAnimations)
                            {
                                StartFadeInAnimation(itemsControl, new List {newItem});
                            }
                        }
    
                        if (!separateAnimations)
                        {
                            StartFadeInAnimation(itemsControl, ne.NewItems);
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Remove)
                    {
                        foreach (var oldItem in ne.OldItems)
                        {
                            var container = itemsControl.ItemContainerGenerator.ContainerFromItem(oldItem) as UIElement;
                            var 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 StartFadeInAnimation(ItemsControl itemsControl, IList newItems)
        {
            foreach (var newItem in newItems)
            {
                var container = itemsControl.ItemContainerGenerator.ContainerFromItem(newItem) as UIElement;
                var fadeInAnimation = GetFadeInAnimation(itemsControl);
                if (container != null && fadeInAnimation != null)
                {
                    Storyboard.SetTarget(fadeInAnimation, container);
                    fadeInAnimation.Begin();
                }
            }
        }
    
        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 void SetFadeOutAnimation(DependencyObject element, Storyboard value)
        {
            element.SetValue(FadeOutAnimationProperty, value);
        }
    
        public static Storyboard GetFadeOutAnimation(DependencyObject element)
        {
            return (Storyboard) element.GetValue(FadeOutAnimationProperty);
        }
    
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.RegisterAttached("ItemsSource",
                typeof (IList),
                typeof (ItemsSourceBehavior),
                new UIPropertyMetadata(null, ItemsSourcePropertyChanged));
    
        public static readonly DependencyProperty FadeInAnimationProperty =
            DependencyProperty.RegisterAttached("FadeInAnimation",
                typeof (Storyboard),
                typeof (ItemsSourceBehavior),
                new UIPropertyMetadata(null));
    
        public static readonly DependencyProperty FadeOutAnimationProperty =
            DependencyProperty.RegisterAttached("FadeOutAnimation",
                typeof (Storyboard),
                typeof (ItemsSourceBehavior),
                new UIPropertyMetadata(null));
    }
    
        

    提交回复
    热议问题