WPF轮播图实现方式(二)

 WPF开发者QQ群: 340500857  | 微信群 -> 进入公众号主页 加入组织

欢迎转发、分享、点赞、在看,谢谢~。  

01

效果预览

02


代码如下

一、EmphasizerCarousel.cs 代码如下

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using WPFDevelopers.Controls.Helpers;


namespace WPFDevelopers.Controls
{


    [DefaultProperty("Children")]
    [ContentProperty("Children")]
    [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
    [TemplatePart(Name = Part_BackCanvasName, Type = typeof(Canvas))]
    public class EmphasizerCarousel : Control, IAddChild
    {
        static EmphasizerCarousel()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(EmphasizerCarousel), new FrameworkPropertyMetadata(typeof(EmphasizerCarousel)));
        }


        public EmphasizerCarousel()
        {
            Loaded += EmphasizerCarousel_Loaded;
            Unloaded += EmphasizerCarousel_Unloaded;
            SizeChanged += EmphasizerCarousel_SizeChanged;
            Children.CollectionChanged += OnItems_CollectionChanged;
        }




        [ReadOnly(true)]
        const string Part_BackCanvasName = "PART_BackCanvas";


        Canvas Part_BackCanvas = default;


        private const int _maxSimpleHeight = 320;


        private int _SimpleCount = 0;


        private FrameworkElement _DisplayItem = null;


        private double _SimpleTop = 0;
        private double _SimpleHeight = 0;
        private double _SimpleWidth = 0;
        //private double _SimpleOffset = 10;


        private double _DisplayHeight = 0;
        private double _DisplayWidth = 0;
        private double _DisplayOffset = 10d;






        private Dictionary<int, Point> _mapCanvasPoint = new Dictionary<int, Point>();


        private Dictionary<int, FrameworkElement> _mapUIwithIndex = new Dictionary<int, FrameworkElement>();


        private ObservableCollection<FrameworkElement> _Children = new ObservableCollection<FrameworkElement>();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public ObservableCollection<FrameworkElement> Children => _Children;


