I'm building a GUI using code that has editable contents. When the user clicks on the static display the control is swapped out for one that allows editing. In some cases, the display control (e.g. Label
) is swapped out for a group of controls such as a ComboBox
and TextBox
.
I want to detect when focus is lost from my group of editable controls in order to switch the interface back from the editor to the display representation for that item.
For example, I might have a GUI in a tree like Panel1(Panel2(Button1, Button2), Button3)
and I would like to detect when focus is lost from Panel2
. I tried the following (F# code):
open System.Windows
let button1 = Controls.Button(Content="1")
let button2 = Controls.Button(Content="2")
let button3 = Controls.Button(Content="3")
[<System.STAThreadAttribute>]
do
let panel1 = Controls.StackPanel()
let panel2 = Controls.StackPanel()
panel2.Children.Add button1 |> ignore
panel2.Children.Add button2 |> ignore
panel1.Children.Add panel2 |> ignore
panel1.Children.Add button3 |> ignore
panel2.LostFocus.Add(fun _ ->
printfn "Panel2 lost focus")
Application().Run(Window(Content=panel1))
|> ignore
The panel2.LostFocus
event is triggered when button3
is clicked after button2
had been clicked, as expected, because the focus as moved out of panel1
to button3
. However, it is also triggered when button2
is clicked after button1
had been clicked even though panel2
never lost focus.
Reading the MSDN documentation about focus in WPF I tried adding:
Input.FocusManager.SetIsFocusScope(panel2, true)
but this actually made the problem worse! Now the panel2.LostFocus
event is triggered only when focus shift from one child of panel2
to another and not when panel2
actually loses focus.
How should I get the desired effect?
Thanks to Ian Voyce on Twitter I was able to get the functionality I needed using the IsKeyboardFocusWithinChanged
event. Here's a demo:
open System.Windows
let Button x =
Controls.Button(Content=x, Width=64.0, Margin=Thickness 3.0)
let Panel ctrls =
let panel = Controls.StackPanel()
for ctrl in ctrls do
panel.Children.Add ctrl
|> ignore
panel
let label = Controls.Label(Content="Edit 12")
let button1 = Button "1"
let button2 = Button "2"
let button3 = Button "3"
let panel = Panel[button1; button2]
[<System.STAThreadAttribute>]
do
label.HorizontalContentAlignment <- HorizontalAlignment.Center
label.MouseLeftButtonDown.Add(fun _ ->
label.Content <- panel)
panel.IsKeyboardFocusWithinChanged.Add(fun e ->
if not(unbox e.NewValue) then
label.Content <- "Edit 12")
Application().Run(Window(Content=Panel[label :> UIElement; button3 :> UIElement]))
|> ignore
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With