質問

わかりました、私は、引数のために通常テキストブロックであるデフォルトのテンプレートを持っているISEDINTINGプロパティを持っているコントロールを持っていますが、ISEDITINGが真である場合、インプレース編集のためにテキストボックスに交換します。コントロールがフォーカスを失うと、まだ編集している場合は、編集モードからドロップアウトして、テキストブロックテンプレートに戻します。かなり簡単ですよね?

Windows Explorerまたはデスクトップ(これは私と同じことです...)でファイルを変更するという動作を考えてください。

問題は、lostfocusイベントを使用できないことです。これは、コントロールにはまだ論理的な焦点があるため、別のウィンドウ(またはフォーカスマネージャーである要素)に切り替えると、lostfocusが発生しないためです。

代わりにlostKeyboardfocusを使用する場合、それが「他のフォーカスマネージャー」の問題を解決しますが、新しいものがあります。編集してテキストボックスを右クリックしてコンテキストメニューを表示します。フォーカスでは、コントロールがキーボードのフォーカスを失い、編集モードを削除し、コンテキストメニューを閉じてユーザーを混乱させます!

ここで、メニューが開く直前にLostKeyboardFocusを無視するためにフラグを設定してから、lostKeyboardfocusイベントでそのFIAGを使用して編集モードからキックアウトするかどうかを決定しましたが、メニューが開いていて、他の場所をクリックしている場合はAPPは、コントロール自体にキーボードフォーカスがなくなっていないため(メニューにはメニューがありました)、コントロールは別のLostKeyBoardFocusイベントを取得しないため、編集モードのままです。 (メニューが閉じたときにチェックを追加する必要があるかもしれません。焦点が合っているものを確認し、コントロールではない場合はeditModeから手動でキックアウトします。それは有望なようです。)

だから...誰もがこの動作をうまくコーディングできる方法を知っていますか?

マーク

役に立ちましたか?

解決

わかりました...これはプログラマーファンのように「楽しい」ものでした。把握するためのキースターの本当の痛みですが、私がしたことを私の顔には素晴らしい大きな笑顔で。 (私が自分でそれを一生懸命にパットしていることを考えると、私の肩のためにicyhotを手に入れる時間です!:p)

とにかくそれはマルチステップなことですが、すべてを理解すると驚くほど簡単です。短いバージョンは使用する必要があります 両方 LostFocus LostKeyboardFocus, 、どちらかではありません。

LostFocus は簡単だ。そのイベントを受け取るたびに、セットしてください IsEditing 偽り。完了して完了。

コンテキストメニューとキーボードのフォーカスを失いました

LostKeyboardFocus コントロールのコンテキストメニューがコントロール自体でそれを発射できるため、もう少し注意が必要です(つまり、コントロールのコンテキストメニューが開くと、コントロールにはまだ焦点がありますが、キーボードのフォーカスを失い、したがって、したがって、 LostKeyboardFocus 火災。)

この動作を処理するために、オーバーライドします ContextMenuOpening (またはイベントを処理)し、メニューが開いていることを示すクラスレベルのフラグを設定します。 (私が使う bool _ContextMenuIsOpening。)次に LostKeyboardFocus オーバーライド(またはイベント)、そのフラグをチェックし、それが設定されている場合は、単にそれをクリアして他に何もしません。しかし、それが設定されていない場合、それはコンテキストメニューの開く以外の何かがコントロールがキーボードのフォーカスを失う原因となっていることを意味します。その場合、あなたは設定したいと思います IsEditing 偽り。

すでにオープンしたコンテキストメニュー

これで、コントロールのコンテキストメニューが開いている場合、したがって、上記のようにコントロールがすでにキーボードフォーカスを失った場合、アプリケーションの他の場所をクリックすると、新しいコントロールがフォーカスを取得する前に、コントロールが最初にキーボードフォーカスを取得するという奇妙な動作があります。 、ただし、一瞬だけで、すぐに新しいコントロールに到達します。

これは実際にここで私たちの利点に機能します。これは私たちも別のものを手に入れることを意味するので LostKeyboardFocus イベントですが、今回は_contextmenuopeningフラグは虚偽に設定され、上記のように、私たちの LostKeyboardFocus その後、ハンドラーが設定されます IsEditing 虚偽に、それはまさに私たちが望むものです。私はセレンディピティが大好きです!

これで、最初にコンテキストメニューを所有するコントロールにフォーカスを設定せずにクリックしたコントロールに焦点を移しました。 ContextMenuClosing イベントとチェックのチェック次にどのコントロールがフォーカスを得るか、それから私たちは設定するだけでした IsEditing 間もなく焦点を当てているコントロールがコンテキストメニューを生み出したコントロールではなかった場合、falseに、基本的にそこに弾丸をかわしました。

警告:デフォルトのコンテキストメニュー

