2009-01-29 21 views
9

Tengo un problema bastante gracioso con WPF. Tengo una vista en árbol, y la selección de elementos funciona bien hasta el momento. El problema es que quiero deseleccionar el elemento seleccionado actualmente cuando el usuario hace clic dentro del área en blanco de la vista en árbol. Por defecto, la vista de árbol mantiene el elemento actual seleccionado, y he añadido una opción de menú contextual para anular la selección, lo cual es bastante duro:¿Cómo deseleccionar todos los elementos seleccionados en una vista de árbol de WPF al hacer clic en algún área vacía?

// Note: This is done recursivly from the start, so it 
// works for child items as well 
treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem) as TreeViewItem).IsSelected = false; 

Por otra parte, esto es contrario a la intuición, ya que requiere que el usuario haga clic derecho primero, y segundo, después de deseleccionarlo de esta manera, el usuario no puede seleccionarlo más haciendo clic en el elemento. ¿Cómo se supone que esto funcione?

Editar: Un poco más información: He añadido un controlador a la TreeView para manejar eventos de clic del ratón, pero el remitente es siempre una instancia TreeView, incluso si hago clic directamente sobre una TreeViewItem. Si agrego un controlador a mi TreeView.ItemTemplate (es decir, el primer elemento secundario de la plantilla), nunca obtengo eventos cuando hago clic en el área vacía (lo cual es bastante lógico). El código es el siguiente:

private void MyTreeView_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     if ((sender as TreeViewItem) == null) 
     { 
      // Always triggered 
      System.Diagnostics.Trace.Write("Empty area clicked"); 
     } 
    } 

Y el XAML para esto es:

<TreeView x:Name="MyTreeView" Margin="3" MouseUp="MyTreeView_MouseUp"> 

Respuesta

0

Esta opción se anulan las TreeViewItem seleccionado en ese momento si se hace clic en ninguno:

private void MyTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e) { 
    if ((sender as TreeViewItem) == null) { 
     TreeViewItem item = MyTreeView.SelectedItem as TreeViewItem; 
     if(item != null){ 
      item.IsSelected = false;      
     } 
    } 
} 

Hope esto es lo estabas buscando!

+0

No funciona, porque después de seleccionar un elemento, el remitente está vinculado al elemento seleccionado actualmente, es decir, incluso si hago clic en el área vacía, el remitente == el elemento seleccionado actualmente. – Anteru

+0

Hmm. Si hace clic en TreeView, el remitente debería ser un TreeView (no TreeViewItem). ¿Agregaste este controlador al TreeView o a cada TreeViewItem? ¿Estás usando el TreeView estándar o uno personalizado? – Pwninstein

+0

Ahh el problema es que no obtengo un TreeViewItem, sino una instancia de mi clase viewmodel, para la cual tengo que encontrar el TreeViewItem correspondiente. Peor aún, después de deseleccionar un elemento mediante programación, no puedo volver a seleccionar haciendo clic en él. – Anteru

5

El problema no seleccionable se puede resolver con una llamada a Focus on the TreeView después de configurar TreeViewItem.IsSelected.

+1

Agradable, buena captura! Me preguntaba por qué no pude seleccionar el mismo nodo una vez que no estaba seleccionado. Gracias! – HaxElit

6

Me pareció que esto funcionaba mucho mejor para mí. Compruebo la fuente original que, si viene de treeviewitem, será una imagen o un bloque de texto. También uso un objeto de vista con una HierarchicalDataTemplate y la BasicTreeViewBase es la clase base para todos mis objetos diferentes. Aquí está el código.

private void TemplateTreeView_MouseDown(object sender, MouseButtonEventArgs e) 
    { 
     if (e.ChangedButton == MouseButton.Right && !(e.OriginalSource is Image) && !(e.OriginalSource is TextBlock)) 
     { 
      BasicTreeViewBase item = TemplateTreeView.SelectedItem as BasicTreeViewBase; 
      if (item != null) 
      { 
       TemplateTreeView.Focus(); 
       item.IsSelected = false; 
      } 
     } 
    } 
+0

¿por qué necesita 'TemplateTreeView.Focus();'? –

+0

@DavidRefaeli que fue hace tanto tiempo que no recuerdo por qué lo tenía. Sé que cuando lo escribí, no funcionó sin él. – Erin

+0

