2008-09-18 5 views
11

В рамках события я хотел бы обратить внимание на конкретный TextBox в шаблоне ListViewItem. XAML выглядит следующим образом:Как я могу получить доступ к ListViewItems в WPF ListView?

<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.

ответ

15

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

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

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

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

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

13

Как отмечали другие, 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; 
} 
+0

Верьте или нет, это помогло мне с чем-то несвязанной, что я пытался выяснить. Как сфокусироваться на следующем текстовом поле в сетке при нажатии клавиши вниз! Итак, +1. – RichardOD 2011-05-24 20:41:40

+0

вот сообщение, если вы заинтересованы: http://northdownsolutionslimited.co.uk/post/How-to-focus-on-the-next-row-textbox-in-a-WPF-DataGrid.aspx – RichardOD 2011-05-24 21:24:02

+0

Проблема с это то, что - в зависимости от * когда * вы это называете - «ViewItems» еще не создан. Таким образом, необходимо прослушать событие StatusChanged, как описано в моем ответе. – 2012-10-27 07:19:23

-1

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

-1

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

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

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

0

Вы можете перетащить ViewTree, чтобы найти пункт 'ListViewItem' набор записей, соответствующий ячейке, вызванной тестом на удар.

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

Например: HitResult находится в TextBlock, который отображается зеленым цветом. Вы хотите получить ручку «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)); 
}