WP8 FlipView

  一直想写点东西,直到今天才真正动笔,唯一的原因就是太懒,太懒...

  大家肯定熟悉安卓各种客户端首页联动图片广告(比如淘宝),可以自动滚动,可以手动滑动,当然是循环的。但是在wp上我看到的做的最好的最早的当属爱壁纸了。

  写在wp8.1的filpview马上来临之际。上代码吧。

    [TemplatePart(Name = InnerBorderName, Type = typeof(Border))]

    [TemplatePart(Name = InnerItemsPresenterName, Type = typeof(ItemsPresenter))]

    [TemplatePart(Name = listMaskerName, Type = typeof(ListBox))]

    public class SlideView : ItemsControl, INotifyPropertyChanged

    {

        private const string InnerBorderName = "InnerBorder";



        private const string InnerItemsPresenterName = "InnerItemsPresenter";



        private const string listMaskerName = "listMasker";



        //视觉树根元素

        Border border = null;



        //滑动元素

        ItemsPresenter itemsPresenter = null;



        ListBox listBox = null;



        //是否正在滑动

        private bool isBusy = false;



        //滑动故事版

        Storyboard sb = null;



        //滑动动画

        DoubleAnimation da = null;



        //缓动函数

        CircleEase ease = null;



        // 计数器

        private DispatcherTimer timer = null;



        //标记索引

        private int index = 1;



        private int selectIndex = 0;



        private double borderWidth = 0;





        // MarkSource

        public static readonly DependencyProperty MarkSourceProperty =

           DependencyProperty.Register("MarkSource", typeof(IEnumerable), typeof(SlideView), new PropertyMetadata(null));



        /// <summary>

        /// 当前位置索引

        /// </summary>

        public int SelectIndex

        {

            get

            {

                return selectIndex;

            }

            set

            {

                selectIndex = value;

                OnPropertyChanged("SelectIndex");

            }

        }



        /// <summary>

        /// 页码数据源

        /// </summary>

        public IEnumerable MarkSource

        {

            get

            {

                return (IEnumerable)GetValue(MarkSourceProperty);

            }

            set

            {

                SetValue(MarkSourceProperty, value);

            }

        }



        /// <summary>

        /// Border宽度

        /// </summary>

        public double BorderWidth

        {

            get

            {

                return borderWidth;

            }

            set

            {

                borderWidth = value;

                OnPropertyChanged("BorderWidth");

            }

        }



        /// <summary>

        /// 构造函数

        /// </summary>

        public SlideView()

        {

            this.DefaultStyleKey = typeof(SlideView);

            this.Loaded += SlideView_Loaded;

            this.Unloaded += SlideView_Unloaded;

        }



        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)

        {

            BorderWidth = this.Width * this.Items.Count;

            if (this.Items.Count >= 4)

            {

                InitParams();

                if (itemsPresenter == null) 

                {

                    itemsPresenter = this.GetTemplateChild(InnerItemsPresenterName) as ItemsPresenter;

                }

                (itemsPresenter.RenderTransform as CompositeTransform).TranslateX = this.Width * (-1);

                StartMove();

            }

            base.OnItemsChanged(e);

        }



        public override void OnApplyTemplate()

        {

            base.OnApplyTemplate();

            border = this.GetTemplateChild(InnerBorderName) as Border;

            if (listBox != null)

            {

                listBox.LayoutUpdated -= listBox_LayoutUpdated;

            }

            listBox = this.GetTemplateChild(listMaskerName) as ListBox;

            if (listBox != null)

            {

                listBox.LayoutUpdated += listBox_LayoutUpdated;

            }

            if (border != null)

            {

                Binding bind = new Binding();

                bind.Path = new PropertyPath("BorderWidth");

                bind.Source = this;

                border.SetBinding(WidthProperty, bind);

            }

            if (itemsPresenter != null)

            {

                itemsPresenter.ManipulationStarted -= itemsPresenter_ManipulationStarted;

                itemsPresenter.ManipulationDelta -= items_ManipulationDelta;

                itemsPresenter.ManipulationCompleted -= items_ManipulationCompleted;

            }

            itemsPresenter = this.GetTemplateChild(InnerItemsPresenterName) as ItemsPresenter;

            if (itemsPresenter != null)

            {

                itemsPresenter.ManipulationStarted += itemsPresenter_ManipulationStarted;

                itemsPresenter.ManipulationDelta += items_ManipulationDelta;

                itemsPresenter.ManipulationCompleted += items_ManipulationCompleted;

            }

        }



        void listBox_LayoutUpdated(object sender, EventArgs e)

        {

            if (listBox.Items.Count > 0) 

            {

                List<Ellipse> ellipseList = FindChildOfType<Ellipse>(listBox);

                if (ellipseList.Count > 0) 

                {

                    foreach (var item in ellipseList) 

                    {

                        if (item.GetValue(Shape.FillProperty) != null) 

                        {

                            continue;

                        }

                        Binding bind = new Binding();

                        bind.Path = new PropertyPath("SelectIndex");

                        bind.Source = this;

                        bind.Converter = new SlideViewMarkColorConverter();

                        bind.ConverterParameter = item.Tag;

                        item.SetBinding(Shape.FillProperty, bind);

                    }

                }

            }

        }



        private void SlideView_Loaded(object sender, System.Windows.RoutedEventArgs e)

        {

            if (itemsPresenter != null)

            {

                InitParams();

                da.Duration = TimeSpan.FromMilliseconds(500);

                ease.EasingMode = EasingMode.EaseOut;

                da.EasingFunction = ease;

                Storyboard.SetTarget(da, itemsPresenter);

                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)"));

                sb.Children.Add(da);

                sb.Completed += sb_Completed;

                timer.Interval = TimeSpan.FromMilliseconds(3000);

                timer.Tick += timer_Tick;

                StartMove();

            }

        }



        private void SlideView_Unloaded(object sender, RoutedEventArgs e)

        {

            StopMove();

            timer = null;

            if (sb != null)

            {

                sb.Stop();

                sb = null;

            }

            if (da != null)

            {

                da = null;

            }

            if (ease != null)

            {

                ease = null;

            }

        }



        /// <summary>

        /// 初始化参数

        /// </summary>

        private void InitParams()

        {

            index = 1;

            SelectIndex = 0;

            isBusy = false;

            if (sb == null) 

            {

                sb = new Storyboard();

            }

            if (da == null) 

            {

                da = new DoubleAnimation();

            }

            if (ease == null) 

            {

                ease = new CircleEase();

            }

            if (timer == null) 

            {

                timer = new DispatcherTimer();

            }

        }



        /// <summary>

        /// 计数器事件--自动滚动

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void timer_Tick(object sender, EventArgs e)

        {

            timer.Stop();

            MoveToNext();

        }



        /// <summary>

        /// 滑动开始事件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void itemsPresenter_ManipulationStarted(object sender, System.Windows.Input.ManipulationStartedEventArgs e)

        {

            //如果还在滑动过程中或者items的数量小于2 则禁止滑动 

            if (isBusy || this.Items.Count < 4)

            {

                e.Complete();

                e.Handled = true;

                return;

            }

        }



        /// <summary>

        /// 滑动过程事件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void items_ManipulationDelta(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)

        {

            //如果还在滑动过程中 则禁止滑动

            if (isBusy || this.Items.Count < 4)

            {

                e.Complete();

                e.Handled = true;

                return;

            }

            StopMove();

            ItemsPresenter b = sender as ItemsPresenter;

            (b.RenderTransform as CompositeTransform).TranslateX += e.DeltaManipulation.Translation.X;

        }



        /// <summary>

        /// 滑动结束事件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void items_ManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)

        {

            //如果还在滑动过程中 则禁止滑动

            if (isBusy || this.Items.Count < 4)

            {

                e.Handled = true;

                return;

            }

            if (e.TotalManipulation.Translation.X < 0)

            {

                if (!e.IsInertial && Math.Abs(e.TotalManipulation.Translation.X) < this.Width / 3)

                {

                    MoveToCurrent();

                }

                else

                {

                    MoveToNext();

                }

            }

            else if (e.TotalManipulation.Translation.X > 0)

            {

                if (!e.IsInertial && Math.Abs(e.TotalManipulation.Translation.X) < this.Width / 3)

                {

                    MoveToCurrent();

                }

                else

                {

                    MoveToPre();

                }

            }

        }



        /// <summary>

        /// 动画完成事件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void sb_Completed(object sender, EventArgs e)

        {

            if (index == 0)

            {

                index = this.Items.Count - 1 - 1;

                (itemsPresenter.RenderTransform as CompositeTransform).TranslateX = index * (-1) * this.Width;

            }

            else if (index == this.Items.Count - 1)

            {

                index = 1;

                (itemsPresenter.RenderTransform as CompositeTransform).TranslateX = (-1) * this.Width;

            }

            CalcIndex();

            isBusy = false;

            StartMove();

        }



        /// <summary>

        /// 切换到下一页

        /// </summary>

        public void MoveToNext()

        {

            if (index + 1 > this.Items.Count)

            {

                return;

            }

            index++;

            da.To = index * (-1) * this.Width;

            isBusy = true;

            sb.Begin();

        }



        /// <summary>

        /// 切换到上一页

        /// </summary>

        public void MoveToPre()

        {

            if (index - 1 < 0)

            {

                return;

            }

            index--;

            da.To = index * (-1) * this.Width;

            isBusy = true;

            sb.Begin();

        }



        /// <summary>

        /// 切换到当前页

        /// </summary>

        private void MoveToCurrent()

        {

            da.To = index * (-1) * this.Width;

            isBusy = true;

            sb.Begin();

        }



        /// <summary>

        /// 重置标记位

        /// </summary>

        private void CalcIndex()

        {

            if (index == 0)

            {

                SelectIndex = this.Items.Count - 2 - 1;

            }

            else if (index == this.Items.Count - 1)

            {

                SelectIndex = 0;

            }

            else

            {

                SelectIndex = index - 1;

            }

        }



        /// <summary>

        /// 开始自动翻动

        /// </summary>

        public void StartMove()

        {

            if (timer != null && !timer.IsEnabled)

            {

                timer.Start();

            }

        }



        /// <summary>

        /// 停止自动翻动

        /// </summary>

        public void StopMove()

        {

            if (timer != null && timer.IsEnabled)

            {

                timer.Stop();

            }

        }



        /// <summary>

        /// 针对属性更改通知的多播事件。

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;



        /// <summary>

        /// 向侦听器通知已更改了某个属性值。

        /// </summary>

        /// <param name="propertyName">用于通知侦听器的属性的名称。此

        /// 值是可选的,可以在从支持

        /// <see cref="CallerMemberNameAttribute"/> 的编译器调用时自动提供。</param>

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)

        {

            var eventHandler = this.PropertyChanged;

            if (eventHandler != null)

            {

                eventHandler(this, new PropertyChangedEventArgs(propertyName));

            }

        }



        static List<T> FindChildOfType<T>(DependencyObject root) where T : class

        {

            List<T> retList = new List<T>();

            var queue = new Queue<DependencyObject>();

            queue.Enqueue(root);



            while (queue.Count > 0)

            {

                DependencyObject current = queue.Dequeue();

                for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)

                {

                    var child = VisualTreeHelper.GetChild(current, i);

                    var typedChild = child as T;

                    if (typedChild != null)

                    {

                        retList.Add(typedChild);

                    }

                    queue.Enqueue(child);

                }

            }

            return retList;

        }

    }

  如果图片多于2张 应该在最前面加上最后一张 再在最后面加上第一张 这样当到最前面或者最后面的时候 直接修改 TranslateX 达到循环的效果。

   样式:

