Q128. IE ベースでない Browser コントロールは存在しないか
A.昨日のエントリでは WPF のWebBrowserコントロールについて書きました。これ IE7という非常に旧いバージョンをベースにしてるので、ページによってはレイアウトが大きく崩れます。それを回避するのがレジストリ編集な訳なんですが、標準コントロールにも関わらずそこまでしなきゃいけないのは、なんとも気持ち悪すぎます。何とかならんかということで、WebBrowser 以外の Browserコントロールを探したところ、一つ面白いのが見つかりました。
DotNetBrowser というChromium ベースのBrowserコントロールが TeamDev 社から提供されてたので、早速これを試してみたいと思います。
続きを読むQ127. WebBrowser コントロールの IE のバージョンを最新にしたい
A.WPF の WebBrowser コントロールのIEのバージョンが 7 なのは有名なようですね。現状ではレジストリを編集するしか手はないようです。以下の記事では、レジストリの編集方法を紹介されてます。
関連記事:WebBrowser コントロールで使われている Internet Explorerを最新のバージョンに変更する
関連記事:WPFのWebBrowserで使用されるIEのバージョンを最新にする
しかしレジストリをいちいち編集するのは気が重いし気持ち悪い。もうちっと何とかならんものかねぇ。。。
Q124. XamDataGrid で絞り込み表示されてるレコードのみ抽出する(其の弐)
A.以前のエントリで、XamDataGrid で絞り込み表示されてるレコードのみ抽出する方法を紹介しましたが、これ一つ落とし穴がありまして、要素の描画タイミングによるのか、正常にレコードを取得できない場合があったりします。
関連記事:Q124. XamDataGrid で絞り込み表示されてるレコードのみ抽出する
例えば、XamDataGrid でフィルタリング実行時のイベント内で、絞り込まれたレコードのみ抽出したい場合、こんなコードになるかと思います。
レコード数やフィールドの少ない簡単な画面ならこれでも問題ありません。ところがレコード数が多かったり、フィールドが多いと、実際の表示と返されるレコードが異なる場合があることが判明しました。恐ろしいですね(汗
Dispatcher を使ってみる
この現象、どうやらレコードやフィールドが多すぎるか、ビューが複雑だと、フィルタリングと要素の描画更新のタイミングがずれるため発生するように思えます。そこで Dispatcher を使い、要素の描画とレコードの取得を同期するようタイミングを調整してみました。
これで、表示されてるレコード数とプログラム内で処理するレコード数の同期は取れました、ただし、状況により恐ろしい事態が発生します。フィルターのテキストボックスに文字列打ち込んでると、たまに
別のユニットが既に開いている間は、undo ユニットを再度開くことはできません。
と、InvalidOperationException 例外を吐いて、アプリケーションがクラッシュします。.NET Framework のソースコード追っかけると、UndoManager の Reopen メソッドで例外起こしてるのですが、恐ろしいですね。危険極まりないので、やみくもに Dispatcher を使うのは止めましょうw
RecordFilter.ApplyPendingFilter メソッド
そこで散々調べた結果*1、RecordFilter.ApplyPendingFilter メソッドを使えば、安全にフィルターの変更をビューに適用できることが判りました。以下、修正したコードです。
RecordFilter.ApplyPendingFilter の解説を読むと、
フィルターの保留中の変更を適用します。
としか書いてないしネットの情報も殆どないので、何のこっちゃ?と思ってたのですが、今回初めて使ってみました。果たしてこれが本来の使用方法かどうか怪しいのですが、とりあえず正常動作してるので、まあ良しといたしましょう(汗
Q126. XamDockManager のレイアウトを保存・読み込み・初期化したい
A.XamDockManager はペインを一元管理できるコントロールでして、ペインのドッキングやフローティング、タブや左右・上下の配置などを自在に配置可能なコンテナコントロールです。Visual Studio のIDE をイメージすれば判りやすいと思います。

ユーザーがペインを自由に配置できるとあれば、当然レイアウトを保存・読み込みしたいというニーズも出てきますね。XamDockManager ではレイアウトの保存・読み込みに LoadLayout・SaveLauout・というメソッドが用意されてます。
サンプルコード
以下、簡単なサンプルを用意しました。XamDockManager のレイアウトの保存・読み込み・初期化するサンプルです。以下の画面は上の画面と同一のものですが、クライアントがレイアウトを変更したものになります。

まずビューです。
お次はコードビハインドです。LoadLayout・SaveLayout の両メソッドで簡単に設定できるのが理解できると思います。レイアウトの状態をファイルに保存するには、オーバーロードされたメソッド(ストリームをパラメータで扱うバージョン)を使えば、簡単に保存できます。
初期化に関しては XamDockManager 側でメソッドが用意されてないため、ContentRendered イベントで要素が初期化されたタイミングを見て、SaveLayout メソッドで初期状態を保存。初期化ボタンクリック時に LoadLayout でXML文字列を読み込んでます。
Q110. XamDataGrid でユーザーが設定したレイアウトを保存したい(其の参)
A.以前、XamDataGrid でユーザーが操作した列の移動や並び替え、グループ化やソートを次回起動時用に保存したい場合、SaveCustomizations メソッドやLoadCustomizations メソッドを使えば、レイアウトの保存・読み込みが可能だという記事を公開しました。
関連記事:Q110. XamDataGrid でユーザーが設定したレイアウトを保存したい
しかし記事の最後に書いたとおり、ClearCustomizations メソッドでフィールドを初期状態に戻そうとすると
- フィールドの表示状態を初期化できない
- フィールドの固定がリセットされてしまう
という問題が発生するため、昨日のエントリでは、その対処としてコンストラクタでフィールドの状態を保存し、ClearCustomizationsメソッド実行後、LoadCustomizations メソッドで表示と固定列を初期化する方法を紹介しました。以下、その対処法の記事になります。
関連記事:Q110. XamDataGrid でユーザーが設定したレイアウトを保存したい(其の弐)
カスタムXamDataGrid
上記で一見解決できたように思えましたが、複雑なUIに配置されたグリッドや、TabItem に配置され起動時に非表示になってるグリッドは、コンストラクタや 画面のContentRendered イベント、いや XamDataGrid のOnRender でもフィールドが生成されてない場合があり、列情報を取得できないことがあることが判りました。
結局、DataPresenterBase.FieldLayoutInitialized イベントの通知後でないと、フィールドの初期状態を正確に取得することはできない模様です。
そこで Window のコードビハインドでいちいちイベント書くのも煩わしいため、XamDataGrid をカスタマイズした方が早いという結論に達しました。それがこちら。
コントロールの継承って好きじゃないですが、今回のケースでは止むを得ません。以上、何かの参考になりましたら幸いです。
Q110. XamDataGrid でユーザーが設定したレイアウトを保存したい(其の弐)
A.以前、XamDataGrid でユーザーが操作した列の移動や並び替え、グループ化やソートを次回起動時用に保存したい場合、XamDataGrid.SaveCustomizations メソッドやXamDataGrid.LoadCustomizations メソッドを使えば、レイアウトの保存・読み込みが可能だという記事を公開しました。
関連記事:Q110. XamDataGrid でユーザーが設定したレイアウトを保存したい
しかしこれ、ひとつ問題がありまして、記事の最後に書きましたが
なお上記一連のメソッドの問題点ですが、FieldLayoutSettings で HeaderPrefixAreaDisplayMode プロパティを FieldChooserButton に設定しフィールドの表示項目を選択できるようにした場合、フィールドの表示/非表示の設定を保存したり、非表示にしたフィールドを ClearCustomizations メソッドで復帰することはできません。現状、表示状態の保存は独自に実装するしかないので、この辺りもう少し何とかして欲しいところです。
また、ClearCustomizations メソッドで FieldPosition を初期化すると、FixedLocation=FixedToNearEdge にしたフィールドが FixedLocation=None になってしまうのも問題です。
という悩ましい問題が存在します。
これ、いったいどういう処理を行ってるのか、幸い Infragistics WPF はソースコードが公開されてる*1ので読んでみました。DataPresenterBase.ClearCustomizations → CustomizationsManager.ClearCustomizations → FieldLayout.ClearCustomizations と遡ってコードを追ってくと、大元の FieldLayout.ClearCustomizations で Visiblity プロパティを初期化してないため、どうにもならんという結論がでました。*2
だめだこりゃ\(^o^)/*3
FieldChooser 対応版
そこで私は考えた。画面初期化時にField の情報をメンバ変数に保持し、いったん LoadCustomizations で読み込んでから ClearCustomizations メソッドを実行すればいいのではと・・・で、以下サンプルです。

まずビュー。
お次はコードビハインドです。Reset_Click メソッドに注目してください。
LoadCustomizations で十分では?と思う向きもおられるかもしれません。しかし列の移動や段組が初期化されないため、まず全プロパティを初期化した後、表示と列固定を初期状態に戻します。これで前回の宿題は解決しましたが、これに気づくまで3年かかりましたw
Q125. コントロールの Visiblity プロパティに bool 値をバインドしたい
A.いまさら感がひしひし漂うネタですが、まあコンセプトが周回遅れブログのため、その辺りは気にせずスルーでお願いできればと思います。
コントロールの Visiblity プロパティに、ビューモデル側の bool 型プロパティをバインドさせたいシーンは多いと思います。Visiblity プロパティの型は System.Windows.Visibilityのため、bool と互換性はありません。こういう具合に型が異なるもの同士をバインドしたいときに用意するのがいわゆるコンバーターです。
参考記事:WPFのデータ変換
殆どのケースでは、上の記事のように自作しなければならないのですが、Visibility と bool 値のバインドでは、標準で用意されているコンバーターが存在します。それが BooleanToVisibilityConverter です。
サンプルコード
以下、簡単なサンプルを考えてみました。チェックボックスをONにすると、テキストを表示するプログラムです。

まずビューモデルです。ちなみにMVVMフレームワークは Livet を使ってます。
お次はビュー。TextBlock の Visibility プロパティとビューモデルの IsChecked プロパティを BooleanToVisibilityConverter を使ってバインドしてます。
上のコードでは少々XAMLが冗長になってしまうので、通常コンバーターを使う場合、リソースに定義して使いまわします。以下、リソースを使ったサンプルです。アプリケーション全体で使用するなら App.xaml (VB なら Application.xaml) に定義しておくといいでしょう。
System.Windows.Controls 空間には他にも幾つかコンバーターが用意されてますが、使いどころが微妙なコンバーターばかりですね。よくありがちな数値とテキストの変換などは標準のコンバーターが用意されてないので、自前のコンバーターを用意する必要があります。
