かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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