デスクトップ アプリを Surface Dial に対応させる

MVP Global Summit 2016 で渡米した際、Microsoft のオールインワン PC “Surface Studio” とともに発表された Surface Dial を入手できたので、さっそくアプリから使ってみました。 アプリでの対応は簡単で、むしろ「どう使わせるか」のアイディア勝負になるデバイスという印象です。

発表時の映像での「Surface Studio の画面に置いて使う」インパクトが強いですが、デバイス自体は BLE (Bluetooth 4.0) で接続するシンプルなものです。 あくまで、Surface シリーズの場合は画面に置いて反応するというだけで、それ以外の PC でも机に置いて使うことができます。 また、今後同様のサードパーティ製品が発売されることも予想されます。

UWP アプリから使う

UWP API の Windows.UI.Input.RadialController という型を使用して、メニュー項目の操作や回転・クリック (押し込み) イベントを購読します。 エントリー ポイントは RadialController.CreateForCurrentView() という静的メソッドで、これが RadialController のインスタンスを取得する唯一の方法です。

また、既定のメニュー項目を削除したりする場合に Windows.UI.Input.RadialControllerConfiguration という型も使用します。

UWP、および Dial の基本的な実装方針に関しては下記エントリーが詳しいです。

http://blog.okazuki.jp/entry/2016/11/11/171706
https://blogs.msdn.microsoft.com/shintak/2016/11/15/dialprogramming/

WPF アプリから使う (基本)

.NET Framework の WPF や Windows Forms アプリでも、UWP アプリと同じ要領で Surface Dial に対応させることができます。 UWP API である RadialController を使用することになるため、プロジェクトに WindowsRuntime を参照させる必要があります。

UWP アプリにおける RadialController のエントリー ポイントが View であるように、デスクトップ アプリの場合はウィンドウ ハンドルです。 つまり、現時点において Surface Dial のコントローラーはウィンドウ単位で作る必要がある、ということです。

まず、RadialControllerRadialControllerConfiguration インスタンスを取得するため、… の COM オブジェクトを取得するためのインターフェイスを用意します。 これは Microsoft のサンプルで公開されている方法です。

続いて以下は、上記インターフェイス経由で実際にインスタンスを取得するコードの例です。 このコードさえ用意してしまえば、デスクトップ アプリからも DesktopRadialController.Create(hWnd) のようなシンプルな呼び出しで Surface Dial のコントローラーを操作できるようになります。

これ以降の手順は、メニュー項目のアイコンを独自に追加する場合 (後述) を除けば UWP と同じです。 WPF であれば HwndSource 経由でウィンドウ ハンドルを取得し、RadialController のインスタンスであれこれしましょう。

さっそくですが、自分のプロダクト (KanColleViewer) を Surface Dial に対応させ、UI を操作できるようにしました。 タブを切り替えるだけの単純な機能ですが、Surface Dial の回転イベントを拾って切り替えています。 このコードは GitHub で公開しています

WPF アプリから使う (つらい)

RadialController に追加するメニュー項目は、既定で用意されているアイコンから選択するか、もしくは独自のアイコンを設定することができます。 先述の KanColleViewer の例は既定のアイコンから選択しました。

  • .CreateFromKnownIcon(): 既定のアイコンを RadialControllerMenuKnownIcon から選択
  • .CreateFromIcon(): 独自のアイコンを IRandomAccessStreamReference で指定

つまり、独自アイコンのメニュー項目を追加したい場合は、デスクトップ アプリの場合でも IRandomAccessStreamReference を用意する必要があります。

Microsoft のサンプルにあるとおり、ファイル システム上に存在する画像ファイルをアイコンとして使用する場合は、StorageFile.GetFileFromPathAsync(path)StorageFile を取得し、RandomAccessStreamReference.CreateFromFile(storage) で OK です。

一方で、アセンブリ内にリソースとして配置した画像ファイルをアイコンとして使用する場合は、少々面倒な操作が必要になります。 先にコードを示しておきます。

