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

StackOverflow https://stackoverflow.com/questions/92328

  •  01-07-2019
  •  | 
  •  

Вопрос

В рамках события я хотел бы сосредоточить внимание на определенном текстовом поле в шаблоне 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() docs, потому что это возвращает null.

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

Ни то , ни другое не помогает myList.ItemContainerGenerator.ContainerFromItem(item), который также возвращает значение null.

Это было полезно?

Решение

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

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

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

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

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

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

Как отмечали другие, текстовое поле myBox не может быть найдено путем вызова FindName в ListView .Однако вы можете получить выбранный в данный момент ListViewItem и использовать класс VisualTreeHelper для получения текстового поля из 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;
}

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

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 в рекурсивном контексте ранее.

Вы можете пройти вверх по дереву просмотра, чтобы найти нужный элемент 'ListViewItem ( Список просмотров)"набор записей, соответствующий ячейке, запущенной в результате теста попадания.

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

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

enter image description here

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

Мы используем аналогичную технику с новой datagrid от 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, как только он будет сгенерирован.

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

private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top