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