        private void OnItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    if (item is FrameworkElement element)
                    {
                        element.PreviewMouseLeftButtonDown += EmphasizerCarousel_MouseLeftButtonDown;


                        element.RenderTransformOrigin = new Point(0.5, 0.5);
                        element.RenderTransform = new TransformGroup
                        {
                            Children =
                            {
                                new ScaleTransform(),
                                new SkewTransform(),
                                new RotateTransform(),
                                new TranslateTransform()
                            }
                        };
                    }
                }
            }


            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (var item in e.NewItems)
                {
                    if (item is FrameworkElement element)
                        element.PreviewMouseLeftButtonDown -= EmphasizerCarousel_MouseLeftButtonDown;


                    if (item == _DisplayItem)
                        _DisplayItem = null;
                }
            }


            OnSizeChangedCallback();
        }


        #region override


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();


            Part_BackCanvas = GetTemplateChild(Part_BackCanvasName) as Canvas;


            if (Part_BackCanvas == null)
                throw new Exception("Some element is not in template!");
        }




        #endregion






        void IAddChild.AddChild(object value)
        {
            throw new NotImplementedException();
        }


        void IAddChild.AddText(string text)
        {
            throw new NotImplementedException();
        }




        private bool OnSizeChangedCallback()
        {
            if (Part_BackCanvas == null)
                return false;


            var vHeight = Part_BackCanvas.ActualHeight;
            var vWidth = Part_BackCanvas.ActualWidth;


            if (vHeight == Double.NaN || vWidth == Double.NaN)
                return false;


            if (vHeight == 0 || vWidth == 0)
                return false;


            Part_BackCanvas.Children.Clear();


            _mapUIwithIndex.Clear();
            _mapCanvasPoint.Clear();


            var vItemCount = Children.Count;
            if (vItemCount <= 0)
                return false;


            _SimpleCount = vItemCount - 1;
            if (_SimpleCount == 0)
            {
                Children[0].Width = vWidth;
                Children[0].Height = vHeight;


                Part_BackCanvas.Children.Add(Children[0]);
                return true;
            }


            //计算并划分显示区域
            var vSimpleHeight = vHeight * 0.4;
            _SimpleHeight = vSimpleHeight;
            if (_SimpleHeight > _maxSimpleHeight)
                _SimpleHeight = _maxSimpleHeight;


            var vSimpleWidth = vWidth / _SimpleCount;
            _SimpleWidth = vSimpleWidth;
            _SimpleTop = vHeight - _SimpleHeight;


            _DisplayHeight = vHeight - _SimpleHeight;
            _DisplayHeight -= _DisplayOffset;


            _DisplayWidth = vWidth;


            if (_DisplayItem == null)
                _DisplayItem = Children[0];


            int nIndex = 0;
            int nPosIndex = 0;
            double Left = 0;


            foreach (var item in Children)
            {
                Part_BackCanvas.Children.Add(item);
                item.Tag = nIndex;


                if (_DisplayItem == item)
                {
                    item.Width = _DisplayWidth;
                    item.Height = _DisplayHeight;
                    item.SetValue(Canvas.LeftProperty, 0d);
                    item.SetValue(Canvas.TopProperty, 0d);
                }
                else
                {
                    item.Width = _SimpleWidth;
                    item.Height = _SimpleHeight;
                    item.SetValue(Canvas.LeftProperty, Left);
                    item.SetValue(Canvas.TopProperty, _SimpleTop);


                    _mapCanvasPoint[nPosIndex] = new Point(Left, _SimpleTop);
                    _mapUIwithIndex[nPosIndex] = item;


                    Left += _SimpleWidth;
                    ++nPosIndex;
                }
                ++nIndex;
            }
            return true;
        }


        private void EmphasizerCarousel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (sender == _DisplayItem)
                return;


            e.Handled = true;


            var vFrameWorker = sender as FrameworkElement;
            if (vFrameWorker == null)
                return;


            if (!int.TryParse(_DisplayItem.Tag.ToString(), out int nIndex))
                return;


            if (!int.TryParse(vFrameWorker.Tag.ToString(), out int nLeaveIndex))
                return;


            int offset = 500;


            var vLeft = (_DisplayWidth - _SimpleWidth) / 2d;
            var vTop = (_DisplayHeight - _SimpleHeight) / 2d;


            //一系列计算 计算得到当前展示页要回到的Dock位置  
            //一系列计算 计算得到当前点击页要移除的Dock位置


            int nTargertIndex = nIndex;


            int nLeaveDockIndex = 0;
            foreach (var item in _mapUIwithIndex)
            {
                if (!int.TryParse(item.Value.Tag.ToString(), out int nItemIndex))
                    continue;


                if (nItemIndex == nLeaveIndex)
                {
                    nLeaveDockIndex = item.Key;
                    _mapUIwithIndex[item.Key] = null;
                    break;
                }
            }


            //如果目标位置Index是1那么他可以放在 0号位也可以放在1号位 主要是看他的前一个位置上的对象的Index是大还是小
            //判定 模拟演练 目标位置放入对象时 目标位置当前的对象时前移还是不动
            var vTargetFrame = _mapUIwithIndex.GetValueOrDefault(nTargertIndex);
            if (vTargetFrame != null)
            {
                if (int.TryParse(vTargetFrame.Tag.ToString(), out int vTargetFrameIndex))
                {
                    //先判定 后续动作是 左移还是右移
                    bool? bLeft2Right = null;


                    if (nTargertIndex > nLeaveDockIndex)
                        bLeft2Right = false;
                    else if (nTargertIndex < nLeaveDockIndex)
                        bLeft2Right = true;


                    if (bLeft2Right == true)
                    {
                        if (vTargetFrameIndex < nIndex)
                            nTargertIndex++;
                    }


                    if (bLeft2Right == false)
                    {
                        if (vTargetFrameIndex > nIndex)
                            nTargertIndex--;
                    }
                }
            }


            if (nIndex >= _mapCanvasPoint.Count)
                nTargertIndex = _mapCanvasPoint.Count - 1;


            if (nIndex < 0)
                nTargertIndex = 0;


            Point point = _mapCanvasPoint.GetValueOrDefault(nTargertIndex);


            //定义动画 
            Storyboard storyboard = new Storyboard
            {
                SpeedRatio = 2,
            };


            int nBegin = 250;
            if (nTargertIndex < nLeaveDockIndex)
            {
                //全部右移
                for (int i = nLeaveDockIndex - 1; i >= nTargertIndex; --i)
                {
                    var vUI = _mapUIwithIndex.GetValueOrDefault(i);
                    if (vUI == null)
                        continue;


                    var vPoint = _mapCanvasPoint.GetValueOrDefault(i + 1);


                    DoubleAnimation animation = new DoubleAnimation()
                    {
                        To = vPoint.X,
                        BeginTime = TimeSpan.FromMilliseconds(nBegin),
                        Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                        EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                    };


                    Storyboard.SetTarget(animation, vUI);
                    Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                    storyboard.Children.Add(animation);


                    if (_mapUIwithIndex.ContainsKey(i + 1))
                        _mapUIwithIndex[i + 1] = vUI;


                    _mapUIwithIndex[i] = null;


                    //nBegin += nBegin;
                }
            }
            else if (nTargertIndex > nLeaveDockIndex)
            {
                //全部左移
                for (int i = nLeaveDockIndex + 1; i <= nTargertIndex; ++i)
                {
                    var vUI = _mapUIwithIndex.GetValueOrDefault(i);
                    if (vUI == null)
                        continue;


                    var vPoint = _mapCanvasPoint.GetValueOrDefault(i - 1);
                    DoubleAnimation animation = new DoubleAnimation()
                    {
                        To = vPoint.X,
                        BeginTime = TimeSpan.FromMilliseconds(nBegin),
                        Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                        EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                    };


                    Storyboard.SetTarget(animation, vUI);
                    Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                    storyboard.Children.Add(animation);


                    if (_mapUIwithIndex.ContainsKey(i - 1))
                        _mapUIwithIndex[i - 1] = vUI;


                    _mapUIwithIndex[i] = null;


                    //nBegin += nBegin;
                }
            }


            if (_mapUIwithIndex.ContainsKey(nTargertIndex))
                _mapUIwithIndex[nTargertIndex] = _DisplayItem;


            //当前打开的界面 先缩放 位移 后 移到等待区
            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = _SimpleWidth,
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("Width"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = _SimpleHeight,
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = vLeft,
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = vTop,
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));
                storyboard.Children.Add(animation);
            }


            {


                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = point.X,
                    BeginTime = TimeSpan.FromMilliseconds(offset),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = point.Y,
                    BeginTime = TimeSpan.FromMilliseconds(offset),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, _DisplayItem);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));
                storyboard.Children.Add(animation);
            }


            //当前选中的界面 移动到目标位置 再放大位移
            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = vLeft,
                    BeginTime = TimeSpan.FromMilliseconds(offset),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = vTop,
                    BeginTime = TimeSpan.FromMilliseconds(offset),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = _DisplayWidth,
                    BeginTime = TimeSpan.FromMilliseconds(offset * 2),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("Width"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = _DisplayHeight,
                    BeginTime = TimeSpan.FromMilliseconds(offset * 2),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = 0,
                    BeginTime = TimeSpan.FromMilliseconds(offset * 2),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));
                storyboard.Children.Add(animation);
            }


            {
                DoubleAnimation animation = new DoubleAnimation()
                {
                    To = 0,
                    BeginTime = TimeSpan.FromMilliseconds(offset * 2),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, offset)),
                    EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut },
                };


                Storyboard.SetTarget(animation, vFrameWorker);
                Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Left)"));
                storyboard.Children.Add(animation);
            }


            _DisplayItem = vFrameWorker;
            storyboard.Begin(vFrameWorker);
        }


        private void EmphasizerCarousel_Loaded(object sender, RoutedEventArgs e)
        {
        }


        private void EmphasizerCarousel_Unloaded(object sender, RoutedEventArgs e)
        {
        }


        private void EmphasizerCarousel_SizeChanged(object sender, SizeChangedEventArgs e) => OnSizeChangedCallback();


    }
}


二、EmphasizerCarousel.xaml 代码如下

<Style TargetType="{x:Type controls:EmphasizerCarousel}" BasedOn="{StaticResource ControlBasicStyle}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:EmphasizerCarousel}">


                    <Grid Background="{TemplateBinding Background}" >
                        <Canvas x:Name="PART_BackCanvas">
                        </Canvas>
                    </Grid>


                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

三、CarouselExampleEx.xaml 代码如下 

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.CarouselExampleEx"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
             xmlns:wpfdev="https://github.com/yanjinhuagood/WPFDevelopers"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <wpfdev:EmphasizerCarousel>
            <Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Craouse/0.jpg"/>
            <Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Craouse/1.jpg"/>
            <Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Craouse/2.jpg"/>
            <Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Craouse/3.jpg"/>
            <Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Craouse/4.jpg"/>
        </wpfdev:EmphasizerCarousel>
    </Grid>
</UserControl>


源码地址

github:https://github.com/yanjinhuagood/WPFDevelopers.git

gitee:https://gitee.com/yanjinhua/WPFDevelopers.git

WPF开发者QQ群: 340500857 

Github:https://github.com/yanjinhuagood

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载请著名作者 出处 https://github.com/yanjinhuagood

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值