2010-01-28 37 views
7

Ok ho lavorato con WPF per un po ', ma ho bisogno di aiuto.WPF ComboBox SelectedItem

Ho un ComboBox come di seguito:

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

Ogni volta che mi allontano dal 1 scheda e poi tornare ad esso la selezione viene rimosso. Penso che la ragione sia che i controlli vengono distrutti quando escono dal campo di applicazione e poi rientrano. Ma in questo caso l'oggetto SelectedItem diventa null che non è proprio ciò che l'utente desiderava, è un evento dovuto all'interfaccia utente ciclo vitale.

Quindi mi chiedo quale sia la strada migliore da percorrere? Sto costruendo questa app con MVVM in modo da poter ignorare una chiamata impostata nella proprietà MyListSelection nel mio ViewModel ma ho i ComboBox dappertutto e non mi piace modificare il mio ViewModel per quello che considero un bug di WPF.

Potrei sottoclasse il ComboBox WPF, ma non c'è un evento SelectedItemChanging Posso solo aggiungere un gestore quando SelectedItem è cambiato.

Qualche idea?

UPDATE:

Ok, dopo aver battuto la testa contro il muro ho scoperto perché il mio problema non riusciva a riprodurre. Se il tipo di elemento dell'elenco è una classe per qualche motivo, l'oggetto SelectedItem viene impostato da WPF su null ma se è un tipo di valore non lo fa.

ecco la mia classe di test (VMBase è solo una classe astratta che implementa 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;} 
} 

Così quando cambio TestList di tipo INT e andare avanti e indietro tra le schede SelectedItem rimane lo stesso. Ma quando è di tipo TestViewModel SelectedTest viene impostato su null quando il tabitem diventa sfocato.

Cosa sta succedendo?

risposta

10

Ho lo stesso identico problema e fino ad ora non riuscivo a capire quale fosse il problema. Ho provato su 4 macchine diverse con lo stesso sistema operativo, la versione .Net e le specifiche hardware e ho potuto riprodurre il problema in due di esse, altre hanno funzionato bene. La soluzione che ho trovato che funziona per me è definire il binding SelectedItem prima di ItemsSource. Stranamente se seguo questo schema, tutto funziona come previsto. Detto questo, devi solo effettuare le seguenti operazioni:

<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

MODIFICATO dopo la modifica in OP. Ciao Jose, non riesco a riprodurre l'errore che hai citato. Quindi la tua ipotesi sul fatto che il Controllo sia distrutto è sbagliata. Il Combobox si comporta come previsto con il codice sottostante anche se ora sta usando un tipo di riferimento. Qualche altro pezzo del tuo codice deve entrare quando cambi 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

Quando il tipo di elenco è un tipo di riferimento, non si comporta allo stesso modo. Vedere il mio post aggiornato – Jose

0

Consiglierei di controllare gli attacchi. Se qualcos'altro nella tua app sta cambiando l'elemento selezionato o la fonte degli articoli, la tua associazione si interromperà. Puoi anche guardare in Visual Studio nella finestra di output per vedere se ci sono errori.

0

Penso che quello che potrebbe mancare qui è un binding TwoWay su SelectedItem. Quando si associa la classe ViewModel che contiene MyList (ItemsSource associata) e MyListSelection (Bond to SelectedItem nella propria custodia), tali informazioni saranno sempre disponibili anche se si è passati a schede diverse. Quindi, quando torni in questa scheda, MyListSelection tornerà di nuovo a ComboBoc.SelectedItem e sarai bravo. Prova questo e fammi sapere.

+1

Il binding SelectedItem è TwoWay per impostazione predefinita. –

0

Penso che questo possa essere risolto con un semplice controllo nullo.

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

Questo perché ComboBox ha la tendenza a ripristinare il suo SelectedIndex quando riciclato. Questo semplice controllo Null lo costringerà a riassociare all'ultimo elemento valido.

+0

Sì, questa è un'opzione che ho utilizzato molte volte, ma l'app ha un sacco di combobox e listview, è abbastanza fastidioso farlo ogni volta. – Jose

+0

In effetti questo potrebbe essere abbastanza fastidioso, ma poi di nuovo dover aumentare la proprietà cambiata anche su ogni proprietà è anche fastidioso. WPF è tutt'altro che perfetto. GL –

+1

Questo non è sempre accettabile poiché i valori nulli a volte possono essere valori validi all'interno di una raccolta. Inoltre, che dire dei casi in cui la proprietà è in realtà una proprietà di dipendenza? Quindi dovresti vedere gli eventi di notifica di Coerce e Change per fare qualcosa di simile che è solo un casino. Secondo me, in realtà non è una soluzione accettabile in generale. – jpierson

0

ho avuto lo stesso problema esatto con un tipo di riferimento nella mia lista. La soluzione era di sovrascrivere Equals() nel mio TestViewModel in modo che WPF sarebbe in grado di eseguire un controllo di uguaglianza di valore (anziché un controllo di riferimento) tra gli oggetti per determinare quale sia l'oggetto SelectedItem. Il mio caso aveva un campo ID che era davvero la caratteristica identificativa di un TestViewModel.

0

Questo comportamento da parte della casella combinata, dovrebbe essere implementato dal compilatore in modo migliore di quello che è ... IE il compilatore dovrebbe controllare e vedere se i tipi per ItemsSource e il valore di riferimento del tipo della proprietà che l'oggetto SelectedItem è destinata a potrà mai restituire il valore che è paragonabile

dovrebbe avvertire che si potrebbe prendere in considerazione l'override dei metodi Equals GetHashCode()() e ...

sprecato un sacco di tempo su questo oggi !!