わかりました...これはProgrammer-funのように「楽しい」ものでした。理解するためのキースターの本当の痛み、しかし私がした私の顔に素敵な大きな笑顔で。(私が自分でそれをとても激しく叩いていることを考慮して、私の肩のためにいくつかのIcyHotを手に入れる時が来ました!:P)
とにかく、それは多段階のことですが、すべてを理解すれば驚くほど簡単です。短いバージョンでは、どちらか一方ではなく、との両方 を使用する必要があります。LostFocus
LostKeyboardFocus
LostFocus
は簡単だ。そのイベントを受信するたびに、IsEditing
falseに設定してください。完了しました。
コンテキストメニューと失われたキーボードフォーカス
LostKeyboardFocus
コントロールのコンテキストメニューがコントロール自体で起動する可能性があるため、少し注意が必要です(つまり、コントロールのコンテキストメニューが開いても、コントロールにはフォーカスがありますが、キーボードフォーカスが失われるため、LostKeyboardFocus
起動します)。
この動作を処理するにContextMenuOpening
は、メニューが開いていることを示すクラスレベルのフラグをオーバーライド(またはイベントを処理)して設定します。(私は使用しますbool _ContextMenuIsOpening
。)次に、LostKeyboardFocus
オーバーライド(またはイベント)で、そのフラグをチェックし、設定されている場合は、単にクリアして他に何もしません。ただし、設定されていない場合は、コンテキストメニューを開く以外の何かが原因で、コントロールがキーボードフォーカスを失っていることを意味します。その場合、IsEditing
falseに設定する必要があります。
すでに-コンテキストメニューを開く
コントロールのコンテキストメニューが開いていて、上記のようにコントロールがすでにキーボードフォーカスを失っている場合、アプリケーションの他の場所をクリックすると、新しいコントロールがフォーカスを取得する前に、コントロールが最初にキーボードフォーカスを取得するという奇妙な動作があります。 、しかしほんの一瞬だけ、それは即座にそれを新しいコントロールに譲ります。
これは実際にはここで有利に機能します。これは、別のLostKeyboardFocus
イベントも取得することを意味しますが、今回は_ContextMenuOpeningフラグがfalseに設定され、上記のように、LostKeyboardFocus
ハンドラーがIsEditing
falseに設定されます。これはまさに私たちが望むものです。私は偶然が大好きです!
これで、最初にコンテキストメニューを所有するコントロールにフォーカスを戻すことなく、クリックしたコントロールにフォーカスが移動しました。次に、ContextMenuClosing
イベントをフックして、次にフォーカスを取得するコントロールを確認するなどの操作を行う必要があります。すぐにフォーカスされるコントロールがコンテキストメニューを生成するコントロールではなかった場合にのみfalseに設定IsEditing
したので、基本的にそこで弾丸をかわしました。
警告:デフォルトのコンテキストメニュー
また、テキストボックスのようなものを使用していて、独自のコンテキストメニューを明示的に設定していない場合は、ContextMenuOpening
イベントが発生しないという警告もあります。これは私を驚かせました。ただし、デフォルトのコンテキストメニューと同じ標準コマンド(切り取り、コピー、貼り付けなど)を使用して新しいコンテキストメニューを作成し、それをテキストボックスに割り当てるだけで、簡単に修正できます。見た目はまったく同じですが、フラグを設定する必要のあるイベントが表示されます。
ただし、サードパーティで再利用可能なコントロールを作成していて、そのコントロールのユーザーが独自のコンテキストメニューを使用したいという問題がある場合でも、誤って優先順位を高く設定して、それらのコントロールを上書きする可能性があります。 !!
それを回避する方法は、テキストボックスが実際にはコントロールのIsEditing
テンプレート内のアイテムであるため、外部コントロールに新しいDPを追加するだけでIsEditingContextMenu
、内部スタイルを介してテキストボックスにバインドし、そのスタイルTextBox
にを追加しました。外部コントロールDataTrigger
のの値をチェックし、IsEditingContextMenu
それがnullの場合は、上記で作成したデフォルトのメニューを設定します。これはリソースに保存されます。
テキストボックスの内部スタイルは次のとおりです(「Root」という名前の要素は、ユーザーが実際にXAMLに挿入する外部コントロールを表します)...
<Style x:Key="InlineTextbox" TargetType="TextBox">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="ContextMenu" Value="{Binding IsEditingContextMenu, ElementName=Root}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Background="White" BorderBrush="LightGray" BorderThickness="1" CornerRadius="1">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditingContextMenu, RelativeSource={RelativeSource AncestorType=local:EditableTextBlock}}" Value="{x:Null}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Cut" />
<MenuItem Command="ApplicationCommands.Copy" />
<MenuItem Command="ApplicationCommands.Paste" />
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
テキストボックスに直接ではなく、スタイルで初期コンテキストメニューバインディングを設定する必要があることに注意してください。そうしないと、スタイルのDataTriggerが直接設定された値に置き換えられ、トリガーが使用できなくなり、ユーザーが使用する場合はすぐに正方形に戻ります。コンテキストメニューの場合は「null」。(メニューを抑制したい場合は、とにかく「null」を使用しません。nullは「デフォルトを使用する」を意味するため、空のメニューに設定します)
これで、ユーザーはfalseのContextMenu
場合に通常のプロパティを使用できます... IsEditingがtrueの場合に使用できます。指定しなかった場合は、定義した内部デフォルトがテキストボックスに使用されます。テキストボックスのコンテキストメニューが実際にnullになることはないため、常に起動し、この動作をサポートするロジックが機能します。IsEditing
IsEditingContextMenu
IsEditingContextMenu
ContextMenuOpening
私が言ったように...これをすべて理解することができる缶の本当の痛み、しかし私がここで本当にクールな達成感を持っていないなら気にしないでください。
これが同じ問題を抱えている他の人たちに役立つことを願っています。ここに返信するか、質問をPMしてください。
マーク