ご覧の通り、アイコン画像リソースの URI から Stream を作成し、バイト配列に読み込み、それを UWP の InMemoryRandomAccessStream に書き込む、という手順です。 ストリームへの書き込みで非同期操作が出てくるため、たかだかメニュー項目の作成で async/await している微妙っぷり (まあ、.AsTask().Wait() してしまえば同期的に書けるものの…)。

上記コードを動かす場合、Windows.Foundation.IAsyncOperation<T> を await するために System.Runtime.WindowsRuntime.dll の参照が必要です。 ちなみに同アセンブリには .NET Framework の Stream を IRandomAccessStream にするための .ToRandomAccessStream() なる便利拡張メソッドがあるのですが、その方法だとアイコンが表示されませんでした。つらい。

UWP であれば RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item3.png")) のように URI を指定して一発で終わりなんですが、WPF アプリの場合はこの方法でしか追加できませんでした (ぐらばく調べ。もっといい方法があったら教えてください…)。 AppX 化したデスクトップ アプリなら ms-appx:// な URI でいけるんじゃないか、と思ってますが未確認。

Win32 アプリから使う

つらそう

おわりに

場合によって若干面倒なことになるものの、デスクトップ アプリでも簡単に Surface Dial に対応させることができる、ということを紹介しました。

重ねて言っておきますが、これはアイディア勝負です。 UWP でも WPF でも実装自体は簡単で、ユーザーにどれほど有用な操作を提供できるかが勝負になると思っています。 先の KanColleViewer に実装した例はあくまでサンプルであって、私自身あの機能自体が有用であるとは微塵も思っていません。

なので、近々アイディアソンをしたいな、と企んでいます。 が、その前に日本発売いつなんだろう…

デスクトップ アプリの生存戦略 (Desktop App Converter)

先日開催された「めとべや東京」改め「まどすた」の 1 回目の勉強会が //build/ 2016 振り返りをテーマとして C# ユーザー会と合同で開催され、私は Project Centennial (Desktop App Converter) を題材として、以下の資料で発表しました。

また、5/23 に開催された de:code 前夜祭のアンカンファレンスでも、この資料の一部を使用しつつ 20 分ほど発表させて頂きました

本記事は、これらの発表を準備するにあたった実施した Project Centennial のセットアップ、および実行手順と、Project Centennial の登場によりデスクトップ アプリ開発者にとってどのような変化があるかについてを共有することを目的としています。

余談ですが、発表タイトルに「// 2016」と入っているのは、およそ 3 年前の「めとべや東京」の第 1 回勉強会でも同じタイトルで登壇しており、要するになんとなくです。

Project Centennial

昨年の //build/ 2015 で、様々なプラットフォーム向けの既存アプリを Windows 10 (UWP) に移行させることを目的とした Windows Bridge が発表されました。 Project Centennial は、その一環として Windows Bridge for Classic Desktop app として登場したもので、既存の Windows デスクトップ (Win32 / .NET) アプリを UWP パッケージ化するものです。

デスクトップ アプリのインストーラーをサイレント モードで動作させて、UWP アプリ パッケージである AppX に変換するのがこのツールの主機能です。 既存のデスクトップ アプリを「UWP アプリと同じようにパッケージ化する」だけであり、実行されるバイナリは従来通りのものである (つまり、既存のコード ベースそのままで UWP アプリを作れるわけではないし、この方法で UWP パッケージ化したデスクトップ アプリは Windows 10 Mobile などでは動作しない) ことに注意してください。

Desktop App Converter の利用方法や得られるメリット等は本記事の後半に記述してあります。

Continue reading

クラシック デスクトップ アプリの Windows テーマ追従

Windows には、パーソナライズの一部としてテーマ設定が存在し、「アクセント カラー」を選択することができます。 また、Windows 10 build 14316 から、アクセント カラーとは別に「app mode」として Light/Dark テーマを選択できるようになりました。

個人設定

Light/Dark

UWP アプリは SystemAccentColor などの一部のリソースを ThemeResource として指定することで、Windows のアクセント カラーやテーマによって決定される色に追従することが可能です。

