Question

Dans un événement, j'aimerais mettre l'accent sur une zone de texte spécifique dans le modèle ListViewItem. Le XAML ressemble à ceci:

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

J'ai essayé ce qui suit dans le code ci-dessous:

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

mais je semble avoir mal compris la documentation FindName () , car elle renvoie null .

De même, ListView.Items n'aide pas, car il contient (bien sûr) mes objets métier liés et aucun ListViewItems.

De même que myList.ItemContainerGenerator.ContainerFromItem (item) , qui renvoie également null.

Était-ce utile?

La solution

Pour comprendre pourquoi ContainerFromItem n'a pas fonctionné pour moi, voici un aperçu du contexte. Le gestionnaire d'événements pour lequel j'avais besoin de cette fonctionnalité se présente comme suit:

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

Après que Add () , ItemContainerGenerator ne crée pas immédiatement le conteneur, car l'événement CollectionChanged peut être géré de manière non virtuelle. UI-thread. Au lieu de cela, il démarre un appel asynchrone et attend que le thread d'interface utilisateur appelle et exécute la génération du contrôle ListViewItem réel.

Pour être averti lorsque cela se produit, ItemContainerGenerator expose un événement StatusChanged qui est déclenché après la génération de tous les conteneurs.

Je dois maintenant écouter cet événement et décider si le contrôle souhaite actuellement définir le focus ou non.

Autres conseils

Comme d'autres l'ont noté, la zone de texte myBox ne peut pas être trouvée en appelant FindName sur ListView. Toutefois, vous pouvez obtenir le ListViewItem actuellement sélectionné et utiliser la classe VisualTreeHelper pour obtenir le TextBox à partir du ListViewItem. Pour ce faire, cela ressemble à quelque chose comme ceci:

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

J'ai remarqué que le titre de la question ne concerne pas directement le contenu de la question, et la réponse acceptée n'y répond pas non plus. J'ai pu "accéder aux ListViewItems d'un WPF ListView " en utilisant ceci:

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

Je ne sais pas à quel point la récursion devient agitée, mais cela semblait bien fonctionner dans mon cas. Et non, je n'ai jamais utilisé return return dans un contexte récursif auparavant.

Vous pouvez parcourir le ViewTree pour trouver le jeu d'enregistrements ' ListViewItem ' qui correspond à la cellule déclenchée par le test de réussite.

De même, vous pouvez obtenir les en-têtes de colonne de la vue parent pour comparer et faire correspondre la colonne de la cellule. Vous souhaiterez peut-être associer le nom de la cellule au nom de l'en-tête de la colonne en tant que clé de votre délégué / filtre de comparateur.

Par exemple: HitResult est sur TextBlock, affiché en vert. Vous souhaitez obtenir le descripteur de l'élément ' ListViewItem '.

 entrer la description de l'image ici

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

Nous utilisons une technique similaire avec la nouvelle grille de données de 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

Cette technique devrait vous convenir, il suffit de passer votre listviewitem une fois générée.

Ou cela peut être fait simplement par

private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top