es una buena solución, tuve que hacer algunos ajustes ya que estás usando tus propias clases aquí creo (TemplateTreeView, BasicTreeViewBase) - pero para mí no necesito cambiar el .Focus() para anular la selección del elemento . –

2

Implementé un control de selección general una vez y requirió este comportamiento.

Así es como mi método se veía (adaptada para vista de árbol):

protected override void OnMouseUp(MouseButtonEventArgs e) 
{ 
    base.OnMouseUp(e); 

    DependencyObject dpSource = e.OriginalSource as DependencyObject; 

    if (dpSource.FindVisualAncestor(o => typeof(TreeViewItem).IsAssignableFrom(o.GetType())) == null) 
      UnselectAll(); 
} 

Básicamente, caminar hasta el árbol de la fuente. Si no se encontró un TreeViewItem, el usuario hizo clic en el espacio vacío.

4

No puede haber más de dos problemas:

  1. La vista de árbol se enganchan por lo que el SelectedItem es un elemento de la colección binded.
  2. Hay muchos niveles, de modo ItemContainerGenerator no contienen objetos nivel más profundo

para todos esta razón yo uso esta función, pero la selección no debe disparar eventos.

private void UnselectTreeViewItem(TreeView pTreeView) 
{ 
    if(pTreeView.SelectedItem == null) 
    return; 

    if(pTreeView.SelectedItem is TreeViewItem) 
    { 
    (pTreeView.SelectedItem as TreeViewItem).IsSelected = false; 
    } 
    else 
    { 
    TreeViewItem item = pTreeView.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem; 
    if (item != null) 
    { 
     item.IsSelected = true; 
     item.IsSelected = false; 
    } 
    } 
} 
1

utilizar la clase de extensión por debajo

public static class TreeViewExtensions 
{ 
    public static TreeViewItem ContainerFromItem(this TreeView treeView, object item) 
    { 
     TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item); 
     if (containerThatMightContainItem != null) 
      return containerThatMightContainItem; 
     else 
      return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item); 
    } 

    private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item) 
    { 
     foreach (object curChildItem in itemCollection) 
     { 
      TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem); 
      TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item); 
      if (containerThatMightContainItem != null) 
       return containerThatMightContainItem; 

      TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item); 
      if (recursionResult != null) 
       return recursionResult; 
     } 
     return null; 
    } 
} 

Luego, en vista de árbol MouseDown caso de utilizar el método de extensión de la siguiente manera:

private void trview_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     if ((sender as TreeViewItem) == null) 
     { 
      if (this.trview.ContainerFromItem(trview.SelectedItem) != null) 
      { 
       this.trview.ContainerFromItem(trview.SelectedItem).IsSelected = false; 
      } 
     } 
     this.trview.Focus(); 
    } 

espero que funcione para usted. Lo tengo trabajando de esta manera ...

+0

Esto funciona, es simple y autónomo. ¡Gracias! – fabspro

1

Me encontré con esta situación yo mismo con una implementación de vista de lista de árbol personalizada después de buscar durante mucho tiempo finalmente encontré una solución que funcionó para mí.

La explicación completa se puede encontrar en http://social.msdn.microsoft.com/Forums/vstudio/en-US/36aca7f7-0b47-488b-8e16-840b86addfa3/getting-treeviewitem-for-the-selected-item-in-a-treeview

La idea básica es que capture el evento TreeViewItem.Selected y guardar la fuente del evento en el atributo Tag en su TreeView. Luego, cuando necesite borrarlo, puede acceder al atributo Tag en su control y establecer el valor IsSelected en False. Esto funciona para mí con 2 niveles de niños anidados. Espero que funcione para usted.

Por el amor de persistencia:

declaración TreeView

<TreeView Name="myTreeView" TreeViewItem.Selected="OnItemSelected" 
    ItemsSource="{Binding Source={StaticResource myHierarchicalData}}"/> 

Gestor de Eventos

private void OnItemSelected(object sender, RoutedEventArgs e) 
{ 
    myTreeView.Tag = e.OriginalSource; 
} 

lógica de selección Borrar

if (myTreeView.SelectedItem != null) 
{ 
    TreeViewItem selectedTVI = myTreeView.Tag as TreeViewItem; 
    // add your code here mine was selectedTVI.IsSelected = false; 
} 
0

Para una vista de árbol C# utiliza treeview.Select edNode = null; No estoy seguro de si esto funciona para WPF.

+0

No funciona porque en WPF, 'SelectedItem' es de solo lectura. –

Cuestiones relacionadas