2014-04-19 14 views
5

Qualcuno può mostrarmi un semplice esempio di lavoro per un'applicazione MVVM WPF per impostare l'Origine elementi della casella combinata B in base a SelectedItem di ComboBox A?È necessario un esempio di lavoro SEMPLICE per impostare WPF MVVM Elementi ComboBox Origine basata su SelectedValue del secondo ComboBox

Sembra da quello che ho trovato su questo sito che diventa troppo complicato fin troppo rapidamente.

Qual è il modo "giusto" MVVM per farlo?

Grazie.

EDIT Ho aggiornato utilizzando l'esempio di Didier. Un estratto della mia XAML:

<ComboBox Name="BrowserStackDesktopOS" ItemsSource="Binding Platforms.AvailableBrowserStackDesktopOSes}" SelectedIndex="0" SelectedItem="{Binding Platforms.BrowserStackDesktopOSSelectedValue, Mode=TwoWay}"/> 

<ComboBox Name="BrowserStackDesktopOSVersion" ItemsSource="{Binding Platforms.AvailableBrowserStackDesktopOSVersions}" SelectedIndex="0" SelectedItem="{Binding Platforms.BrowserStackDesktopOSVersionSelectedValue, Mode=TwoWay}"/> 

<ComboBox Name="BrowserStackDesktopBrowser" ItemsSource="{Binding Platforms.AvailableBrowserStackDesktopBrowsers}" SelectedIndex="0" SelectedItem="{Binding Platforms.BrowserStackDesktopBrowserSelectedValue, Mode=TwoWay}"/> 

<ComboBox Name="BrowserStackDesktopBrowserVersion" ItemsSource="{Binding Platforms.AvailableBrowserStackDesktopBrowserVersions}" SelectedIndex="0" SelectedItem="{Binding Platforms.BrowserStackDesktopBrowserVersionSelectedValue, Mode=TwoWay}"/> 

E un esempio del mio codice dietro:

public string BrowserStackDesktopOSSelectedValue { 
     get { return (string)GetValue(BrowserStackDesktopOSSelectedValueProperty); } 
     set { SetValue(BrowserStackDesktopOSSelectedValueProperty, value); 
       AvailableBrowserStackDesktopOSVersions = AvailableBrowserStackDesktopPlatforms.GetOSVersions(BrowserStackDesktopOSSelectedValue); 
       NotifyPropertyChanged("BrowserStackDesktopOSSelectedValue"); 
     } 
    } 

Tuttavia quando si seleziona un valore per la prima ComboBox non succede nulla. Sto volendo la risorsa elementi del prossimo ComboBox a popolata.

Cosa ho fatto di sbagliato?

risposta

8

Fondamentalmente è necessario esporre nella raccolta di valori MVVM 2 per le selezioni di caselle combinate e due proprietà per i valori selezionati. All'inizio solo la prima collezione se riempita di valori. Quando il primo valore selezionato cambia, la seconda raccolta può essere compilata con valori appropriati. Ecco un esempio di implementazione:

codice dietro:

public partial class MainWindow : Window 
{ 

    public MainWindow() 
    { 
     InitializeComponent(); 

     //Set the data context of the window 
     DataContext = new TestVM(); 
    } 
} 


public class TestVM : INotifyPropertyChanged 
{ 

    #region Class attributes 

    protected static string[] firstComboValues = new string[] { "Choice_1", "Choice_2" }; 

    protected static string[][] secondComboValues = 
     new string[][] { 
       new string[] { "value_1_1", "value_1_2", "value_1_3" }, 
       new string[] { "value_2_1", "value_2_2", "value_2_3" } 
     }; 


    #endregion 

    #region Public Properties 

    #region FirstSelectedValue 

    protected string m_FirstSelectedValue; 

    /// <summary> 
    /// 
    /// </summary> 
    public string FirstSelectedValue 
    { 
     get { return m_FirstSelectedValue; } 
     set 
     { 
      if (m_FirstSelectedValue != value) 
      { 
       m_FirstSelectedValue = value; 
       UpdateSecondComboValues(); 
       NotifyPropertyChanged("FirstSelectedValue"); 
      } 
     } 
    } 

    #endregion 

    #region SecondSelectedValue 

    protected string m_SecondSelectedValue; 

    /// <summary> 
    /// 
    /// </summary> 
    public string SecondSelectedValue 
    { 
     get { return m_SecondSelectedValue; } 
     set 
     { 
      if (m_SecondSelectedValue != value) 
      { 
       m_SecondSelectedValue = value; 
       NotifyPropertyChanged("SecondSelectedValue"); 
      } 
     } 
    } 

    #endregion 

    #region FirstComboValues 

    protected ObservableCollection<string> m_FirstComboValues; 

    /// <summary> 
    /// 
    /// </summary> 
    public ObservableCollection<string> FirstComboValues 
    { 
     get { return m_FirstComboValues; } 
     set 
     { 
      if (m_FirstComboValues != value) 
      { 
       m_FirstComboValues = value; 
       NotifyPropertyChanged("FirstComboValues"); 
      } 
     } 
    } 

    #endregion 

    #region SecondComboValues 

    protected ObservableCollection<string> m_SecondComboValues; 

