かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Windows ストアアプリで引っ張って更新(未完成)

エロリジェクトで有名なしんじさんが引っ張って更新の方法をBlogに書かれてました。

ただ、この方法だとUIの仮想化が働かないような気がするんだけどどうだろう?ということで、これとは別にアプローチで頑張ってみようと思いました。

結果ダメでした

ListViewのVisualTreeからScrollViewerをひっぱってきて、そのコンテンツの移動量を見てやってやろうと思ったら、なぜか下にひっぱったときに0になってしまう…。なので、3秒間以上つまんでたら更新するようにしてみました。(BehaviorのPullToRefreshedイベントが発行される)

やりきれない思い。

using Microsoft.Xaml.Interactivity;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace App13
{
    [TypeConstraint(typeof(ListView))]
    public class PullToRefreshBehavior : DependencyObject, IBehavior
    {
        private ListView TargetObject { get; set; }

        public DependencyObject AssociatedObject
        {
            get { return this.TargetObject; }
        }

        public event EventHandler PullToRefreshed;

        private void OnPullToRefreshed()
        {
            var h = this.PullToRefreshed;
            if (h != null)
            {
                h(this, EventArgs.Empty);
            }
        }

        public void Attach(DependencyObject associatedObject)
        {
            this.TargetObject = (ListView)associatedObject;
            this.TargetObject.Loaded += (_, __) =>
            {
                var startTime = DateTime.Now;
                var viewer = VisualTreeUtils.FindVisualTreeChild<ScrollViewer>(this.TargetObject);
                var target = (FrameworkElement)viewer.Content;
                target.LayoutUpdated += (___, ____) =>
                {
                    var point = target.TransformToVisual(this.TargetObject).TransformPoint(new Point(0, 0));
                    Debug.WriteLine("({0}, {1})", point.X, point.Y);
                    if (point.Y >= 0 && (DateTime.Now - startTime).TotalMilliseconds > 3000)
                    {
                        this.OnPullToRefreshed();
                    }
                    startTime = DateTime.Now;
                };
            };
        }

        public void Detach()
        {
        }
    }

    static class VisualTreeUtils
    {
        public static T FindVisualTreeChild<T>(FrameworkElement root)
            where T : FrameworkElement
        {
            Debug.WriteLine("{0}", root.GetType());
            foreach(var current in GetChildren(root).Where(c => c != null).OfType<FrameworkElement>())
            {
                var findTarget = current as T;
                if (findTarget != null)
                {
                    return findTarget;
                }

                var found = FindVisualTreeChild<T>(current);
                if (found != null)
                {
                    return found;
                }
            }

            return null;
        }

        private static IEnumerable<FrameworkElement> GetChildren(FrameworkElement root)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
            {
                yield return VisualTreeHelper.GetChild(root, i) as FrameworkElement;
            }
        }
    }
}