<Style TargetType="snControls:SlideView">

        <Setter Property="Background" Value="{x:Null}" />

        <Setter Property="BorderThickness" Value="0" />

        <Setter Property="TabNavigation" Value="Once" />

        <Setter Property="IsTabStop" Value="False" />

        <Setter Property="ItemsPanel">

            <Setter.Value>

                <ItemsPanelTemplate>

                    <VirtualizingStackPanel Orientation="Horizontal" />

                </ItemsPanelTemplate>

            </Setter.Value>

        </Setter>

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="snControls:SlideView">

                    <Grid Background="{TemplateBinding Background}"

                          Height="{TemplateBinding Height}"

                          Width="{TemplateBinding Width}">

                        <Border Height="{TemplateBinding Height}"

                                BorderThickness="{TemplateBinding BorderThickness}"

                                x:Name="InnerBorder">

                            <ItemsPresenter x:Name="InnerItemsPresenter">

                                <ItemsPresenter.RenderTransform>

                                    <CompositeTransform/>

                                </ItemsPresenter.RenderTransform>

                            </ItemsPresenter>

                        </Border>

                        <ListBox VerticalAlignment="Bottom"

                                 HorizontalAlignment="Center"

                                 x:Name="listMasker"

                                 ItemsSource="{TemplateBinding MarkSource}">

                            <ListBox.ItemsPanel>

                                <ItemsPanelTemplate>

                                    <VirtualizingStackPanel Orientation="Horizontal" />

                                </ItemsPanelTemplate>

                            </ListBox.ItemsPanel>

                            <ListBox.ItemTemplate>

                                <DataTemplate>

                                    <Ellipse Margin="0 0 4 12"

                                             Width="8"

                                             Height="8"

                                             Tag="{Binding MarkIndex}">

                                    </Ellipse>

                                </DataTemplate>

                            </ListBox.ItemTemplate>

                        </ListBox>

                    </Grid>

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

 

