December 2009

Volume 24 Number 12

いろいろな場所へ - Windows タッチ アプリケーションのモバイル ユーザー向け強化

Gus Class | December 2009

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

Windows 7 には Windows タッチが導入されています。この Windows タッチは、タッチ入力に対応するハードウェアでのタッチ入力を強化し、タッチ アプリケーションを構築するためのしっかりとしたプラットフォームを提供します。これにより、コンピューターに関する経験や知識もまちまちな、あらゆる年齢層のユーザーが、簡単なトレーニングや指導で理解できる、きわめて直感的なインターフェイスを開発できる可能性が広がります。

この機能を支える鍵となるが、Windows タッチ API です。この API を使用すると、ユーザーが触れている画面上の位置や、画面上での指の動きなどに関する情報を入手できます。また、ユーザー インターフェイス要素に現実の物理特性を取り入れることもできます。実際に物を動かすように画面上のオブジェクトを移動できるようになります。輪ゴムを伸ばすような指の動きで、オブジェクトを拡張できます。十分に検討を重ねて実装されたタッチ アプリケーションを操作していると、未来のテクノロジを扱っているように感じられるばかりでなく、アプリケーションを使用していることさえ気付かないでしょう。マウス、スタイラスペン、ショートカット キーを使ってメニュー項目を選択して、アプリケーション機能を使用することはなくなります。

モバイル機器に合わせて作成するアプリケーションには、ユーザーが使用する環境に合わせて操作性を確保するという、明確な要件があります。十分検討しないまま実装されたタッチ アプリケーションでは、Windows タッチを使用する目的がまったく損なわれてしまうことがあります。Windows ユーザー エクスペリエンス ガイドラインの「タッチ」 (https://msdn.microsoft.com/ja-jp/library/cc872774.aspx) では、操作中のユーザーのエクスペリエンスを向上するために、開発者が使用できる方法を中心に説明しています。Windows ユーザー エクスペリエンス ガイドラインでは、モバイル アプリケーション開発者に関連するさまざまなシナリオについて説明し、Windows タッチ開発で潜在的な落とし穴に陥るのを防げるようにしています。

このコラムでお伝えしたいことを 1 つだけ挙げるとするならば、モバイル ユーザーを対象にアプリケーションを作成するときは、そのアプリケーションの種類に独特の側面を考慮すべきだと言うことです。たとえば、アプリケーションで Windows コントロールを使用するのであれば、コントロールをユーザーが触れやすい大きさにし、十分な間隔をとるようにします。アプリケーションでフリック (画面に瞬間的に触れる操作) を使用できるようにするならば、フリック操作を確実に処理する必要があります。

重要なことを優先する

このコラムでは、サンプルのタッチ アプリケーションを取り上げ、これをモバイル アプリケーション向けに強化します。ここでは、皆さんが COM と Windows タッチについてある程度の知識があり、Windows タッチ対応のハードウェアをお持ちであると想定しています。Windows タッチの入門書については、go.microsoft.com/fwlink/?LinkId=156612 (英語) または Yochay Kiriaty の記事 (msdn.microsoft.com/magazine/ee336016.aspx、英語) をご覧ください。

サンプル コードは、MSDN Code Gallery (code.msdn.microsoft.com/ windowstouchmanip、英語) に用意しました。[Downloads] タブに 2 つの .zip ファイルがあり、1 つ目はモバイル向けに強化していないコードで、2 つ目がモバイル向けに強化したコードです。Multiple Manipulators.zip というファイルをダウンロードして展開し、プロジェクトをコンパイルします。

実を言うと、サンプル コードを使用すると、手袋をはめたまま針に糸を通そうとするような感じを受けます。なぜなら、ユーザーが不満を感じるところまで、機能が削減されているためです。たとえば、重なり合った領域にある重なり合ったオブジェクトを選択しようとすると、両方のオブジェクトが選択されて移動します。オブジェクトのサイズを変更すると、もう一度サイズ変更できなくなるほどオブジェクトが小さくなりすぎることがあります。ここでは、こうした問題を解決する方法を示し、全般的な操作性、オブジェクトの選択、自然なユーザー インターフェイスの使用といった領域でユーザー エクスペリエンスを向上するために変更を加える方法を紹介します。モバイル アプリケーションでの考慮事項は、ユーザーがそのモバイル アプリケーションをどのように操作するかによってそれぞれ異なることを覚えておいてください。ここで紹介する問題は、このアプリケーションのみのガイドラインとして使用してください。

全般的な操作性

モバイル アプリケーションでグラフィカル オブジェクトを操作するときは、ユーザーがキーボードやマウスを使わないでタスクを実行できなければなりません。モバイル ユーザーが高い DPI 設定を使用していたり、複数の画面に接続したりしていても、アプリケーションの動作は一貫していなくてはなりません (高 DPI の要件の詳細については、go.microsoft.com/fwlink/?LinkId=153387 (英語) を参照してください)。

サンプル アプリケーションでは、マウスやキーボードを使用しないでユーザーからの入力を取得する際の問題を、Windows タッチが暗黙のうちに対処します。ユーザーはタッチ入力を使用して、オブジェクトの変換や拡大縮小などの操作を実行できます。タッチ入力用にデザインしたアプリケーションでもマウスとキーボードの入力をサポートすることを検討しておけば、ユーザーがマウス入力も含めてどのような入力を行っても、操作プロセッサを呼び出すことができます。図 1 は、サンプル アプリケーションの Drawable クラスにいくつかのユーティリティ関数を追加することにより、ユーザーがマウス入力を使ってタッチ入力のシミュレーションを行えるようにする方法を示しています。このとき、WndProc にハンドラーを追加して、マウス入力を入力プロセッサにフックする必要があります (図 2 参照)。

図 1 マウスを使用してタッチ入力のシミュレーションを行う場合のユーティリティ関数

VOID Drawable::FillInputData(TOUCHINPUT* inData, DWORD cursor, DWORD eType, DWORD time, int x, int y)
{
    inData->dwID = cursor;
    inData->dwFlags = eType;
    inData->dwTime = time;
    inData->x = x;
    inData->y = y;
}

void Drawable::ProcessMouseData(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
    lParam){
    TOUCHINPUT tInput;
    if (this->getCursorID() == MOUSE_CURSOR_ID){          
        switch (msg){
            case WM_LBUTTONDOWN:
                FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_DOWN, (DWORD)GetMessageTime(),LOWORD(lParam) * 100,HIWORD(lParam) * 100);
                ProcessInputs(hWnd, 1, &tInput, 0);
                break;

            case WM_MOUSEMOVE:
                if(LOWORD(wParam) == MK_LBUTTON)
                {
                    FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_MOVE, (DWORD)GetMessageTime(),LOWORD(lParam) * 100, HIWORD(lParam) * 100);
                    ProcessInputs(hWnd, 1, &tInput, 0);
                }          
                break;

            case WM_LBUTTONUP:
                FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_UP, (DWORD)GetMessageTime(),LOWORD(lParam) * 100, HIWORD(lParam) * 100);            
                ProcessInputs(hWnd, 1, &tInput, 0);
                setCursorID(-1);
                break;
            default:
                break;
        }   
    }     
}

