如何访问WPF ListView的ListViewItems?
时间:2020-03-06 14:21:09 来源:igfitidea点击:
在一个事件中,我想将焦点放在ListViewItem模板内的特定TextBox上。 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。
解决方案
正如其他人指出的那样,无法通过在ListView上调用FindName来找到myBox TextBox。但是,我们可以获取当前选定的ListViewItem,并使用VisualTreeHelper类从ListViewItem获取TextBox。这样做看起来像这样:
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; }
我们对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线程上处理。相反,它启动异步调用并等待UI线程回调并执行实际的ListViewItem控件生成。
为了在发生这种情况时得到通知,ItemContainerGenerator
公开了一个StatusChanged
事件,该事件在生成所有Container之后触发。
现在,我必须听听此事件并确定控件当前是否要设置焦点。
或者可以简单地通过
private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e) { //textbox can be catched like this. var textBox = ((TextBox)sender); EmailValidation(textBox.Text); }