颜色转换器:

  

 public class SlideViewMarkColorConverter : IValueConverter

    {

        private SolidColorBrush brush = new SolidColorBrush();

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            if ((int)value == (int)parameter)

            {

                brush.Opacity = 1;

                brush.Color = Colors.White;

            }

            else 

            {

                brush.Opacity = 0.4;

                brush.Color = Colors.Black;

            }

            return brush;

        }



        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new NotImplementedException();

        }

    }

  MarkSource绑定实体,其中MarkIndex修改为实体的sn,如果实体数量大于1 ItemsSource前insert最后一个 ItemsSource后add第一个。

 

最后如果将控件放在枢轴里面 是无法滑动的。

在页面后置代码loadevent里面加上

 slideview.UseOptimizedManipulationRouting = false;

            slideview.AddHandler(PivotItem.ManipulationStartedEvent, new EventHandler<ManipulationStartedEventArgs>(myPivotItem_ManipulationStarted), true);

            slideview.AddHandler(PivotItem.ManipulationDeltaEvent, new EventHandler<ManipulationDeltaEventArgs>(myPivotItem_ManipulationDelta), true);

            slideview.AddHandler(PivotItem.ManipulationCompletedEvent, new EventHandler<ManipulationCompletedEventArgs>(myPivotItem_ManipulationCompleted), true);

myPivotItem_ManipulationStarted
myPivotItem_ManipulationDelta
myPivotItem_ManipulationCompleted 事件里面判断
if (e.OriginalSource.GetType() == typeof(Image))

            {

                e.Handled = true;

            }

这个判断确保如果是图片 就阻止枢轴滑动 如果页面还有其他图片 也可以用坐标 等等...

 



                            

你可能感兴趣的:(view)