次の方法で共有


UI 最前線

Silverlight、Windows Phone 7、およびマルチタッチ Thumb

Charles Petzold

コード サンプルのダウンロード

Charles Petzold多くの Silverlight のプログラマにとって、Windows Phone 7 に関する最も魅力的なニュースは、サポートされる 2 つのプログラミング インターフェイスの 1 つが Silverlight であることです (もう 1 つは XNA です)。Silverlight のプログラマは、新しい携帯電話向けアプリケーションの作成に既存の知識とスキルを活用できるだけでなく、Web 用と携帯電話用にコードを共有する Silverlight プログラムを構築できます。

もちろん、コード (特に UI コード) の共有が最初に思うほど簡単にいくことはめったにありません。携帯電話に使用する Silverlight のバージョンは、Silverlight for Windows Phone と呼ばれ、Silverlight 3 のほぼ必要最低限の機能だけをサポートします。そのため、Web と携帯電話のアプリケーション コードを共有する場合は、ドキュメントを詳しく調べることをお勧めします。オンライン ドキュメントでは、Silverlight のクラスごとに、そのクラスをサポートする環境が示されています。各クラス内では、プロパティ、メソッド、およびイベントの一覧にアイコンを添えて、Windows Phone 7 でのサポート状況が示されます。

Web 用の Silverlight アプリケーションで入力を受け取るには、キーボード、マウス、そしておそらくマルチタッチを使用します。Windows Phone 7 プログラムでは、マルチタッチが主要入力手段で、マウスは使用しません。ハードウェア キーボードを備えた携帯電話もあるかもしれませんが、Silverlight プログラムは、仮想キーボード (SIP: ソフトウェア入力パネル) のみを利用して、TextBox コントロールから入力を受け取ります。

既存の Silverlight プログラムがキーボードやマウスから直接入力を受け取っておらず、完全にコントロールのみを利用しているのであれば、マルチタッチへの変換についての心配はありません。プログラムの中で独自のマウス ロジックを使用している場合、プログラムを携帯電話に移植するときに、そのロジックをそのまま保持できます。

携帯電話では、プライマリ タッチ イベントがマウス イベントに変換されるため、既存のマウス ロジックがうまく機能します (プライマリ タッチ イベントとは、画面に触れていない状態から指が最初に画面にタッチする操作全体を指します)。

マウスからマルチタッチに移行する場合は、いくぶん配慮が必要です。Web 用の Silverlight でも Silverlight for Windows Phone でも、Touch.FrameReported 静的イベントがサポートされますが、マルチタッチにとってはやや低レベルのインターフェイスです。2010 年 3 月号のコラム「Finger Style: Silverlight でのマルチタッチ サポートの詳細」(msdn.microsoft.com/magazine/ee336026) では、このイベントを取り上げました。

Silverlight for Windows Phone では、Surface SDK で最初に採用され、Windows Presentation Foundation (WPF) の一部となった Manipulation イベントのサブセットがサポートされます。これは、マルチタッチがしだいに主流になりつつある一例です。携帯電話では変換機能と拡大縮小機能のみがサポートされ、回転はサポートされません。慣性も実装されませんが、独自の慣性を実装できるだけの情報は入手できます。Web 版の Silverlight では、これらの Manipulation イベントはまだサポートされていません。

つまり、Web 用の Silverlight と Silverlight for Windows Phone でコードを共有する場合、マウス イベントか Touch.FrameReported のいずれかを引き続き使用することになります。

Thumb を検討する

ただし、もう 1 つ選択肢があります。Manipulation イベントの変換だけをサポートする必要があり、マウスとタッチのどちらを使ってもかまわなければ、非常に純粋な形で変換をサポートするコントロールがあります。それは Thumb コントロールです。

実際には、Thumb コントロールを目にしたことがないかもしれません。Thumb コントロールは、System.Windows.Controls.Primitives 名前空間に隠ぺいされていて、主に ScrollBar テンプレートと Slider テンプレートで使用することを目的に用意されています。しかし、このコントロールを他の処理に使用することもできます。私は最近、Thumb を、Manipulation イベントの変換機能の高度な実装と考えるようになりました。

さて、Thumb は正確には "マルチ" タッチ コントロールではありません。1 度に 1 回のタッチしかサポートしません。ただし、Thumb を少し詳しく調べてみると、タッチの計算のサポートに加えて、Silverlight アプリケーションと Windows Phone 7 アプリケーション間でのコードの共有を試すチャンスがありそうです。