図 2 WndProc の変更

case WM_LBUTTONDOWN:
    case WM_MOUSEMOVE:   
    case WM_LBUTTONUP:
        for (i=0; i<drawables; i++){
          // contact start
          if (message == WM_LBUTTONDOWN && draw[i]->IsContacted(LOWORD(lParam), HIWORD(lParam), MOUSE_CURSOR_ID)){
              draw[i]->setCursorID(MOUSE_CURSOR_ID);
          }
          // contact end
          if (message == WM_LBUTTONUP && draw[i]->getCursorID() == MOUSE_CURSOR_ID){
            draw[i]->setCursorID(-1);      
          }
          draw[i]->ProcessMouseData(hWnd, message, wParam, lParam);
        }        
        InvalidateRect(hWnd, NULL, false);
        break;

高 DPI の要件に対処するには、ビルドの設定にプロジェクト マニフェストを追加して、アプリケーションが DPI の設定を認識するようにします。これにより、DPI のレベルが変化したときに、座標空間を補正します (DPI のレベルを変更した後のアプリケーションの動作に確認する場合は、デスクトップを右クリックして [個人設定] をクリックし、コントロール パネルの [ディスプレイ] で DPI のレベルを変更してください)。

次の XML は、このマニフェストを定義して、アプリケーションを高 DPI 設定互換にする方法を示しています。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" 
   xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <asmv3:application>
    <asmv3:windowsSettings xmlns=
