ページ

2011年5月30日月曜日

[WP7] ScrollViewer のスクロールに反応する

Windows Phone 7 の ScrollViewer (確認してませんが、Silverlight や WPF でも同じかも) にはスクロールしたことを伝えるイベントがありません。
けど、スクロール量を表す HorizontalOffset、VerticalOffset プロパテイがあります。
ならば、これらにデータバインディングしてやればスクロールしたときにあわせて処理をできるんじゃないかと。

public static readonly DependencyProperty MyVerticalOffsetProperty =
    DependencyProperty.Register("MyVerticalOffset", typeof(double), typeof(MyView), new PropertyMetadata(0.0, MyVerticalOffset_PropertyChanged));

private static void MyVerticalOffset_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    // ScrollViewer.VerticalOffset が変わったときにここが呼ばれる
}

こんなのを用意しておいて、あとは

XAML で書くなら
<ScrollViewer VerticalOffset="{Binding MyVerticalOffset, Mode=TwoWay}" />

コードで書くなら
var binding = new Binding("MyVerticalOffset") { Source = this, Mode = BindingMode.TwoWay };
this.ScrollViewer.SetBinding(ScrollViewer.VerticalOffsetProperty, binding);

こんな感じでいいのかなぁ?と。

がっ!これはできないんですね。
ScrollViewer.VerticalOffset プロパティなんかは readonly なので、実行時に例外が発生してしまいます。
BindingMode.OneWayToSource にしてやればよさそうにも思えますが、Silverlight には OneWayToSource がありません。
というわけで、どうもこのアプローチはダメみたいです。

しかし、添付プロパテイであれば相手が readonly でも添付できるようです。

private static int verticalOffsetAttachedPropertyCounter = 0;

// コンストラクタ
public MyView()
{
    InitializeComponent();

    // ScrollViewer.VerticalOffset に添付プロパティをアタッチして変化を検出する
    var property = DependencyProperty.RegisterAttached("MyVerticalOffset" + verticalOffsetAttachedPropertyCounter, typeof(double), typeof(MyView), new PropertyMetadata(0.0, MyVerticalOffset_PropertyChanged));
    ++verticalOffsetAttachedPropertyCounter;
    var binding = new Binding("VerticalOffset") { Source = this.ScrollViewer };
    this.ScrollViewer.SetBinding(property, binding);
}

private void MyVerticalOffset_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    // ScrollViewer.VerticalOffset が変わったときにここが呼ばれる
}

こんな感じでうまくいきました。
上記ではインスタンス上で RegisterAttached してますが、もちろん、普段の依存関係プロパティのように static に RegisterAtached しても構いません。
その場合、当然 MyVerticalOffset_PropertyChanged() メソッドも static にすることになります。
今回、static ではなくインスタンスでやっているのは MyVerticalOffset_PropertyChanged() メソッドに渡される DependencyObject が添付先(今回の場合だと ScrollViewer)になっちゃうからです。依存関係プロパティのときはここに自分が渡されるのでキャストすれば自分のインスタンス変数にアクセスできますが、添付プロパティだと自分にアクセスするすべがなくなっちゃいます。そんなわけで static ではなくインスタンスにしています。

ただ、これ、インスタンスが作られるたびに RegisterAttached してるわけですが、いいのかな?まぁ、UnregisterAttached みたいな解除するための API が無いのでどうしようも無いんですが。

0 件のコメント:

コメントを投稿