2010-01-28 5 views
7

Ok travaillé avec WPF pendant un moment, mais j'ai besoin d'aide.WPF ComboBox SelectedItem

J'ai un ComboBox comme ci-dessous:

<TabControl> 
    <TabItem Header="1"> 
     <ComboBox ItemsSource="{Binding MyList}" SelectedItem="{Binding MyListSelection}"/> 
    </TabItem> 
    <TabItem Header="2"/> 
</TabControl> 

Chaque fois que je me éloigne de l'onglet 1 et puis de revenir sur elle la sélection se retire. Je pense que la raison en est que les contrôles sont détruits quand ils sortent de la portée et puis de retour. Mais dans le processus de cela SelectedItem devient null ce qui n'est pas vraiment ce que l'utilisateur voulait, c'est un événement dû à l'interface utilisateur cycle de la vie. Donc, je me demande quel est le meilleur itinéraire à suivre? Je construis cette application avec MVVM pour que je puisse ignorer un appel sur la propriété MyListSelection dans mon ViewModel mais j'ai des ComboBox partout et je n'aime pas modifier mon ViewModel pour ce que je considère comme un bug de WPF.

Je pourrais sous-classer le WPF ComboBox, mais il n'y a aucun événement SelectedItemChanging Je peux seulement ajouter un gestionnaire quand SelectedItem a changé.

Des idées?

MISE À JOUR:

D'accord, après avoir battu la tête contre le mur, j'ai découvert pourquoi mon problème ne pouvait pas se reproduire. Si le type d'élément de liste est une classe pour une raison quelconque, SelectedItem est défini par WPF sur null mais s'il s'agit d'un type de valeur, ce n'est pas le cas.

ici est ma classe de test (VMBase est juste une classe abstraite qui implémente INotifyPropertyChanged):

public class TestListViewModel : VMBase 
{ 
    public TestListViewModel() 
    { 
     TestList = new List<TestViewModel>(); 
     for (int i = 0; i < 10; i++) 
     { 
      TestList.Add(new TestViewModel(i.ToString())); 
     } 
    } 

    public List<TestViewModel> TestList { get; set; } 

    TestViewModel _SelectedTest; 
    public TestViewModel SelectedTest 
    { 
     get { return _SelectedTest; } 
     set 
     { 
      _SelectedTest = value; 
      OnPropertyChanged("SelectedTest"); 
     } 
    } 
} 

public class TestViewModel : VMBase 
{ 
    public string Name {get;set;} 
} 

Alors, quand je change TestList de type int et des allers-retours entre les onglets SelectedItem reste le même. Mais quand il est de type TestViewModel SelectedTest est mis à null lorsque le tabitem est flou.

Que se passe-t-il?

Répondre

10

J'ai exactement le même problème, et jusqu'à présent, je ne pouvais pas comprendre quel est le problème. J'ai testé dans 4 machines différentes avec le même système d'exploitation, la version .Net et les spécifications matérielles et je pouvais reproduire le problème dans deux d'entre eux, dans les autres, ça marchait très bien. La solution de contournement que je pourrais trouver qui fonctionne pour moi est de définir la liaison SelectedItem avant le ItemsSource. Bizarrement, si je suis ce modèle, tout fonctionne comme prévu. Cela dit, il vous suffit de faire ce qui suit:

<Window x:Class="ComboBoxInTabItemSpike.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <StackPanel> 
     <TabControl> 
      <TabItem Header="1"> 
       <ComboBox SelectedItem="{Binding MySelect}" ItemsSource="{Binding MyList}"/> 
      </TabItem> 
      <TabItem Header="2"/> 
     </TabControl> 
     <TextBlock Text="{Binding MySelect}"/> 
    </StackPanel> 
</Window> 
0

EDITED après modification de l'OP. Salut Jose, je suis incapable de reproduire l'erreur que vous mentionnez. Donc, votre hypothèse sur la destruction du Contrôle est fausse. Le Combobox se comporte comme prévu avec le code ci-dessous même s'il utilise maintenant un type de référence. Une autre partie de votre code doit être lancée lorsque vous modifiez TabItems.

<Window x:Class="ComboBoxInTabItemSpike.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <StackPanel> 
     <TabControl> 
      <TabItem Header="1"> 
       <ComboBox ItemsSource="{Binding MyList}" 
          SelectedItem="{Binding MySelect}"/> 
      </TabItem> 
      <TabItem Header="2"/> 
     </TabControl> 
     <TextBlock Text="{Binding MySelect}"/> 
    </StackPanel> 
