2番目のComboBoxのSelectedValueに基づいてWPFMVVM ComboBoxItemsSourceを設定する簡単な作業例が必要

2014年04月19日に質問されました。  ·  閲覧回数 11.2k回  ·  ソース

Andrew Gray picture
2014年04月19日

コンボボックスAのSelectedItemに基づいてコンボボックスBのItemsSourceを設定するWPFMVVMアプリケーションの簡単な作業例を誰かに見せてもらえますか?

このサイトで私が見つけたものから、それは非常にすぐに複雑になりすぎるようです。

それを成し遂げるための「正しい」MVVMの方法は何ですか?

ありがとうございました。

編集私はディディエの例を使用して更新しました。 私の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}"/>

そして、背後にある私のコードの例:

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

ただし、最初のComboBoxの値を選択しても、何も起こりません。 次のComboBoxのItemsourceを入力してほしい。

私は何を間違えましたか?

回答

Dmitry picture
2014年04月19日
9

基本的に、コンボボックスの選択用の値のコレクション2と、選択した値用の2つのプロパティをMVVMで公開する必要があります。

最初は、値が入力されている場合は最初のコレクションのみ。 最初に選択した値が変更されると、2番目のコレクションに適切な値が入力されます。 実装例は次のとおりです。

背後にあるコード:

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
}

そして関連するXAML

<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>

ご覧のとおり、コンボボックスのSelectedValueプロパティはTwoWayモードでバインドされているため、コンボボックスのSelectedValueプロパティが変更されると、VM側の値が変更されます。 また、 FirstSelectedValueプロパティセッターでは、2番目のコンボボックスの値を更新するためにUpdateSecondComboValues()メソッドが呼び出されます。

編集:

これは、INotifPropertyChangedとDependencyObjectの両方を混在させたために発生します。 そのうちの1つを選択する必要があります。 通常、VMにINotifyPropertyChangedを実装すると、プロパティセッターのコードが機能します。

ただし、DependencyObjectから継承する場合は、setter / getterにコードを記述しないでください。 TwoWayバインディングによって呼び出されることはありません。 内部でGetValue(...)を呼び出すだけです。 DependencyPropertyの変更に対してアクションを実行できるようにするには、プロパティ変更ハンドラーを使用して別の方法で宣言する必要があります。

#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

ちなみに、私は常にDr WPFスニペットを使用してDPを作成しているので、はるかに高速になります。