Accendo / Publikované

WPF ListBox keyboard focus

Autor:  Libor Bešenyi

Dátum:  20.1.2011

 

Sometimes is necessary to select value in listbox / combox with keyboard focus (for example when is user returns from modal window managed this listbox).

GUI selection is very simple, if we want to select data from enumeration, we can use this code:

// ---------------------------------------------------------------------

public enum SameEnum

{

        ValueA,

        Value2,

        Value3,

        Value4,

        AnotherValue

} // SameEnum

 

// ---------------------------------------------------------------------

public MainWindow()

{

        InitializeComponent();

 

        ListBoxTest.ItemsSource = Enum.GetValues(typeof(SameEnum)).Cast<SameEnum>();

                       

        ListBoxTest.SelectedValue = SameEnum.Value4;

        ListBoxTest.Focus();

}

 

This will work, but when we push the arrow key (up / down) item focus will be removed. For keyboard focus we must select directly ListBoxItem, not just value. ListBoxItem is embedded in ItemContainer, so we can hook event StatusChange (in run-time) – but we must remove handler when it will be done, so code is bit of horrible:

// ---------------------------------------------------------------------

public static void SetValue<T>(Selector control, Func<T, bool> predicate)

{

        var item = control.ItemsSource.OfType<T>().FirstOrDefault(predicate);

 

        if (item != null && (control as ListBox != null))

        {

                EventHandler smernikNaEvent = null;

                smernikNaEvent = (object sender, EventArgs e) =>

                {

                        if ((control as ListBox).ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)

                        {

                               int selectedIndex = (control as ListBox).SelectedIndex;

                               if (selectedIndex < 0)

                                                       control.ItemContainerGenerator.StatusChanged -= smernikNaEvent;

                               else

                               {

                                       ListBoxItem listBoxItem = (control as ListBox).ItemContainerGenerator.ContainerFromIndex(selectedIndex) as ListBoxItem;

                                       if (listBoxItem != null)

                                       {

                                               listBoxItem.Focus();

                                               control.ItemContainerGenerator.StatusChanged += null;

 

                                               control.ItemContainerGenerator.StatusChanged -= smernikNaEvent;

                                       } // if

                               } // else

                        } // if

                };

 

                control.ItemContainerGenerator.StatusChanged += smernikNaEvent;

        } // if

 

        control.SelectedValue = control.ItemsSource.OfType<T>().FirstOrDefault(predicate);

}

 

// ---------------------------------------------------------------------

public MainWindow()

{

        InitializeComponent();

 

        ListBoxTest.ItemsSource = Enum.GetValues(typeof(SameEnum)).Cast<SameEnum>();

                       

        SetValue<SameEnum>(ListBoxTest, item => item == SameEnum.Value4);

}

 

In .Net 3.5 can be problem with GUI focusing called from control.Focus(), so there is workaround to use this routine:

 

// ---------------------------------------------------------------------

public static void Focus(this Dispatcher dispatcher, UIElement element)

{

        dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate() { element.Focus(); }));

}

 

We must use this line: Dispatcher.CurrentDispatcher.Focus(listBoxItem);

Instead of:                         listBoxItem.Focus();