"https://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

プロジェクトのプロパティにプロジェクト マニフェストを追加すると、アプリケーションは、ユーザーが設定した DPI に関係なく、タッチ入力情報を操作プロセッサに適切に送信します。ScreenToClient メソッド (詳細については、go.microsoft.com/fwlink/?LinkID=153391 (英語) を参照してください。) を使用して、座標空間が画面の座標ではなくアプリケーションの座標に設定されるようにすることもできます。図 3 に、画面座標をクライアント座標に変換するために、Drawable クラスの ProcessInputs メンバー関数に加える変更を示します。これで、ユーザーが外部モニターを Windows タッチ対応の PC に接続したときに、アプリケーションの座標空間が一貫性を保ち、DPI に対応するようになります。

図 3 画面座標からクライアント座標への変換

POINT ptInput;
void Drawable::ProcessInputs(HWND hWnd, UINT cInputs, 
     PTOUCHINPUT pInputs, LPARAM lParam){
  for (int i=0; i < static_cast<INT>(cInputs); i++){
...
      ScreenToClient(hWnd, &ptInput);
                
      if (ti.dwFlags & TOUCHEVENTF_DOWN){
        if (IsContacted( ptInput.x, ptInput.y, ti.dwID) ){
          pManip->ProcessDownWithTime(ti.dwID, static_cast<FLOAT>
(ptInput.x), static_cast<FLOAT>( ptInput.y), ti.dwTime);                  
          setCursorID(ti.dwID);                  
            
          if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) {
            // Error handling                
          }
        }
      }
      if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE){
        pManip->ProcessMoveWithTime(ti.dwID, static_cast<FLOAT>
(ptInput.x), static_cast<FLOAT>( ptInput.y), ti.dwTime);                  
      }
      if (pInputs[i].dwFlags & TOUCHEVENTF_UP){
        pManip->ProcessUpWithTime(ti.dwID, static_cast<FLOAT>
(ptInput.x), static_cast<FLOAT>( ptInput.y), ti.dwTime);
        setCursorID(-1);
      }      
      // If you handled the message and don’t want anything else done 
      // with it, you can close it
   
  }
}

オブジェクトの選択

ユーザーが思いどおりにオブジェクトを選択できるようにするには、重なり合ったオブジェクトを自然かつ直観的に選択できるようにするだけでなく、表示されているフォーム要素が小さい画面や、タッチ入力の解像度が限られている画面で、オブジェクトを選択して簡単に変換できなければなりません。

現状のままアプリケーションを操作すると、ユーザーが重なり合ったオブジェクトを選択したときに、指が触れた位置の下にあるすべてのオブジェクトにタッチ データが送信されます。最初に触れたオブジェクトが検出されたらタッチ入力の処理を停止するようにアプリケーションを変更するには、オブジェクトの選択時にタッチ入力ハンドルを閉じる必要があります。図 4 に、最初に触れたオブジェクトを検出後、タッチ メッセージの処理を停止するように、タッチ入力ハンドラーを更新する方法を示します。

図 4 タッチ入力ハンドラーの更新

POINT ptInput;
void Drawable::ProcessInputs(HWND hWnd, UINT cInputs, 
     PTOUCHINPUT pInputs, LPARAM lParam){
  BOOL fContinue = TRUE;
  for (int i=0; i < static_cast<INT>(cInputs) && fContinue; i++){
...                
      if (ti.dwFlags & TOUCHEVENTF_DOWN){
        if (IsContacted( ptInput.x, ptInput.y, ti.dwID) ){
          pManip->ProcessDownWithTime(ti.dwID, static_cast<FLOAT>
(ptInput.x), static_cast<FLOAT>(ptInput.y), ti.dwTime);                  
          setCursorID(ti.dwID);                  
            
          fContinue = FALSE;
        }
      }
...
  }
  CloseTouchInputHandle((HTOUCHINPUT)lParam);

}

この変更を実装すると、タッチしたオブジェクトの検出後に、配列内の他のオブジェクトにタッチ データが送信されなくなります。マウス入力した最初のオブジェクトしかタッチ入力を受け取らないようにアプリケーションを変更するには、マウス ボタン押下時の入力処理ステートメントで switch ステートメントから抜け出し、マウス入力のロジックをスキップします。図 5 は、マウス入力ハンドラーの switch ステートメントに加える変更を示しています。

