none
DataGridTemplateColumn下のCheckBoxの値の取得について RRS feed

  • 質問

  • <Window x:Class="DataGrindBindTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid Margin="10,0,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <DockPanel Grid.Row="0">
                <DataGrid x:Name="ItemListGrid" ItemsSource="{Binding}" AutoGenerateColumns="False"  PreviewMouseLeftButtonDown="ItemListGrid_PreviewMouseLeftButtonDown">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Width="60" Binding="{Binding Path=id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="品番" Width="40" Binding="{Binding Path=product_id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="商品名" Width="60" Binding="{Binding Path=item_name}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="よみ" Width="60" Binding="{Binding Path=item_yomi}" IsReadOnly="True"/>
                        <DataGridTemplateColumn Header="表示">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=display, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
        </Grid>
    </Window>
    


    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace DataGrindBindTest
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
    
            public class Item
            {
                public int id { get; set; }
                public String product_id { get; set; }
                public String item_name { get; set; }
                public String item_yomi { get; set; }
                public bool display { get; set; }
    
            }
    
            public ObservableCollection<Item> ItemCollection;
    
            public MainWindow()
            {
                InitializeComponent();
     
                ItemCollection = new ObservableCollection<Item>();
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true });
                ItemCollection.Add(new Item() { id = 1, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true });
     
                ItemListGrid.ItemsSource = ItemCollection;
            }
    
            private void ItemListGrid_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                DataGrid dataGrid = sender as DataGrid;
                if (dataGrid.SelectedItems.Count > 0)
                {
                    Item Item = (Item)dataGrid.SelectedItems[0];
                    int id = Item.id;
                    bool display = Item.display;
    
                    //取得したIDとdisplayを元にデータベースを更新する予定
                }
            }
    
    
    
    
        }
    }
    

    お世話になります。

    上記のようなコードでデータバインドを行っています。
    WPFの仕様として、DataGridCheckBoxColumnは一度クリックしてアクティブにしないと
    チェックを入れられない仕様とのことで、DataGridTemplateColumnを用いてチェックボックスを配置しシングルクリックでチェックされるようにしています。

    このDataGridTemplateColumn下にあるチェックボックスの値が変更されたことを検知し、
    その行のIDとチェックボックスの値を取得し、その2つを元にデータベースを更新するような動作をさせたいと考えております。

    調べている中でPreviewMouseLeftButtonDownイベントを使うといいらしいと言うところまでは調べられたのですが
    取得できるのはフォーカスが移動する前の値のようです(クリックする「前」の状態を取得する為と理解しています)。

    目的はシングルクリックでチェックボックスにチェックを入れて、チェックボックスの状態とそれをデーターベースにひもづけるIDを
    取得することなのですがどのような方法がありますでしょうか。

    どうぞよろしくお願いいたします。


    2014年6月29日 11:37

