Как я могу получить доступ к ListViewItems в WPF ListView?

В рамках события я хотел бы сосредоточить внимание на конкретном TextBox в шаблоне ListViewItem. XAML выглядит так:

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

Я пробовал следующее в коде позади:

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

но я, кажется, неправильно понял документы FindName(), потому что он возвращает null.

Также ListView.Items не помогает, потому что он (конечно) содержит мои связанные бизнес-объекты, а не ListViewItems.

Также не работает myList.ItemContainerGenerator.ContainerFromItem(item), который также возвращает значение null.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
15
0
40 704
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Как отмечали другие, MyBox TextBox нельзя найти, вызвав FindName в ListView. Однако вы можете получить текущий выбранный ListViewItem и использовать класс VisualTreeHelper для получения TextBox из ListViewItem. Это выглядит примерно так:

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;
}

Хотите верьте, хотите нет, но это помогло мне с чем-то не связанным с тем, что я пытался выяснить. Как сфокусироваться на следующем текстовом поле в сетке при нажатии клавиши вниз! Итак, +1.

RichardOD 25.05.2011 00:41

вот пост, если вам интересно: northdownsolutionslimited.co.uk/post/…

RichardOD 25.05.2011 01:24

Проблема в том, что - в зависимости от когда, который вы вызываете - ViewItems может еще не быть создан. Таким образом, необходимость прослушивать событие StatusChanged, как описано в моем ответе.

David Schmitt 27.10.2012 11:19

Мы используем аналогичную технику с новой сеткой данных WPF:

Private Sub SelectAllText(ByVal cell As DataGridCell)
    If cell IsNot Nothing Then
        Dim txtBox As TextBox= GetVisualChild(Of TextBox)(cell)
        If txtBox IsNot Nothing Then
            txtBox.Focus()
            txtBox.SelectAll()
        End If
    End If
End Sub

Public Shared Function GetVisualChild(Of T As {Visual, New})(ByVal parent As Visual) As T
    Dim child As T = Nothing
    Dim numVisuals As Integer = VisualTreeHelper.GetChildrenCount(parent)
    For i As Integer = 0 To numVisuals - 1
        Dim v As Visual = TryCast(VisualTreeHelper.GetChild(parent, i), Visual)
        If v IsNot Nothing Then
            child = TryCast(v, T)
            If child Is Nothing Then
                child = GetVisualChild(Of T)(v)
            Else
                Exit For
            End If
        End If
    Next
    Return child
End Function

Техника должна быть применима для вас, просто передайте свой listviewitem после того, как он сгенерирован.

Ответ принят как подходящий

Чтобы понять, почему ContainerFromItem не работал у меня, вот некоторая предыстория. Обработчик событий, в котором мне понадобилась эта функция, выглядит так:

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

После Add()ItemContainerGenerator не создает контейнер сразу, потому что событие CollectionChanged может быть обработано в потоке, отличном от UI-потока. Вместо этого он запускает асинхронный вызов и ждет, пока поток пользовательского интерфейса выполнит обратный вызов и выполнит фактическое создание элемента управления ListViewItem.

Чтобы получить уведомление, когда это произойдет, ItemContainerGenerator выдает событие StatusChanged, которое запускается после создания всех контейнеров.

Теперь я должен прослушать это событие и решить, хочет ли элемент управления в данный момент установить фокус или нет.

Это определенно ответ. Чтобы добавить некоторую информацию, я заметил, что событие вызывается два раза. В первый раз ContainerFromItem выдает нулевое значение, а во второй раз возвращает ожидаемый объект listviewitem. Это спасло мне день!

g1ga 01.12.2013 20:25

Это событие не отображается в WinRT

Scott Nimrod 26.08.2014 19:33

Или это можно просто сделать

private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }

Я заметил, что название вопроса не имеет прямого отношения к содержанию вопроса, и принятый ответ не отвечает на него. Я смог «получить доступ к ListViewItems в WPF ListView» с помощью этого:

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);
    }
}

Я не уверен, насколько беспокойной становится рекурсия, но в моем случае она, похоже, сработала нормально. И нет, я раньше не использовал yield return в рекурсивном контексте.

Проблема в том, что - в зависимости от когда, который вы вызываете - ViewItems может еще не быть создан. Таким образом, необходимость прослушивать событие StatusChanged, как описано в моем ответе.

David Schmitt 27.10.2012 11:18

Спасибо! Работал "как есть" и делал именно то, что хотел. Хорошая работа :)

Stephen Hosking 19.12.2017 07:19

Вы можете пройти вверх по ViewTree, чтобы найти набор записей элемента «ListViewItem», который соответствует ячейке, инициированной проверкой попадания.

Точно так же вы можете получить заголовки столбцов из родительского представления для сравнения и сопоставления столбца ячейки. Вы можете связать имя ячейки с именем заголовка столбца в качестве ключа для делегата / фильтра компаратора.

Например: HitResult находится в TextBlock, показанном зеленым цветом. Вы хотите получить дескриптор ListViewItem.

/// <summary>
///   ListView1_MouseMove
/// </summary>
/// <param name = "sender"></param>
/// <param name = "e"></param>
private void ListView1_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) {
  if (ListView1.Items.Count <= 0)
    return;

  // Retrieve the coordinate of the mouse position.
  var pt = e.GetPosition((UIElement) sender);

  // Callback to return the result of the hit test.
  HitTestResultCallback myHitTestResult = result => {
    var obj = result.VisualHit;

    // Add additional DependancyObject types to ignore triggered by the cell's parent object container contexts here.
    //-----------
    if (obj is Border)
      return HitTestResultBehavior.Stop;
    //-----------

    var parent = VisualTreeHelper.GetParent(obj) as GridViewRowPresenter;
    if (parent == null)
      return HitTestResultBehavior.Stop;

    var headers = parent.Columns.ToDictionary(column => column.Header.ToString());

    // Traverse up the VisualTree and find the record set.
    DependencyObject d = parent;
    do {
      d = VisualTreeHelper.GetParent(d);
    } while (d != null && !(d is ListViewItem));

    // Reached the end of element set as root's scope.
    if (d == null)
      return HitTestResultBehavior.Stop;

    var item = d as ListViewItem;
    var index = ListView1.ItemContainerGenerator.IndexFromContainer(item);
    Debug.WriteLine(index);

    lblCursorPosition.Text = $"Over {item.Name} at ({index})";

    // Set the behavior to return visuals at all z-order levels.
    return HitTestResultBehavior.Continue;
  };

  // Set up a callback to receive the hit test result enumeration.
  VisualTreeHelper.HitTest((Visual)sender, null, myHitTestResult, new PointHitTestParameters(pt));
}

Другие вопросы по теме