如何访问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);
}