一方で、クラシック デスクトップ アプリにおいては、例えば WPF ではそのようなリソースが定義されておらず、フレームワークとして Windows テーマに追従する機能は実装されていません。 今回は、クラシック デスクトップ アプリにおいて、Windows のテーマ設定に追従する方法について解説します。

Continue reading

Microsoft MVP for Visual Studio and Development Technologies を受賞しました

2016 年 4 月期で Microsoft MVP Award を再々受賞しました。 Award Category は Visual Studio and Development Technologies になります (長すぎでしょ)。

先期も WPF 一辺倒な活動内容だったとはいえ、初めて雑誌連載をさせて頂いたりと貴重な経験も得られ、これまで以上に学びの多い年でした。

KanColleViewer は公開から 2 年経ち、おかげさまで DL 数約 350 万ほどになりました。 プラグイン システム導入やら Roslyn による Code-aware Library にしてみるやらと、個人的にやりたいことはやりつくしました。 とはいえ艦これ自体は今後もプレイするし、WPF の需要がなくなることもないと考えているので、今後は今までと違うアプローチで攻めていきたいです。

一方、MVP Award 自体の制度変更によって複数部門での受賞が可能となったこともあり、Windows Development での受賞も狙ってみたのですが、こちらはダメでした。

Windows Development の Contribution Area に「Classic Windows Development」がある (し、現状 Windows Development の人で UWP アプリちゃんと作ってる人そう多くない印象だった) ので、WPF アプリでもイケるかと思いきや、そんなことはなかった模様です。。。

というより、やはり //build/ 2016 の day1 Keynote を観て、Windows クライアントに関しては UWP しか眼中にない感じがひしひしと伝わってきました。 時代は定規。

ということで、先期は SylphyHorn の UWP 化など片足だけ突っ込んだような状態でしたが、今期はちゃんと UWP で完結したアプリを仕上げる、というところは避けて通れなさそうですね。

また、めとべや東京 vNext も計画中です。 Microsoft は UWP に本気出していますし、WPF も負けていられないし、という感じで Windows クライアント開発コミュニティとして続けていきたいと思っていますので、今後ともよろしくお願いいたします。

Continue reading

Surface Book を使って 2 ヵ月が経った

昨年 11/1 ~ 11/5 にシアトル Microsoft 本社で開催された MVP Global Summit に出席しており、滞在先ホテルの近くにある Microsoft Store で Surface Book衝動買いしました

Surface Book は Surface Pro 4 とともに発表された Surface ブランドのラップトップ PC です。 そして今日、日本での価格と発売日が発表され、購入を検討されている方も多いかと思います。

そこで、購入から 2 ヵ月ちょっと経った今、使用者として雑感をつらつら適当に述べたいと思います。

買ったもの

Surface Book
・CPU: Core i7-6600U
・RAM: 16 GB
・SSD: 512 GB

surface_memo_1

Continue reading

仮想デスクトップでの遊び方

12/19 に開催しためとべや東京勉強会 #10 で Windows 10 の仮想デスクトップに関する話をしたのですが、セッション資料を公開し忘れていました。すみません。

別に使い方の紹介とか指南とかそういう内容ではなく、C# コードで Windows デスクトップ アプリから操作してみた、という話です。

この内容は既にこのブログで解説しています

手っ取り早く仮想デスクトップ操作してみたい方は、こちらのライブラリを使ってみてください。 ReadMe にある通り、NuGet からもインストールできます。
https://github.com/Grabacr07/VirtualDesktop

また、現在 SylphyHorn の UWP 化を進めています。 そろそろ成果が出そうなので、当ブログも UWP に関する話題が増えていくことでしょう (願望)。

2016 年がんばります

2015 年、お疲れさまでした。

昨年は、一昨年と変わらないオンライン活動、めとべや東京に加え、日経ソフトウエアでの連載でお世話になりました。

雑誌記事連載は初めてだったこともあり、毎度締切に追われる形となってアレでしたが、少ないページ数の中で要点をまとめる技術等、大変貴重な勉強ができました。 幸いなことに延長のリクエストまで頂き、お読み頂いた方、またご協力頂いた皆様、ありがとうございました。

