none
explicitly setting focus on a list item of a ListView control

    Question

  •  

    Is there a way to explicitly set focus on a listview’s selected item (say ‘x’)  from codebehind ?

    So that when an application loads or when the listview is repopulated the item gets the focus

    and pressing arrowkeys scroll the listview with respect to the item ‘x’.

     

    -          sushil

    Saturday, August 18, 2007 12:32 AM

Answers

  • Hi

     

    If you want to focus a DataTemplate generated element, the following code illustrates how to do this.

     

    Code Snippet

    <Window x:Class="TestSolution.MainWindow"

            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

            xmlns:local="clr-namespace:TestSolution"

            Title="MainWindow">

        <Window.Resources>

            <local:CustomerCollection x:Key="myCustomers"/>

     

            <DataTemplate x:Key="myDataTemplate">

                <Border BorderBrush="BlueViolet" BorderThickness="1" Margin="0,2,0,2">

                    <Grid>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="Auto"/>

                            <ColumnDefinition Width="*"/>

                        </Grid.ColumnDefinitions>

                        <TextBlock x:Name="idTextBlock" Text="{Binding Path=Id}" Grid.Column="0" Margin="2" Width="60"/>

                        <TextBox x:Name="nameTextBox" Text="{Binding Path=Name}" Grid.Column="1" Margin="2" Width="200"/>

                    </Grid>

                </Border>

            </DataTemplate>

        </Window.Resources>

     

        <StackPanel>

            <ListView x:Name="myListView" Width="300" Height="200"

                     ItemsSource="{Binding Source={StaticResource myCustomers}}"

                     ItemTemplate="{StaticResource myDataTemplate}">

            </ListView>

            <TextBox x:Name="searchIdTextBox" Margin="10"/>

            <Button Click="Button_Click" Margin="10">Search</Button>

        </StackPanel>

    </Window>

     

    public partial class MainWindow : Window

    {

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            foreach (Customer c in this.myListView.Items)

            {

                if (c.Id == this.searchIdTextBox.Text)

                {

                    this.myListView.SelectedItem = c;

                    this.myListView.ScrollIntoView(c);

                    this.myListView.Focus();

     

                    ListBoxItem item = (ListBoxItem)(this.myListView.ItemContainerGenerator.ContainerFromItem(c));

     

                    if (item.IsLoaded)

                        FocusItem(item);

                    else

                        item.Loaded += new RoutedEventHandler(item_Loaded);

     

                    break;

                }

            }

        }

     

        private void item_Loaded(object sender, RoutedEventArgs e)

        {

            ListBoxItem item = (ListBoxItem)e.Source;

            FocusItem(item);

            item.Loaded -= new RoutedEventHandler(item_Loaded);

        }

     

        private void FocusItem(ListBoxItem item)

        {

            ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(item);

            DataTemplate dataTemplate = (DataTemplate)this.Resources["myDataTemplate"];

            TextBox nameTextBox = (TextBox)dataTemplate.FindName("nameTextBox", contentPresenter);

            nameTextBox.Focus();

        }

     

        private static childItem FindVisualChild<childItem>(DependencyObject obj)

            where childItem : DependencyObject

        {

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)

            {

                DependencyObject child = VisualTreeHelper.GetChild(obj, i);

                if (child != null && child is childItem)

                    return (childItem)child;

                else

                {

                    childItem childOfChild = FindVisualChild<childItem>(child);

                    if (childOfChild != null)

                        return childOfChild;

                }

            }

            return null;

        }

    }

     

    public class Customer

    {

        public string Id { get; set; }

        public string Name { get; set; }

    }

     

    public class CustomerCollection : ObservableCollection<Customer> { }

     

     

    Regards

    Wei Zhou

    Tuesday, August 21, 2007 6:53 AM

