代码之家  ›  专栏  ›  技术社区  ›  David Schmitt

如何访问wpf listview的listviewitems?

  •  13
  • David Schmitt  · 技术社区  · 16 年前

    在事件中,我想将焦点放在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 没有帮助,因为它(当然)包含我绑定的业务对象,没有ListViewItem。

    也不 myList.ItemContainerGenerator.ContainerFromItem(item) ,也返回空值。

    6 回复  |  直到 16 年前
        1
  •  17
  •   David Schmitt    14 年前

    了解原因 ContainerFromItem 不适合我,这里有一些背景。我需要此功能的事件处理程序如下所示:

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

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

    当发生这种情况时,通知 集装箱发电机 暴露一个 StatusChanged 生成所有容器后激发的事件。

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

        2
  •  14
  •   Abe Heidebrecht    16 年前

    正如其他人所指出的,无法通过在ListView上调用findname找到MyBox文本框。但是,可以获取当前选定的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;
    }
    
        3
  •  4
  •   Cillié Malan    12 年前

    我注意到问题标题与问题的内容没有直接关系,并且被接受的答案也没有回答它。我已经能够通过以下方式“访问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);
        }
    }
    

    我不知道递归有多忙,但在我的例子中它似乎工作得很好。不,我没用过 yield return 在以前的递归上下文中。

        4
  •  0
  •   Latency    8 年前

    您可以遍历视图树以查找项' 列表视图项 '与从命中测试触发的单元格相对应的记录集。

    同样,您可以从父视图中获取列标题,以比较和匹配单元格的列。您可能希望将单元格名称绑定到列标题名称,作为比较器委托/过滤器的键。

    例如:hitResult位于以绿色显示的文本块上。您希望获得' 列表视图项 '.

    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));
    }
    
        5
  •  -1
  •   Bob King    16 年前

    我们对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之后传递它。

        6
  •  -1
  •   Daniel Kurz    12 年前

    或者可以简单地通过

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