Thumb では、次の 3 つのイベントを定義します。

  • DragStarted: ユーザーが指またはマウスでコントロールに最初に触れたときに発生します。
  • DragDelta: マウスまたは指の動きを画面との相対で示します。
  • DragCompleted: マウスまたは指が離れたことを示します。

DragDelta イベントには、イベント引数として HorizontalChange プロパティと VerticalChange プロパティがあります。これらのプロパティは、前回イベントが発生してからのマウスまたは指の動きを示します。一般に、ドラッグ可能な要素の RenderTransform プロパティに設定された TranslateTransform の X プロパティと Y プロパティ、または Canvas.Left および Canvas.Top が添付されたプロパティに、段階的変化を加えることで、このイベントを処理します。

既定の状態では、Thumb はかなり簡素です。他のコントロールと同様、HorizontalAlignment プロパティと VerticalAlignment プロパティは Stretch に設定されるため、通常、Thumb は許容領域を占有します。それ以外の場合、Silverlight の Thumb は単なる 4x4 ピクセルの正方形です。Silverlight for Windows Phone では、Thumb は 48x48 ピクセルの正方形ですが、見た目は、幅が 12 ピクセルの透明な境界線で四方を囲まれた 24x24 ピクセルの正方形です。

少なくとも、Thumb には明示的に Height と Width を設定することになります。図 1 に、Silverlight のバージョンと Windows Phone 7 のバージョンの Thumb を並べて示します。Windows Phone 7 のバージョンでは、携帯電話既定の暗い色の上に明るい色を表示するテーマを使用します。どちらも Height と Width に 72 を設定し、Background を Blue に設定しています。Silverlight バージョンでは、これは Thumb が押されたときに変化するグラデーションになります。いずれの Thumb も、Foreground プロパティは設定していません。

image: The Silverlight and Windows Phone Thumb Controls

図 1 Silverlight と Windows Phone の Thumb コントロール

ほとんどの場合、Thumb のサイズを変更するだけでなく、ControlTemplate を適用して、コントロールの見た目を再定義します。この ControlTemplate は、ごくシンプルにできます。

コントロールを共有する

画面内でビットマップをドラッグできるようにするシンプルなコントロールが必要だとします。非常に簡単なアプローチとしては、Image 要素と Thumb の両方共 1 つのセル グリッド置き、Thumb と Image に同じサイズを指定して重ね合わせます。Thumb の ControlTemplate を透明な四角形にすると、Thumb は表示されなくなりますが、引き続きドラッグ イベントは発生します。