図 5 マウス入力ハンドラーの Switch ステートメントの変更

case WM_LBUTTONDOWN:
        for (i=0; i<drawables; i++){
          if (draw[i]->IsContacted(LOWORD(lParam), HIWORD(lParam), MOUSE_CURSOR_ID)){
              draw[i]->setCursorID(MOUSE_CURSOR_ID);
              draw[i]->ProcessMouseData(hWnd, message, wParam, lParam);   
              break;
          }
        }
...

次に、繰り返しオブジェクトを選択したり、サイズ変更したりできるよう、ユーザーがオブジェクトのサイズを変更する際に、オブジェクトが小さくなりすぎないようにします。これに対処するには、Manipulations API の設定を使用して、オブジェクトのサイズ変更時の最小サイズを制限します。そのためには、Drawable オブジェクトの操作プロセッサ ユーティリティに以下の変更を加えます。

void Drawable::SetUpManipulator(void){
  pManip->put_MinimumScaleRotateRadius(4000.0f);  
}

これで、オブジェクトの倍率を変更するときに、4,000 センチピクセル未満のスケール値が無視されるようになります。各 Drawable オブジェクトは、SetUpManipulator メソッド内に一意制約のセットを保持することで、オブジェクトが適切に操作されるようにします。

自然なユーザー インターフェイス

見た目や操作の感覚が自然になるようにアプリケーションをデザインするには、ユーザーが複数のオブジェクトを同時に操作できるようにします。画面上でオブジェクトを移動するときは、実際に物が動くのと同じような単純な物理特性を持たせます。さらに、ユーザーが画面外でオブジェクトを操作できないようにします。

Manipulations API を使用するアプリケーションでは、この API の仕様により、複数のオブジェクトを同時に操作できます。この例では Manipulations API を使用しているため、同時操作が自動的に有効になります。Gestures API を使用して Windows タッチをサポートすると、複数オブジェクトの同時操作や (パンとズーム、ズームと回転などの) 複合ジェスチャは実行できません。このため、モバイル PC を対象とする Windows タッチ アプリケーションをデザインする場合は、Manipulations API を使用します。

Windows タッチ API には、単純な物理特性 (慣性) のサポートを可能にする IInertiaProcessor インターフェイスがあります。IInertiaProcessor は、IManipulationProcessor インターフェイスと同じメソッドをいくつか使用することで、既に Manipulations API を使用しているアプリケーションに慣性のサポートを簡単に追加できます。慣性のサポートを有効にするには、操作プロセッサの既存のイベント シンクを拡張して、Drawable オブジェクトの IInertiaProcessor インターフェイス インスタンスへの参照を追加する必要があります。その後、イベント シンクにあるイベント データを IInertiaProcessor オブジェクトに接続し、IInertiaProcessor インターフェイスをトリガーするためのタイマーを使用して、慣性の操作イベントをトリガーします。では、この操作を詳しく見ていきましょう。

まず、イベント シンクを更新して、IInertiaProcessor インターフェイスへのデータの送信のサポートを有効にする必要があります。次に示すメンバーとコンストラクターの定義を、イベント シンクを実装するヘッダーに追加します。

