WPF のバインドで INotifyPropertyChanged の無いソースがメモリに残る仕組み
INotifyPropertyChanged の無いソースに OneWay・TwoWay でバインドした画面を閉じると、.NET 10 ではウィンドウは回収されるが、ソースは PropertyDescriptor の登録表のキーとして残り、登録が外れるまで回収されない。
WPF・XAML・C# および Windows アプリ開発に関する技術記事を掲載しています。
INotifyPropertyChanged の無いソースに OneWay・TwoWay でバインドした画面を閉じると、.NET 10 ではウィンドウは回収されるが、ソースは PropertyDescriptor の登録表のキーとして残り、登録が外れるまで回収されない。
ボタンから実行した AsyncRelayCommand の未捕捉の例外は既定で DispatcherUnhandledException に届き、FlowExceptionsToTaskScheduler 付きではビューモデルを残した計測で次の実行後の GC まで届かなかった。
ObservableValidator の必須チェックは起動直後の空欄にエラーを出さず、HasErrors で止めた保存ボタンも押せてしまう。HasErrors の False が未検証を含むことを実測で確かめ、ValidateAllProperties を呼ぶ場所を比べる。
Toolkit の RelayCommand と Prism の DelegateCommand は、InvalidateRequerySuggested を呼んでもボタンが有効にならない。理由を参照アセンブリとボタンの実測で示し、CanExecute を反映させる書き方を示す。
await の後で UI スレッドへ戻る async メソッドを UI スレッドで .Result で待つと、後続が UI スレッドの空きを待ち続けて画面が固まる。回避策の ConfigureAwait(false) と Task.Run も、書き方しだいで同じように固まる。
DispatcherUnhandledException で Handled を立てても、new Thread の例外でアプリが落ち、待たれない Task の例外はすぐにはどこにも届かない。届き先は例外を最初に受け止める層で決まる。16 の条件を .NET 10 で実測した。
WPF の DataGrid のセルを編集中のままツールバーやメニューの保存を押すと、入力前の値が保存される。実際のマウスとキーで測ると、セルの編集中に呼ぶ CommitEdit() でも行は確定しなかった。原因と、保存の前に行を確定する実装を示す。
Mode を書かずに TreeViewItem の IsExpanded や ColumnDefinition の Width をバインドすると、展開ボタンや GridSplitter の操作でバインドが黙って外れる。既定の向きと書き込みの経路を実測し、外れる条件と避け方を示す。
WPF の一覧の UI 仮想化は、置き場所・パネル・スクロールの設定のどれかで、例外も出さずに外れる。1,000 件を高さ 200 の領域に表示し、実体化したコンテナーの数で条件を実測した。手元で原因を切り分ける手順と、原因ごとの直し方を示す。
MaxLength・CharacterCasing・DisplayDateStart・IsSnapToTickEnabled などの入力制限は、コードやバインドから入る値を止めない。3 つの経路を .NET 10 で実測し、値の範囲をビューモデルで守る実装を示す。
Application.ThemeMode を Dark に切り替えても、一部のウィンドウや文字色が Light のまま残る。実測した原因は、ウィンドウ側やネストした Fluent 辞書、StaticResource、SystemColors のキーである。切り分けと対処を示す。
WPF の Fluent テーマを適用しても、独自の Style を当てたコントロールだけが旧外観に戻る。原因を Fluent テーマの供給方式から説明し、BasedOn による解決と、それでも効かない配置パターンを実測の対応表で整理する。
フィルタを設定した直後は正しいのに、項目のプロパティを変えても絞り込み結果が変わらない。ICollectionView が再評価を行う契機を .NET 10 の実測で切り分け、Refresh とライブフィルタを再評価コストと選択状態への影響から比較する。
ViewModel が正しい列挙体の値を保持しているのにラジオボタンが未選択になるのは、GroupName を省くと同じ親に置いた別々の列挙体のボタンが 1 グループに統合されるためである。原因を実測で示し、コンバーターを含む 4 方式の使い分けを整理する。
ウィンドウを閉じたのにタスクマネージャーにプロセスが残る。WPF の終了は「アプリケーションの終了」と「プロセスの終了」の 2 段階であり、どちらで止まったかで原因も対処も変わる。ShutdownMode と前景スレッドの切り分け手順を .NET 10 の実測で示す。
UserControl に依存関係プロパティを追加したのに、内部の {Binding Title} だけが空欄になる。DataContext の継承という原因を .NET 10 の実測で切り分け、RelativeSource・ElementName・内側ルートへの委譲を比較する。
検証コードは動いているのにエラーが画面に出ない。ValidatesOnDataErrors の既定値、AdornerLayer の不在、ソース更新のタイミングといった原因を .NET 10 の実測で切り分け、IDataErrorInfo を含む検証方式を比較する。
TreeView.SelectedItem は読み取り専用で代入もバインドもできない。選択の実体が TreeViewItem 側にある点を踏まえ、ItemContainerStyle と ItemContainerGenerator の 2 方式を実測して比較する。
DataTemplate 内でバインドしても親の ViewModel に届かないのは、DataContext がアイテムへ切り替わるためである。RelativeSource・ElementName・x:Reference・PlacementTarget の到達範囲を比較する。
Style のトリガーを書いても外観が変わらない原因の多くは、XAML の属性で直接指定したローカル値がスタイルトリガーより優先されることにある。値優先順位の仕組みと、Setter への移行・SetCurrentValue・ClearValue による解決方法を整理する。
BitmapImage で表示した画像ファイルがロックされ、削除・上書きできなくなる。原因である既定のキャッシュ動作と、BitmapCacheOption.OnLoad・StreamSource による解決方法を、読み込み方ごとの実測結果とともに整理する。
既定の文字列ソートでは item10 が item2 より前に並ぶ。Win32 API の StrCmpLogicalW を P/Invoke で呼び出して IComparer を実装し、エクスプローラーに近い自然順ソートを実現する方法を、利点と欠点とともに解説する。
自作 RelayCommand の CanExecute を変えてもボタンが更新されないのは CanExecuteChanged が発火されないため。CommandManager.RequerySuggested への委譲と手動発火の使い分けを整理する。
TextBox の GetBindingExpression().UpdateSource() を View から呼ぶ際に起きる null・一括更新・方向の取り違え・MVVM 設計の落とし穴を整理し、BindingGroup と添付ビヘイビアによる一括確定の実装を示す。
TextBox.Text の既定が LostFocus であるために起きる「入力が ViewModel に届かない」問題を、UpdateSourceTrigger の三値の使い分けと、IME 入力・検証タイミング・フォーカスを奪わないボタンといった落とし穴から整理する。
バインド中の ObservableCollection を別スレッドから変更すると NotSupportedException が発生する。原因のスレッドアフィニティと、EnableCollectionSynchronization・Dispatcher による解決策を整理する。
Fluent テーマの TextBox がフォーカス時に表示するクリアボタンを、入力挙動を変えずに非表示化する。名前付きパーツを操作する方法と AcceptsReturn を使う方法を .NET 10・9 対応で整理し、選択の分岐点を比較で示す。
縦方向の StackPanel に置いた ScrollViewer がスクロールしないのは、StackPanel が高さを制約せずに測定するためである。原因と、Grid・DockPanel・MaxHeight による解決方法と選択基準を解説する。
コンバーターを書かずに Binding.StringFormat で数値・通貨・日付を書式化する方法を整理する。既定のカルチャが en-US になる問題、ContentControl での制約、MultiBinding による複数値の結合までを扱う。
キーごとの集計で GroupBy が抱える中間グルーピングの割り当てコストを整理し、.NET 9 の CountBy・AggregateBy・Index を辞書への直接集計で .NET Framework に実装する方法と、GroupBy 委譲実装との挙動差を解説する。
SQL の外部結合に当たる .NET 10 の LeftJoin・RightJoin と Shuffle を .NET Framework に実装し、GroupJoin との対応、擬似シャッフルとの違い、IQueryable での落とし穴を解説する。
WPF の Binding が動かないとき、Visual Studio の出力ウィンドウにはバインディングエラーが記録される。エラーメッセージの構造の読み方、PresentationTraceSources によるトレースの詳細化、エラーパターン別の対処を整理する。
.NET 8 の ToDictionary 新オーバーロード(KeyValuePair 版・タプル版)を .NET Framework で再現する際に問われる、既存オーバーロードとの共存・名前付き引数による解決先の固定・notnull 制約の一致というシグネチャ設計を解説する。
既存の OrderBy への委譲だけで完結する Order・OrderDescending のポリフィルを題材に、戻り値型 IOrderedEnumerable が決める ThenBy 互換性と、環境によって異なるソート時例外という 2 つの互換性論点を解説する。
.NET Framework で Chunk・MaxBy・MinBy・DistinctBy を代用する GroupBy・全件ソートの回避イディオムを実行コストの面から検証し、計算量を改善するポリフィルの実装と、バージョン別シンボルによる移行ガードの選び方を解説する。
LINQ ポリフィル自作時の 3 つの設計原則(引数検証とイテレータの分離・バッファリングの最小化・条件付きコンパイルによる移行ガード)を、.NET Core 期に追加された Append・Prepend・TakeLast・SkipLast の実装を題材に解説する。
WPF DataGrid のソート状態は SortDescriptions と SortDirection の 2 か所にあり、片方だけ戻すと食い違う。明示クリア・Sorting イベント・ICollectionView・Behavior の 4 つの初期化方法を比較する。
.NET Framework 環境では ??= などの C# 新構文が使用できない場合がある。各バージョンで追加された演算子と初期化の糖衣構文を、net48 に対して実際にコンパイルした結果とともに整理する。BCL 型を要する構文と、その補い方も実測で示す。
C# 14 の extension ブロックで Directory のような静的クラスを対象にすると、静的メンバーは追加できるがインスタンスメンバーは CS0721 / CS9303 で拒否される。レシーバーの書き方による境界と、インスタンス形式で呼ぶ代替策を整理する。
StaticResource は XAML ロード時に参照先を確定するため、実行時にリソースを差し替えても参照側に反映されない。差し替えに追随させるには DynamicResource を使う。両者の評価タイミングの違いと、OS 設定との連動を含む使い分けの基準を解説する。
WPF で Label を大量配置した際に描画が遅くなる原因を、visual ツリーの実測に基づいて整理する。TextBlock との差、アンダーバーを含む文字列が招く追加コスト、仮想化を有効にした場合に差が残るか、改善をどの順で行うかまでを扱う。
WPF の Label にアンダーバー(_)を含む文字列を表示すると画面上で消える。原因である ContentPresenter.RecognizesAccessKey の働きと、影響を受けるコントロールの範囲、4 つの回避方法を実測付きで解説する。
DataGridTemplateColumn の CellTemplate と CellEditingTemplate を分けて定義し、WPF DataGrid のセルで表示時と編集中に異なるコントロールを使う方法を解説する。標準列で足りる場合との判断基準も整理する。
TreePaste の開発で発生した、タスクトレイから表示した WPF の ContextMenu がフォーカス移動後も閉じない問題について、StaysOpen=False と表示直前の SetForegroundWindow の併用で解消する方法と、その原因を整理する。
追加ライブラリなしで WPF に Fluent の外観を適用する方法。App.xaml でのテーマの設定、ライトとダークに追従する Fluent テーマのブラシ、SystemColors がダークモードに追従しないことを、実測した図と表で示す。
TextBlock は部分選択やコピーを前提としていない。WPF で表示専用のテキストを編集不可のまま選択・コピー可能にするため、TextBox を IsReadOnly で使い、キャレットと枠線を抑えて TextBlock と同じ見た目に整える方法を解説する。
ItemsSource に渡すデータ構造によって、DisplayMemberPath・SelectedItem・SelectedValue の設定方法が変わる。文字列リスト・オブジェクトリスト・Enum などを扱う 5 つの実装パターンと選択の分岐点を整理する。
ListBox の仮想化有効時に選択状態が維持されない理由と、各アイテムの IsSelected で MVVM で安定させる方法を解説する。ItemContainerStyle のバインドだけでは選択が失われる条件と、SelectionChanged を併用した対処を実測で示す。
WPF DataGrid の列ヘッダーによる標準のソートに加え、コードからのソート制御と ListCollectionView.CustomSort による独自ロジックの並び替えを実装例とともに解説し、用途ごとの推奨アプローチを表で整理する。
WPF の DatePicker の日付を yyyy/MM/dd などの固定形式で表示する方法。DatePickerTextBox のスタイル、XAML での区切り文字のエスケープ、コードビハインドで Text を書き換えても効かない理由、コンバーターの使いどころを実測で示す。