none
【ComboBoxColumnとTextColumnを連動させる方法】 RRS feed

  • 質問

  • お世話になります。

    ComboBoxColumnの値を変更した場合、連動してTextColumnの値も変更させようとしています。

    ComboBxoColumn上でEnterキーやTABキー(TextColumnの編集状態)にした場合、
    TextColumnの値も変更されるのですが
    マウスを使って、フォーカスを移動させた場合、TextColumnの値が変更されないようです。

    XAMLの設定は、以下のように設定しています。

        <my:DataGrid.Columns>
            <my:DataGridComboBoxColumn 
                Header="CD"
                x:Name="mComboBox"
                SelectedValueBinding="{Binding FirstName}"
                DisplayMemberPath="Code"
                SelectedValuePath="FirstName"
                >
            </my:DataGridComboBoxColumn>
            <my:DataGridTextColumn Header="名前" Binding="{Binding FirstName}" />
        </my:DataGrid.Columns>
    
    

    通常の、ComboBoxとLabelを連動させている箇所を参考にTextColumnのバインド記述を

    Binding="{Binding ElementName=mComboBox, Path=SelectedItem.FirstName}
    


    のようにしてみたのですが、動作していないようです。
    連動させる方法をご存知の方がいらっしゃいましたら、宜しくお願いします。
    • 編集済み Kinsuma 2009年4月27日 2:42
    2009年4月27日 2:40

回答

  • えムナウさんがおしゃっている【ComboBox用のテーブルを作成】とは
    単に、DataSetのインスタンスを別に作成しちゃえば良いってことでしょうか。

    Dim Grid用 As 社員(型付DataSet)
    Dim ComboBox用 As 社員(型付DataSet)
    ※型付DataSetもxsdを作成するようにしました
    
    もともと、Grid用のDataSetをComboBoxにも適用させていたのですが
    動作確認している時に、挙動がおかしいのに気づき、現在は上記のように別で作成している状態です。


    挙動がおかしいのに気づいて別インスタンスを作成していたんですね。
    失礼しました。以前の質問の状況でやられていたとばかり思っていました。


    私にとって不思議なのは、TextColumnを<my:DataGridTextColumn Header="名前" Binding="{Binding FirstName}" />
    のように設定しただけで、ComboBoxのCodeから紐づくFirstNameと連携されているのか?
    このFirstNameはGrid用のDataSetと関連付けられているはずなのに...
    むしろこの、TextColumnのBinding方法が違うのでは...に行き着いてしまったわけです。

    設定としては、正しいのでしょうか??
    ※Windowsアプリでも確認してみたいと思います。
    ComboBoxの選択は mComboBox.ItemsSource DisplayMemberPath SelectedValuePath で決まります。
    ComboBox用のテーブルの一つのRowを選択したことになります。
    ComboBoxではそのRowの SelectValuePath で指定したColumnの値が SelectedValue に設定されます。

    DataGridは その行について DataGridComboBoxColumn の SelectedValue が変わったことを検知して SelectedValueBinding に指定されている Columnの値を変更します。
    したがって DataGridTextColumn も Grid用のテーブルの選択されている Row に従った値に変わります。  

    つまり、ComboBox用のテーブルは値は変化しません。
    Grid用のテーブルの FirstName のみが ComboBox で選択された値に変わります。
    Grid用のテーブルの Code も変化しません。(XAML上に出てきませんから)
    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12
    • 回答としてマーク Kinsuma 2009年4月28日 8:45
    2009年4月28日 7:59
  • DataRow は INotifyPropertyChanged をサポートしていないので 実はWPFで使うのにあまり適さないんです。
    ObservableCollection (INotifyCollectionChanged) と INotifyPropertyChanged を使ったクラスを使用してください。
    こんな風に INotifyPropertyChanged をちゃんと実行すれば大丈夫です。

    データベースからなら LinqToSQL とか LinqToEntities とかは INotifyCollectionChanged や INotifyPropertyChanged サポートしています。



    Public Class PersonRow
        Implements ComponentModel.INotifyPropertyChanged
    
        Public Sub New()
            _code = ""
            _firstName = ""
        End Sub
    
        Public Sub New(ByVal code As String, ByVal firstName As String)
            _code = code
            _firstName = firstName
        End Sub
    
        Public Event PropertyChanged As ComponentModel.PropertyChangedEventHandler Implements ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Public Sub NotifyPropertyChanged(ByVal propertyName As String)
            RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs(propertyName))
        End Sub
    
        Private _code As String
        Public Property Code() As String
            Get
                Return _code
            End Get
            Set(ByVal value As String)
                If value <> _code Then
                    _code = value
                    NotifyPropertyChanged("Code")
                End If
            End Set
        End Property
    
        Private _firstName As String
        Public Property FirstName() As String
            Get
                Return _firstName
            End Get
            Set(ByVal value As String)
                If value <> _firstName Then
                    _firstName = value
                    NotifyPropertyChanged("FirstName")
                End If
            End Set
        End Property
    End Class







            Dim list As New ObjectModel.ObservableCollection(Of PersonRow)
            list.Add(New PersonRow("001", "001さん"))
            list.Add(New PersonRow("002", "002さん"))
            list.Add(New PersonRow("003", "003さん"))
            list.Add(New PersonRow("004", "004さん"))
            DataGrid1.DataContext = list
    
    



    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12
    • 編集済み Mnow1959 2009年4月28日 9:50
    • 回答としてマーク Kinsuma 2009年4月30日 4:34
    2009年4月28日 9:41

