はい、プログラムで機能を有効/無効にする従来のアプローチがありますが、WPF は、WinForms や古いテクノロジでは実際には不可能だったいくつかの新しい可能性も開きます。
これを行うための 4 つの WPF 固有の方法について説明します。
Rectangle と VisualBrush を使用して、ウィンドウのコンテンツをそのコンテンツの画像に密かに自動的に置き換えることができるため、効果的に無効にすることができます。ユーザーにはウィンドウが変更されていないように見えますが、実際のコンテンツは画像の下にあるため、ヒット テストに使用したり、選択したイベントをウィンドウに転送したりすることもできます。
ウィンドウの ResourceDictionary に MergedDictionary を追加して、すべての TextBoxes を TextBlocks にしたり、すべての Buttons を無効にしたりすることができます。したがって、すべての UI をループして選択的に有効/無効にする代わりに、MergedDictionaries コレクションからオブジェクトを追加または削除するだけです。
InputManager を使用して、無効化されたウィンドウの特定の部分で実際のマウス イベントをプログラムで生成および処理し、「承認済み」のものへのヒット テストを行わないマウス イベントを禁止できます。
データ バインディングとスタイルを使用して、個々のコントロールを繰り返し処理するのではなく、有効/無効にします。
ウィンドウをウィンドウの写真に置き換える詳細
このソリューションでは、アプリ ウィンドウを反復処理し、各コンテンツを次のように元のコンテンツと Rectangle を含むグリッドに置き換えます。
<Window ...>
<Grid>
<ContentPresenter x:Name="OriginalContent" />
<Rectangle>
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=OriginalContent}" />
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
これは、プログラムによって、またはウィンドウでテンプレートを使用して行うことができますが、私の好みは、カスタム コントロールを作成し、そのテンプレートを使用して上記の構造を作成することです。これが完了したら、ウィンドウを次のように簡単にコーディングできます。
<Window ...>
<my:SelectiveDisabler>
<Grid x:Name="LayoutRoot"> ... </Grid> <!-- Original content -->
</my:SelectiveDisabler>
</Window>
マウス イベント ハンドラーを Rectangle に追加VisualTreeHelper.HitTest
し、ContentPresenter を呼び出して、元のコンテンツでクリックされたオブジェクトを特定します。この時点から、マウス イベントを無視するか、処理のために元のコンテンツに転送するか、またはスポイト コントロールまたはオブジェクト選択機能の場合は、目的のオブジェクト/情報を単に抽出するかを選択できます。
MergedDictionary アプローチの詳細
明らかに、ウィンドウのリソースにマージされた ResourceDictionary を使用して、UI 全体のスタイルを変更できます。
これを行う単純な方法は、マージされた ResourceDictionary に暗黙的なスタイルを作成して、すべての TextBoxes を TextBlocks として表示し、すべての Button を Borders として表示するなどです。独自のスタイルまたは ControlTemplate が明示的に設定された TextBox は更新を逃します。さらに、必要に応じてすべてのオブジェクトを取得できない可能性があり、ボタンから Commands または Click イベントを簡単に削除する方法はありません。これらは明示的に指定されており、スタイルがそれをオーバーライドしないためです。
これを行うためのより良い方法は、マージされた ResourceDictionary のスタイルで添付プロパティを設定し、PropertyChangedCallback でコード ビハインドを使用して、本当に変更したいプロパティを更新することです。添付された「ModalMode」プロパティが true に設定されている場合、多くのプロパティ (Template、Command、Click、IsEnabled など) のすべてのローカル値とバインディングがオブジェクトのプライベート DependencyProperty に保存され、これらが標準値で上書きされます。 . たとえば、ボタンの Command プロパティは一時的に null に設定されます。添付された「ModalMode」プロパティが false になると、すべての元のローカル値とバインディングが一時ストレージからコピーされ、一時ストレージがクリアされます。
このメソッドは、別の添付プロパティ「IgnoreModalMode」を追加するだけで、UI の一部を選択的に有効/無効にする便利な方法を提供します。ModalMode の変更を適用したくない UIElements では、これを手動で True に設定できます。ModalMode PropertyChangedCallback はこれをチェックし、true の場合は何もしません。
InputManager アプローチの詳細
マウスをキャプチャすると、どこに移動してもマウス座標を取得できます。CompositionTarget.TransformToDevice() を使用してこれらを画面座標に変換してから、各候補ウィンドウで CompositionTarget.TransformFromDevice() を使用します。マウス座標が範囲内にある場合は、無効なウィンドウのヒット テストを行い (これは、ウィンドウが無効になっていても実行できます)、ユーザーがクリックしたオブジェクトが気に入った場合は、InputManager.ProcesInput を使用してマウス イベントを発生させます。無効になっていない場合とまったく同じように、他のウィンドウで処理されます。
データバインディングの使用に関する詳細
スタイルを使用して、ボタン、メニュー項目などの IsEnabled プロパティを次のような静的な値にバインドできます。
<Setter Property="IsEnabled" Value="{Binding NonModal, Source={x:Static local:ModalModeTracker.Instance}}" />
デフォルトでは、NonModal プロパティが false になると、これらのスタイルを持つすべてのアイテムが自動的に無効になります。ただし、個々のコントロールをオーバーライドしIsEnabled="true"
て、モーダル モードでも有効なままにすることができます。MultiBinding および EDF ExpressionBinding を使用して、より複雑なバインディングを実行し、必要なルールを設定できます。
これらのアプローチはいずれも、ビジュアル インターフェイスを反復して機能を有効または無効にする必要はありません。これらのどれを実際に選択するかは、モーダル モードで実際に提供したい機能と、残りの UI をどのように設計するかによって決まります。
いずれにせよ、WPF を使用すると、WinForms の時代よりもはるかに簡単になります。WPF のパワーが気に入りませんか?