كيف يمكنني الوصول إلى ListViewItems الخاص بـ WPF ListView؟

StackOverflow https://stackoverflow.com/questions/92328

  •  01-07-2019
  •  | 
  •  

سؤال

خلال حدث ما، أود التركيز على مربع نص معين داخل قالب 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 لا يساعد، لأن ذلك (بالطبع) يحتوي على كائنات الأعمال المقيدة الخاصة بي ولا يحتوي على ListViewItems.

لا يفعل myList.ItemContainerGenerator.ContainerFromItem(item), ، والذي يُرجع أيضًا قيمة فارغة.

هل كانت مفيدة؟

المحلول

لفهم السبب ContainerFromItem لم تنجح معي، هنا بعض الخلفية.يبدو معالج الأحداث حيث كنت بحاجة إلى هذه الوظيفة كما يلي:

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

بعد Add() ال ItemContainerGenerator لا يقوم بإنشاء الحاوية على الفور، لأن CollectionChanged يمكن التعامل مع الحدث على مؤشر ترابط غير واجهة المستخدم.بدلاً من ذلك، يبدأ مكالمة غير متزامنة وينتظر رد الاتصال لمؤشر ترابط واجهة المستخدم وتنفيذ إنشاء عنصر تحكم ListViewItem الفعلي.

ليتم إعلامك عند حدوث ذلك، ItemContainerGenerator يعرض أ StatusChanged الحدث الذي يتم إطلاقه بعد إنشاء جميع الحاويات.

الآن يجب أن أستمع إلى هذا الحدث وأقرر ما إذا كان عنصر التحكم يريد حاليًا ضبط التركيز أم لا.

نصائح أخرى

كما لاحظ آخرون، لا يمكن العثور على myBox TextBox عن طريق استدعاء FindName على ListView.ومع ذلك، يمكنك الحصول على ListViewItem المحدد حاليًا، واستخدام فئة VisualTreeHelper للحصول على TextBox من 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;
}

لاحظت أن عنوان السؤال لا يرتبط بشكل مباشر بمحتوى السؤال، ولا الإجابة المقبولة تجيب عليه.لقد تمكنت من "الوصول إلى ListViewItems الخاص بـ WPF ListView" باستخدام هذا:

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 لأعلى للعثور على العنصر 'ListViewItemمجموعة السجلات التي تتوافق مع الخلية التي تم تشغيلها من اختبار النتائج.

وبالمثل، يمكنك الحصول على رؤوس الأعمدة من العرض الأصلي لمقارنة عمود الخلية ومطابقته.قد ترغب في ربط اسم الخلية باسم رأس العمود كمفتاح لمفوض/مرشح المقارنة الخاص بك.

على سبيل المثال:HitResult موجود على TextBlock الموضح باللون الأخضر.ترغب في الحصول على المقبض إلى '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));
}

نحن نستخدم تقنية مشابهة مع شبكة البيانات الجديدة الخاصة بـ 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

يجب أن تكون هذه التقنية قابلة للتطبيق بالنسبة لك إلى حد ما، ما عليك سوى تمرير عنصر عرض القائمة الخاص بك بمجرد إنشائه.

أو يمكن القيام بذلك ببساطة عن طريق

private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top