panorama控件是wp中最有特色的控件之一,它提供了wp独有的全景视图,但是在使用该控件的时候,我碰到了这么一个问题。我想在代码中指定panorama的SelectedIndex,但是该属性是只读的,通过在网上寻找,最终还是没有找到一个完美的解决方案,网上的解决方案大抵如下:
1.使用panorama.DefaultItem=panorama.Items[newIndex];
这个方法它只是将第index处的item放到了第一个位置,而且没有过渡的动画,不符合要求。
2.通过设置SelectedItemProperty这个依赖属性来改变SelectedItem的值。
panorama.SetValue(Panorama.SelectedItemProperty, pan.Items[newIndex]);
但是就算这么设了,还是没有滑动的动画效果,具体请参考网友提供的代码。
为了得到一个完美的解决方案,楼主深入挖掘了panorama控件的动画运作方式以及它的控件模板。最终搞定了这个问题,代码比较多。下面是主要的一个函数
/// <summary> /// 平滑过渡到指定的Item /// </summary> /// <param name="panorama"></param> /// <param name="index">指定的Item索引</param> /// <param name="duration">动画时长</param> /// <param name="complated">动画完成后需要执行的动作</param> public static void GoTo(this Panorama panorama, int index, Duration duration, Action complated = null) { if (panorama.Items.Count == 0) return; while (index < 0) index += panorama.Items.Count; while (index >= panorama.Items.Count) index -= panorama.Items.Count; if (index == panorama.SelectedIndex) return; var bgLayer = panorama.Descendants<PanningBackgroundLayer>().FirstOrDefault(); var titleLayer = panorama.Descendants<PanningTitleLayer>().FirstOrDefault(); var itemsLayer = panorama.Descendants<PanningLayer>().LastOrDefault(); var panel = panorama.Descendants<PanoramaPanel>().FirstOrDefault(); if (panel == null) return; var direction = -1; if (index > panorama.SelectedIndex) { var length1 = index - panorama.SelectedIndex; var length2 = panorama.Items.Count - length1; if (length2 < length1) direction = 1; } else { var length1 = panorama.SelectedIndex - index; var length2 = panorama.Items.Count - length1; if (length1 <= length2) direction = 1; } var currentOffset = -GetOffsetX(panel, panorama.SelectedIndex); var offset = -(int) GetOffsetX(panel, index); if (Math.Sign(offset - currentOffset) != Math.Sign(direction)) { if (bgLayer != null) bgLayer.Wraparound(direction); if (titleLayer != null) titleLayer.Wraparound(direction); if (itemsLayer != null) itemsLayer.Wraparound(direction); } panorama.SetValue(Panorama.SelectedItemProperty, panorama.Items[index]); UpdateItemPositions(panorama, panel); if (bgLayer != null) bgLayer.GoTo(offset, duration, null); if (titleLayer != null) titleLayer.GoTo(offset, duration, null); if (itemsLayer != null) itemsLayer.GoTo(offset, duration, complated); }
当想以动画形式过渡到某个项时,直接如下使用即可:
panorama.GoTo(0, new Duration(TimeSpan.FromMilliseconds(800)));
为了实现最终的目的,还必须的用到LinqToTree这个利器。
全部的代码可以在此处下载:http://vdisk.weibo.com/s/yVSnUWjPhIz8j
注意:wp8中PanningLayer的GoTo函数只有两个参数,只需要将下面几行代码更改一下即可使用
if (bgLayer != null) bgLayer.GoTo(offset, duration, null); if (titleLayer != null) titleLayer.GoTo(offset, duration, null); if (itemsLayer != null) itemsLayer.GoTo(offset, duration, complated);
改为:
if (bgLayer != null) bgLayer.GoTo(offset, duration); if (titleLayer != null) titleLayer.GoTo(offset, duration); if (itemsLayer != null) itemsLayer.GoTo(offset, duration); if (complated == null) return; var timer = new DispatcherTimer {Interval = duration.TimeSpan}; timer.Tick += (sender, e) => { complated(); timer.Stop(); }; timer.Start();