3

画面上の中央の項目がビューポートの中央に配置されるように、ドラッグが終了した後に自動的に位置にスナップするスクロール サーフェス リストボックスを作成しようとしています。

中央のアイテムを取得しましたが、いつものように 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.
}

リストはスナップしますが、スナップする場所はやや混沌としているようです。画面の中央に線があり、アイテムの真ん中に見えることもありますが、横にずれたり、アイテムの間にあることもあります。完全に突き止めることはできませんが、リストの 1 番目と 4 番目の四分位数はうまく機能しているように見えますが、2 番目と 3 番目の四分位数は徐々に中心に向かって外れています。

WPF でポジショニングを使用する方法についてのヘルプを探しています。パーセンテージベースの座標と「スクリーン単位」座標の相対性と違いのすべてが、この時点で多少混乱しています。

4

1 に答える 1

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-horizo​​ntal-verticaloffset-properties-of-a-scrollviewer.aspx

編集:まあ、それほど時間はかかりませんでした。ScrollViewerOffsetMediator を拡張して Horizo​​ntalOffset を含め、上記の投稿で提案されているようにアニメーションを作成しました。魅力のように機能します。これが最終的に誰かを助けることを願っています。

Edit2: SnapList の完全なコードは次のとおりです。

SnapList.xaml

SnapList.xaml.cs

このプロジェクトの一部がハードコーディングされていたため、かなり怠惰になったことに注意してください。このコードから何を行い、何を望まないかを判断するには、ある程度の裁量が必要です。それでも、この機能が必要な人にとっては、出発点としてうまく機能するはずです。

コードも上に貼り付けたものから変更されています。リストが移動可能なコントロールに格納されている場合、 Windows.GetWindow を使用すると悪い結果が得られることがわかりました。相対的な動きのコントロールを割り当てることができるようにしました (階層内のリストのすぐ上にあるコントロールにすることをお勧めします)。他にもいくつか変更があったと思います。リストのカスタム フォーカル ポイントを定義できるなど、多くのカスタマイズ オプションを追加しました。

于 2012-08-06T17:45:19.733 に答える