上記のコントロールを、通常の Silverlight プロジェクトと Windows Phone 7 プロジェクトの両方で使用できるようにします。ここでは Windows Phone 7 Developer Tools (xbox.http://xbox.create.msdn.com) がインストールされていることを前提とします。これらのツールを使用すると、Visual Studio から Windows Phone 7 プロジェクトを作成できます。

まず、通常の Silverlight 4 プロジェクト (DragImage ) を作成します。最終的な DragImage ソリューションには、通常の DragImage プロジェクト (Silverlight プログラムそのもの) と、(Silverlight プログラムを HTML または ASP.NET ページでホストする) DragImage.Web プロジェクトが含まれます。

次に、新しいプロジェクトの種類として Windows Phone Application (Windows Phone アプリケーション) をソリューションに追加します。このプロジェクトに DragImage.Phone という名前を付けます (携帯電話や携帯電話エミュレーターのプログラム一覧にこの名前を表示することはありません。この表示名は、WMAppManifest.xml ファイルの App タグの Title 属性で変更できます)。

DragImage.Web プロジェクトか DragImage.Phone プロジェクトのいずれかを右クリックし、表示されるコンテキスト メニューから [スタートアップ プロジェクトに設定] をクリックすることで、通常の Silverlight プログラムか Windows Phone 7 プログラムのいずれかを実行します。Visual Studio のツール バーに表示されるドロップダウンを使用して、実際の携帯電話デバイスまたは携帯電話エミュレーターにプログラムを配置します (Visual Studio では、このドロップダウンが [Windows Phone 7 Device] (Windows Phone 7 デバイス) に設定されている場合、携帯電話がアタッチされていないと、プロジェクトがビルドされません)。

DragImage プロジェクト (通常の Silverlight プロジェクト) では、[Silverlight ユーザー コントロール] 型の新しい項目を追加します。これに DraggableImage という名前を付けます。通常どおり、Visual Studio では、このコントロールの DraggableImage.xaml ファイルと DraggableImage.xaml.cs ファイルが作成されます。

図 2 に、コントロールのビジュアル ツリーを備えた DraggableImage.xaml を示します。外側の LayoutRoot という標準グリッドは、コントロールのコンテナー内をすべて占有します。内側のグリッドは左上隅に揃えられますが、TranslateTransform が RenderTransform プロパティに割り当てられているため、外側のグリッド内を移動します。内側のグリッドは、Image 要素とそれを覆う Thumb コントロールを保持し、その Template プロパティは、透明な四角形のみを含むビジュアル ツリーに設定されています。

図 2 DraggableImage.xaml

<UserControl x:Class="DragImage.DraggableImage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  Name="ctrl">
    
  <Grid x:Name="LayoutRoot">
    <Grid HorizontalAlignment="Left"
          VerticalAlignment="Top">
      <Image Name="image" Stretch="None"
             Source="{Binding ElementName=ctrl, Path=Source}" />
      <Thumb DragDelta="OnThumbDragDelta">
        <Thumb.Template>
          <ControlTemplate>
            <Rectangle Fill="Transparent" />
          </ControlTemplate>
        </Thumb.Template>
      </Thumb>
      <Grid.RenderTransform>
        <TranslateTransform x:Name="translate" />
      </Grid.RenderTransform>
    </Grid>
  </Grid>
</UserControl>

Image 要素の Source プロパティは、コントロール自体の Source プロパティにバインドされています。そのプロパティは、DraggableImage.xaml.cs ファイルで定義されます (図 3 参照)。このファイルでは、TranslateTransform の X プロパティと Y プロパティの値を変更することによって、Thumb の DragDelta イベントも処理します。

図 3 DraggableImage.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace DragImage {
  public partial class DraggableImage : UserControl {
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source",
      typeof(ImageSource),
      typeof(DraggableImage),
      new PropertyMetadata(null));

    public DraggableImage() {
      InitializeComponent();
    }

    public ImageSource Source {
      set { SetValue(SourceProperty, value); }
      get { return (ImageSource)GetValue(SourceProperty); }
    }

    void OnThumbDragDelta(object sender, DragDeltaEventArgs args) {
      translate.X += args.HorizontalChange;
      translate.Y += args.VerticalChange;
    }
  }
}

そのコントロールを Windows Phone 7 プロジェクトと共有するには、DragImage.Phone プロジェクトを右クリックして [追加] をポイントし、[既存の項目] をクリックして、[既存項目の追加] ダイアログ ボックスを表示します。DragImage プロジェクト ディレクトリに移動します。DraggableImage.xaml および DraggableImage.xaml.cs を選択しますが、[追加] はクリックしません。代わりに、[追加] の右にある小さな矢印をクリックして、[リンクとして追加] をクリックします。DragImage.Phone プロジェクトにファイルが表示されます。このファイルのアイコンには、2 つのプロジェクト間でファイルが共有されていることを示す小さな矢印が表示されます。

ここで、DraggableImage ファイルに変更を加えて、両方のプロジェクトで変更後のバージョンを使用します。

これをテストするには、ビットマップが必要です。各プロジェクト内の Images ディレクトリにビットマップを格納します (ビットマップのコピーを作成する必要はありません。リンクを使用して、ビットマップを Images ディレクトリに追加できます)。

MainPage.xaml ファイルが 2 つ表示されます。1 つは通常の Silverlight プロジェクトのもので、もう 1 つは Windows Phone 7 プロジェクトのものです。Silverlight プロジェクトの MainPage.xaml では、(従来から) "local" という名前が付けられる XML 名前空間バインドを追加します。

xmlns:local="clr-namespace:DragImage"

ここで、ページに DraggableImage を追加します。

<Grid x:Name="LayoutRoot" Background="White">
  <local:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

Windows Phone 7 プロジェクトの MainPage クラスは DragImage.Phone という名前空間に含まれますが、共有する DraggableImage クラスは DragImage 名前空間に含まれます。DragImage 名前空間では、XML 名前空間バインドが必要です。これは "shared" という名前を付けます。

xmlns:shared="clr-namespace:DragImage"

ここで、ページのコンテンツ領域に DraggableImage を追加します。

<Grid x:Name="ContentPanel" 
  Grid.Row="1" Margin="12,0,12,0">
  <shared:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

