2012-10-06 19 views
7

Estoy usando MVVM y estoy mostrando dos cuadros de lista en una ventana. Estoy vinculando desde ambos listboxes simultáneamente a diferentes campos, llámalos A y B. A y B están ambos modificando C. Para que esto funcione, quiero tener solo un elemento de los dos listboxes IsSelected a la vez, de modo que A no anula C cuando B IsSelected. ¿Cómo puedo restringir esto?Múltiples cajas de listas WPF con solo un elemento seleccionado a la vez

Gracias!

+1

¿Desea borrar la selección en un ListBox cuando se realiza una selección en otro ListBox? Encuentro tu pregunta difícil de entender. – Simon

+0

Básicamente, quiero saber cómo puedo vincular algo como una propiedad IsFocused/IsSelected Listbox a un bool en mi modelo de vista. – user1722960

+2

Puede vincular el SelectedIndex de un ListBox a una propiedad en su modelo de vista. Entonces, por ejemplo, cuando se establece SelectedIndexA de ListBoxA, puede establecer SelectedIndexB of ListBoxB en -1 para borrar las selecciones de ListBoxB y viceversa. ¿Esa es tu intención? – Simon

Respuesta

8

Puedo pensar en un par de formas de hacerlo.

Una forma es que puede vincular el ListBox.SelectedIndex de sus 2 ListBoxes a las propiedades de ViewModel que notifican cambios.

Por ejemplo, en su opinión:

<ListBox SelectedIndex="{Binding SelectedIndexA}"> 
    <ListBoxItem Content="Item 1"/> 
    <ListBoxItem Content="Item 2"/> 
</ListBox> 
<ListBox SelectedIndex="{Binding SelectedIndexB}"> 
    <ListBoxItem Content="Item 1"/> 
    <ListBoxItem Content="Item 2"/> 
</ListBox> 

Y en su modelo de vista:

public int SelectedIndexA 
{ 
    get { return _selectedIndexA; } 
    set 
    { 
     _selectedIndexA = value; 
     _selectedIndexB = -1; 
     OnPropertyChanged("SelectedIndexB"); 
    } 
} 

public int SelectedIndexB 
{ 
    get { return _selectedIndexB; } 
    set 
    { 
     _selectedIndexB = value; 
     _selectedIndexA = -1; 
     OnPropertyChanged("SelectedIndexA"); 
    } 
} 

Otra forma sería con una propiedad adjunta como 'Nombre de grupo' donde se puede Selectores de grupo (ListBox hereda de Selector) para garantizar que solo un Selector del grupo tenga un elemento seleccionado en cualquier momento.

Por ejemplo:

public static class SingleSelectionGroup 
{ 
    public static readonly DependencyProperty GroupNameProperty = 
     DependencyProperty.RegisterAttached("GroupName", typeof(string), typeof(SingleSelectionGroup), 
              new UIPropertyMetadata(OnGroupNameChanged)); 

    public static string GetGroupname(Selector selector) 
    { 
     return (string) selector.GetValue(GroupNameProperty); 
    } 

    public static void SetGroupName(Selector selector, string value) 
    { 
     selector.SetValue(GroupNameProperty, value); 
    } 

    private static void OnGroupNameChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
    { 
     var selector = (Selector) dependencyObject; 

     if (e.OldValue != null) 
      selector.SelectionChanged -= SelectorOnSelectionChanged; 
     if (e.NewValue != null) 
      selector.SelectionChanged += SelectorOnSelectionChanged; 
    } 

    private static void SelectorOnSelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (e.AddedItems.Count == 0) 
      return; 

     var selector = (Selector) sender; 
     var groupName = (string) selector.GetValue(GroupNameProperty); 
     var groupSelectors = GetGroupSelectors(selector, groupName); 

