Surface Studioと同時に発表されて何かと話題のSurface Dialですが、こいつのAPIはWindows 10 AUでこっそりと入っていたみたいです。 ということで簡単にですが使い方を書いてみようと思います。
何もしないという選択肢
まず、Surface Dialですが何もしなくてもデフォルトの挙動だけで乗り切るというのもあります。 その場合は、スクロールや拡大縮小(システムのメニューでカスタマイズ可能)などの操作ができるという感じになります。
ということで、何もしなくてもSurface Dialでアプリを動かすことはできます。
そうはいっても
そうはいってもオリジナルの挙動をさせたいというのがあると思います。
メニューを追加したい
Surface Dialを長押ししたときに出るメニューに自分の項目を追加することができます。
まず、Surface Dialを使うには、RadialController
クラスのインスタンスを取得する必要があります。
これはRadialController
クラスのCreateForCurrentView
というstatic
メソッドで作成可能です。
次にメニュー項目を作成します。
メニュー項目はRadialControllerMenuItem
クラスで、これもstatic
なファクトリメソッドが用意されてるので、それで作ることができます。
CreateFromKnownIcon
メソッドは名前の通りRadialControllerMenuKnownIcon
列挙体に定義されたアイコンを持ったものが作成できます。
RadialControllerMenuKnownIcon enumeration - Windows app development
RadialControllerMenuItem
クラスが出来たら、Invoked
イベントを購読することで選択されたときの処理を書くことができます。
コード例を以下に示します。
public sealed partial class MainPage : Page { private RadialController RadialController { get; set; } public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.RadialController = RadialController.CreateForCurrentView(); var item1 = RadialControllerMenuItem.CreateFromKnownIcon("Item1", RadialControllerMenuKnownIcon.InkColor); item1.Invoked += Item1_Invoked; this.RadialController.Menu.Items.Add(item1); } private async void Item1_Invoked(RadialControllerMenuItem sender, object args) { var dlg = new MessageDialog("Item1 invoked"); await dlg.ShowAsync(); } }
実行してItem1を選択すると以下のようになります。
RadialControllerMenuItem
はCreateFromIcon
メソッドを使うことでカスタムのアイコンを使うこともできます。
var item1 = RadialControllerMenuItem.CreateFromIcon("Item1", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/CustomIcon.png")));
押し込みを検出したい
メニューを選択すると、イベントが反応するようになります。
一番簡単なのが押し込まれたときのイベントです。
RadialController
クラスのButtonClicked
イベントを購読することで、そのメニュー選択時のクリック動作を処理することができます。
RadialController
クラスのMenu
プロパティからGetSelectedMenuItem
メソッドで現在選択されてるメニューが取得できるので、それを見るか、その中のTag
を見て
動作をカスタマイズするか、自分でInvoked
イベントで今ステータスがどういう状況にあるのか管理するフラグでも持たせておくのがいいと思います。
先ほどのコードにButtonClicked
を追加したものをいかに示します。
public sealed partial class MainPage : Page { private RadialController RadialController { get; set; } public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.RadialController = RadialController.CreateForCurrentView(); var item1 = RadialControllerMenuItem.CreateFromKnownIcon("Item1", RadialControllerMenuKnownIcon.InkColor); item1.Invoked += Item1_Invoked; this.RadialController.Menu.Items.Add(item1); this.RadialController.ButtonClicked += RadialController_ButtonClicked; } private async void RadialController_ButtonClicked(RadialController sender, RadialControllerButtonClickedEventArgs args) { var item = sender.Menu.GetSelectedMenuItem(); var dlg = new MessageDialog($"Selected menu is {item.DisplayText}"); await dlg.ShowAsync(); } private async void Item1_Invoked(RadialControllerMenuItem sender, object args) { var dlg = new MessageDialog("Item1 invoked"); await dlg.ShowAsync(); } }
実行して、Item1を選択した状態でSurface Dialを押し込むと以下のようにダイアログが出てきます。
回転の検出
次に回転の検出方法です。 Surface Dialは名前のとおりくるくる回ります。
どれくらい回ったらイベントは発火するかという値の設定がRotationResolutionInDegress
プロパティで設定できて、RotationChanged
イベントで回転時の処理が書けます。
イベント引数から、どれくらい回転したかという情報もとることができるので、それを見て処理をする形になると思います。
クリック時と同じように、今どんなメニューが選択されてる状態なのかというのもとることができます。
public sealed partial class MainPage : Page { private RadialController RadialController { get; set; } public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.RadialController = RadialController.CreateForCurrentView(); var item1 = RadialControllerMenuItem.CreateFromKnownIcon("Item1", RadialControllerMenuKnownIcon.InkColor); item1.Invoked += Item1_Invoked; this.RadialController.Menu.Items.Add(item1); this.RadialController.ButtonClicked += RadialController_ButtonClicked; // 回転 this.RadialController.RotationResolutionInDegrees = 10; this.RadialController.RotationChanged += RadialController_RotationChanged; } private void RadialController_RotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args) { Debug.WriteLine($"{args.RotationDeltaInDegrees} rotated"); } private async void RadialController_ButtonClicked(RadialController sender, RadialControllerButtonClickedEventArgs args) { var item = sender.Menu.GetSelectedMenuItem(); var dlg = new MessageDialog($"Selected menu is {item.DisplayText}"); await dlg.ShowAsync(); } private async void Item1_Invoked(RadialControllerMenuItem sender, object args) { var dlg = new MessageDialog("Item1 invoked"); await dlg.ShowAsync(); } }
実行するとデバッグウィンドウにこんな表示がされます。
10 rotated 10 rotated 10 rotated 10 rotated 10 rotated 10 rotated 10 rotated 10 rotated -10 rotated -10 rotated -10 rotated -10 rotated -10 rotated -10 rotated -10 rotated -10 rotated
プログラムからのメニューの設定
今まではユーザーがSurface Dialを長押ししてくるくると回してメニューを選ぶという操作をしていましたが、プログラムからメニューを設定することもできます。
これはMenu
プロパティのSelectMenuItem
メソッドにRadialControllerMenuItem
を渡すことでできます。
例えば、ボタンクリック時にメニューを切り替えるのは以下のようになります。
private void Button_Click(object sender, RoutedEventArgs e) { // プログラムからメニューの選択 this.RadialController.Menu.SelectMenuItem( this.RadialController.Menu.Items.First()); }
こうすることで、コンテキストに応じてメニューを動的にプログラムから切り替えるといったことができます。
未確認事項
Surface Dialの位置とか
Surface Dialをクリックしたときや回転したときのイベント引数にContract
というプロパティがあります。
これは現時点でnull
が入ってきてるのですが、Positionなどが取れるので、おそらく画面上にSurface Dialを置いたときの位置などが取れるのだと思います。
Surface Studioか、今後Bookなどでもファームアップデートで動くといいな。
Surface Dialを画面に置いたときの動作など
それっぽいイベントがあるのですが、上記の理由で試せていません。
まとめ
APIは単純で簡単に使えますが、マウスと競合するのでおそらくデモのようにペンと組み合わせて使うことになるデバイスだと思います。 アイデア勝負っぽいですね。