また、日本国内で Windows 10 Mobile 端末が活発化してきた今、ようやく自分の中で UWP アプリ開発の機運が高まってきたところです。

これまで、KanColleViewer、MetroRadiance、MetroTrilithon などいくつかのライブラリを公開する等 WPF を中心としたオンライン活動を続けてきましたが、来年は WPF に加えて UWP にも活動範囲を広げて頑張っていこうと思います。


今年ブログ書かなさ過ぎたの大変反省しています。 原因は 2 点で、

  • 単純に忙しかった
  • このサイトが重すぎて私自身が管理ページに入れない

書きためているネタはたくさんあるので、上記課題を解消しつつ、情報発信していきたいデス、、、


今年もよろしくお願い致します。

アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ

先日のエントリー「Windows 10 の仮想デスクトップを制御しようとして失敗した話」で

  • アプリ (C# コード) から仮想デスクトップの作成、削除、移動はできた
  • 同一プロセス内のウィンドウを、任意の仮想デスクトップに移動させることはできた
  • 他プロセスのウィンドウは、他の仮想デスクトップに移動させることはできなかった

の 3 つめの影響により「グローバル キーフックを使って現在アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ」が頓挫していた話をしました。 が、@tmyt の協力で完成しました。

どうやって回避したか (といっても、別に Windows 10 や IVirtualDesktopManager 固有の話ではなく、昔からあるネイティブなテクニック) の話は @tmytブログを待つとして 解説頂きました、せっかくなので完成したアプリの紹介をします。

SylphyHorn

という名前にしました (てきとう)。

ダウンロード: SylphyHorn ver.1.0.zip

※お約束ですが、アプリの使用はすべて自己責任で行ってください。このアプリを使用した結果生じた損害について、開発者は一切責任を負いません。

起動すると、タスク トレイに常駐します。 メニューは設定と終了のみ。超シンプル。

tasktray

アクティブ ウィンドウを別の仮想デスクトップへ

既定の設定では、

  • アクティブ ウィンドウを左の仮想デスクトップへ移動し、そこに切り替え
    Ctrl(left) + Alt(left) + Win(left) + ←
  • アクティブ ウィンドウを右の仮想デスクトップへ移動し、そこに切り替え
    Ctrl(left) + Alt(left) + Win(left) + →
  • 仮想デスクトップを作成し、アクティブ ウィンドウをそこへ移動し、切り替え
    Ctrl(left) + Alt(left) + Win(left) + D

と、Windows 標準の操作に Alt キーを加えることで、アクティブ ウィンドウを巻き込むようにしています。 また、設定画面から

  • 上記のショートカット設定
  • 仮想デスクトップのループ (端に到達したら逆側に飛ばす)
  • スタートアップにショートカットを作成

などが設定できます。

settings

デスクトップ切り替え通知

仮想デスクトップを切り替えると、そのデスクトップが何番目かを表示する通知機能も実装しました。 設定画面から On/Off できます。

notification

その他

細かいですが、設定画面や通知ウィンドウは Windows のテーマ設定によって色が変わります。

予定する機能

今後追加する予定の機能としては

  • 通知を表示するモニターを選択できるようにする
  • エラー発生時のダイアログが無骨なので何とかする
  • アプリ アイコン (is ひどい (ので何とかしたい))

くらいです。 他に何か欲しい機能、不具合等ありましたら @Grabacr07 まで。

ソース コード

当然ながら公開してます。

https://github.com/Grabacr07/SylphyHorn
(readme 書くのサボってるマン)

まとめ

Windows が標準でショートカット キーに対応してくれたら、お役御免となるアプリです。 それまでの間だけお付き合い頂ければ。

Windows 10 の仮想デスクトップを制御しようとして失敗した話

Windows 10 で仮想デスクトップ機能が加わり、個人的にそこそこ活用しているつもりなのですが、残念な点が 1 つ。 アクティブ ウィンドウごと仮想デスクトップを移動したいことが結構あるのですが、それに対するショートカット キーがありません。

仮想デスクトップ間の移動は Ctrl + Win + 左右キー ですが、ウィンドウも一緒に、となると基本的にはマウス操作を伴います (しかも結構面倒なやつ)。 一応、マウス使わずにやる方法もあるようですが、Ctrl + Win + 左右キー に近い手軽なものがほしいです… (フィードバック済み)

で、公式で実装されるまでは自分でアプリ作るか、と思って Windows 10 の仮想デスクトップ周りの API を調べ始め、最終的に頓挫しました。 具体的には、以下のような状況です。

  • アプリ (C# コード) から仮想デスクトップの作成、削除、移動はできた
  • 同一プロセス内のウィンドウを、任意の仮想デスクトップに移動させることはできた
  • 他プロセスのウィンドウは、他の仮想デスクトップに移動させることはできなかった

一番やりたかったのが「グローバル キーフックを使って現在アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ」だったのですが、3 番目の理由により現時点では実現できませんでした。 ざんねん。

しかしまあ、せっかく調べた&仮想デスクトップ関連 API の C# ラッパー的なものを作ったので、公開しておきます。 だいたいここ見ながら作りました:

2015/09/14 追記:
最終的に完成しました。
http://grabacr.net/archives/5701

Continue reading

WPF 4.6 での修正点、透明な子ウィンドウや High DPI 改善について

Japan C# Users Group の Visual Studio 2015 リリース記念勉強会 にて、.NET Framework 4.6 で WPF に加わった修正点について発表しました。 大きな変更はなく、細かい機能追加やバグ修正程度なのですが、2つ程ピックアップして紹介しています。

Transparent Child Window support

WPF の子ウィンドウが透過できるようになります。

そもそも子ウィンドウをどういうときに使うのか… というと、Win32 相互運用シナリオや、MDI を実現したいケースでしょうか。

Win32 相互運用、つまり WPF ウィンドウ内に HwndHost (WebBrowser や WindowsForms コントロール、Win32 要素など) を表示するとき、MSDN にも記述がある通り、様々な制約があります。 特に、「HwndHost は、同じトップレベル ウィンドウの他の WPF 要素の上に表示されます。」というのが曲者で、ZIndex 等を一切無視し、HwndHost が一番上に表示されるため、その上に WPF 要素を表示することはできません。

Browser (HwndHost) は必ず全要素の上に描画されてしまう

ウィンドウ ハンドルを 1 つしか持たない WPF アプリと、それぞれの要素がウィンドウ ハンドルを持つ GDI 等の旧来のテクノロジーは、まさに水と油のような関係であり、仕組み上仕方ないことではあります。 そこで、WebBrowser や過去の資産を HwndHost 上で表示し、さらにその上に WPF 要素を表示したいとき、独立したウィンドウ ハンドルを持つ子ウィンドウはその解決方法のひとつとなります (他には、System.Windows.Controls.Primitives.Popup などが使えたり)。

(ちなみに Win32 相互運用についてはいくらかノウハウを持っているのですが、個人的にこれ以降使う気がしないので、ブログにまとめていません… もし知りたい方がいらっしゃれば @Grabacr07 に圧力をかけまくるとそのうち書き始めるかもしれません…)

また、WPF は MDI をサポートしていません。 個人的には MDI は好みではない (自宅も職場もトリプル モニター、というマルチ モニター厨なので相性が悪い) ものの、もし実装する場合は、子ウィンドウが役に立つかもしれません。

子ウィンドウを作るコードの例を示します。 一般的な WPF アプリ開発において HwndSourceParameters を使って HwndSource を自作するようなシナリオは殆どなさそうなので、見慣れないかもしれません。

サンプル コード全体は後ほどアップデートするかも。

実行して「Create child」ボタンを押すと、このような MDI 子ウィンドウっぽいものが表示されます。 .NET Framework 4.5.2 またはそれ以前をターゲットにしているアプリでは、17 行目の Background で Alpha に 128 等を指定して透過させようとしても、透明になりません (黒くなるだけ)。

MDI のようなもの (非透過)

そこで、アプリケーションのターゲット フレームワークを .NET Framework 4.6 にした上で、HwndSourceParameters のオブジェクト初期化子に以下を追加します。 また、背景を Alpha に 128 を指定して子ウィンドウのルート要素を透過させてみます。

実行すると以下のように。 子ウィンドウが透過できているのが判るかと思います。

透過した子ウィンドウ

この HwndSourceParameters.UsesPerPixelTransparency プロパティが、.NET Framework 4.6 で追加されたメンバーです。 true を指定すると、上記のように WPF 子ウィンドウが透過できるようになります。

これは、WebBrowser 等の HwndSource に対しても、透過したうえでその上に表示することができます。 よって、先述の Win32 相互運用シナリオにおける HwndHost の前後関係の問題にも寄与できるでしょう。

注意しなければならないのが、この機能を使うためには、アプリケーション マニフェストで Windows 8 向けのビルドであることを明示する必要があるということです。

app.manifest を作ると、上記のようなコードが最初から記述されているはずです。 ここで、Windows 8 のコメントを外すだけです。

余談ですが、app.manifest での指定をせずに UsesPerPixelTransparency=”True” を使用すると、Win32Exception がスローされて表示できません。

High DPI Improvements

Hight DPI 環境下におけるレイアウトの丸め処理が改善されました。

元々どんな問題があったかというと、私の MetroRadiance がモロにその影響を受けていて、こちらの記事 で指摘して頂いています。 この場合のレイアウト システムの詳細な動作については把握できていませんが、1 dip (デバイス非依存ピクセル) を指定したとき、DPI 150 % 環境下で Round(1 * 1.5) = 2 になるはずが、1 になることがあるようです。

.NET Framework 4.6 では、このような BorderThickness や Margin のレイアウトの丸め処理が改善されており、ズバり上記のような問題が発生しなくなりました。 1 枚目が .NET Framework 4.5.2 での実行結果、2 枚目が 4.6 での実行結果です。

Bottom だけ 1px になってる。超ダサい。 すべて 2px に。キレイ。

この改善は、.NET Framework 4.6 をターゲットにしたアプリであれば、特にコードの修正等は必要なく、既定で適用されます (要はバグ修正的なノリなんですかね)。 また、.NET Framework 4.5.2 またはそれ以前をターゲットにしたアプリでも、.NET Framework 4.6 がインストールされている環境であれば、app.config に以下のようなコードを記述することで、この改善を受けることができます。

Switch.MS.Internal.DoNotApplyLayoutRoundingToMarginsAndBorderThickness (長ぇ)。

そして、High DPI に関しては、上記の改善のほかに “Multi-DPI setup” 環境下での改善も入っているようです (Per-Monitor DPI のこと?)。 ブラックアウトする問題が修正されているそうですが、そういう事象に遭遇したことがないので、よくわからず…。 調査中。

Visual Studio 2015 での新機能

WPF 開発で使える Visual Studio 2015 の新機能をちょこっと紹介。

Peek Definition と CodeLens はセッション資料にある通りです。 Live VisualTree と Live Property Explorer は、アプリ実行中に VisualTree の参照や各要素のプロパティ値の修正を可能にするものです。 Web 開発におけるブラウザーの開発者ツールのような感覚です。

実行中にガイドを表示しながら要素を選択して…

ガイドを表示しながら要素を選択(画像クリックで GIF アニメーション)

Live VisualTree で要素を選択したりデザイナー上に一発で呼びだしたり…

要素選んだりデザイナーで表示したり(画像クリックで GIF アニメーション)

Live Property Explorer で実行中にプロパティを書き替えたり。

プロパティを書き替え(画像クリックで GIF アニメーション)

すばらすぃ。 アプリのデバッグ効率が飛躍的に向上します。 まじ最強。

まとめ

という感じでした。 他にもタッチ サポートが改善されたりしているようですが、バグ修正っぽいのでスルーしています。

何か判ったことがあれば追記します。