• 自定义panel实现,并实现item更改和移除动画。


    原地址:https://www.cnblogs.com/yk250/p/10043694.html  无图无真相:

    1,重写panel类(模拟实现一个竖直方向排列的panel,相当于默认的StackPanel实现的效果):

         其中 availableSize 是xaml中传入的大小。调用child的Measure方法测量后得到DesiredSize用于计算实际返回的大小。

       public class VStackPanel: Panel
        {
            protected override Size MeasureOverride(Size availableSize)
            {
                Size size = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
                Size measureSize = new Size() { Width = availableSize .Width};
                foreach (UIElement item in InternalChildren)
                {
                    item.Measure(size);
                    measureSize.Height += item.DesiredSize.Height;
                }
                return measureSize;
            }
            protected override Size ArrangeOverride(Size finalSize)
            {
                double height = 0;
                foreach (UIElement item in InternalChildren)
                {               
                    Point Point = new Point(0, height);
                    item.Arrange(new Rect(Point, item.DesiredSize));
                    height += item.RenderSize.Height;              
                }
               return finalSize;
            }
    }
    

    Xaml中我们使用一个简单的ItemsControl做测试(重写ItemsPanelTemplate,使用自定义的 VStackPanel):

      <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:VStackPanel></local:VStackPanel>
                    </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>

    添加数据项后显示和普通的StackPanel并无区别。

    2,给 Add,Insert,Move统一制作布局动画。(Remove操作需要单独处理。) 

          首先,写一个AnimateBasePanel 的抽象类(只能被继承不能被实例化)基于Panel,用户布局更改的操作: 

    public abstract  class AnimateBasePanel : Panel
    {
    
    }

          然后,定义一个附加属性对象 AnimatePanelItemData,用于记录child布局前后位置的变化:

      public class AnimatePanelItemData
        {
            public Point Target;
            public Point Current;
        }
     public static AnimatePanelItemData GetData(DependencyObject obj)
            {
                return (AnimatePanelItemData)obj.GetValue(DataProperty);
            }
    
            public static void SetData(DependencyObject obj, AnimatePanelItemData value)
            {
                obj.SetValue(DataProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty DataProperty =
                DependencyProperty.RegisterAttached("Data", typeof(AnimatePanelItemData), typeof(AnimateBasePanel), new PropertyMetadata(null));

       当新增child时,定义一个可被重载的虚方法:(新增时,将AnimatePanelItemData附加到child上并且设置child的RenderTransform作为动画起始位置,最后使用 child.Arrange(bounds)定位child在panel中的位置和大小。)   

      protected virtual void ArrangeNewChid(UIElement child, Rect bounds)
      {
        var data = new AnimatePanelItemData();
        child.SetValue(DataProperty, data);
        child.RenderTransformOrigin = new Point(0.5, 0.5);
        TransformGroup group = new TransformGroup();
        group.Children.Add(new TranslateTransform() { X = -child.DesiredSize.Width, Y = child.DesiredSize.Height });
        child.RenderTransform = group;
        data.Current = new Point();
        data.Target = bounds.Location;
        child.Arrange(bounds);
        CreateTransition(child, new Point(), TimeSpan.FromMilliseconds(200), null).Begin();
     }

        对已经存在的child当布局发生改变时,即Insert,Move时定义一个同样的虚方法。(获取附加属性值,然后计算布局前后的TranslateTransform的X,Y的偏移量,最后同样使用child.Arrange(bounds)定位child在panel中的位置和大小。)

       protected virtual void ArrangeExistsChid(UIElement child, Rect bounds)
            {
                AnimatePanelItemData data = (AnimatePanelItemData)child.GetValue(DataProperty);
                data.Current = new Point(data.Target.X, data.Target.Y);
                data.Target = bounds.Location;
                child.RenderTransformOrigin = new Point(0.5, 0.5);
                TransformGroup group = new TransformGroup();
                child.RenderTransform = group;
                group.Children.Add(new TranslateTransform() { X = -(data.Target.X - data.Current.X), Y = -(data.Target.Y - data.Current.Y) });
                child.Arrange(bounds);
                CreateTransition(child, new Point(), TimeSpan.FromMilliseconds(200), null).Begin();
            }

    其中 CreateTransition 为最后布局更新时执行的动画:

       internal static Storyboard CreateTransition(UIElement element, Point newLocation, TimeSpan _duration, EasingFunctionBase easing)
            {
                var duration = new Duration(_duration);
                var sb = new Storyboard
                {
                    Duration = duration
                };
                var translateAnimationX = new DoubleAnimation
                {
                    To = newLocation.X,
                    Duration = duration
                };
                var translateAnimationY = new DoubleAnimation
                {
                    To = newLocation.Y,
                    Duration = duration
                };
                if (easing != null)
                {
                    translateAnimationX.EasingFunction = easing;
                    translateAnimationY.EasingFunction = easing;
                }
                Storyboard.SetTarget(translateAnimationX, element);
                Storyboard.SetTarget(translateAnimationY, element);
                Storyboard.SetTargetProperty(translateAnimationX,
                    new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"));        
                Storyboard.SetTargetProperty(translateAnimationY,
                    new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"));
                sb.Children.Add(translateAnimationX);
                sb.Children.Add(translateAnimationY);
                return sb;
            }

    最后统一处理(用于子类调用):

      protected  virtual void ArrangeChild(UIElement child, Rect bounds)
            {
                AnimatePanelItemData data = (AnimatePanelItemData)child.GetValue(DataProperty);
                if (data == null)
                {
                    ArrangeNewChid(child, bounds);
                }
                else
                {
                    ArrangeExistsChid(child, bounds);
                }
            }

    3,我们使用AnimateBasePanel代替VStackPanel的基类(使用AnimateBasePanel的方法代替ArrangeChild代替item.Arrange方法。):

       public class VStackPanel: AnimateBasePanel
        {
            protected override Size MeasureOverride(Size availableSize)
            {
                Size size = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
                Size measureSize = new Size() { Width = availableSize .Width};
                foreach (UIElement item in InternalChildren)
                {
                    item.Measure(size);
                    measureSize.Height += item.DesiredSize.Height;
                }
                return measureSize;
            }
            protected override Size ArrangeOverride(Size finalSize)
            {
                double height = 0;
                foreach (UIElement item in InternalChildren)
                {               
                    Point Point = new Point(0, height);
                    ArrangeChild(item, new Rect(Point,item.DesiredSize));
                    height += item.RenderSize.Height;              
                }
               return finalSize;
            }
    }

    也可以重载AnimateBasePanel的 ArrangeNewChid或ArrangeExistsChid方法:

    protected override void ArrangeNewChid(UIElement child, Rect bounds)
    {
      var data = new AnimatePanelItemData();
      child.SetValue(DataProperty, data);
      child.RenderTransformOrigin = new Point(0.5, 0.5);
      TransformGroup group = new TransformGroup();
      child.RenderTransform = group;
      group.Children.Add(new TranslateTransform() { X = -child.DesiredSize.Width, Y = 0 });
      data.Current = new Point();
      data.Target = bounds.Location;
      child.Arrange(bounds);
      CreateTransition(child, new Point(), TimeSpan.FromMilliseconds(200), null).Begin();
    }

    到此为止,除了remove的布局没有完全实现外,其他布局已经能正常表现了。

    4,使用blend库进行删除动画的制作:

          首先使用mvvm模式定义好ModelBase(CallerMemberName属性需要使用C#新特性。)  

      public class ModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

           然后定义每一项item的数据模型:(只使用一个用于显示的Name属性和用于删除的IsDelete属性和一个Delete方法。)

        public class TestModel : ModelBase
        {
            public virtual void Delete()
            {
                IsDelete = true;
            }
            public string Name { get; set; }
    
            private bool _isDelete;
            /// <summary>
            /// _isDelete
            /// </summary>
            public bool IsDelete
            {
                get { return _isDelete; }
                set { _isDelete = value; RaisePropertyChanged(); }
            }
        }

        接着,定义一个简单的ViewModel:

    public class MainViewModel : ModelBase
        {
    
            public MainViewModel()
            {
                TestModels = new ObservableCollection<TestModel>() {
                };
            }
    
            private ObservableCollection<TestModel> _TestModels;
            /// <summary>
            /// _TestModels
            /// </summary>
            public ObservableCollection<TestModel> TestModels
            {
                get
                {
                    return _TestModels;
                }
                set { _TestModels = value; RaisePropertyChanged(); }
            }
    
        }

    前台Xaml 随意写一下:(实现当remove删除某一项的时候,先调用Delete把动画执行完成以后再使用RemoveItemInListBoxAction删除该项。

      <ItemsControl ItemsSource="{Binding TestModels}" BorderBrush="Red" BorderThickness="1">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:VStackPanel></local:VStackPanel>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border BorderBrush="Red" BorderThickness="1" Height="30" x:Name="br" RenderTransformOrigin=".5,.5" Loaded="br_Loaded">
                            <Border.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform/>
                                    <SkewTransform/>
                                    <RotateTransform/>
                                    <TranslateTransform/>
                                </TransformGroup>
                            </Border.RenderTransform>
                            <i:Interaction.Triggers>
                                <ei:DataTrigger Binding="{Binding IsDelete}" Value="true">
                                    <ei:ControlStoryboardAction x:Name="StoryboardAction">
                                        <ei:ControlStoryboardAction.Storyboard>
                                            <Storyboard>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="br">
                                                    <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="{Binding ElementName=br,Path=ActualWidth}"/>
                                                </DoubleAnimationUsingKeyFrames>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="br">
                                                    <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                                </DoubleAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </ei:ControlStoryboardAction.Storyboard>
                                    </ei:ControlStoryboardAction>
                                </ei:DataTrigger>
                                <ei:StoryboardCompletedTrigger Storyboard="{Binding ElementName=StoryboardAction,Path=Storyboard}">
                                    <pi:RemoveItemInListBoxAction></pi:RemoveItemInListBoxAction>
                                </ei:StoryboardCompletedTrigger>
                            </i:Interaction.Triggers>
                            <TextBlock VerticalAlignment="Center" HorizontalAlignment="Stretch" Text="{Binding Name}"></TextBlock>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

    其中 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"  xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity" 引用一下。

    代码 :https://files.cnblogs.com/files/yk250/CustomPanelTest.rar

  • 相关阅读:
    吉文斯旋转
    MinHash
    MinHash 原理
    Mahout SlopOne
    svd++
    openwrt定制管理
    苹果新的编程语言 Swift 语言进阶(九)--方法和下标
    2014年百度之星程序设计大赛
    unixbench安装及使用
    数据库连接-ADO.NET
  • 原文地址:https://www.cnblogs.com/yk250/p/10043694.html
Copyright © 2020-2023  润新知