回答

  • 一番素直な実装としてはItemクラスが変更されたことを検出することです。

    そのためにはSystem.ComponentModel.INotifyPropertyChangedインターフェースのイベントが普通は使われます。
    INotifyPropertyChangedを使用すればWPFが自動的にプロパティの変更を検知してくれるので、コードからプロパティの値を更新したときにもユーザーインターフェース側の表示更新を自動でしてもらえます。
    他にはコレクションが変更されたことを検出してくれるINotifyCollectionChangedインターフェースのイベントもあります。これはObservableCollection<T>が使用しているのでコレクションの追加削除を行うと自動で表示を更新してもらえます。

    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
    
            public class Item : System.ComponentModel.INotifyPropertyChanged 
            {
                public int id
                {
                    get { return _id; }
                    set
                    {
                        if(_id != value)
                        {
                            _id = value;
                            OnPropertyChanged("id");
                        }
                    }
                }
                private int _id;
    
                public String product_id
                {
                    get{return _product_id;}
                    set
                    {
                        if(_product_id != value)
                        {
                            _product_id = value;
                            OnPropertyChanged("product_id");
                        }
                    }
                }
                    private String _product_id;
    
                public String item_name
                {
                    get { return _item_name; }
                    set
                    {
                        if (_item_name != value)
                        {
                            _item_name = value;
                            OnPropertyChanged("item_name");
                        }
                    }
                }
                private String _item_name;
    
                public String item_yomi
                {
                    get {return _item_yomi;}
                    set
                    {
                        if(_item_yomi  !=value)
                        {
                            _item_yomi = value;
                            OnPropertyChanged("item_yomi");
                        }
                    }
    
           
                }
                private  String _item_yomi;
    
                public bool display 
                {
                    get { return _display; }
                    set{
                        if (_display != value)
                        {
                            _display = value;
                            OnPropertyChanged("display ");
                        }
                    }
                }
                private bool _display;
    
    
    
                public bool IsPropertyChanged { get; set; }
    
    
                #region INotifyPropertyChanged メンバー
    
                /// <summary>
                /// プロパティが変化したことを通知するイベント
                /// </summary>
    
                public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
                /// <summary>
                /// プロパティが変化したら呼び出す
                /// </summary>
                /// <param name="name">変化したプロパティの名前</param>
                protected virtual void OnPropertyChanged(string name)
                {
                    IsPropertyChanged = true;
                    System.ComponentModel.PropertyChangedEventHandler pc = PropertyChanged;
                    if (pc != null)
                    {
                        pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
                    }
                }
    
                #endregion
            }
    
            public ObservableCollection<Item> ItemCollection;
    
            public MainWindow()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
                //コレクションが変化したときに発生するイベントに処理を登録する
                ItemCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ItemCollection_CollectionChanged);
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true ,IsPropertyChanged = false });
                ItemCollection.Add(new Item() { id = 1, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true, IsPropertyChanged = false });
    
                ItemListGrid.ItemsSource = ItemCollection;
            }
    
            void ItemCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                //コレクションに追加された時と、削除された時に自動でイベントを登録・解除する。
                if (e.OldItems != null)
                {
                    foreach (Item item in e.OldItems)
                    {
                        item.PropertyChanged += item_PropertyChanged;
                    }
                }
                if (e.NewItems != null)
                {
                    foreach (Item item in e.NewItems)
                    {
                        item.PropertyChanged += item_PropertyChanged;
                    }
                }
            }
    
            void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                Item item = (Item)sender;
                System.Diagnostics.Debug.WriteLine(item.id.ToString() + "\t"+e.PropertyName +"\t");
                if (item.display)
                {
                    //取得したIDとdisplayを元にデータベースを更新する
                }
            }
    
            private void ItemListGrid_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                //使用しない
    
                //DataGrid dataGrid = sender as DataGrid;
                //if (dataGrid.SelectedItems.Count > 0)
                //{
                //    Item Item = (Item)dataGrid.SelectedItems[0];
                //    int id = Item.id;
                //    bool display = Item.display;
    
                //    //取得したIDとdisplayを元にデータベースを更新する予定
                //}
            }
        }
    }

    Itemクラスを変更できないという事情があるのでしたら、

    public class ItemVM : System.ComponentModel.INotifyPropertyChanged
    {
        public ItemVM(Item item)
        {
            this._item = item;
        }
    
        private Item _item;
    
        public int id
        {
            get { return _item.id; }
            set
            {
                if (_item.id != value)
                {
                    _item.id = value;
                    OnPropertyChanged("id");
                }
            }
        }
    
        public String product_id
        {
            get { return _item.product_id; }
            set
            {
                if (_item.product_id != value)
                {
                    _item.product_id = value;
                    OnPropertyChanged("product_id");
                }
            }
        }
    
        public String item_name
        {
            get { return _item.item_name; }
            set
            {
                if (_item.item_name != value)
                {
                    _item.item_name = value;
                    OnPropertyChanged("item_name");
                }
            }
        }
    
        public String item_yomi
        {
            get { return _item.item_yomi; }
            set
            {
                if (_item.item_yomi != value)
                {
                    _item.item_yomi = value;
                    OnPropertyChanged("item_yomi");
                }
            }
    
    
        }
    
        public bool display
        {
            get { return _item.display; }
            set
            {
                if (_item.display != value)
                {
                    _item.display = value;
                    OnPropertyChanged("display ");
                }
            }
        }
    
    
    
        public bool IsPropertyChanged { get; set; }
    
    
        #region INotifyPropertyChanged メンバー
    
        /// <summary>
        /// プロパティが変化したことを通知するイベント
        /// </summary>
    
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
        /// <summary>
        /// プロパティが変化したら呼び出す
        /// </summary>
        /// <param name="name">変化したプロパティの名前</param>
        protected virtual void OnPropertyChanged(string name)
        {
            IsPropertyChanged = true;
            System.ComponentModel.PropertyChangedEventHandler pc = PropertyChanged;
            if (pc != null)
            {
                pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
            }
        }
    
        #endregion
    }

    のようにラップするクラスを作って、DataContextに渡すことになるでしょう。

    これもできない場合は、ToggleButton.Checked,UncheckedというRoutedイベントを捕まえることで,DataGridの内側にあるCheckBoxの変更を知ることができます。(CheckBoxはToggleButtonを継承しています)

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid Margin="10,0,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <DockPanel Grid.Row="0">
                <DataGrid x:Name="ItemListGrid" ItemsSource="{Binding}" AutoGenerateColumns="False" 
                          ToggleButton.Checked="ItemListGrid_Checked" 
                          ToggleButton.Unchecked="ItemListGrid_Unchecked"
                          TextBoxBase.TextChanged="ItemListGrid_TextChanged">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Width="60" Binding="{Binding Path=id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="品番" Width="40" Binding="{Binding Path=product_id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="商品名" Width="60" Binding="{Binding Path=item_name}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="よみ" Width="60" Binding="{Binding Path=item_yomi}" IsReadOnly="True"/>
                        <DataGridTemplateColumn Header="表示">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=display, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
        </Grid>
    </Window>

    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
    
            public class Item
            {
                public int id { get; set; }
                public String product_id { get; set; }
                public String item_name { get; set; }
                public String item_yomi { get; set; }
                public bool display { get; set; }
            }
    
    
            public ObservableCollection<Item> ItemCollection;
    
            public MainWindow()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true });
                ItemCollection.Add(new Item() { id = 1, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true });
    
                ItemListGrid.ItemsSource = ItemCollection;
            }
    
            private void ItemListGrid_Checked(object sender, RoutedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void ItemListGrid_TextChanged(object sender, TextChangedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void ItemListGrid_Unchecked(object sender, RoutedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void OnCellChange(RoutedEventArgs e)
            {
                DataGridRow row = FindRow(e.OriginalSource);
                if (row != null)
                {   //見つけたDataGridRowが表示しているデータを取得する
                    Item item = this.ItemListGrid.ItemContainerGenerator.ItemFromContainer(row) as Item;
                    if (item != null)
                    {
                        System.Diagnostics.Debug.WriteLine(item.id.ToString());
                        if (item.display)
                        {
                            //取得したIDとdisplayを元にデータベースを更新する
                        }
                    }
                }
            }
    
            private DataGridRow FindRow(object originalSouce)
            {//イベントを発生させた何かの親をたどってDataGridRowを探す
                DataGridRow row=null;
                DependencyObject d=originalSouce as DependencyObject;
                while (d != null && row == null)
                {
                    d=System.Windows.Media.VisualTreeHelper.GetParent(d);
                    row = d as DataGridRow;
                }
                return row;
            }
        }
    }
    ほかにも方法はありますが、だいたいこの3種類を使えれば大抵のデータ変更の検出は行えるようになります。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


    2014年6月29日 12:58
  • ビヘイビアを使った方法も載せておきます。

    <Window x:Class="test2010wpf.DataGridBindTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            Title="DataGridBindTest" Height="350" Width="525">
        <Grid Margin="10,0,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <DockPanel Grid.Row="0">
                <DataGrid x:Name="ItemListGrid" ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Width="60" Binding="{Binding Path=id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="品番" Width="40" Binding="{Binding Path=product_id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="商品名" Width="60" Binding="{Binding Path=item_name}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="よみ" Width="60" Binding="{Binding Path=item_yomi}" IsReadOnly="True"/>
                        <DataGridTemplateColumn Header="表示">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=display, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Click">
                                                <i:InvokeCommandAction 
                                                    Command="{Binding ClickCommand, 
                                                                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                                                    CommandParameter="{Binding }"
                                                                       />
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </CheckBox>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
        </Grid>
    </Window>


    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Collections.ObjectModel;
    
    namespace test2010wpf
    {
        /// <summary>
        /// DataGridBindTest.xaml の相互作用ロジック
        /// </summary>
        public partial class DataGridBindTest : Window
        {
            public DataGridBindTest()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true });
                ItemCollection.Add(new Item() { id = 2, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true });
    
                this.DataContext = this;
    
                ClickCommand = new DelegateCommand<Item>(Click);
            }
    
            public ObservableCollection<Item> ItemCollection { get; set; }
    
            public ICommand ClickCommand { get; private set; }
    
            private void Click(Item item)
            {
                System.Diagnostics.Debug.WriteLine(item.id);
            }
        }
    
        public class Item
        {
            public int id { get; set; }
            public String product_id { get; set; }
            public String item_name { get; set; }
            public String item_yomi { get; set; }
            public bool display { get; set; }
    
        }
    }

    Blendがインストールされていない状態、すなわちビヘイビアが使えない状態の場合は、以下からダウンロードして下さい。

    Microsoft Expression Blend 4 Software Development Kit (SDK) for .NET 4
    http://www.microsoft.com/ja-jp/download/details.aspx?id=10801

    (追記)
    DelegeteCommandは多くの場所で作成、紹介されているので、検索してみて下さい。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年6月30日 1:36
    モデレータ
  • IsPropertyChangedを追加する必要はありません。

    現在はdisplayプロパティの変更をしたタイミングで逐次DB更新のようですが、複数のItemをまとめて更新するようにした場合に変更したItemを見つけるためのフラグにできます。
    また、display列以外の例えばitem_name列でも変更できるようにした場合に、各プロパティ毎に変更されたことを記述しなくともOnPropertyChangedを呼び出すことで変更されたことをフラグに反映することが可能になります。

    サンプルとして提示したコードでは必要があって追加しているわけではありませんが、今後機能が拡張された時に、こういったやり方もあるという意味で書いてあります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2014年7月1日 3:54

