首页 > 代码库 > WP8 FlipView

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;        }    }

  如果有4张图片 应该在最前面加上最后一张 再在最后面加上第一张 这样当到最前面或者最后面的时候 直接修改 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 =http://www.mamicode.com/= (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;            }

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