これが、2 つの Silverlight プロジェクト (1 つは Web 用でもう 1 つは Windows Phone 7 用) の間でコントロールを共有する最もシンプルな方法です。コントロールには Thumb を使用するため、どちらのプログラムでもマウスまたはタッチが機能します。

DragImage ソリューションのダウンロード可能なコードには、DragImage.Wpf というプロジェクトも含めています。この WPF プログラムでも、このコントロールを使用しています。ただし、一般に、Silverlight と WPF 間のコントロールの共有は、Silverlight と Windows Phone 7 間のコントロールの共有よりも難しくなります。

色と解像度

マウスとタッチによる入力とは別に、Silverlight と Windows Phone 7 の間でコードを共有する場合、他にも対処すべき問題が 2 つあります。それは、色とビデオの解像度です。

デスクトップ上では、Silverlight は白い背景に黒いテキストを表示します (ただし、Silverlight プログラムでは、SystemColors クラスを使用して、ユーザーが選んだ Windows の色を表示することもできます)。Windows Phone 7 の既定では、黒い背景に白いテキストを表示します (ただし、ユーザーが色のテーマを変更して白い背景に黒いテキストを表示している場合は除きます)。Windows Phone 7 には、あらかじめ定義された便利なリソース キー (PhoneForegroundBrush や PhoneBackgroundBrush など) が用意されているため、選択された配色をプログラムから使用できます。

明示的な色を使用するコードまたはマークアップを Silverlight と Windows Phone 7 で共有するには、適切な色にするため、実行しているプラットフォームを特定する方法が必要です。

ビデオの解像度の問題は、もう少し複雑です。Silverlight の全座標はピクセル単位で、その規則は携帯電話にも適用されます。デスクトップのビデオ ディスプレイの平均的な解像度は、100 DPI (ドット/インチ) 程度です (たとえば、21 インチのビデオ ディスプレイで 1600 × 1200 ピクセル、または対角 2000 ピクセルに対応すると仮定すると、これは 105 DPI の解像度になります)。既定では、Windows はディスプレイの解像度が 96 DPI であると想定しますが、ユーザーはこれを変更して、画面を読みやすくすることができます。

Windows Phone 7 デバイスの画面は、480 × 800 ピクセルで対角 933 ピクセルです。しかし、画面は対角 3.5 インチしかないため、解像度は約 264 DPI となります。これは、デスクトップ画面の解像度の 2.75 倍です。

つまり、デスクトップで適切に表示される特定のサイズの共有要素は、携帯電話では小さすぎることになります。ただし、携帯電話を見るときの距離は、通常、デスクトップ画面よりも近いので、携帯電話で表示するために要素を正確に 2.75 倍する必要はありません。

タッチ目的で使用する場合、Thumb はどれほど大きくすればよいでしょう。私が確認した条件では、触れる対象の幅と高さには 9 mm (0.25 インチ) 必要です。解像度が 96 ピクセル/インチのデスクトップ画面では、これは 34 ピクセルですが、携帯電話では 93 ピクセルです。

一方、Windows Phone 7 デバイスの標準ボタンの高さは 72 ピクセルしかありませんが、これは十分なように思われます。最適なアプローチは、使いやすく、かつ見た目が不恰好にならないところまで試してみることでしょう。

調整する

従来から、プリプロセッサ ディレクティブを使用して条件付きコンパイルを行い、さまざまなプラットフォームに合うように、プログラムを調整していました。Silverlight プログラムでは条件付きコンパイル シンボル "SILVERLIGHT" が定義され、Windows Phone 7 プログラムでは "SILVERLIGHT" と "PHONE" の両方が定義されます (これらは、プロジェクト プロパティ ページの [ビルド] タブで確認できます)。つまり、次のようなコードを記述できます。

#if PHONE
  // Code for Windows Phone 7
#else
  // Code for regular Silverlight
#endif

または、実行時に Environment.OSVersion オブジェクトにアクセスすることで区別できます。Platform プロパティが PlatformID.WinCE に設定され、Version.Major プロパティが 7 以降の場合、コードは Windows Phone 7 デバイス (または Windows Phone 8 や 9) で実行されています。

理論的には、マークアップ互換性 (mc) 名前空間で定義される AlternateContent タグと Choice タグを使用して、XAML ファイルの条件付きセクションを定義することが可能ですが、これらのタグは Silverlight でサポートされているとは思えません。

しかし、XAML にはデータ バインドを含めることができます。このバインドを利用して、プラットフォームに応じて異なるオブジェクトを参照できます。また、XAML では、StaticResource 参照を使用して、さまざまなプラットフォーム向けに異なるオブジェクトを取得できます。ここではこのアプローチを使用して、TextTransform プログラムを作成します。

