Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access the ListViewItems of a WPF ListView?

Within an event, I'd like to put the focus on a specific TextBox within the ListViewItem's template. The XAML looks like this:

<ListView x:Name="myList" ItemsSource="{Binding SomeList}">
    <ListView.View>
        <GridView>
            <GridViewColumn>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <!-- Focus this! -->
                        <TextBox x:Name="myBox"/>

I've tried the following in the code behind:

(myList.FindName("myBox") as TextBox).Focus();

but I seem to have misunderstood the FindName() docs, because it returns null.

Also the ListView.Items doesn't help, because that (of course) contains my bound business objects and no ListViewItems.

Neither does myList.ItemContainerGenerator.ContainerFromItem(item), which also returns null.

like image 677
David Schmitt Avatar asked Sep 18 '08 13:09

David Schmitt


People also ask

What is List view in WPF?

The ListView control provides the infrastructure to display a set of data items in different layouts or views. For example, a user may want to display data items in a table and also to sort its columns. The types referenced in this article are available in the Code reference section.

How to delete a items in ListView WPF?

RemoveAt method to delete an item from the collection of items in the ListView. The RemoveAt method takes the index of the item in the collection. Now, we modify our application and add a new button called Delete Item. The XAML code for this button looks like below.


3 Answers

To understand why ContainerFromItem didn't work for me, here some background. The event handler where I needed this functionality looks like this:

var item = new SomeListItem();
SomeList.Add(item);
ListViewItem = SomeList.ItemContainerGenerator.ContainerFromItem(item); // returns null

After the Add() the ItemContainerGenerator doesn't immediately create the container, because the CollectionChanged event could be handled on a non-UI-thread. Instead it starts an asynchronous call and waits for the UI thread to callback and execute the actual ListViewItem control generation.

To be notified when this happens, the ItemContainerGenerator exposes a StatusChanged event which is fired after all Containers are generated.

Now I have to listen to this event and decide whether the control currently want's to set focus or not.

like image 65
David Schmitt Avatar answered Oct 11 '22 12:10

David Schmitt


As others have noted, The myBox TextBox can not be found by calling FindName on the ListView. However, you can get the ListViewItem that is currently selected, and use the VisualTreeHelper class to get the TextBox from the ListViewItem. To do so looks something like this:

private void myList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (myList.SelectedItem != null)
    {
        object o = myList.SelectedItem;
        ListViewItem lvi = (ListViewItem)myList.ItemContainerGenerator.ContainerFromItem(o);
        TextBox tb = FindByName("myBox", lvi) as TextBox;

        if (tb != null)
            tb.Dispatcher.BeginInvoke(new Func<bool>(tb.Focus));
    }
}

private FrameworkElement FindByName(string name, FrameworkElement root)
{
    Stack<FrameworkElement> tree = new Stack<FrameworkElement>();
    tree.Push(root);

    while (tree.Count > 0)
    {
        FrameworkElement current = tree.Pop();
        if (current.Name == name)
            return current;

        int count = VisualTreeHelper.GetChildrenCount(current);
        for (int i = 0; i < count; ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(current, i);
            if (child is FrameworkElement)
                tree.Push((FrameworkElement)child);
        }
    }

    return null;
}
like image 38
Abe Heidebrecht Avatar answered Oct 11 '22 12:10

Abe Heidebrecht


I noticed that the question title does not directly relate to the content of the question, and neither does the accepted answer answer it. I have been able to "access the ListViewItems of a WPF ListView" by using this:

public static IEnumerable<ListViewItem> GetListViewItemsFromList(ListView lv)
{
    return FindChildrenOfType<ListViewItem>(lv);
}

public static IEnumerable<T> FindChildrenOfType<T>(this DependencyObject ob)
    where T : class
{
    foreach (var child in GetChildren(ob))
    {
        T castedChild = child as T;
        if (castedChild != null)
        {
            yield return castedChild;
        }
        else
        {
            foreach (var internalChild in FindChildrenOfType<T>(child))
            {
                yield return internalChild;
            }
        }
    }
}

public static IEnumerable<DependencyObject> GetChildren(this DependencyObject ob)
{
    int childCount = VisualTreeHelper.GetChildrenCount(ob);

    for (int i = 0; i < childCount; i++)
    {
        yield return VisualTreeHelper.GetChild(ob, i);
    }
}

I'm not sure how hectic the recursion gets, but it seemed to work fine in my case. And no, I have not used yield return in a recursive context before.

like image 38
Cillié Malan Avatar answered Oct 11 '22 14:10

Cillié Malan