ここのところ、acer w500 での windows 8 環境を整えるのに四苦八苦していましたが、なんとなく開発環境が整ったのでお試しコーディングを開始します。
開発環境としては、
- windows 8 customer preview 版を acer w500 にインストール
- windows 8 customer preivew + visual studio 11 beta を vaio type s にインストール
- windows 7 上で visual studio 2010 で開発
という具合です。acer と vaio はデスクトップPCから、リモートデスクトップで接続して操作します。スレートPC(タブレットPC)を使うのは、実際に動かして試してみるためですね。visual studio 11 beta には metro アプリを動かすための「シミュレーター」の機能があるのですが、vaio type s が多少非力(cpu 1.6GHz)なのとタッチ操作自体は実機のほうがやりやすい(ユーザーインターフェースを考察しやすい)ので、この構成になっています。
win8cp + vs11beta の組み合わせは、vmware でも可能なのですが、win8cp のパフォーマンスチューニングがいまひとつなのか、実用に耐えるスピードで vs11beta を動かせませんでした
なので、多少遅いですが、win8+vs11 の組み合わせは仮想環境ではなくて実際のマシンに入れるのが得策です(イライラして、創造力が失われますからね)。
さて、普通ならば metro アプリをと思うところなのですが、ひとまず win8 のデスクトップアプリを作ってみます。つーか、acer で win8 を使うと、ソフトキーボードの大きさに辟易するのでこれをなんとかしたい、というのが今回の主旨。実は、iPad も同じ問題を抱えていて、メモを取るだけでも画面の半分以上を占有してしまうという状態なのです。
まあ、タブレットPCで両手打ちをするのであれば、これが選択肢として上がるのは仕方がないのですが、折角の貴重な画面の半分をキーボードに占有されるのは「閲覧性が悪い」というわけで、コンセプトデザイン(苦笑)的に作ってみました。
何処かで見たようが画面なのは、さておき…某iPhoneでこのサイズで打っている訳だから、タブレットでも打てない訳はないよね、という具合です。
■製作開始の前に
で、単純にマウス/指でぽちぽち打つ分には、mouse click か mouse down/up を取ればよかろうと思っていたのですが、意外とハマりました。
- ソフトキーボードの画面を前面に出てこないようにする。
- マウスと指のタッチイベントが、win form, wpf, metro で異なる。
ところですね。後、諸々出て来そうなのですが、まだ製作途中なので。
■ソフトキーボードの画面を前面に出す
C#で2ch専用ブラウザをつくろう スクリーンキーボードの作成
http://cs2ch.blog123.fc2.com/blog-entry-85.html
を参考にして、ウィンドウスタイルに WS_EX_NOACTIVATE を設定します。設定の仕方は、Form クラスの CreateParams プロパティをオーバーライドという方法。これが一番正当なやりかたです。
private const int WS_EX_NOACTIVATE = 0x8000000; protected override CreateParams CreateParams { get { CreateParams p = base.CreateParams; if (!base.DesignMode) { p.ExStyle = p.ExStyle | (WS_EX_NOACTIVATE); } return p; } }
ソフトキーボード自体にボタンを付ける場合には、ボタンコントロールがアクティブにならない様にします。今回は、iPhone の画面キャプチャをそのまま使うために、ボタンスタイルの変更はしなかったのですが、こんな風に ButtonEx クラスを作って貼りつけるそうです。
class ButtonEx : Button { public ButtonEx() { //アクティブにしたくなウインドウ(Form)に配置する //コントロールは下記をつけないと駄目 base.SetStyle(ControlStyles.Selectable, false); } }
これで、メモ帳(win8の場合は出すのに苦労しますが)などにキーを送ることができます。キーの送信自体は、keybd_event 関数を使えば ok です。
■タッチイベントを拾う
ウィンドウをアクティブにしないほうは簡単にできたのですが、実はタッチイベントのほうが実は大変でした。
こんな風に、iPhone でキーを操作した時みたいに、ボタンの上にガイドを出したかったのです。iPad/win8 のソフトキーボードが大きいのは、両手で打てるように作っているのもそうですが、指でキートップの刻印が隠れてしまうのが主原因と思われます。ハード的にキーボードが別になっている場合は、ブラインドタッチが基本だったり、キートップを見ながらぽちぽち打つサイズになっているのでそれなりに大きくなります。
キー付きの携帯電話の場合、キーサイズが小さくなりますが、何処を押しているか(何処を押せばよいのか)は、ボタンの凹凸で分かります。しかし、iPhoneのようなスマートフォンの画面を直接操作する場合は、通常のボタンのようなハード的なフィードバックがないのでキー自体が探しづらいのですよね。
なので、apple はキーの上にガイドを出すように決めたのです(これが特許になっているかどうかは不明です)。ワタクシ的に云えば、iPad にも同じようなサイズのソフトキーを付けて欲しかった(付けてほしい)ですね。
このガイドを出すのに、普通のデスクトップアプリならば、
- マウスがポイントされたとき(mouse overイベント)
- マウスを押した時(mouse downイベント)
のどちらかで表示させます。しかし、タブレットの場合は、マウスとは違って mouse over というイベントは発生しません。当然、指が空中に浮いている間はタブレットはそれを判別できないので、最初のイベント mouse down になります…というハズなのですが、実は違います。
windows にはマウスの右クリックというイベントがあるので、これをエミュレートするために、タブレットPCの場合は「指を長押ししたときに右クリックと見なす」という処理が os に入っています。なので、指で画面を押した瞬間には、mouse down イベントが発生しないのですよ。これがハマりどころです。
「マウス」と「指」とはかなり操作が違うことを覚えてておかなければなりません。
windows xp のTable PCでは、Microsoft.Ink というクラスライブラリがあって、これを使えるのですが、windows 8 にはありません(多分、windows 7 にもありません)。
Microsoft.Ink 名前空間
http://msdn.microsoft.com/ja-jp/library/microsoft.ink(v=vs.90).aspx
じゃあ、windows 8 のデスクトップアプリ(非metroアプリ)ではどうするかというと、WPFアアプリ/コントロールを使います。
UIElement.TouchDown イベント (System.Windows)
http://msdn.microsoft.com/ja-jp/library/system.windows.uielement.touchdown.aspx
XAML のコントロール(UIElement)には、TouchDownというタブレット用のイベントが用意されています。試すと分かるのですが、mouse down イベントは違ったタイミングで呼び出され、きちんと「指で画面を押した瞬間」にイベントが発生します。
ちなみに、スタイラスを使ったときの StylusDown イベントもあるのですが、指との違いはなんでしょうかねぇ?スタイラス自体に何かの機能があるときは、こっちのイベントが呼び出されるのかもしれません。
という訳で、タッチダウンのイベントは、以下のように取ります。
private void UserControl_TouchDown(object sender, TouchEventArgs e) { GlobalData.MainForm.label1.Text = "touch down"; TouchPoint pos = e.GetTouchPoint(this); GlobalData.OnMouseDown(new System.Drawing.Point((int)pos.Position.X, (int)pos.Position.Y)); } private void UserControl_TouchUp(object sender, TouchEventArgs e) { GlobalData.MainForm.label1.Text = "touch up"; TouchPoint pos = e.GetTouchPoint(this); GlobalData.OnMouseUp(new System.Drawing.Point((int)pos.Position.X, (int)pos.Position.Y)); } private void UserControl_TouchMove(object sender, TouchEventArgs e) { TouchPoint pos = e.GetTouchPoint(this); GlobalData.OnMouseMove(new System.Drawing.Point((int)pos.Position.X, (int)pos.Position.Y)); }
GetTouchPoint メソッドは、マウスの位置を取得するものです。対象コントロールからの相対位置になります。今回は、画像を貼りつけ(Imageコントロール)を使っているので、重宝します。ちなみに、マウス位置をおなじみの「System.Windows.Forms.Cursor.Position」で取得しようとすると、「指」と「マウス」の違いで四苦八苦します。具体的には TouchDown イベントが発生したときには、System.Windows.Forms.Cursor.Position の値は更新されていません。故に、以前のマウス位置(タッチ位置)が取得されてしまうという厄介な出来事が起こります。
GlobalData.OnMouseDown 関数は、form コントロールの上に xaml のコントロールを貼りつけているので、イベントをアップさせるための自作の仕組みです。このあたりは、全面的に WPF アプリケーションで作れば不必要だと思います。
■metro アプリのタッチイベントは?
ちなみに、metro アプリのタッチイベントは WPF アプリとは異なります。
IE10のタッチイベントってなんだろう – vantguarde – web:g
http://web.g.hatena.ne.jp/vantguarde/20110915/1316013489
これは、javascript の例ですが、C# も場合も同様に、
- PointerPressed
- PointerReleased
- Tapped
などがあります。
Pointer が付いているものは、マウス、指、スタイラスを一括で扱うイベントです。ボタンクリックにあたるものは Tapped イベントを使えばOKです。ボタンコントロール自体には、Click イベントがあります。
private void buttonClearClick(object sender, RoutedEventArgs e) { listMessage.Items.Clear(); } private void rectPointerPressed(object sender, PointerEventArgs e) { listMessage.Items.Add("pointer pressed"); } private void rectPointerReleased(object sender, PointerEventArgs e) { listMessage.Items.Add("pointer released"); } private void rectTapped(object sender, TappedRoutedEventArgs e) { listMessage.Items.Add("pointer tapped"); }
WPF アプリの場合は、ボタンをダブルクリックすると自動的にイベントを作ってくれますが、metro の場合はイベント名を入れないといけません。どっちがいいのか微妙なのですが、WPF に合わせて欲しいなと思っています。
ちなみに、iPhone を作成する XCode の場合には、イベント名は自分で付ける方式です。なので、どちらが「良い」とも限りません。慣習ってところです。
ってな訳で、WPF アプリを作っていたからといって、スムースに metro アプリに移行できるかというと違う訳で、優位性としては、
- xaml を知っている方が有利(win form だと出てこないので)。なので、silverlight を触っている人もokかと。
ってところですが、
- でも、イベント関係が違うので、思い込むと迷走するかも。
という落とし穴があります。
なので「指でタッチしてスムースに動く」アプリケーションを作る場合には、デスクトップPCでシミュレーションだけでなく、実機が必須な予感がしています(なので、買ったというもある)。
ちょっと、長くなったので、一旦切ります。実際にソフトキーボードを作るノウハウは、別の記事にでも。
スクリーンキーボードとリモートデスクトップで検索してたどり着きました。
若干エントリと関係ない質問になってしまうのですが、タブレットのWin8からWin7にリモートデスクトップした場合、スクリーンキーボードや画面タッチは文字入力やマウス操作として上手く使えるのでしょうか?
試してみると、win8->win8 のリモートデスクトップは大丈夫ですね。スクリーンキーボードをリモート元のものを使うか、リモート先のものを使うか、が微妙なところですが、どちらでもいけます。リモートデスクトップを全画面にしておくと、リモート元のスクリーンキーボードを出すのにちょっとコツがいりますが。
同じように、win8->win7 の場合も大丈夫だと思います。
試していただいてありがとうございます!
何とかなりそうで安心しました。