WPF のバインドで INotifyPropertyChanged の無いソースがメモリに残る仕組み
INotifyPropertyChanged の無いソースに OneWay・TwoWay でバインドした画面を閉じると、.NET 10 ではウィンドウは回収されるが、ソースは PropertyDescriptor の登録表のキーとして残り、登録が外れるまで回収されない。
WPF・XAML に関する記事の一覧です。 データバインディング、コントロール、スタイル、仮想化、UI の挙動を扱います。 いずれも 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 による解決方法を、読み込み方ごとの実測結果とともに整理する。
自作 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 による複数値の結合までを扱う。
WPF の Binding が動かないとき、Visual Studio の出力ウィンドウにはバインディングエラーが記録される。エラーメッセージの構造の読み方、PresentationTraceSources によるトレースの詳細化、エラーパターン別の対処を整理する。
WPF DataGrid のソート状態は SortDescriptions と SortDirection の 2 か所にあり、片方だけ戻すと食い違う。明示クリア・Sorting イベント・ICollectionView・Behavior の 4 つの初期化方法を比較する。
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 を書き換えても効かない理由、コンバーターの使いどころを実測で示す。