Toolkitトラップ

/ Silverlight, Windows Phone

Windows Phoneアプリ開発には半ば必須となっているSilverlight Toolkit
いつの間にかPhone用みたいな扱いになっていますが、元はPC用Silverlightの拡張ライブラリなわけで、Silverlight2の頃から利用してたりします。
そういう意味ではSilverlightベースの開発全般において必須なライブラリなわけですが、WPアプリ開発を進めていく中でいくつかの細かい不具合に遭遇したのでメモっておきます。

  • スライダーコントロールの動きがガクガクになる

    付箋紙アプリでも文字サイズの調整などに使っているSliderコントロールですが、何かの拍子にスムーズにスライドできなくなる現象に遭遇。
    色々と調べてみると、ピクチャー取込用のページで画像の移動やリサイズをジェスチャーで行うために使っていたGestureListnerのインスタンスを生成すると挙動不審になることが判明。
    仕方ないので、Manipulation系のイベントを拾って凌ぐことにしたわけですが、なにぶんグラフィックの扱いには慣れておらず納得のいく動きになるまでエラい時間がかかりました。

    さらに、先日ちょっとした機能追加をしていたらば、またもやスライダーが挙動不審に…
    これまた色々といじった部分を調べたところ、どうやらContextMenuが悪さをしている様子。
    試しに、元々パノラマのページに入れてあるContextMenuを開いてからスライダーを操作してみると、バッチリ再現。
    Toolkit付属のサンプルアプリでも再現しちゃいます。
    仕方ないので、機能追加する箇所ではContextMenuを使わないようにしましたが、パノラマページでのContextMenu廃止はちょっぴり難アリと思い、ひとまず放置。
    ContextMenuで出来る操作は編集ページに入ってからも可能なので、付箋紙アプリユーザの方は極力ContextMenuを使わないことをオススメしておきます…
    何とか簡単に回避する術はないものかとContextMenuコントロールのソースも覗いてみましたが、スキル不足のためあっさり断念。Toolkitのバージョンアップ待ちですわw

  • LockablePivotを使うとイメージ描画が破壊される

    こちらも付箋紙アプリの改修に伴って発見したのですが、Lockable PivotというPivotの派生コントロールがToolkitに含まれているのですが、標準のPivotコントロールをLockable Pivotに置き換えたところ、編集ページで付箋の完成形画像がうまく取得できないという事態が発生。
    具体的にはGridに積み重ねたUIElement群をWriteableBitmapでJPEG化しているのですが、いったんピボットをロックしてしまうと非アクティブなPivotItemに含まれるUIElementをうまく画像化できない、といった症状。
    全く何も出力されないわけではなく、背景色がなくなったり、テキストのスケールがおかしくなったりといった感じに画像化される感じ。
    該当のPivotItemを再度表示してあければ回復するのですが、付箋紙の保存処理はどのピボットアイテムがアクティブであっても可能なため、このままでは使えません。
    仕方ないので、LockablePivotのソースを紐解いてみたところ、ロック処理というのはPivot内のアイテムをアクティブなもの以外全てバッファ保持してから削除し、アンロック時に元に戻す、という割と大胆な手法を使っているようで、結果的に非表示アイテムの描画処理に影響を及ぼしている雰囲気。
    何故LockablePivotを使いたかったのかというと、テキスト入力時にフリック操作をするとピボットアイテムが切り替わってしまうという問題があったためで、逆に言うとピボットページにTextBoxが配置してある場合はすべからくLockablePivotを使うべきという話だったりするのですが、今回はこれについても対応を断念、放置ですorz
    付箋紙アプリでやろうとしていることがそれなりに特殊だったのでこの件はまともに影響を受けましたが、一般的な利用においてはそれほど問題にはならないかもしれません。
    普通に使う場合は、TextBoxのGotFoucsイベントでロック、LostFocusイベントでアンロック、という感じでいけると思います。

  • 以上、まだ使っているToolkitコントロールがあまりないので他にも色々あるかもしれませんが、おかしな挙動が発生した際に問題の切り分けを素早く行うために、どのコントロールをどのタイミングで追加したのか、きっちり把握しながら進めるのが吉かと思われ。


    編集可能なDataGridのテンプレ

    / C#, Silverlight

    DataGridコントロールの復習がてら、簡易タイムレコーダー「Azume」に旅費交通費の入力/印刷機能を追加してみました。

    表示のみ(ReadOnly)で利用する場合は比較的簡単に扱えるDataGridですが、編集可能にしようと思うと色々と面倒なことになるのがいつものパターン。
    ありがちなのが「編集用セルとしてComboBoxコントロールを使うパターン」で、これまでにも何度かトライしているのですが、これといった定番の実装パターンができていなかったので、少し整理してみました。

    ■データソース定義

    public class ExpensesEntity : BaseEntity
    {
    	public DateTime Date { get; set; }
    }
    
    public class DateItemsSoruce
    {
    	public DateTime Date { get; set; }
    	public string DisplayDate
    	{
    		get
    		{
    			return Date.ToString("M/d (ddd)");
    		}
    	}
    }
    
    // (説明用に値は直接放り込んでいます…)
    
    // DataGridのItemsSourceに設定
    public ObservableCollection<ExpensesEntity> listExpenses =
    	new ObservableCollection<ExpensesEntity>(
    		new ExpensesEntity { Date = DateTime.Parse("2011/08/10") },
    		new ExpensesEntity { Date = DateTime.Parse("2011/08/11") },
    	);
    
    // DateComboBoxのItemsSourceに設定
    public ObservableCollection<DateItemsSource> listDate =
    	new ObservableCollection<DateItemsSource>(
    		new DateItemsSource { Date = DateTime.Parse("2011/08/01") },
    		// …
    		new DateItemsSource { Date = DateTime.Parse("2011/08/31") },
    	);
    

    ■XAML定義

    <UserControl.Resources>
    	<DataTemplate x:Key="DateTemplate">
    		<TextBlock Text="{Binding Date, StringFormat='M/d (ddd)', ConverterCulture=ja-JP}"/>
    	</DataTemplate>
    	<!-- HACK - SelectedItem、SelectedValueのバインディング定義はXAML上では行わない -->
    	<DataTemplate x:Key="DateEditTemplate">
    		<ComboBox DisplayMemberPath="DisplayDate" SelectedValuePath="Date"/>
    	</DataTemplate>
    <UserControl.Resources>
    
    <sdk:DataGrid x:Name="dgData" AutoGenerateColumns="False"
    	PreparingCellForEdit="dgData_PreparingCellForEdit"
    	CellEditEnding="dgData_CellEditEnding">
    	<sdk:DataGrid.Columns>
    		<sdk:DataGridTemplateColumn Header="日付"
    			CellTemplate="{StaticResource DateTemplate}"
    			CellEditingTemplate="{StaticResource DateEditTemplate}"/>
    	</sdk:DataGrid.Columns>
    </sdk:DataGrid>
    

    ■コードビハインド

    public EntryExpenses()
    {
    	InitializeComponent();
    
    	//データグリッドのItemsSource設定
    	dgData.ItemsSource = listExpenses
    }
    
    private void dgData_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
    {
    	//HACK - ComboBoxのItemsSource、Binding設定はこのタイミングで実施
    	if (e.Column.Header == "日付")
    	{
    		var cbo = e.EditingElement as ComboBox;
    
    		cbo.ItemsSource = listDate;
    
    		var bind = new System.Windows.Data.Binding("Date");
    		bind.Mode = System.Windows.Data.BindingMode.TwoWay;
    		cbo.SetBinding(ComboBox.SelectedValueProperty, bind);
    	}
    }
    
    private void dgData_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
    	if (e.Column.Header == "xxxxx")
    	{
    		//TODO - セル編集終了時処理を記述
    		//
    		//	編集前後の値を取得できるので比較処理が可能
    		//
    		//	編集前の値: EditingElement.DataContext
    		//	編集後の値: コントロール値(EditingElement)
    	}
    }
    

    長々と書いてしまいましたが、ポイントとなるのは「ComboBoxのItemsSourceプロパティへのデータソース設定、SelectedValueプロパティへのバインディング設定は、XAMLではなく、PreparingCellForEditイベントで逐一実施する」という点。
    どうもComboBoxコントロールのバグの気配がするのですが、当初XAML上に記述したところ、どうにもマトモに動いてくれず、あれこれいじった挙句に安定してくれたのが上記コード。
    ちなみに、値のバインド先にSelectedItemプロパティを使っているサンプルも多く見かけましたが、SelectedValuePathを設定するケースにおいてはSelectedValueプロパティのほうが具合がよろしいようです。
    その他はとりたてて注意する箇所はなさげで、データソース側にValidation定義を施しておけば簡単な入力検証はコードレスでいけるので、うまく型にハメることさえできればスピーディに開発できるであろうことは周知の通りです。

    まぁ、分かってしまえばコードを書くのはどうってことないのですが、デザイン周りをカスタマイズしようとなると、DataGrid辺りの複雑なコントロールはあちこちいじらないといけないので、かなりヘビー。
    一応Blendは使っているものの、既定の追加テンプレートはVisualStateごとの細かい設定が大量に施されているので、大胆にいじくり回すにはかなり勇気がいる感じ。


    電力使用率ガジェット(v1.1.2)

    / Silverlight

    どうにも日跨りがうまくいっていないようで、またもや修正。
    日中は普通に動いているし、特に落ちるわけでもなく、2時くらいまで進めば正常モードに戻るので、まぁ気にしなければどうってことないレベルなのですが、こんな小さいプログラムでバグったままというのも情けないので一応直しておきます。
    使っていただいている奇特な方で深夜の挙動が気になる方はアップデートしてみてください。

    ついでにその他の背景色もキャプってみたので貼っておきますね。


    画像をクリックするとダウンロードできます

    とりあえず、このバージョンでLive Galleryのアップデート申請しとこうと思います。


    電力使用率ガジェット(v1.1.1)

    / Silverlight

    やはり少々バグってました…orz
    またもや日跨りの処理を中心に諸々腐ってた次第。
    1時過ぎまでは一時的にデータ更新が止まるようなので、その間は前日データを見せるようにしました。旧版で入れてたロジックを端折ったのがマズかったようで…
    ということで、ひとまず修正版に差し替えておきます。


    画像をクリックするとダウンロードできます

    ちなみに、指定日付のデータに未来時刻まで付いてくるために旧版が正常動作しない、という件は一時的な現象だったようで、今現在は未来時刻のデータが混ざることもなくなっていて、Live Galleryに上がっている旧版(v1.0.1)も正常に動作している模様。
    なので、Live Galleryのほうはもう一晩様子を見てからアップデート申請しようと思います。


    電力使用率ガジェット(v1.1.0)

    / Silverlight

    7/1に東電がCSVデータの形式を変更したことに伴って、東京電力電力供給状況APIさんの返却値が変わってしまったようで、指定日のデータに未来時刻のデータまでくっついてくるようになったため、マトモに表示されなくなってしまったので、急遽ガジェットのアップデートを実施しました。


    画像をクリックするとダウンロードできます

    変更点は以下の通りです。

    • 5分毎に更新される最新状況の表示に対応しました。(当日分データの取得は30分毎に変更)
    • 当日ピーク値のツールチップ表示を追加しました。画面下部の需給表示のテキストにマウスカーソルを持っていくと表示されます。
    • 画面デザインを微変更しました。(テキスト色を若干明るくして、背景に反射っぽい影を付けただけ)

    久しぶりにいじったのでバグってる可能性は否めません…
    数日使ってみておかしいようなら直します。
    Live Galleryにアップしているほうは数日様子をみてからにするつもり。

    それよりも、旧版でときどき過去時刻のデータが最新として表示される症状が出ていたのが気になっているのですが、原因不明で今のところ放置プレイ…
    とりあえず、エラーを含め、表示がおかしくなった場合は、一度設定ダイアログを出してOKボタンを押してみてください。
    データの再取得を含め、表示がリセットされます。