</Window> 

using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Windows; 

namespace ComboBoxInTabItemSpike 
{ 
    public partial class Window1 : Window, INotifyPropertyChanged 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      MyList=new ObservableCollection<TestObject>(
       new[]{new TestObject("1"),new TestObject("2"),new TestObject("3") }); 
      DataContext = this; 
     } 

     public ObservableCollection<TestObject> MyList { get; set; } 

     private TestObject mySelect; 
     public TestObject MySelect 
     { 
      get { return mySelect; } 
      set{ mySelect = value; 
      if(PropertyChanged!=null) 
       PropertyChanged(this,new PropertyChangedEventArgs("MySelect"));} 
     } 

     public TestObject MySelectedItem 
     { 
      get { return (TestObject)GetValue(MySelectedItemProperty); } 
      set { SetValue(MySelectedItemProperty, value); } 
     } 

     public static readonly DependencyProperty MySelectedItemProperty = 
      DependencyProperty.Register("MySelectedItem", 
           typeof(TestObject), 
           typeof(Window1), 
           new UIPropertyMetadata(null)); 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 

    public class TestObject 
    { 
     public string Name { get; set; } 

     public TestObject(string name) 
     { 
      Name = name; 
     } 

     public override string ToString() 
     { 
      return Name; 
     } 
    } 
} 
+1

Lorsque le type de liste est un type de référence, il ne se comporte pas de la même manière. Voir ma mise à jour – Jose

0

Je vous recommande de vérifier les fixations. Si quelque chose d'autre dans votre application est en train de changer l'élément sélectionné ou la source des éléments, alors votre liaison sera rompue. Vous pouvez également rechercher dans Visual Studio dans la fenêtre de sortie s'il existe des erreurs.

0

Je pense que ce que vous pouvez manquer ici est une liaison TwoWay sur SelectedItem. Lorsque vous liez votre classe ViewModel qui contient le MyList (ItemsSource lié) et MyListSelection (lien à SelectedItem dans votre cas) aura toujours ces informations même si vous êtes allé à différents onglets. Donc, quand vous revenez à cet onglet, MyListSelection se reconnecte à ComboBoc.SelectedItem et vous serez bon. Essayez ceci et faites le moi savoir.

+1

La liaison SelectedItem est TwoWay par défaut. –

0

Je pense que cela pourrait être résolu avec un simple test nul.

public TestViewModel SelectedTest 
{ 
    get { return _SelectedTest; } 
    set 
    { 
     if(value != null) 
      _SelectedTest = value; 
     OnPropertyChanged("SelectedTest"); 
    } 
} 

Ceci est dû au fait que ComboBox a tendance à réinitialiser son SelectedIndex lorsqu'il est recyclé. Cette simple vérification null l'obligera à se reconnecter au dernier élément valide.

+0

Oui, c'est une option que j'ai employée plusieurs fois, mais l'application a beaucoup de comboboxes et de listes, c'est assez énervant de le faire à chaque fois. – Jose

+0

En effet, cela pourrait être assez ennuyeux, mais encore une fois avoir à augmenter la propriété a changé même sur chaque propriété est aussi ennuyeux. WPF est loin d'être parfait. GL –

+1

Cela n'est pas toujours acceptable car les valeurs nulles peuvent parfois être des valeurs valides dans une collection. Aussi qu'en est-il des cas où la propriété est en réalité une propriété de dépendance? Ensuite, vous devriez entrer dans les événements de notification Coerce et Change afin de faire quelque chose de similaire qui est juste un gâchis. À mon avis, ce n'est vraiment pas une solution acceptable en général. – jpierson

0

j'avais exactement le même problème avec un type de référence dans ma liste. La solution consistait à remplacer Equals() dans mon TestViewModel afin que WPF puisse effectuer une vérification d'égalité des valeurs (au lieu d'une vérification des références) entre les objets pour déterminer lequel est SelectedItem. Il m'est arrivé d'avoir un champ ID qui était vraiment la caractéristique d'identification d'un TestViewModel.

0

Ce comportement de la zone de liste déroulante doit être implémenté par le compilateur de façon plus efficace que ... IE le compilateur doit vérifier et voir si les types de ItemsSource et la valeur de référence de type de la propriété que SelectedItem est lié à la retournerai jamais une valeur qui est comparable

Il devrait avertir que vous pourriez envisager les impérieuses Equals() et GetHashCode() ...

perdu beaucoup de temps aujourd'hui !!