6

モーダル ダイアログ ボックスとして動作すると同時に、同じアプリケーションの他の特定のウィンドウで選択した操作を容易にする WPF ウィンドウを作成したいと考えています。この動作の例は、Adobe Photoshop で見ることができます。これは、ユーザーがスポイト ツールを使用して画像から選択できるようにする複数のダイアログを提供する一方で、他のすべてのアプリケーション機能を事実上無効にします。

今後の方法は、非モーダルで常に最上位のダイアログを作成し、ダイアログに適用されないアプリケーション機能をプログラムで無効にすることだと思います。WPFでこれを達成する簡単な方法はありますか? または、採用できるデザインパターンがあるかもしれません。

4

4 に答える 4

2

はい、プログラムで機能を有効/無効にする従来のアプローチがありますが、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 のパワーが気に入りませんか?

于 2010-05-14T17:46:39.643 に答える
0

これを解決する最善の方法は、前述の InputManager アプローチを使用することだと思います。このデザイン パターンを使用すると、コマンドをツールバーのボタンやメニュー項目などに接続でき、それぞれがコマンドに指定した CanExecute ハンドラーを呼び出すことができます。このハンドラーでは、最前面に表示される非モーダル ウィンドウが開いている場合は、コマンドを有効にしないように設定します。

http://msdn.microsoft.com/en-us/library/ms752308.aspx

于 2010-05-17T11:49:23.743 に答える
0

あなたが探しているのは、Multiple Document Interfaceに似ています。これはデフォルトでは WPF では利用できませんが、これをサポートするための努力がいくつかあります (無料商用の両方) 。

アプリケーションの現在の状態を特定し、これに応じて UI 要素を有効/無効にするのはあなた次第です。

于 2010-05-14T14:51:34.740 に答える
0

プログラムで特定のアプリ機能を無効にする常時表示ウィンドウがこれを行う方法だと思います。このフォームが開いている間に有効にできる機能の「ホワイト リスト」を保持し、リストにない機能をすべて無効にする方が簡単かもしれません (可能なすべての「ブラック リスト」を維持しようとするのとは対照的に) 。有効にすることはできません)。

于 2010-05-14T14:56:02.897 に答える