如何访问WPF ListView的ListViewItem?

Dav*_*itt 14 wpf listview internals

在一个事件中,我想把重点放在ListViewItem模板中的特定TextBox上.XAML看起来像这样:

<ListView x:Name="myList" ItemsSource="{Binding SomeList}">
    <ListView.View>
        <GridView>
            <GridViewColumn>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <!-- Focus this! -->
                        <TextBox x:Name="myBox"/>
Run Code Online (Sandbox Code Playgroud)

我在后面的代码中尝试了以下内容:

(myList.FindName("myBox") as TextBox).Focus();
Run Code Online (Sandbox Code Playgroud)

但我似乎误解了FindName()文档,因为它返回了null.

ListView.Items没有帮助,因为(当然)包含我绑定的业务对象而没有ListViewItems.

也没有myList.ItemContainerGenerator.ContainerFromItem(item),也返回null.

Dav*_*itt 18

要理解为什么ContainerFromItem不适合我,这里有一些背景知识.我需要此功能的事件处理程序如下所示:

var item = new SomeListItem();
SomeList.Add(item);
ListViewItem = SomeList.ItemContainerGenerator.ContainerFromItem(item); // returns null
Run Code Online (Sandbox Code Playgroud)

Add()ItemContainerGenerator不立即创建容器,因为CollectionChanged事件可能在非UI线程处理.相反,它启动异步调用并等待UI线程回调并执行实际的ListViewItem控件生成.

要在发生这种情况时收到通知,将ItemContainerGenerator公开StatusChanged在生成所有Container之后触发的事件.

现在我必须听这个事件并决定控件当前是否想要设置焦点.


Abe*_*cht 14

正如其他人所说,通过在ListView上调用FindName无法找到myBox TextBox.但是,您可以获取当前选定的ListViewItem,并使用VisualTreeHelper类从ListViewItem获取TextBox.这样做看起来像这样:

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;
}
Run Code Online (Sandbox Code Playgroud)


Cil*_*lan 5

我注意到问题标题与问题内容没有直接关系,接受的答案也没有回答它。我已经能够通过使用以下方法“访问 WPF ListView 的 ListViewItems”:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定递归有多忙,但在我的情况下它似乎工作得很好。不,我yield return以前没有在递归上下文中使用过。