Frage

Im Rahmen einer Veranstaltung, würde Ich mag den Fokus auf eine bestimmte TextBox setzen innerhalb der Vorlage ListViewItem. Die XAML sieht wie folgt aus:

<ListView x:Name="myList" ItemsSource="{Binding SomeList}">
    <ListView.View>
        <GridView>
            <GridViewColumn>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <!-- Focus this! -->
                        <TextBox x:Name="myBox"/>

Ich habe versucht, hinter dem folgend in dem Code:

(myList.FindName("myBox") as TextBox).Focus();

aber ich glaube, die FindName() docs zu haben falsch verstanden, weil es null zurück.

Auch hat der ListView.Items nicht helfen, denn das (natürlich) enthält meine gebundenen Geschäftsobjekte und keine Listviewitem.

Auch nicht myList.ItemContainerGenerator.ContainerFromItem(item), die auch den Wert null zurück.

War es hilfreich?

Lösung

Um zu verstehen, warum ContainerFromItem nicht für mich arbeiten, hier einige Hintergrundinformationen. Der Event-Handler, wo ich diese Funktion wie folgt aussieht benötigt:

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

Nach der Add() hat der ItemContainerGenerator nicht sofort die Container erstellen, weil das CollectionChanged Ereignis auf einem Nicht-UI-Thread behandelt werden könnte. Stattdessen beginnt er einen asynchronen Aufruf und wartet auf den UI-Thread die tatsächliche ListViewItem Steuer Generation zu Rückruf und auszuführen.

benachrichtigt werden, wenn dies geschieht, setzt das ItemContainerGenerator ein StatusChanged Ereignis, das ausgelöst wird, nachdem alle Container erzeugt werden.

Jetzt muss ich zu diesem Ereignis hören und entscheiden, ob die Steuerung zur Zeit des möchten Fokus setzen oder nicht.

Andere Tipps

Wie andere haben darauf hingewiesen, The myBox TextBox kann nicht durch den Aufruf auf dem Findname Listview zu finden. Sie können jedoch die ListViewItem erhalten, die derzeit ausgewählt ist, und verwenden Sie die VisualTreeHelper Klasse die TextBox aus dem ListViewItem zu bekommen. Um dies zu tun sieht ungefähr wie folgt aus:

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;
}

bemerkte ich, dass die Frage Titel nicht direkt auf den Inhalt der Frage bezieht sich, und es ist auch nicht die akzeptierte Antwort beantworten. Ich in der Lage gewesen, „die Listviewitem eines WPF Listview-Zugriff“ durch diesen mit:

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);
    }
}

Ich bin mir nicht sicher, wie hektisch die Rekursion wird, aber es schien in meinem Fall gut zu funktionieren. Und nein, ich habe nicht yield return in einem rekursiven Kontext verwendet vor.

Sie können die viewtree durchqueren, um den Artikel zu finden ‚ ListViewItem ‘ Datensatz, der auf die Zelle aus Treffertest ausgelöst entspricht.

Ebenso können Sie die Spaltenüberschriften aus der übergeordneten Ansicht erhalten, um die Zelle der Spalt zu vergleichen und anzeigen lassen. Möglicherweise möchten Sie den Zellennamen auf den Spaltenkopf Namen als Schlüssel für Ihren Komparator Delegat / Filter binden.

Zum Beispiel: HitResult ist auf Textblock in grün dargestellt. Sie möchten den Griff in die ' ListViewItem ' erhalten.

/// <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));
}

Wir verwenden eine ähnliche Technik mit WPF Der neue Datagrid:

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

Die Technik sollte recht anwendbar für Sie einfach Ihre ListViewItem passieren, sobald es erzeugt wird.

oder es kann durch

einfach getan werden,
private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top