class CManipulationEventSink : _IManipulationEvents
{
public:
    CManipulationEventSink(IInertiaProcessor *inert, Drawable* d);
    CManipulationEventSink(IManipulationProcessor *manip, IInertiaProcessor *inert, Drawable* d);

...
protected:
    IInertiaProcessor*      m_pInert;
    BOOL fExtrapolating;

また、次に示すように、タイマーに使用する HWND を設定するため、イベント シンクにメンバーとアクセス メソッドを追加します。
public:

void SetWindow(HWND hWnd) {m_hWnd = hWnd;}
...
private:
...
HWND m_hWnd;

次に、IManipulationProcessor インターフェイスを受け取るコンストラクターを、IInertiaProcessor インターフェイスを受け取るように変更し、IInertiaProcessor インターフェイスのみを受け取るコンストラクターを追加します。IManipulationProcessor インターフェイスを受け取るコンストラクターは、IInertiaProcessor インターフェイスへの参照を使用して、ManipulationCompleted イベントから慣性をトリガーします。IInertiaProcessor インターフェイスのみを受け取るコンストラクターは、慣性のイベントを処理します。図 6 に、これらのコンストラクターの実装を示します。

図 6 IManipulationProcessor と IInertiaProcesor のコンストラクターの実装

CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *manip, IInertiaProcessor *inert, Drawable* d){
    drawable = d;
    // Yes, we are extrapolating inertia in this case
    fExtrapolating = false;

    //Set initial ref count to 1
    m_cRefCount = 1;

    m_pManip = NULL;
    m_pInert = inert;    

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points
    IConnectionPointContainer* spConnectionContainer;
    
    hr = manip->QueryInterface(
      IID_IConnectionPointContainer, 
      (LPVOID*) &spConnectionContainer
      );

    if (spConnectionContainer == NULL){
        // Something went wrong, try to gracefully quit        
    }

    //Get a connection point
    hr = spConnectionContainer->FindConnectionPoint
(__uuidof(_IManipulationEvents), &m_pConnPoint);

    if (m_pConnPoint == NULL){
        // Something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise
    hr = m_pConnPoint->Advise(this, &dwCookie);
}
CManipulationEventSink::CManipulationEventSink(IInertiaProcessor *inert, Drawable* d)
{
    drawable = d;
    // Yes, we are extrapolating inertia in this case
    fExtrapolating = true;

    //Set initial ref count to 1
    m_cRefCount = 1;

    m_pManip = NULL;
    m_pInert = inert;    

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points
    IConnectionPointContainer* spConnectionContainer;
    
    hr = inert->QueryInterface(
      IID_IConnectionPointContainer, 
      (LPVOID*) &spConnectionContainer
      );

    if (spConnectionContainer == NULL){
        // Something went wrong, try to gracefully quit        
    }

    //Get a connection point
    hr = spConnectionContainer->FindConnectionPoint
(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // Something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise
    hr = m_pConnPoint->Advise(this, &dwCookie);

次に、Drawable クラスを更新して、慣性のサポートを有効にします。そのため、図 7 に示す転送の定義と、pInert メンバー変数を追加します。

図 7 Drawable クラスの更新

interface IInertiaProcessor;
public:
...
    // Inertia Processor Initiation
    virtual void SetUpInertia(void);

...
protected:

    HWND m_hWnd;
    
    IManipulationProcessor* pManip;
    IInertiaProcessor*      pInert;
    CManipulationEventSink* pEventSink;

次のコードは、SetUpInertia メソッドの最も単純な実装を示しています。このメソッドはすべての処理を終了し、慣性プロセッサをリセットしてから、すべての構成設定を行います。

void Drawable::SetUpInertia(void){
    // Complete any previous processing
    pInert->Complete();

    pInert->put_InitialOriginX(originX*100);
    pInert->put_InitialOriginY(originY*100);
       
    // Configure the inertia processor
    pInert->put_DesiredDeceleration(.1f);  
}

Drawable クラスを更新したら、Drawable コンストラクターを変更して、新しいイベント シンクのコンストラクターを組み込みます (図 8 参照)。

図 8 新しいイベント シンクのコンストラクターの組み込み

Drawable::Drawable(HWND hWnd){
. . 
  
    // Initialize manipulators  
    HRESULT hr = CoCreateInstance(CLSID_ManipulationProcessor,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_IUnknown,
          (VOID**)(&pManip)
    );

    // Initialize inertia processor
    hr = CoCreateInstance(CLSID_InertiaProcessor,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_IUnknown,
          (VOID**)(&pInert)
    );

    //TODO: test HR 
    pEventSink = new CManipulationEventSink(pManip,pInert, this);
    pInertSink = new CManipulationEventSink(pInert, this);
    pEventSink->SetWindow(hWnd);
    pInertSink->SetWindow(hWnd);

    SetUpManipulator();
    SetUpInertia();
    m_hWnd = hWnd;
}

ここで、次に示すタイマー メッセージ ハンドラーをメイン プログラムに追加します。

case WM_TIMER:
        // wParam indicates the timer ID
        for (int i=0; i<drawables; i++){
            if (wParam == draw[i]->GetIndex() ){
                BOOL b;       
                draw[i]->ProcessInertia(&b);        
            }
        }
    break;

タイマーのハンドラーを作成し、タイマーをセットアップしたら、慣性がなくなったことを示すイベントの completed メッセージからタイマーをトリガーする必要があります。図 9 は、ユーザーがオブジェクトの操作を終えたときにタイマーを開始し、慣性が完了したらタイマーを停止するように変更を加えた completed イベントを示しています。

図 9 Completed イベントへの変更

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cCompletedEventCount ++;

    m_fX = x;
    m_fY = y;


    if (m_hWnd){
        if (fExtrapolating){
            //Inertia Complete, stop the timer used for processing
            KillTimer(m_hWnd,drawable->GetIndex());
        }else{ 
            // Setup velocities for inertia processor
            float vX, vY, vA = 0.0f;
            m_pManip->GetVelocityX(&vX);
            m_pManip->GetVelocityY(&vY);
            m_pManip->GetAngularVelocity(&vA);

            drawable->SetUpInertia();

            // Set up the touch coordinate data
            m_pInert->put_InitialVelocityX(vX / 100);
            m_pInert->put_InitialVelocityY(vY / 100);        
                          
            // Start a timer
            SetTimer(m_hWnd, drawable->GetIndex(), 50, 0);   
    
            // Reset sets the initial timestamp
            pInert->Reset();     
        }
    }
}

タイマー間隔 (SetTimer の 3 つ目のパラメーター) を減らすとアニメーションがスムーズになりますが、トリガーされる更新イベントが多くなるため、イベント ハンドラーが実行する操作の種類によっては、パフォーマンスが低下する可能性があります。たとえば、この値を 5 に変更するとアニメーションが非常にスムーズになりますが、CManipulationEventSink::ManipulationDelta の呼び出しが増加するため、ウィンドウの更新が頻繁になります。

これで、アプリケーションをビルドして実行できますが、もう少し変更を加えないと、操作したオブジェクトが画面外に移動してしまいます。オブジェクトが画面外で操作されないようにするには、弾性境界を使用するように IInertiaProcessor インターフェイスを構成します。図 10 に、Drawable オブジェクト向けに画面の境界を初期化するために、SetUpInertia メソッドに加える変更を示します。

図 10 画面境界の初期化

void Drawable::SetUpInertia(void){
(...)
            
    // Reset sets the  initial timestamp       
    pInert->put_DesiredDeceleration(.1f);

    RECT rect;
    GetClientRect(m_hWnd, &rect);        

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    int wMargin = width  * .1;
    int hMargin = height * .1;

    pInert->put_BoundaryLeft(rect.left * 100);
    pInert->put_BoundaryTop(rect.top * 100);
    pInert->put_BoundaryRight(rect.right * 100);
    pInert->put_BoundaryBottom(rect.bottom * 100);

    pInert->put_ElasticMarginTop((rect.top - hMargin) * 100);
    pInert->put_ElasticMarginLeft((rect.left + wMargin) * 100);
    pInert->put_ElasticMarginRight((rect.right - wMargin) * 100);
    pInert->put_ElasticMarginBottom((rect.bottom + hMargin) * 100);

...
}

今後の展望

Windows タッチ API を使用すると、既存のアプリケーションの価値が効率よく高まると同時に、アプリケーションを目立たせることができます。アプリケーションを使用する状況に応じた処理ができるように、さらに多くの時間をかけて Windows タッチ API を最大限に活用してください。アプリケーションのモビリティとユーザビリティの要件を考慮することで、アプリケーションがより直感的になり、ユーザーは短時間でアプリケーションの機能にたどりつくことができます (Windows タッチの完全なドキュメントへの参照を含むその他のリソースについては、MSDN (msdn.microsoft.com/library/dd562197(VS.85).aspx、英語) を参照してください)。

Windows Presentation Framework (WPF) と .NET Framework 4 のリリース時には、マイクロソフトは複数の接触ポイントを有効にするコントロールを使用するマネージ開発をサポートする予定です。複数の入力をサポートするようにアプリケーションの機能を強化することを考えている、マネージ コードを扱う開発者には、このリリースが注目です。現時点では、C# 用の Windows タッチのマネージ ラッパーのサンプルが、Windows SDK に含まれています。

Gus "gclassy" Class * は、マイクロソフトのプログラミング関連のライター兼エバンジェリストです。マイクロソフトでは、Windows タッチ、Tablet PC、および DRM システムに関する業務に携わってきました。彼は、ブログ (gclassy.com、英語) で開発者の課題を取り上げ、プログラミングの例を紹介しています。*

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

ご質問やご意見は goplaces@microsoft.com (英語のみ) までお送りください。