トップ回答者
ViewModelクラスにバインドされたDataGridのセル値を更新したい

質問
-
VS2012のMicrosoft Visual C# 2012でWPFプログラムを始めたばかりの初心者です
「DataGridでコードから値を設定し、その値を「Esc」キーでキャンセルできるようにしたい」で回答されたコードに対してセルの値をプログラムから変更する方法がわからず悩んでいます
例えばウィンドウにボタンを配し、クリックしたら2行目の数量を10に変更する場合は、MainWindowからViewModelクラスのDetailsに2行目の数量10を連絡してあげると思うのですが実現方法がよくわかりません
ViewModel.Details[1].Countみたいな方法ではうまくいきませんでした(オブジェクト参照が必要です、とエラーが出ます)
ヒント、文献などご紹介頂きたくよろしくお願いします
回答
-
ちょんさんの仰られてるとおり、ViewModel に Commandを用意し、Button のコマンドとバインドしてやればいいと思います。以下、ヒントです。
まず RelayCommand クラスを用意します
public sealed class RelayCommand : ICommand { public event EventHandler CanExecuteChanged; private Func<bool> _canExecuteAction; private Action _executeAction; #region コンストラクタ /// <summary> /// コンストラクタ /// </summary> public RelayCommand(Action executeAction) { this._executeAction = executeAction; this._canExecuteAction = () => true; } #endregion public Action ExecuteAction { get { return _executeAction; } set { _executeAction = value; } } /// <summary> /// 現在の状態でこの RelayCommand を実行できるかどうかを判断します。 /// </summary> public bool CanExecute(object parameter) { return _canExecuteAction(); } /// <summary> /// 現在のコマンドの対象で RelayCommand を実行します。 /// </summary> public void Execute(object parameter) { if (_executeAction != null) { try { _executeAction(); } catch (Exception) { throw; } } } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } }
次に、2行目の数量を10 に変更するコマンドを ViewModel に用意します。
private void Change() { this.Details[1].Count = 10; } private RelayCommand _to10Command; public RelayCommand To10Command { get { return _to10Command ?? (_to10Command = new RelayCommand(Change)); } }
最後に View に追加したButton に ViewModel のコマンドをバインドします。
<Button Height="24" Content="変更" Command="{Binding To10Command}"/>
以上、簡略ながら参考になれば幸いです。
ひらぽん http://d.hatena.ne.jp/hilapon/
すべての返信
-
ちょんさんの仰られてるとおり、ViewModel に Commandを用意し、Button のコマンドとバインドしてやればいいと思います。以下、ヒントです。
まず RelayCommand クラスを用意します
public sealed class RelayCommand : ICommand { public event EventHandler CanExecuteChanged; private Func<bool> _canExecuteAction; private Action _executeAction; #region コンストラクタ /// <summary> /// コンストラクタ /// </summary> public RelayCommand(Action executeAction) { this._executeAction = executeAction; this._canExecuteAction = () => true; } #endregion public Action ExecuteAction { get { return _executeAction; } set { _executeAction = value; } } /// <summary> /// 現在の状態でこの RelayCommand を実行できるかどうかを判断します。 /// </summary> public bool CanExecute(object parameter) { return _canExecuteAction(); } /// <summary> /// 現在のコマンドの対象で RelayCommand を実行します。 /// </summary> public void Execute(object parameter) { if (_executeAction != null) { try { _executeAction(); } catch (Exception) { throw; } } } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } }
次に、2行目の数量を10 に変更するコマンドを ViewModel に用意します。
private void Change() { this.Details[1].Count = 10; } private RelayCommand _to10Command; public RelayCommand To10Command { get { return _to10Command ?? (_to10Command = new RelayCommand(Change)); } }
最後に View に追加したButton に ViewModel のコマンドをバインドします。
<Button Height="24" Content="変更" Command="{Binding To10Command}"/>
以上、簡略ながら参考になれば幸いです。
ひらぽん http://d.hatena.ne.jp/hilapon/
-
DataGrid上で単価と数量を入力し、ボタンを押すと金額を計算する例を書いてみました。基本的な構造がわかるように、あえてシンプルなコードにしています。
基本的な流れとしては、DataGridにソースを提供しているViewModelで計算し、ソースを変更して、OnPropertyChangedによってDataGridにソースの金額に変化があったことを知らせ、DataGridが金額の部分のみを再表示するという流れです。これはMVVMの基本的なパターンの一つです。
今回は伝統的なスパゲティだとしても、次回はそうならないようにいくらかでも参考になれば幸いです。なお、理解できなところがあれば、忌憚なくお尋ねください。<Window x:Class="test2010wpf.DataGridMVVM2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:test2010wpf" Title="DataGridMVVM" Height="365" Width="344"> <Window.DataContext> <local:DataGridMVVM2ViewModel /> </Window.DataContext> <Grid Height="249"> <DataGrid Name="dg" ItemsSource="{Binding UIObjects}" AutoGenerateColumns="False" CanUserAddRows="False" Margin="0,0,0,41"> <DataGrid.Columns> <DataGridTextColumn Header="単価" Binding="{Binding Path=単価}" /> <DataGridTextColumn Header="数量" Binding="{Binding Path=数量}" /> <DataGridTextColumn Header="金額" Binding="{Binding Path=金額}" /> </DataGrid.Columns> </DataGrid> <Button Content="計算" Command="{Binding 計算Command}" Height="23" Width="75" Margin="123,214,0,12" HorizontalAlignment="Left" /> </Grid> </Window>
/// <summary> /// ViewModel /// </summary> public class DataGridMVVM2ViewModel: INotifyPropertyChanged { //コンストラクタ public DataGridMVVM2ViewModel() { //プロパティの設定------------------- DataTable dt = new DataTable(); dt.Columns.Add("単価", typeof(int)); dt.Columns.Add("数量", typeof(int)); dt.Columns.Add("金額", typeof(int)); DataRow dr = dt.NewRow(); dr["単価"] = 10; dr["数量"] = 2; dr["金額"] = 0; dt.Rows.Add(dr); dr = dt.NewRow(); dr["単価"] = 30; dr["数量"] = 5; dr["金額"] = 0; dt.Rows.Add(dr); UIObjects = new 仕入UIObjectCollection(dt); //コマンドの設定------------------ 計算Command = new RelayCommand(計算); } //プロパティ------------------------- /// <summary> /// UIに表示するデータを取得する。 /// </summary> 仕入UIObjectCollection _UIObjects; public 仕入UIObjectCollection UIObjects { get { return _UIObjects; } set { _UIObjects = value; OnPropertyChanged("UIObjects"); } } //コマンド-------------------------- /// <summary> /// 計算コマンド /// </summary> public ICommand 計算Command { get; private set; } //コマンドハンドラ--------------------- private void 計算(object obj) { foreach (var item in UIObjects) item.金額 = item.単価 * item.数量; } //INotifyPropertyChanged メンバー-------- public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { var d = PropertyChanged; if (d != null) d(this, new PropertyChangedEventArgs(propertyName)); } } /// <summary> /// 仕入UIObject /// </summary> public class 仕入UIObject : INotifyPropertyChanged { public 仕入UIObject(int 単価, int 数量) { this.単価 = 単価; this.数量 = 数量; this.金額 = 0; } int _単価; public int 単価 { get { return _単価; } set { _単価 = value; OnPropertyChanged("単価"); } } int _数量; public int 数量 { get { return _数量; } set { _数量 = value; OnPropertyChanged("数量"); } } int _金額; public int 金額 { get { return _金額; } set { _金額 = value; OnPropertyChanged("金額"); } } //INotifyPropertyChanged メンバー ---------------------------- public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { var d = PropertyChanged; if (d != null) d(this, new PropertyChangedEventArgs(propertyName)); } } /// <summary> /// 仕入のコレクションを表すクラス /// </summary> public class 仕入UIObjectCollection : ObservableCollection<仕入UIObject> { //コンストラクタ public 仕入UIObjectCollection(DataTable dt) { foreach (DataRow dr in dt.Rows) this.Add(new 仕入UIObject((int)dr["単価"], (int)dr["数量"])); } }
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/