すべての返信

  • Windows のWPFToolkitで確認してみましたがマウスでの動作は正しくしています。

    ただ、初期表示の段階からComboBoxを変更すると(たとえば001を002に)、code=001,FirstName=002さんになります。
    もともとのcode=002,FirstName=002さんもあるのでSelectedValuePath="FirstName"がどっちを参照するかはDataGridの実装しだいになります。

    そのような異常な状態での実験はやめて、ComboBox用のテーブルを作成することをお勧めします。

    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12
    2009年4月28日 5:27
  • えムナウさん たびたびお世話になります。

    >そのような異常な状態での実験
    すみませんです^^; WPFの動作が良く分からず投稿しております。

    えムナウさんがおしゃっている【ComboBox用のテーブルを作成】とは
    単に、DataSetのインスタンスを別に作成しちゃえば良いってことでしょうか。

    Dim Grid用 As 社員(型付DataSet)
    Dim ComboBox用 As 社員(型付DataSet)
    ※型付DataSetもxsdを作成するようにしました
    


    もともと、Grid用のDataSetをComboBoxにも適用させていたのですが
    動作確認している時に、挙動がおかしいのに気づき、現在は上記のように別で作成している状態です。

    私にとって不思議なのは、TextColumnを<my:DataGridTextColumn Header="名前" Binding="{Binding FirstName}" />
    のように設定しただけで、ComboBoxのCodeから紐づくFirstNameと連携されているのか?
    このFirstNameはGrid用のDataSetと関連付けられているはずなのに...
    むしろこの、TextColumnのBinding方法が違うのでは...に行き着いてしまったわけです。

    設定としては、正しいのでしょうか??
    ※Windowsアプリでも確認してみたいと思います。
    • 編集済み Kinsuma 2009年4月28日 6:42 体裁
    2009年4月28日 6:40
  • えムナウさんがおしゃっている【ComboBox用のテーブルを作成】とは
    単に、DataSetのインスタンスを別に作成しちゃえば良いってことでしょうか。

    Dim Grid用 As 社員(型付DataSet)
    Dim ComboBox用 As 社員(型付DataSet)
    ※型付DataSetもxsdを作成するようにしました
    
    もともと、Grid用のDataSetをComboBoxにも適用させていたのですが
    動作確認している時に、挙動がおかしいのに気づき、現在は上記のように別で作成している状態です。


    挙動がおかしいのに気づいて別インスタンスを作成していたんですね。
    失礼しました。以前の質問の状況でやられていたとばかり思っていました。


    私にとって不思議なのは、TextColumnを<my:DataGridTextColumn Header="名前" Binding="{Binding FirstName}" />
    のように設定しただけで、ComboBoxのCodeから紐づくFirstNameと連携されているのか?
    このFirstNameはGrid用のDataSetと関連付けられているはずなのに...
    むしろこの、TextColumnのBinding方法が違うのでは...に行き着いてしまったわけです。

    設定としては、正しいのでしょうか??
    ※Windowsアプリでも確認してみたいと思います。
    ComboBoxの選択は mComboBox.ItemsSource DisplayMemberPath SelectedValuePath で決まります。
    ComboBox用のテーブルの一つのRowを選択したことになります。
    ComboBoxではそのRowの SelectValuePath で指定したColumnの値が SelectedValue に設定されます。

    DataGridは その行について DataGridComboBoxColumn の SelectedValue が変わったことを検知して SelectedValueBinding に指定されている Columnの値を変更します。
    したがって DataGridTextColumn も Grid用のテーブルの選択されている Row に従った値に変わります。  

    つまり、ComboBox用のテーブルは値は変化しません。
    Grid用のテーブルの FirstName のみが ComboBox で選択された値に変わります。
    Grid用のテーブルの Code も変化しません。(XAML上に出てきませんから)
    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12
    • 回答としてマーク Kinsuma 2009年4月28日 8:45
    2009年4月28日 7:59
  • えムナウさん 詳しい説明ありがとう御座います!

    ComboBoxの SelectedValueBindingの設定値が、GridのColumn値を示していたんですね。
    それで、TextColumnの値も変更される。。。なるほどです!

    で、そもそもの質問内容に間違いがありまして^^;
    表示されている既存行に対して、ComboBoxの値を変更するとTextColumnの値も変更されていました!
    これは、正しい動作です。

    しかし、新規行の時には正しく動作しないようです。
    ※設定される時もあるような。。。

    何か設定値があるのでしょうか。

    2009年4月28日 8:45
  • DataRow は INotifyPropertyChanged をサポートしていないので 実はWPFで使うのにあまり適さないんです。
    ObservableCollection (INotifyCollectionChanged) と INotifyPropertyChanged を使ったクラスを使用してください。
    こんな風に INotifyPropertyChanged をちゃんと実行すれば大丈夫です。

    データベースからなら LinqToSQL とか LinqToEntities とかは INotifyCollectionChanged や INotifyPropertyChanged サポートしています。



    Public Class PersonRow
        Implements ComponentModel.INotifyPropertyChanged
    
        Public Sub New()
            _code = ""
            _firstName = ""
        End Sub
    
        Public Sub New(ByVal code As String, ByVal firstName As String)
            _code = code
            _firstName = firstName
        End Sub
    
        Public Event PropertyChanged As ComponentModel.PropertyChangedEventHandler Implements ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Public Sub NotifyPropertyChanged(ByVal propertyName As String)
            RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs(propertyName))
        End Sub
    
        Private _code As String
        Public Property Code() As String
            Get
                Return _code
            End Get
            Set(ByVal value As String)
                If value <> _code Then
                    _code = value
                    NotifyPropertyChanged("Code")
                End If
            End Set
        End Property
    
        Private _firstName As String
        Public Property FirstName() As String
            Get
                Return _firstName
            End Get
            Set(ByVal value As String)
                If value <> _firstName Then
                    _firstName = value
                    NotifyPropertyChanged("FirstName")
                End If
            End Set
        End Property
    End Class







            Dim list As New ObjectModel.ObservableCollection(Of PersonRow)
            list.Add(New PersonRow("001", "001さん"))
            list.Add(New PersonRow("002", "002さん"))
            list.Add(New PersonRow("003", "003さん"))
            list.Add(New PersonRow("004", "004さん"))
            DataGrid1.DataContext = list
    
    



    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12
    • 編集済み Mnow1959 2009年4月28日 9:50
    • 回答としてマーク Kinsuma 2009年4月30日 4:34
    2009年4月28日 9:41
  • えムナウさん ご返答ありがとう御座います!

    しかもコード(VBで)まで添えていただきまして...^^;

    INotifyPropertyChanged で WPF に 教えてあげるわけですね。
    なるほど。。。
    ※本当に勉強になります!

    実際に、クラスを作成して、動作させてみたところ思い通りの動作を確認できました。

    これでまた一歩前進です。 ありがとう御座いました。
    2009年4月30日 4:34