これで、テキストボックスのようなものを使用していて、独自のコンテキストメニューを明示的に設定していない場合、あなたが しないでください 取得します ContextMenuOpening イベント、それは私を驚かせました。ただし、デフォルトのコンテキストメニュー(カット、コピー、貼り付けなど)と同じ標準コマンドを持つ新しいコンテキストメニューを作成し、テキストボックスに割り当てるだけで簡単に修正できます。まったく同じように見えますが、フラグを設定するために必要なイベントが表示されます。

ただし、サードパーティの繰り返しのコントロールを作成しているかのように問題があり、そのコントロールのユーザーが独自のコンテキストメニューを持ちたいと考えている場合、誤ってより高い優先順位に設定し、彼らのものをオーバーライドすることができます。 !

テキストボックスが実際にはのアイテムであるため、その方法は IsEditing 私のコントロールのためのテンプレート、私は単に外側のコントロールに新しいDPを追加しました IsEditingContextMenu 次に、内部を介してテキストボックスにバインドします TextBox スタイル、次にaを追加しました DataTrigger そのスタイルでは、の値をチェックします IsEditingContextMenu 外側のコントロールで、それがnullの場合、上記で作成したデフォルトメニューを設定します。これはリソースに保存されます。

これがテキストボックスの内部スタイルです(「ルート」という名前の要素は、ユーザーが実際に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が「デフォルトを使用する」という意味で空のメニューに設定するでしょう)

したがって、ユーザーはレギュラーを使用できます ContextMenu 財産時 IsEditing 偽です...彼らはを使用できます IsEditingContextMenu Iseditingが真実であり、彼らが指定しなかった場合 IsEditingContextMenu, 、定義した内部デフォルトは、テキストボックスに使用されます。テキストボックスのコンテキストメニューは実際には決してnullではないので、 ContextMenuOpening 常に火がついているため、この動作をサポートするロジックは機能します。

私が言ったように...これをすべて把握することができますが、ここで本当にクールな達成感がなければ気にします。

これが同じ問題でここにいる他の人に役立つことを願っています。ここに返信するか、質問をしてください。

マーク

他のヒント

残念ながら、複雑な問題に対する簡単な解決策を探しています。単に、最小限のインタラクションを必要とするスマートな自動コミットユーザーインターフェイスコントロールを使用することと、それらから「切り替える」ときに「正しいことをする」ことができるという問題です。

それが複雑である理由は、正しいことがアプリケーションのコンテキストに依存するためです。 WPFが取るアプローチは、論理的なフォーカスとキーボードフォーカスの概念を提供し、あなたの状況であなたのために正しいことをする方法を決定できるようにすることです。

コンテキストメニューが開いている場合はどうなりますか?アプリケーションメニューが開かれた場合はどうなりますか?焦点が別のアプリケーションに切り替えられた場合はどうなりますか?ローカルコントロールに属するポップアップが開かれた場合はどうなりますか?ユーザーがEnterを押してダイアログを閉じる場合はどうなりますか?これらすべての状況は処理できますが、コミットボタンがある場合、またはユーザーがEnterを押してコミットする必要がある場合はすべて消えます。

したがって、3つの選択肢があります。

  • 論理的な焦点があるとき、コントロールを編集状態にとどめさせます
  • 明示的なコミットまたは適用メカニズムを追加します
  • 自動コミットをサポートしようとすると発生するすべての乱雑なケースを処理します

簡単ではないでしょうか:

    void txtBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        TextBox txtBox = (sender as TextBox);

        if (e.NewFocus is ContextMenu && (e.NewFocus as ContextMenu).PlacementTarget == txtBox)
        {
            return;
        }

        // Rest of code for existing edit mode here...
    }

コンテキストメニューの問題についてはわかりませんが、私は同様のことをしようとしていましたが、マウスキャプチャを使用すると(ちょうどほぼ)あなたが後にする動作が得られることがわかりました。

ここで答えを参照してください: コントロールは、マウスをそのコントロールの外側でクリックするにはどうすればよいですか?

確かではありませんが、これは役立つ可能性があります。編集可能なコンボボックスでも同様の問題がありました。私の問題は、呼び出されていないOnLostfocus Overrideメソッドを使用していたことでした。修正は、私がlostfocusイベントにコールバックを添付していたので、うまく機能しました。

私は同様の問題の解決策を探してここを通り抜けました:私は ListBox これは、焦点を失います ContextMenu 開くと、私はそれが起こることを望んでいません。

私の簡単な解決策は設定することでした FocusableFalse, 、両方のために ContextMenu そしてその MenuItemS:

<ContextMenu x:Key="QueryResultsMenu" Focusable="False">
    <ContextMenu.Resources>
        <Style TargetType="MenuItem">
            <Setter Property="Focusable" Value="False"/>
        </Style>
    </ContextMenu.Resources>
    <MenuItem ... />
</ContextMenu>

これが将来の探求者に役立つことを願っています...

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top