     foreach (var groupSelector in groupSelectors.Where(gs => !gs.Equals(sender))) 
     { 
      groupSelector.SelectedIndex = -1; 
     } 
    } 

    private static IEnumerable<Selector> GetGroupSelectors(DependencyObject selector, string groupName) 
    { 
     var selectors = new Collection<Selector>(); 
     var parent = GetParent(selector); 
     GetGroupSelectors(parent, selectors, groupName); 
     return selectors; 
    } 

    private static DependencyObject GetParent(DependencyObject depObj) 
    { 
     var parent = VisualTreeHelper.GetParent(depObj); 
     return parent == null ? depObj : GetParent(parent); 
    } 

    private static void GetGroupSelectors(DependencyObject parent, Collection<Selector> selectors, string groupName) 
    { 
     var childrenCount = VisualTreeHelper.GetChildrenCount(parent); 
     for (int i = 0; i < childrenCount; i++) 
     { 
      var child = VisualTreeHelper.GetChild(parent, i); 
      var selector = child as Selector; 
      if (selector != null && (string) selector.GetValue(GroupNameProperty) == groupName) 
       selectors.Add(selector); 

      GetGroupSelectors(child, selectors, groupName); 
     } 
    } 
} 

Y en su opinión:

<ListBox my:SingleSelectionGroup.GroupName="Group A"> 
    <ListBoxItem Content="Item 1 (Group A)"/> 
    <ListBoxItem Content="Item 2 (Group A)"/> 
</ListBox> 
<ListBox my:SingleSelectionGroup.GroupName="Group A"> 
    <ListBoxItem Content="Item 1 (Group A)"/> 
    <ListBoxItem Content="Item 2 (Group A)"/> 
</ListBox> 

<ListBox my:SingleSelectionGroup.GroupName="Group B"> 
    <ListBoxItem Content="Item 1 (Group B)"/> 
    <ListBoxItem Content="Item 2 (Group B)"/> 
</ListBox> 
<ListBox my:SingleSelectionGroup.GroupName="Group B"> 
    <ListBoxItem Content="Item 1 (Group B)"/> 
    <ListBoxItem Content="Item 2 (Group B)"/> 
</ListBox> 

Si usted tiene que hacer clic en un elemento dos veces antes de que se pone de relieve se puede utilizar una solución rápida como esto:

<Style TargetType="ListBoxItem"> 
    <Style.Triggers> 
     <EventTrigger RoutedEvent="GotKeyboardFocus"> 
      <BeginStoryboard> 
       <Storyboard> 
        <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(ListBoxItem.IsSelected)"> 
         <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" /> 
        </BooleanAnimationUsingKeyFrames> 
       </Storyboard> 
      </BeginStoryboard> 
     </EventTrigger> 
    </Style.Triggers> 
</Style> 
+0

¡¡excelente respuesta !!! He revisado la respuesta a continuación. Todavía tengo un pequeño problema. Si puedes tratar de resolverlo, será muy amable de tu parte. Si se vuelve libre, por favor eche un vistazo a mi problema aquí: http://stackoverflow.com/questions/22209877/movefocus-from-one-listbox-to-another – Vishal

6

Si alguien ha usado ListBox/ListView dentro de ItemsControl y tiene el siguiente problema después de usar la respuesta anterior:

  1. Al hacer clic en cualquier elemento de ListBox1 se selecciona el elemento.
  2. Al hacer clic en cualquier elemento de listbox2, el elemento seleccionado de listbox1 no se selecciona, pero no se selecciona ningún elemento en listbox2.
  3. Al hacer clic de nuevo en cualquier elemento de listbox2 se selecciona el elemento.

Sólo añadir a continuación xaml al estilo de su ListBoxItem:

<Style TargetType="ListBoxItem"> 
    <Style.Triggers> 
     <Trigger Property="IsKeyboardFocusWithin" Value="True"> 
      <Setter Property="IsSelected" Value="True"></Setter> 
     </Trigger> 
    </Style.Triggers> 
</Style> 

Además, si alguien está recibiendo el siguiente error después de usar el código en respuesta anterior:

GroupName is already registered by Selector 

favor cambie el third parameter typeof(......) en declaración de propiedad de dependencia al Nombre de su clase.

+0

Ese es un excelente seguimiento. ¡Gracias! – usefulBee

+0

@usefulBee De nada. – Vishal

+0

Gracias por investigar eso. Hasta el día de hoy, no había necesitado la propiedad adjunta, así que vi el problema con los elementos seleccionados que no estaban resaltados. Como una solución rápida, utilicé un EventTrigger en lugar de lo anterior porque los disparadores regulares se reinician cuando el valor es falso, lo que significa que la selección se borra cuando el elemento pierde el foco del teclado. Esto no fue deseado en mi situación. – Simon

Cuestiones relacionadas