    /// <summary> 
    /// 
    /// </summary> 
    public ObservableCollection<string> SecondComboValues 
    { 
     get { return m_SecondComboValues; } 
     set 
     { 
      if (m_SecondComboValues != value) 
      { 
       m_SecondComboValues = value; 
       NotifyPropertyChanged("SecondComboValues"); 
      } 
     } 
    } 

    #endregion 

    #endregion 

    public TestVM() 
    { 
     FirstComboValues = new ObservableCollection<string>(firstComboValues); 
    } 

    /// <summary> 
    /// Update the collection of values for the second combo box 
    /// </summary> 
    protected void UpdateSecondComboValues() 
    { 
     int firstComboChoice; 
     for (firstComboChoice = 0; firstComboChoice < firstComboValues.Length; firstComboChoice++) 
     { 
      if (firstComboValues[firstComboChoice] == FirstSelectedValue) 
       break; 
     } 


     if (firstComboChoice == firstComboValues.Length)// just in case of a bug 
      SecondComboValues = null; 
     else 
      SecondComboValues = new ObservableCollection<string>(secondComboValues[firstComboChoice]); 

    } 


    #region INotifyPropertyChanged implementation 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void NotifyPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    #endregion 
} 

E il XAML associato

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    x:Name="window" x:Class="Testing1.MainWindow"> 

    <Grid> 

     <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Width=" 300"> 
      <Grid.RowDefinitions> 
       <RowDefinition/> 
       <RowDefinition Height="10"/> 
       <RowDefinition/> 
      </Grid.RowDefinitions> 

      <ComboBox x:Name="FirstOne" ItemsSource="{Binding FirstComboValues}" SelectedItem="{Binding FirstSelectedValue, Mode=TwoWay}"/> 

      <ComboBox x:Name="SecondOne" ItemsSource="{Binding SecondComboValues}" SelectedItem="{Binding SecondSelectedValue, Mode=TwoWay}" Grid.Row="2"/> 

     </Grid> 

    </Grid> 

</Window> 

Come si può vedere le SelectedValue proprietà di caselle combinate sono binded in modalità TwoWay così quando SelectedValue la proprietà della casella combinata cambia cambia il valore sul lato VM. E in FirstSelectedValue viene chiamato il metodo setter UpdateSecondComboValues() per aggiornare i valori per la seconda casella combinata.

EDIT:

Succede perché si mescolato sia INotifPropertyChanged e DependencyObject. Dovresti scegliere uno di loro. Solitamente si implementa INotifyPropertyChanged nella VM e il codice nel setter della proprietà funzionerà.

Se si eredita da DependencyObject, tuttavia, non si deve scrivere alcun codice nel setter/getter. Non verrà mai chiamato dal binding TwoWay. Chiamerà semplicemente GetValue (...) internamente. Per essere in grado di eseguire un'azione su DependencyProperty cambiare si deve dichiarare in modo diverso con una proprietà modificata gestore:

#region BrowserStackDesktopOSSelectedValue 

/// <summary> 
/// BrowserStackDesktopOSSelectedValue Dependency Property 
/// </summary> 
public static readonly DependencyProperty BrowserStackDesktopOSSelectedValue Property = 
    DependencyProperty.Register("BrowserStackDesktopOSSelectedValue ", typeof(string), typeof(YourVM), 
     new FrameworkPropertyMetadata((string)null, 
      new PropertyChangedCallback(OnBrowserStackDesktopOSSelectedValue Changed))); 

/// <summary> 
/// Gets or sets the BrowserStackDesktopOSSelectedValue property. This dependency property 
/// indicates .... 
/// </summary> 
public string BrowserStackDesktopOSSelectedValue 
{ 
    get { return (string)GetValue(BrowserStackDesktopOSSelectedValue Property); } 
    set { SetValue(BrowserStackDesktopOSSelectedValue Property, value); } 
} 

/// <summary> 
/// Handles changes to the BrowserStackDesktopOSSelectedValue property. 
/// </summary> 
private static void OnBrowserStackDesktopOSSelectedValue Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    YourVM target = (YourVM)d; 
    string oldBrowserStackDesktopOSSelectedValue = (string)e.OldValue; 
    string newBrowserStackDesktopOSSelectedValue = target.BrowserStackDesktopOSSelectedValue ; 
    target.OnBrowserStackDesktopOSSelectedValue Changed(oldBrowserStackDesktopOSSelectedValue , newBrowserStackDesktopOSSelectedValue); 
} 

/// <summary> 
/// Provides derived classes an opportunity to handle changes to the BrowserStackDesktopOSSelectedValue property. 
/// </summary> 
protected virtual void OnBrowserStackDesktopOSSelectedValue Changed(string oldBrowserStackDesktopOSSelectedValue , string newBrowserStackDesktopOSSelectedValue) 
{ 
    //Here write some code to update your second ComboBox content. 
    AvailableBrowserStackDesktopOSVersions = AvailableBrowserStackDesktopPlatforms.GetOSVersions(BrowserStackDesktopOSSelectedValue); 
} 

#endregion 

Tra l'altro ho sempre utilizzare Dr WPF snippets scrivere PS così si va molto più veloce.

+0

Grazie Didier, vedere il mio MODIFICA sopra. –

+0

Ok ho modificato la mia risposta. – Dmitry

+0

Grazie a Didier, questo ha funzionato. –