WPF ListView의 ListViewItems에 어떻게 액세스할 수 있나요?
문제
이벤트 내에서 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
물론 여기에는 바인딩된 비즈니스 개체가 포함되어 있고 ListViewItem이 없기 때문에 도움이 되지 않습니다.
둘 다 그렇지 않습니다 myList.ItemContainerGenerator.ContainerFromItem(item)
, null도 반환합니다.
해결책
이유를 이해하려면 ContainerFromItem
나에게는 효과가 없었습니다. 여기에 몇 가지 배경이 있습니다.이 기능이 필요한 이벤트 핸들러는 다음과 같습니다.
var item = new SomeListItem();
SomeList.Add(item);
ListViewItem = SomeList.ItemContainerGenerator.ContainerFromItem(item); // returns null
후 Add()
그만큼 ItemContainerGenerator
컨테이너를 즉시 생성하지 않습니다. CollectionChanged
이벤트는 UI 스레드가 아닌 스레드에서 처리될 수 있습니다.대신 비동기 호출을 시작하고 UI 스레드가 콜백하고 실제 ListViewItem 컨트롤 생성을 실행할 때까지 기다립니다.
이런 일이 발생하면 알림을 받으려면 ItemContainerGenerator
노출하다 StatusChanged
모든 컨테이너가 생성된 후 발생하는 이벤트입니다.
이제 이 이벤트를 듣고 컨트롤이 현재 포커스를 설정할지 여부를 결정해야 합니다.
다른 팁
다른 사람들이 지적했듯이 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 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
이전에 재귀적인 맥락에서.
ViewTree를 탐색하여 '항목을 찾을 수 있습니다.목록보기항목' 적중 테스트에서 트리거된 셀에 해당하는 레코드 세트입니다.
마찬가지로, 상위 뷰에서 열 헤더를 가져와서 셀 열을 비교하고 일치시킬 수 있습니다.비교기 대리자/필터의 키로 셀 이름을 열 헤더 이름에 바인딩할 수 있습니다.
예를 들어:HitResult는 녹색으로 표시된 TextBlock에 있습니다.'에 대한 핸들을 얻고 싶습니다.목록보기항목'.
/// <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));
}
우리는 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을 전달하기만 하면 됩니다.
아니면 간단히 할 수도 있습니다.
private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
{
//textbox can be catched like this.
var textBox = ((TextBox)sender);
EmailValidation(textBox.Text);
}