DragImage ソリューションを作成したのと同じ方法で、TextTransform ソリューションを作成します。このソリューションには、TextTransform (Silverlight プログラム)、TextTransform.Web (Silverlight プログラムをホストする Web プロジェクト)、および TextTransform.Phone (Windows Phone 7) の、3 つのプロジェクトがあります。

Silverlight プロジェクトでは、次に、UserControl から派生する TextTransformer コントロールを作成します。このコントロールを、Silverlight プロジェクトと Windows Phone 7 プロジェクトで共有します。TextTransformer コントロールでは、テキスト文字列 ("TEXT" という単語) をハードコーディングしています。この文字列を、隅に 4 つの Thumb コントロールを配置した Border で囲みます。Thumb を移動すると、Border と TextBlock に非アフィン変換を適用します (これは、Border で形成する四角形の角がへこんでいなければ、正しく機能します)。

TextTransformer.xaml ファイルでは、Thumb 用に新しいテンプレートを作成しませんが、Style を定義します (図 4 参照)。

図 4 TextTransformer.xaml の Thumb のスタイル

<Style x:Key="thumbStyle" TargetType="Thumb">
  <Setter Property="HorizontalAlignment" 
          Value="Left" />
  <Setter Property="VerticalAlignment" 
          Value="Top" />
  <Setter Property="Width" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="Height" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="RenderTransform">
    <Setter.Value>
      <TranslateTransform 
        X="{StaticResource HalfThumbOffset}"
        Y="{StaticResource HalfThumbOffset}" />
    </Setter.Value>
  </Setter>
</Style>

ThumbSize および HalfThumbOffset への参照に注目してください。テキストを表示する TextBlock は、プロパティの継承により正常な Foreground プロパティを取得しますが、Border は、同じ前景色を使用して明示的に色を指定する必要があります。

<Border Name="border"
        BorderBrush="{StaticResource ForegroundBrush}"
        BorderThickness="1">

これらのリソースはどこで定義するのでしょう。これらは、App.xaml で定義します。通常の Silverlight プロジェクトでは、App.xaml ファイルに以下のものを含む Resources コレクションを含めます。

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush" Color="White" />
  <SolidColorBrush x:Key="ForegroundBrush" Color="Black" />
  <system:Double x:Key="ThumbSize">36</system:Double>
  <system:Double x:Key="HalfThumbOffset">-18</system:Double>
</Application.Resources>

Windows Phone 7 プログラムの App.xaml ファイルは、2 つのブラシの定義済みリソースを参照して、より大きな ThumbSize と HalfThumbOffset の値を定義します。

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush"
     Color="{StaticResource PhoneBackgroundColor}" />
  <SolidColorBrush x:Key="ForegroundBrush"
     Color="{StaticResource PhoneForegroundColor}" />
  <system:Double x:Key="ThumbSize">96</system:Double>
  <system:Double x:Key="HalfThumbOffset">-48</system:Double>
</Application.Resources>

図 5 はブラウザーで実行中のプログラムを示し、図 6 は Windows Phone 7 エミュレーターで実行中のプログラムを示しています。このエミュレーターは、携帯電話のピクセルの高密度を補うために、フルサイズの 50% で表示しています。

image: The TextTransform Program in the Browser

図 5 ブラウザーで実行中の TextTransform プログラム

image: The TextTransform Program on the Phone Emulator

図 6 携帯電話エミュレーターで実行中の TextTransform プログラム

これらの技法は、デスクトップと携帯電話間のコードの共有が実現したことを示しています。このトピックについてもう少し詳しく掘り下げるのであれば、Surface Toolkit for Windows Touch に、WPF 開発者向けの SurfaceThumb コントロールが含まれています。これは、通常の Thumb コントロールに似ていますが、本当のマルチタッチのサポートと、Thumb がフリックされたときのイベントが追加されています。詳細については、Surface Toolkit for Windows Touch Beta のページ (msdn.microsoft.com/library/ee957351、英語) を参照してください。

Charles Petzold は MSDN マガジンの記事を長期にわたって担当している寄稿編集者です。新しく執筆した「Programming Windows Phone 7」は、bit.ly/cpebookpdf (英語) から無料でダウンロードできます。

この記事のレビューに協力してくれた技術スタッフの Doug KramerRobert Levy に心より感謝いたします。