すべての返信

  • 一番素直な実装としてはItemクラスが変更されたことを検出することです。

    そのためにはSystem.ComponentModel.INotifyPropertyChangedインターフェースのイベントが普通は使われます。
    INotifyPropertyChangedを使用すればWPFが自動的にプロパティの変更を検知してくれるので、コードからプロパティの値を更新したときにもユーザーインターフェース側の表示更新を自動でしてもらえます。
    他にはコレクションが変更されたことを検出してくれるINotifyCollectionChangedインターフェースのイベントもあります。これはObservableCollection<T>が使用しているのでコレクションの追加削除を行うと自動で表示を更新してもらえます。

    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
    
            public class Item : System.ComponentModel.INotifyPropertyChanged 
            {
                public int id
                {
                    get { return _id; }
                    set
                    {
                        if(_id != value)
                        {
                            _id = value;
                            OnPropertyChanged("id");
                        }
                    }
                }
                private int _id;
    
                public String product_id
                {
                    get{return _product_id;}
                    set
                    {
                        if(_product_id != value)
                        {
                            _product_id = value;
                            OnPropertyChanged("product_id");
                        }
                    }
                }
                    private String _product_id;
    
                public String item_name
                {
                    get { return _item_name; }
                    set
                    {
                        if (_item_name != value)
                        {
                            _item_name = value;
                            OnPropertyChanged("item_name");
                        }
                    }
                }
                private String _item_name;
    
                public String item_yomi
                {
                    get {return _item_yomi;}
                    set
                    {
                        if(_item_yomi  !=value)
                        {
                            _item_yomi = value;
                            OnPropertyChanged("item_yomi");
                        }
                    }
    
           
                }
                private  String _item_yomi;
    
                public bool display 
                {
                    get { return _display; }
                    set{
                        if (_display != value)
                        {
                            _display = value;
                            OnPropertyChanged("display ");
                        }
                    }
                }
                private bool _display;
    
    
    
                public bool IsPropertyChanged { get; set; }
    
    
                #region INotifyPropertyChanged メンバー
    
                /// <summary>
                /// プロパティが変化したことを通知するイベント
                /// </summary>
    
                public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
                /// <summary>
                /// プロパティが変化したら呼び出す
                /// </summary>
                /// <param name="name">変化したプロパティの名前</param>
                protected virtual void OnPropertyChanged(string name)
                {
                    IsPropertyChanged = true;
                    System.ComponentModel.PropertyChangedEventHandler pc = PropertyChanged;
                    if (pc != null)
                    {
                        pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
                    }
                }
    
                #endregion
            }
    
            public ObservableCollection<Item> ItemCollection;
    
            public MainWindow()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
                //コレクションが変化したときに発生するイベントに処理を登録する
                ItemCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ItemCollection_CollectionChanged);
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true ,IsPropertyChanged = false });
                ItemCollection.Add(new Item() { id = 1, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true, IsPropertyChanged = false });
    
                ItemListGrid.ItemsSource = ItemCollection;
            }
    
            void ItemCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                //コレクションに追加された時と、削除された時に自動でイベントを登録・解除する。
                if (e.OldItems != null)
                {
                    foreach (Item item in e.OldItems)
                    {
                        item.PropertyChanged += item_PropertyChanged;
                    }
                }
                if (e.NewItems != null)
                {
                    foreach (Item item in e.NewItems)
                    {
                        item.PropertyChanged += item_PropertyChanged;
                    }
                }
            }
    
            void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                Item item = (Item)sender;
                System.Diagnostics.Debug.WriteLine(item.id.ToString() + "\t"+e.PropertyName +"\t");
                if (item.display)
                {
                    //取得したIDとdisplayを元にデータベースを更新する
                }
            }
    
            private void ItemListGrid_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                //使用しない
    
                //DataGrid dataGrid = sender as DataGrid;
                //if (dataGrid.SelectedItems.Count > 0)
                //{
                //    Item Item = (Item)dataGrid.SelectedItems[0];
                //    int id = Item.id;
                //    bool display = Item.display;
    
                //    //取得したIDとdisplayを元にデータベースを更新する予定
                //}
            }
        }
    }

    Itemクラスを変更できないという事情があるのでしたら、

    public class ItemVM : System.ComponentModel.INotifyPropertyChanged
    {
        public ItemVM(Item item)
        {
            this._item = item;
        }
    
        private Item _item;
    
        public int id
        {
            get { return _item.id; }
            set
            {
                if (_item.id != value)
                {
                    _item.id = value;
                    OnPropertyChanged("id");
                }
            }
        }
    
        public String product_id
        {
            get { return _item.product_id; }
            set
            {
                if (_item.product_id != value)
                {
                    _item.product_id = value;
                    OnPropertyChanged("product_id");
                }
            }
        }
    
        public String item_name
        {
            get { return _item.item_name; }
            set
            {
                if (_item.item_name != value)
                {
                    _item.item_name = value;
                    OnPropertyChanged("item_name");
                }
            }
        }
    
        public String item_yomi
        {
            get { return _item.item_yomi; }
            set
            {
                if (_item.item_yomi != value)
                {
                    _item.item_yomi = value;
                    OnPropertyChanged("item_yomi");
                }
            }
    
    
        }
    
        public bool display
        {
            get { return _item.display; }
            set
            {
                if (_item.display != value)
                {
                    _item.display = value;
                    OnPropertyChanged("display ");
                }
            }
        }
    
    
    
        public bool IsPropertyChanged { get; set; }
    
    
        #region INotifyPropertyChanged メンバー
    
        /// <summary>
        /// プロパティが変化したことを通知するイベント
        /// </summary>
    
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
        /// <summary>
        /// プロパティが変化したら呼び出す
        /// </summary>
        /// <param name="name">変化したプロパティの名前</param>
        protected virtual void OnPropertyChanged(string name)
        {
            IsPropertyChanged = true;
            System.ComponentModel.PropertyChangedEventHandler pc = PropertyChanged;
            if (pc != null)
            {
                pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
            }
        }
    
        #endregion
    }

    のようにラップするクラスを作って、DataContextに渡すことになるでしょう。

    これもできない場合は、ToggleButton.Checked,UncheckedというRoutedイベントを捕まえることで,DataGridの内側にあるCheckBoxの変更を知ることができます。(CheckBoxはToggleButtonを継承しています)

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid Margin="10,0,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <DockPanel Grid.Row="0">
                <DataGrid x:Name="ItemListGrid" ItemsSource="{Binding}" AutoGenerateColumns="False" 
                          ToggleButton.Checked="ItemListGrid_Checked" 
                          ToggleButton.Unchecked="ItemListGrid_Unchecked"
                          TextBoxBase.TextChanged="ItemListGrid_TextChanged">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Width="60" Binding="{Binding Path=id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="品番" Width="40" Binding="{Binding Path=product_id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="商品名" Width="60" Binding="{Binding Path=item_name}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="よみ" Width="60" Binding="{Binding Path=item_yomi}" IsReadOnly="True"/>
                        <DataGridTemplateColumn Header="表示">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=display, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
        </Grid>
    </Window>

    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
    
            public class Item
            {
                public int id { get; set; }
                public String product_id { get; set; }
                public String item_name { get; set; }
                public String item_yomi { get; set; }
                public bool display { get; set; }
            }
    
    
            public ObservableCollection<Item> ItemCollection;
    
            public MainWindow()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true });
                ItemCollection.Add(new Item() { id = 1, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true });
    
                ItemListGrid.ItemsSource = ItemCollection;
            }
    
            private void ItemListGrid_Checked(object sender, RoutedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void ItemListGrid_TextChanged(object sender, TextChangedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void ItemListGrid_Unchecked(object sender, RoutedEventArgs e)
            {
                OnCellChange(e);
            }
    
            private void OnCellChange(RoutedEventArgs e)
            {
                DataGridRow row = FindRow(e.OriginalSource);
                if (row != null)
                {   //見つけたDataGridRowが表示しているデータを取得する
                    Item item = this.ItemListGrid.ItemContainerGenerator.ItemFromContainer(row) as Item;
                    if (item != null)
                    {
                        System.Diagnostics.Debug.WriteLine(item.id.ToString());
                        if (item.display)
                        {
                            //取得したIDとdisplayを元にデータベースを更新する
                        }
                    }
                }
            }
    
            private DataGridRow FindRow(object originalSouce)
            {//イベントを発生させた何かの親をたどってDataGridRowを探す
                DataGridRow row=null;
                DependencyObject d=originalSouce as DependencyObject;
                while (d != null && row == null)
                {
                    d=System.Windows.Media.VisualTreeHelper.GetParent(d);
                    row = d as DataGridRow;
                }
                return row;
            }
        }
    }
    ほかにも方法はありますが、だいたいこの3種類を使えれば大抵のデータ変更の検出は行えるようになります。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


    2014年6月29日 12:58
  • ビヘイビアを使った方法も載せておきます。

    <Window x:Class="test2010wpf.DataGridBindTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            Title="DataGridBindTest" Height="350" Width="525">
        <Grid Margin="10,0,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <DockPanel Grid.Row="0">
                <DataGrid x:Name="ItemListGrid" ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Width="60" Binding="{Binding Path=id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="品番" Width="40" Binding="{Binding Path=product_id}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="商品名" Width="60" Binding="{Binding Path=item_name}" IsReadOnly="True"/>
                        <DataGridTextColumn Header="よみ" Width="60" Binding="{Binding Path=item_yomi}" IsReadOnly="True"/>
                        <DataGridTemplateColumn Header="表示">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=display, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Click">
                                                <i:InvokeCommandAction 
                                                    Command="{Binding ClickCommand, 
                                                                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                                                    CommandParameter="{Binding }"
                                                                       />
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </CheckBox>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
        </Grid>
    </Window>


    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Collections.ObjectModel;
    
    namespace test2010wpf
    {
        /// <summary>
        /// DataGridBindTest.xaml の相互作用ロジック
        /// </summary>
        public partial class DataGridBindTest : Window
        {
            public DataGridBindTest()
            {
                InitializeComponent();
    
                ItemCollection = new ObservableCollection<Item>();
    
                ItemCollection.Add(new Item() { id = 1, product_id = "果物1", item_name = "林檎", item_yomi = "りんご", display = true });
                ItemCollection.Add(new Item() { id = 2, product_id = "果物2", item_name = "蜜柑", item_yomi = "みかん", display = true });
    
                this.DataContext = this;
    
                ClickCommand = new DelegateCommand<Item>(Click);
            }
    
            public ObservableCollection<Item> ItemCollection { get; set; }
    
            public ICommand ClickCommand { get; private set; }
    
            private void Click(Item item)
            {
                System.Diagnostics.Debug.WriteLine(item.id);
            }
        }
    
        public class Item
        {
            public int id { get; set; }
            public String product_id { get; set; }
            public String item_name { get; set; }
            public String item_yomi { get; set; }
            public bool display { get; set; }
    
        }
    }

    Blendがインストールされていない状態、すなわちビヘイビアが使えない状態の場合は、以下からダウンロードして下さい。

    Microsoft Expression Blend 4 Software Development Kit (SDK) for .NET 4
    http://www.microsoft.com/ja-jp/download/details.aspx?id=10801

    (追記)
    DelegeteCommandは多くの場所で作成、紹介されているので、検索してみて下さい。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年6月30日 1:36
    モデレータ
  • ご回答ありがとうございます。

    一番素直な実装とのことで一番目の方法を採用したいと思います。
    一つ質問なのですが、IsPropertyChanged と言うプロパティが追加されていますが
    これはどう言った意図で追加されているものなのでしょうか?

    見たところ、初期化時にfalse、OnPropertyChangedの中でtrueに設定されている他は
    条件分岐等にも使われていないようで、設定しなくても希望する動作にはなったので気になりました。

    お手数をおかけいたしますがよろしくお願いいたします。
    2014年7月1日 0:36
  • ご回答ありがとうございます。

    Blendはまだ導入していないのですが、コードとしてはすっきりするようで
    今後の課題として考えたいと思います
    2014年7月1日 0:36
  • IsPropertyChangedを追加する必要はありません。

    現在はdisplayプロパティの変更をしたタイミングで逐次DB更新のようですが、複数のItemをまとめて更新するようにした場合に変更したItemを見つけるためのフラグにできます。
    また、display列以外の例えばitem_name列でも変更できるようにした場合に、各プロパティ毎に変更されたことを記述しなくともOnPropertyChangedを呼び出すことで変更されたことをフラグに反映することが可能になります。

    サンプルとして提示したコードでは必要があって追加しているわけではありませんが、今後機能が拡張された時に、こういったやり方もあるという意味で書いてあります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2014年7月1日 3:54
  • IsPropertyChangedの件、ありがとうございました。
    そのような使い道が考えられるのですね。

    ありがとうございます。

    2014年7月3日 0:43
  • Blendはまだ導入していないのですが、コードとしてはすっきりするようで
    今後の課題として考えたいと思います
    誤解があるかもしれませんので念のためにですが、Blendは導入しなくても上で述べたSDKをダウンロードしてインストールすれば、それでOKです。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年7月3日 0:51
    モデレータ