Pregunta

Dentro de un evento, me gustaría poner el foco en un determinado cuadro de texto dentro del ListViewItem de la plantilla.El código XAML se parece a esto:

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

He intentado lo siguiente en el código detrás:

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

pero me parece que han entendido mal el FindName() docs, porque vuelve null.

También la ListView.Items no ayuda, ya que (por supuesto) contiene mi obligados los objetos de negocio y no ListViewItems.

Tampoco myList.ItemContainerGenerator.ContainerFromItem(item), que también devuelve null.

¿Fue útil?

Solución

Para entender por qué ContainerFromItem no funciona para mí, aquí algunos antecedentes.El controlador de eventos en el que necesitaba esta funcionalidad se parece a esto:

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

Después de la Add() el ItemContainerGenerator no crear inmediatamente el recipiente, debido a que el CollectionChanged evento podría ser manejado en un no-IU-hilo.En lugar de ello se inicia una llamada asincrónica y espera a que el subproceso de interfaz de usuario para la devolución de llamada y ejecutar el real ListViewItem de control de generación.

Para ser notificado cuando esto sucede, el ItemContainerGenerator expone un StatusChanged evento que se dispara después de que todos los Contenedores se generan.

Ahora tengo que escuchar a este evento y decidir si el control en la actualidad quiere establecer el foco o no.

Otros consejos

Como otros han señalado, El miordenador cuadro de texto, no se puede encontrar llamando FindName en el ListView.Sin embargo, usted puede conseguir el ListViewItem que está seleccionado actualmente, y el uso de la VisualTreeHelper clase para obtener el cuadro de texto de la ListViewItem.Para ello se ve algo como esto:

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

Me di cuenta de que la pregunta del título no se relacionan directamente con el contenido de la pregunta, y tampoco la aceptó responder a contestar.He sido capaz de "acceso a la ListViewItems de un ListView de WPF" mediante el uso de este:

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

No estoy seguro de lo agitada que la recursividad se, pero parecía funcionar muy bien en mi caso.Y no, no los he usado yield return en un recursiva contexto antes.

Que se puede recorrer hasta el ViewTree para encontrar el elemento 'ListViewItem'conjunto de registros que corresponde a la celda activa a partir de aciertos de la prueba.

Del mismo modo, usted puede obtener los encabezados de columna de la vista principal para comparar y combinar la celda de la columna.Puede que desee enlazar el nombre de la celda del encabezado de columna nombre como su clave para su comparador delegado/filtro.

Por ejemplo:HitResult es en TextBlock que se muestra en verde.Usted desea obtener el identificador de la '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));
}

Utilizamos una técnica similar con WPF nuevo 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

La técnica debe ser bastante aplicable para usted, sólo tiene que pasar el listviewitem una vez generado.

O simplemente se puede hacer por

private void yourtextboxinWPFGrid_LostFocus(object sender, RoutedEventArgs e)
    {
       //textbox can be catched like this. 
       var textBox = ((TextBox)sender);
       EmailValidation(textBox.Text);
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top