All replies


  • I am not sure if this will work but do give it a try

    ListBoxItem item = myListBox.ItemContainerGenerator.ContainerFromItem(myListBox.SelectedItem) as ListBoxItem;
                item.Focus();

    regards
    Saturday, August 18, 2007 9:45 AM
  • Hi

     

    If you want to focus a DataTemplate generated element, the following code illustrates how to do this.

     

    Code Snippet

    <Window x:Class="TestSolution.MainWindow"

            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

            xmlns:local="clr-namespace:TestSolution"

            Title="MainWindow">

        <Window.Resources>

            <local:CustomerCollection x:Key="myCustomers"/>

     

            <DataTemplate x:Key="myDataTemplate">

                <Border BorderBrush="BlueViolet" BorderThickness="1" Margin="0,2,0,2">

                    <Grid>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="Auto"/>

                            <ColumnDefinition Width="*"/>

                        </Grid.ColumnDefinitions>

                        <TextBlock x:Name="idTextBlock" Text="{Binding Path=Id}" Grid.Column="0" Margin="2" Width="60"/>

                        <TextBox x:Name="nameTextBox" Text="{Binding Path=Name}" Grid.Column="1" Margin="2" Width="200"/>

                    </Grid>

                </Border>

            </DataTemplate>

        </Window.Resources>

     

        <StackPanel>

            <ListView x:Name="myListView" Width="300" Height="200"

                     ItemsSource="{Binding Source={StaticResource myCustomers}}"

                     ItemTemplate="{StaticResource myDataTemplate}">

            </ListView>

            <TextBox x:Name="searchIdTextBox" Margin="10"/>

            <Button Click="Button_Click" Margin="10">Search</Button>

        </StackPanel>

    </Window>

     

    public partial class MainWindow : Window

    {

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            foreach (Customer c in this.myListView.Items)

            {

                if (c.Id == this.searchIdTextBox.Text)

                {

                    this.myListView.SelectedItem = c;

                    this.myListView.ScrollIntoView(c);

                    this.myListView.Focus();

     

                    ListBoxItem item = (ListBoxItem)(this.myListView.ItemContainerGenerator.ContainerFromItem(c));

     

                    if (item.IsLoaded)

                        FocusItem(item);

                    else

                        item.Loaded += new RoutedEventHandler(item_Loaded);

     

                    break;

                }

            }

        }

     

        private void item_Loaded(object sender, RoutedEventArgs e)

        {

            ListBoxItem item = (ListBoxItem)e.Source;

            FocusItem(item);

            item.Loaded -= new RoutedEventHandler(item_Loaded);

        }

     

        private void FocusItem(ListBoxItem item)

        {

            ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(item);

            DataTemplate dataTemplate = (DataTemplate)this.Resources["myDataTemplate"];

            TextBox nameTextBox = (TextBox)dataTemplate.FindName("nameTextBox", contentPresenter);

            nameTextBox.Focus();

        }

     

        private static childItem FindVisualChild<childItem>(DependencyObject obj)

            where childItem : DependencyObject

        {

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)

            {

                DependencyObject child = VisualTreeHelper.GetChild(obj, i);

                if (child != null && child is childItem)

                    return (childItem)child;

                else

                {

                    childItem childOfChild = FindVisualChild<childItem>(child);

                    if (childOfChild != null)

                        return childOfChild;

                }

            }

            return null;

        }

    }

     

    public class Customer

    {

        public string Id { get; set; }

        public string Name { get; set; }

    }

     

    public class CustomerCollection : ObservableCollection<Customer> { }

     

     

    Regards

    Wei Zhou

    Tuesday, August 21, 2007 6:53 AM
  • I sincerely hope MS is working on a real solution to this problem.  The code presented here sends shivers down my spine.  It requires that the code have intimate knowledge of the UI - including the data template, the fact that there was one, and even the control in the template that should get the focus.  There seems to be a general problem in WPF in that a parent element does not forward keyboard focus on to its first child that can receive such focus. It seems that this would solve a number of problems (including the fact that a new Window displays with no keyboard focus unless it is explicitly set).

     

     

    Tuesday, August 28, 2007 3:01 PM
  • I found that it's clearer to move the IsLoaded handler into FocusItem, also if you can pass in the path to the control, you can make the method generic (and static too):

    Code Block

    public static void FocusItem(FrameworkElement item, string controlPath)

    {

          if (!item.IsLoaded)

          {

                RoutedEventHandler onload = null;

                onload = delegate

                      {

                            item.Loaded -= onload;

                            FocusItem(item, controlPath);

                      };

                item.Loaded += onload;

                return;

          }

          UIElement currentElement = item;

          foreach (string controlName in controlPath.Split('/'))

          {

                ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(currentElement);

                currentElement = (UIElement)contentPresenter.ContentTemplate.FindName(controlName, contentPresenter);

          }

          currentElement.Focus();

    }


    Thursday, October 18, 2007 11:34 AM
  • Marlon’s approach did not account for virtualization, but for setting focus to the SelectedItem, you shouldn’t need Wei’s entire approach, which sets focus to an element within the data template of the ListViewItem and not just to the ListViewItem itself. The part about waiting until Loaded could also be skipped as long as the entire process is run after the ListView containers have been generated.

     

    This should be all that is needed:

     

    ListViewItem listViewItem = (ListViewItem)_listView.ItemContainerGenerator.ContainerFromItem(dataItem);

    if (listViewItem == null)

    {

        _listView.ScrollIntoView(dataItem);

        listViewItem = (ListViewItem)_listView.ItemContainerGenerator.ContainerFromItem(dataItem);

    }

     

    if (listViewItem != null)

    {

        listViewItem.Focus();

    }

     

    Ben


    This posting is provided "AS IS" with no warranties, and confers no rights.
    • Proposed as answer by Green-D Friday, March 23, 2012 10:42 PM
    Tuesday, August 19, 2008 11:52 PM
  • what if i want to edit the item?
    suppose its a list of names, and i want to edit the selected name.
    how can i achieve this?
    Thursday, November 13, 2008 7:19 PM