2008-09-18 28 views
11

Trong một sự kiện, tôi muốn đặt trọng tâm vào một TextBox cụ thể trong mẫu của ListViewItem. Các XAML trông như thế này:Làm thế nào tôi có thể truy cập vào ListViewItems của một ListView WPF?

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

Tôi đã thử những điều sau đây trong các mã sau:

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

nhưng tôi dường như đã hiểu lầm FindName() tài liệu, bởi vì nó sẽ trả về null.

Ngoài ra, ListView.Items không giúp ích gì, bởi vì điều đó (tất nhiên) chứa các đối tượng kinh doanh bị ràng buộc của tôi và không có ListViewItems.

Không myList.ItemContainerGenerator.ContainerFromItem(item), cũng trả về giá trị rỗng.

Trả lời

15

Để hiểu lý do tại sao ContainerFromItem không hoạt động đối với tôi, ở đây một số nền. Xử lý sự kiện mà tôi cần chức năng này trông như thế này:

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

Sau Add() các ItemContainerGenerator không ngay lập tức tạo ra các container, vì sự kiện CollectionChanged có thể bị xử lý về một tổ chức phi UI sợi. Thay vào đó, nó bắt đầu một cuộc gọi không đồng bộ và chờ cho chuỗi giao diện người dùng gọi lại và thực thi điều khiển ListViewItem thực tế.

Để được thông báo khi điều này xảy ra, ItemContainerGenerator cho thấy sự kiện StatusChanged được kích hoạt sau khi tất cả Vùng chứa được tạo.

Bây giờ tôi phải nghe sự kiện này và quyết định xem điều khiển hiện có muốn đặt tiêu điểm hay không.

+1

Điều này chắc chắn là câu trả lời. Để thêm một số thông tin, tôi nhận thấy rằng sự kiện được gọi hai lần. Lần đầu tiên ContainerFromItem tạo ra một giá trị null, trong khi lần thứ hai nó trả về đối tượng listviewitem mong đợi. Điều này đã cứu ngày của tôi! – g1ga

+0

Sự kiện này không được hiển thị trên WinRT –

13

Như những người khác đã lưu ý, Hộp văn bản myBox không thể tìm thấy bằng cách gọi FindName trên ListView. Tuy nhiên, bạn có thể lấy ListViewItem hiện đang được chọn và sử dụng lớp VisualTreeHelper để lấy TextBox từ ListViewItem. Để làm như vậy trông giống như sau:

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

Tin hay không, điều này đã giúp tôi có một cái gì đó không liên quan mà tôi đã cố gắng tìm ra. Làm thế nào để tập trung vào hộp văn bản tiếp theo trong một lưới khi phím xuống được nhấn! Vì vậy, +1. – RichardOD

+0

đây là bài đăng nếu bạn quan tâm: http://northdownsolutionslimited.co.uk/post/How-to-focus-on-the-next-row-textbox-in-a-WPF-DataGrid.aspx – RichardOD

+0

Sự cố với điều này là - tùy thuộc vào * khi * bạn đang gọi điều này - 'ViewItems' có thể chưa được tạo. Vì vậy, sự cần thiết phải lắng nghe sự kiện 'StatusChanged' như được mô tả trong câu trả lời của tôi. –

-1

Chúng tôi sử dụng một kỹ thuật tương tự với DataGrid mới WPF của:

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 

Kỹ thuật này nên được khá thích hợp cho bạn, chỉ cần vượt qua ListViewItem của bạn khi nó đã được tạo ra.

-1

Hoặc nó có thể được chỉ đơn giản là thực hiện bằng cách

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

tôi nhận thấy rằng tiêu đề câu hỏi không liên quan trực tiếp đến nội dung của câu hỏi, và cũng như câu trả lời chấp nhận trả lời nó. Tôi đã có thể "truy cập ListViewItems của một WPF ListView" bằng cách sử dụng này:

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

Tôi không chắc chắn như thế nào bận rộn các đệ quy được, nhưng nó dường như làm việc tốt trong trường hợp của tôi. Và không, tôi đã không sử dụng yield return trong ngữ cảnh đệ quy trước đây.

+0

Sự cố với điều này là - tùy thuộc vào * khi * bạn đang gọi điều này - 'ViewItems' có thể chưa được tạo. Vì vậy, sự cần thiết phải lắng nghe sự kiện 'StatusChanged' như được mô tả trong câu trả lời của tôi. –

+0

Thankyou! Làm việc "như là", và đã làm chính xác những gì tôi muốn. Công việc tốt đẹp :) –

0

Bạn có thể duyệt qua ViewTree để tìm mục 'ListViewItem' bộ bản ghi tương ứng với ô được kích hoạt từ thử nghiệm lần truy cập.

Tương tự, bạn có thể nhận tiêu đề cột từ chế độ xem gốc để so sánh và đối sánh với cột của ô. Bạn có thể muốn liên kết tên ô với tên tiêu đề cột làm khóa cho đại biểu/bộ lọc so sánh của bạn.

Ví dụ: HitResult bật TextBlock màu xanh lá cây. Bạn muốn có được tay cầm cho '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)); 
} 
Các vấn đề liên quan