スクロール途中の停止位置を制御するには?[ユニバーサルWindowsアプリ開発]WinRT/Metro TIPS

ユニバーサルプロジェクトの共有プロジェクトにカスタムコントロールを作成し、画像が切れないようにスクロール位置を調整する方法を解説する。

» 2014年08月21日 00時00分 公開
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 画面をスクロールさせたときに停止位置を制御したいと思ったことはないだろうか? 例えば、複数の画像を横スクロールで見せるUI(ユーザーインターフェース)をユニバーサルプロジェクトで作っているときだ。Windowsの広い画面ではよいのだが、Phoneの狭い画面では画像がきっちり(途中で切れたりせずに)表示される位置でスクロールを止めたくなるだろう。本稿ではその方法を説明する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #87」からダウンロードできる。

例題

 本稿では例として、複数の画像を横スクロールで見せるUIを考えてみる。ScrollViewerコントロール(Windows.UI.Xaml.Controls名前空間)を使って素直に作ると、スクロールできる範囲内ならどこでも止められてしまう(次の画像)。

スクロール中のどこでも止められてしまう(Windows 8.1)
スクロール中のどこでも止められてしまう(Windows Phone 8.1エミュレーター) スクロール中のどこでも止められてしまう(上:Windows 8.1、下:Windows Phone 8.1エミュレーター)
Windows Phoneの場合、このように画像が切れた状態でも表示されることになる。これはエンドユーザーにとってストレスだろう。画像がきちんと表示される位置でスクロールを止められないだろうか?
なお、これらの画像は「Windows アプリ アート ギャラリー」を使用させていただいた。

 画面の広いWindowsではこれでも困らない。しかし、画面の狭いWindows Phoneでは、正確にスクロール位置を操作しないときちんと画像が見られない。これはエンドユーザーにとってストレスだろう。Windows Phoneでは指で慣性を付けてスワイプするだけで、次の画像がきちんと表示されることが望ましい。また、Windowsでも、複数アプリを並べて表示しているときには同じことがいえる。

 解決策として画面幅が狭いときにはFlipViewコントロール(Windows.UI.Xaml.Controls名前空間)に切り替えるという手段も考えられるが、かなり面倒だ。画像がきちんと表示される位置でスクロールが停止してくれたら、どれだけ楽だろうか。

事前準備

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
  • Visual Studio 2013 Update 2(またはそれ以降)*4を適用済みのVisual Studio 2013(以降、VS 2013)*5

*1 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。

*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。

*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。

*4 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 3のもの)。

*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションで構わない。Visual Studio Express 2013 with Update 3 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。


用語

 本稿では、紛らわしくない限り次の略称を用いる。

  • Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
  • Phone:Windows Phone 8.1

サンプルコードについて

 Visual Studio 2013 Update 2では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するVBのコードはユニバーサルプロジェクトではなく、PCL(ポータブルクラスライブラリ)を使ったプロジェクトのものである*7

*6 VB用のユニバーサルプロジェクトは、来年にリリースされるといわれているVisual Studio「14」からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。

*7 Visual Studio 2013 Update 2のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。PCLに置いたものは、コードだけでなくXAML(画面)やリソースディクショナリなども共通に利用できる。そこで別途公開のサンプルコードでも、VBはWindows用/Phone用/共通コード(PCL)の3プロジェクト構成とした。ユニバーサルプロジェクトで作らなくてもユニバーサルWindowsアプリはリリースできるのである(「WinRT/Metro TIPS:ユニバーサルプロジェクトで開発するには?」参照)。


スクロール途中の停止位置を指定するには?

 ScrollViewerコントロールのHorizontalSnapPointsTypeプロパティなどを利用すればよい。

 ただし、先にお断りしておくが、有効なのはタッチ操作とマウスのホイール操作の場合である(スクロールバーを操作したときには効かない)。また、画像などを配置するコンテナーとして、StackPanelコントロール(Windows.UI.Xaml.Controls名前空間)を使わねばならない(後述の「IScrollSnapPointsInfoインターフェースについて」を参照)。

 それではまず、冒頭の画像に示した横スクロールするUIから作っていこう。ユニバーサルプロジェクトの共有プロジェクトに新しくユーザーコントロールを作り、そこに次のようなコードを記述する(横スクロールするUIの部分のみを示す)。

<UserControl.Resources>
  <Style TargetType="Image">
    <Setter Property="Width" Value="360" />
    <Setter Property="MinHeight" Value="400" />
    <Setter Property="Margin" Value="10,0,10,0" />
    <Setter Property="VerticalAlignment" Value="Top" />
  </Style>
</UserControl.Resources>

……省略……

<ScrollViewer x:Name="ScrollViewer1" ……省略……
              HorizontalScrollBarVisibility="Auto" HorizontalScrollMode="Auto" 
              VerticalScrollBarVisibility="Hidden" VerticalScrollMode="Disabled">
  <StackPanel Orientation="Horizontal" Margin="0,40,360,0">
    <Image Source="Assets/……省略……"/>
    ……省略……
    <Image Source="Assets/……省略……"/>
  </StackPanel>
