Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect focus lost from a group of WPF controls

Tags:

c#

focus

wpf

f#

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?

like image 913
J D Avatar asked Nov 23 '12 20:11

J D


1 Answers

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
like image 144
J D Avatar answered Sep 28 '22 00:09

J D