捕捉SurfaceListBox

时间:2012-07-24 19:37:02

标签: wpf pixelsense

我正在寻找创建一个滚动的surfacelistbox,它会在拖动完成后自动捕捉到一个位置,以便屏幕上的中心项目在视口中居中。

我已经获得了中心项目,但现在像往常一样,WPF处理大小,屏幕位置和偏移的方式令我感到困惑。

目前我已选择订阅SurfaceScrollViewer的ManipulationCompleted事件,因为在我完成滚动手势后似乎一直闪现(而ScrollChanged事件往往会提前触发)。

void ManipCompleted(object sender, ManipulationCompletedEventArgs e)
{
    FocusTaker.Focus(); //reset focus to a dummy element
    List<FrameworkElement> visibleElements = new List<FrameworkElement>();
    for (int i = 0; i < List.Items.Count; i++)
    {
        SurfaceListBoxItem item = List.ItemContainerGenerator.ContainerFromIndex(i) as SurfaceListBoxItem;
        if (ViewportHelper.IsInViewport(item) && (List.Items[i] as string != "Dummy"))
        {
            FrameworkElement el = item as FrameworkElement;
            visibleElements.Add(el);
        }
    }

    int centerItemIdx = visibleElements.Count / 2;
    FrameworkElement centerItem = visibleElements[centerItemIdx];

    double center = ss.ViewportWidth / 2;

    //ss is the SurfaceScrollViewer
    Point itemPosition = centerItem.TransformToAncestor(ss).Transform(new Point(0, 0));

    double desiredOffset = ss.HorizontalOffset + (center - itemPosition.X);

    ss.ScrollToHorizontalOffset(desiredOffset); 

    centerItem.Focus(); //this also doesn't seem to work, but whatever.
}

列表快照,但它的快照似乎有些混乱。我在屏幕中央有一条线,有时它看起来正好位于物品的中间,但有时它会偏向一侧,甚至是物品之间。不能确定下来,但似乎列表的第一和第四个四分位数运作良好,但第二个和第三个四分位数逐渐偏向中心。

只是寻找一些如何在WPF中使用定位的帮助。所有的相对性和基于百分比的坐标和“屏幕单位”坐标之间的差异让我有点困惑。

1 个答案:

答案 0 :(得分:2)

经过大量的反复试验后,我最终得到了这个:

void ManipCompleted(object sender, ManipulationCompletedEventArgs e)
{
    FocusTaker.Focus(); //reset focus
    List<FrameworkElement> visibleElements = new List<FrameworkElement>();
    for (int i = 0; i < List.Items.Count; i++)
    {
        SurfaceListBoxItem item = List.ItemContainerGenerator.ContainerFromIndex(i) as SurfaceListBoxItem;
        if (ViewportHelper.IsInViewport(item))
        {
            FrameworkElement el = item as FrameworkElement;
            visibleElements.Add(el);
        }
    }

    Window window = Window.GetWindow(this);

    double center = ss.ViewportWidth / 2;

    double closestCenterOffset = double.MaxValue;
    FrameworkElement centerItem = visibleElements[0];
    foreach (FrameworkElement el in visibleElements)
    {
        double centerOffset = Math.Abs(el.TransformToAncestor(window).Transform(new Point(0, 0)).X + (el.ActualWidth / 2) - center);
        if (centerOffset < closestCenterOffset)
        {
            closestCenterOffset = centerOffset;
            centerItem = el;
        }
    }                        

    Point itemPosition = centerItem.TransformToAncestor(window).Transform(new Point(0, 0));

    double desiredOffset = ss.HorizontalOffset - (center - itemPosition.X) + (centerItem.ActualWidth / 2);

    ss.ScrollToHorizontalOffset(desiredOffset);

    centerItem.Focus();
}

此代码块有效地确定哪个可见列表元素与列表的中心线重叠,并将该元素捕捉到精确的中心位置。捕捉有点突然,所以我将不得不考虑某种动画,但除此之外我对它很满意!我可能会使用此处的动画来制作动画:http://blogs.msdn.com/b/delay/archive/2009/08/04/scrolling-so-smooth-like-the-butter-on-a-muffin-how-to-animate-the-horizontal-verticaloffset-properties-of-a-scrollviewer.aspx

编辑:嗯,这没多久。我将ScrollViewerOffsetMediator扩展为包含Horizo​​ntalOffset,然后根据上面的帖子建议简单地创建动画。奇迹般有效。希望这最终有助于某人。

Edit2:这是SnapList的完整代码:

SnapList.xaml

SnapList.xaml.cs

请注意,我很懒,因为这个项目进行了一些硬编码。需要一定的自由裁量权来确定您从此代码中执行和不需要的操作。不过,我认为这应该可以作为任何想要这种功能的人的起点。

代码也改变了我上面粘贴的内容;我发现当列表被放置在可以移动的控件中时,使用Windows.GetWindow会得到错误的结果。我做了这样你就可以为你的动作指定一个相对的控件(推荐它是层次结构中列表上方的控件)。我认为其他一些事情也发生了变化;我添加了很多自定义选项,包括能够为列表定义自定义焦点。