</ScrollViewer>

横スクロールするUIのコード例(XAML)
別途公開のサンプルコードでは、共有プロジェクトの「SnappedScrollControl.xaml」ファイルに記述している。
またこの他に、共有プロジェクトにAssetsフォルダーを作り、そこに画像ファイルをいくつか用意して、それらを<Image>タグのSource属性に指定しておいてほしい。
なお、StackPanelコントロールの右側に画像1つ分のマージンを取っている。こうしておかないと、スクロール途中の停止位置を指定したときに、最後の画像までスクロールできなくなることがあるためだ。

 上のコードでは、冒頭に載せた画像のように、スクロール途中のどこでも止めることができる。

 画像の左端がScrollViewerコントロールの左端に合う位置でスクロールが止まるようにするには、次のコードのようにする。

this.ScrollViewer1.HorizontalSnapPointsType = SnapPointsType.MandatorySingle;
this.ScrollViewer1.HorizontalSnapPointsAlignment = SnapPointsAlignment.Near;

Me.ScrollViewer1.HorizontalSnapPointsType = SnapPointsType.MandatorySingle
Me.ScrollViewer1.HorizontalSnapPointsAlignment = SnapPointsAlignment.Near

画像の左端がUIの左端に合う位置でスクロールが止まるようにするコード(上:C#、下:VB)
このコードは、ユーザーコントロールのコンストラクターの末尾に記述する(別途公開のサンプルコードでは、トグルスイッチのON/OFFで切り替えるようにしている)。
なお、動的に設定を変える必要がなければ、XAMLコード側に記述すればよい。

 上で設定しているプロパティの主要な値を次の表に示しておく。

説明
SnapPointsType.None 任意の場所で止まる(デフォルト)
SnapPointsType.Mandatory 必ずスナップポイントで止まる(慣性が付いているときは、複数アイテムをまたいでスクロールする)
SnapPointsType.MandatorySingle 必ず次のスナップポイントで止まる(慣性が付いていても、次のアイテムで止まる)
HorizontalSnapPointsTypeプロパティの主な値
「スナップポイント」については、「IScrollSnapPointsInfoインターフェースについて」の項を参照。

説明
SnapPointsAlignment.Near アイテムの左端がScrollViewerコントロールの左端に一致する位置で止まる(デフォルト)
SnapPointsAlignment.Far アイテムの右端がScrollViewerコントロールの右端に一致する位置で止まる
HorizontalSnapPointsAlignmentプロパティの主な値
ここで「左端」/「右端」といっているのは、書記方向が左から右へ書く言語の場合である。「Near」/「Far」というのは、書記方向の原点から見て近い方/遠い方を意味している。

実行結果

 上で作ったユーザーコントロールをWindowsとPhoneの画面に配置して実行してみると、次の画像のようになる。[スナップあり]を有効にすると、タッチ操作(またはマウスホイール操作)でスクロールさせたときに、左端の画像の左端がScrollViewerコントロールの左端に合う位置に必ず止まる。

左端の画像はその全体が表示される位置に必ず止まる(Windows)
左端の画像はその全体が表示される位置に必ず止まる(Phoneエミュレーター) 左端の画像はその全体が表示される位置に必ず止まる(上:Windows、下:Phoneエミュレーター)
それ以外の位置でスクロールをやめても、自動的にその位置までスクロールされる(タッチ操作またはマウスホイール操作の場合)

IScrollSnapPointsInfoインターフェースについて

 ScrollViewerコントロールに対してスクロール停止位置を座標で指定できれば便利だと思えるのだが、残念ながらそうなってはいない。実際のところは、IScrollSnapPointsInfoインターフェース(Windows.UI.Xaml.Controls.Primitives名前空間)を実装したコントロールの位置に合わせて止まるという仕組みになっているのだ(正確にはコントロールの位置ではなく、IScrollSnapPointsInfoインターフェースのGetIrregularSnapPointsメソッド/GetRegularSnapPointsメソッドが返す「スナップポイント」の位置に止まる)。

 そして、IScrollSnapPointsInfoインターフェースを備えているコンテナコントロールは、今のところはStackPanelコントロール(Windows.UI.Xaml.Controls名前空間)だけである(他にItemsPresenterコントロール(Windows.UI.Xaml.Controls名前空間)がある)。それで本稿ではStackPanelコントロールを使ったのだ*8

*8 GridViewコントロールなどでスナップポイントを利用したいとき、簡易的にはアイテムテンプレートにStackPanelコントロールを使えばよい。その手法は、ブログ記事「The taming of the Metro GridView」(英語)などで紹介されている。


まとめ

 ScrollViewerコントロールの中にStackPanelコントロールを配置した場合には、ScrollViewerコントロールのプロパティでスクロール停止位置を制御できる。エンドユーザーに喜ばれるUIの構築に活用してほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。