WP7.5 でリストボックスのスクロールエンドで圧縮されるときのイベントを取る
Windows Phone のリストボックスでは、上下にスクロールしたのち端まで行くと一瞬圧縮されます。アプリケーションによってはこれをトリガーにしているアプリケーションもあります。 Windows Phone 7.5(Mango)では、この瞬間を(直接ではないですが)イベントとして取得する事ができます。 ■Mangoにはスクロール圧縮が状態定義されている! からくりを一言でいうと、Windows Phone 7.5(Mango)では、ScrollViewerのVisualStateに"Scrolling"や"NotScrolling"以外に、以下のVisualStateが定義されているのです。 VerticalCompression VisualStateGroup CompressionTop CompressionBottom NoVerticalCompression HorizontalCompression VisualStateグループ CompressionLeft CompressionRight NoHorizontalCompression つまり、このVisualStateGroupの変化を取得し、その時の状態を確認してみればスクロールエンドの状態を取得する事ができるのです。 そして、 ListboxはVisualTreeを紐解けば(中の構造を開いてみると)ScrollViewer が使われています。 ですから、Listboxのスクロールエンドの状態ももちろん取り出すことができます。 ■処理の流れ 手順はこんな感じ VisualStateが把握できるよう再定義 ListboxからScrollViewerを取り出す ScrollViewerからVisualStateGroupを取り出す VisualStateGroupの変化に対するイベントを取得(CurrentStateChanging) VisualStateの状態を見て必要なState(例 CompressionTop)に合わせて処理を実行する という感じ。 ■実装してみよう まずは新しくプロジェクトを作ってみます。DataBound Application が簡単でしょうね。 ① VisualStateの再定義 そして、MainPage.Xaml の17行目あたりに以下のVisualStateの再定義コードを追加(1) 複雑に見えますが、デフォルトのScrollViewerとの定義の違いは青い部分だけ。 <phone:PhoneApplicationPage.Resources> <Style TargetType="ScrollViewer"> <Setter Property="VerticalScrollBarVisibility" Value="Auto"/> <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Padding" Value="0"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="BorderBrush" Value="Transparent"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ScrollViewer"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ScrollStates"> <VisualStateGroup.Transitions> <VisualTransition GeneratedDuration="00:00:00.5"/> </VisualStateGroup.Transitions> <VisualState x:Name="Scrolling"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="VerticalScrollBar"/> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HorizontalScrollBar"/> </Storyboard> </VisualState> <VisualState x:Name="NotScrolling"/> </VisualStateGroup> <VisualStateGroup x:Name="VerticalCompression"> <VisualState x:Name="NoVerticalCompression"/> <VisualState x:Name="CompressionTop"/> <VisualState x:Name="CompressionBottom"/> </VisualStateGroup> <VisualStateGroup x:Name="HorizontalCompression"> <VisualState x:Name="NoHorizontalCompression"/> <VisualState x:Name="CompressionLeft"/> <VisualState x:Name="CompressionRight"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid Margin="{TemplateBinding Padding}"> <ScrollContentPresenter x:Name="ScrollContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}"/> <ScrollBar x:Name="VerticalScrollBar" HorizontalAlignment="Right" Height="Auto" IsHitTestVisible="False" IsTabStop="False" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Opacity="0" Orientation="Vertical" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{TemplateBinding VerticalOffset}" ViewportSize="{TemplateBinding ViewportHeight}" VerticalAlignment="Stretch" Width="5"/> <ScrollBar x:Name="HorizontalScrollBar" HorizontalAlignment="Stretch" Height="5" IsHitTestVisible="False" IsTabStop="False" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Opacity="0" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{TemplateBinding HorizontalOffset}" ViewportSize="{TemplateBinding ViewportWidth}" VerticalAlignment="Bottom" Width="Auto"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </phone:PhoneApplicationPage.Resources> ② VisualStateの変化をハンドリング 続けて、MainPage.xaml.cs に、ListboxからScrollViewerを取り出し、その中のVisualStateの変化をハンドリングする関数を追加 private void ListBoxCompressionHandling(ListBox targetlistbox) { VisualStateGroup vgroup = new VisualStateGroup(); // ListBox の初めに定義されている ScrollViewerを取り出す ScrollViewer ListboxScrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(targetlistbox, 0); // Visual State はコントロールテンプレートの常に最上位に定義されている FrameworkElement element = (FrameworkElement)VisualTreeHelper.GetChild(ListboxScrollViewer, 0); // Visual State を取り出しその中から 縦横Compression のVisualStateを取り出す foreach (VisualStateGroup group in VisualStateManager.GetVisualStateGroups(element)) if (group.Name == "VerticalCompression") vgroup = group; //縦横Compressionの状態が変わった時のイベントハンドラ vgroup.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(ScrollViewer_CurrentStateChanging); } ③ イベント発生時の処理はご自由に♪ void ScrollViewer_CurrentStateChanging(object sender, VisualStateChangedEventArgs e) { ApplicationTitle.Text = e.NewState.Name; //switch (e.NewState.Name) //{ // case "CompressionTop": // break; // case "CompressionBottom": // break; // case "NoVerticalCompression": // break; // default: // break; //} } ④ Listboxとの紐づけ 最後に、おなじく、MainPage.xaml.cs の中の MainPage_Loaded イベントの中で、リストボックスをハンドリングするように、作成した関数を呼ぶ。 private void MainPage_Loaded(object sender, RoutedEventArgs e) { : ListBoxCompressionHandling(MainListBox); } ■まとめ 以上で完了です。Xamlの定義と、ListBoxCompressionHandling 関数だけコピペして使えば、簡単に利用できますし、ScrollViewerで使えば、縦以外に横のスクロールエンドも定義できます。
Continued here:
WP7.5 でリストボックスのスクロールエンドで圧縮されるときのイベントを取る


