none
WPF MVVM法 DataGrid  バインドデータを変更しても表示が変わらない。 RRS feed

  • 質問

  • 初投稿になります。

    現在WPFでMVVM法を使用し、ソフトを作成しているのですが、DataGridでつまずいています。

    どなたか助けていただけると助かります。

    MVVM法を学習し始めて最初のソフトなので間違っている部分があると思います。

    ソフトは、左のリストからグループを選択して、ボタンを押すと、データグリッド内のスライダーが、レベルマックスになるようなソフトを作成したいのですが、コード内でバインドしている変数の値を変更しても、画面が変わってくれません。

    また、スクロールバーを動かすと隠れている部分の値は変わってくれています。


    ソフトを簡単にしたサンプルコードを以下に記載します。

    まずはXamlから

    <Window x:Class="SampleProgram.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local ="clr-namespace:SampleProgram.VIEWMODEL"
            Title="MainWindow" Height="500" Width="600">
    
        <Window.DataContext>
            <local:ViewModel />
        </Window.DataContext>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="2*" />
            </Grid.ColumnDefinitions>
            <DataGrid Grid.ColumnSpan="2" ItemsSource="{Binding Path=Model_List}" AutoGenerateColumns="False" SelectedItem="{Binding Path=Current_Model}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="グループ"  Width="*" >
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock  Text="{Binding Path=Group}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
            <Button Content="Reset" Grid.Row="1" Name="button1" Margin="20,30" Command="{Binding Path=Data_Reset_Command}" />
            <Button Content="Full" Name="button2" Grid.Row="1" Grid.Column="1" Margin="20,30" Command="{Binding Path=Data_Full_Command}" />
            <DataGrid  CanUserAddRows="False" CanUserDeleteRows="False" Grid.Column="2"   AutoGenerateColumns="False" ItemsSource="{Binding Path=Current_Model.Persons}" Grid.RowSpan="2">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="ID" Width="*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=ID}"  />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="フェーダー" Width="5*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Slider Margin="5" Minimum="0" Maximum="255" Value="{Binding Path=Data, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" LargeChange="130" SmallChange="1" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="レベル" Width="*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBox  Text="{Binding Path=Data, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>

    続いてViewModel

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using System.Windows;
    using System.Windows.Input;
    using System.ComponentModel;
    using SampleProgram.MODEL;
    
    namespace SampleProgram.VIEWMODEL
    {
        class ViewModel: ViewModelBase
        {
            private Model_Repositry _repository;
    
            public ViewModel( )
            {
                _repository = new Model_Repositry();
                _model_list       = _repository.Get_Model_List();
                WireCommands();
            }
    
            private void WireCommands()
            {
                Data_Full_Command = new Command(Data_Full);
                Data_Reset_Command = new Command(Data_Reset);
            }
    
    
            public Command Data_Full_Command
            {
                get;
                private set;
            }
            public void Data_Full()
            {
                Current_Model.Data_Full();
                NotifyPropertyChange("Current_Model");
            }
    
            public Command Data_Reset_Command
            {
                get;
                private set;
            }
            public void Data_Reset()
            {
                Current_Model.Data_Reset();
                NotifyPropertyChange("Current_Model");
            }
    
            private List<Model> _model_list;
            public List<Model> Model_List
            {
                get { return this._model_list; }
                set
                {
                    this._model_list = value;
                    NotifyPropertyChange("Model_List");
                }
            }
    
            private Model _current_model;
            public Model Current_Model
            {
                get
                {
                    return _current_model;
                }
    
                set
                {
                    if (_current_model != value)
                    {
                        _current_model = value;
                        NotifyPropertyChange("Current_Model");
                        Data_Full_Command.IsEnabled = true;
                        Data_Reset_Command.IsEnabled = true;
                    }
                }
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Input;
    
    namespace SampleProgram
    {
        public class Command : ICommand
        {
            private readonly Action _handler;
            private bool _isEnabled;
    
            public Command(Action handler)
            {
                _handler = handler;
            }
    
            public bool IsEnabled
            {
                get { return _isEnabled; }
                set
                {
                    if (value != _isEnabled)
                    {
                        _isEnabled = value;
                        if (CanExecuteChanged != null)
                        {
                            CanExecuteChanged(this, EventArgs.Empty);
                        }
                    }
                }
            }
    
            public bool CanExecute(object parameter)
            {
                return IsEnabled;
            }
    
            public event EventHandler CanExecuteChanged;
    
            public void Execute(object parameter)
            {
                _handler();
            }
        }
    }


    using System;
    using System.ComponentModel;
    
    namespace SampleProgram.VIEWMODEL
    {
        public abstract class ViewModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void NotifyPropertyChange(string propName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propName));
                }
            }
        }
    }

    次にModel

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace SampleProgram.MODEL
    {
        class Model
        {
            public Model() {
                Persons = new Person[512];
                for (int i = 0; i < 512; i++)
                {
                    Person add_data = new Person();
                    add_data.ID = i + 1;
                    Persons[i] = add_data;
                }
    
            }
    
            public int Group
            {
                get;
                set;
            }
    
            public Person[] Persons
            {
                get;
                set;
            }
    
            public class Person {
                public int ID
                {
                    get;
                    set;
                }
                public int Data
                {
                    get;
                    set;
                }
            }
    
    
            public void Data_Full()
            {
                for (int i = 0; i < 512; i++)
                {
                    Persons[i].Data = 255;
                }
            }
    
            public void Data_Reset()
            {
                for (int i = 0; i < 512; i++)
                {
                    Persons[i].Data = 0;
                }
            }
    
        }
    }

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace SampleProgram.MODEL
    {
        class Model_Repositry
        {
            public Model_Repositry()
            {
                Creat_List();
            }
    
            private List<Model> _model_list;
            public List<Model> Get_Model_List()
            {
                return _model_list;
            }
    
            public void Creat_List() {
                _model_list = new List<Model>();
                for (int i = 0; i < 10; i++)
                {
                    Model add_model = new Model();
                    add_model.Group = i;
                    _model_list.Add(add_model);
                }
            }
        }
    }

    以上がコードです。

    どなたか原因を教えていただけると助かります。

    • 編集済み y_nakata 2015年3月12日 7:42
    2015年3月12日 5:45

回答

  • > そもそもスクロールバーを動かしたら隠れている部分は値が変わっているのは、スクロールバーを動かして隠れている行が表示されるたびに、データを取りに行っているからなのでしょうか?

    ですね。仮想化されてるため、再描画時にバインドされてるデータが反映されます。

    まず一番手っ取り早い解決策としては、Hongliang さんが仰られてるとおり、Person クラスに INotifyPropertyChanged を実装することでしょうね。以下のコードで動作確認できました。ちなみに名前空間等は適当なものに変えてます。また INotifyPropertyChanged の実装が手間なので、ViewModelBase を流用させて頂きました。

    public class Person : ViewModel.ViewModelBase {
        public int ID { get; set;}
        private int _Data;
        public int Data { 
            get { return _Data; }
            set {
                if (_Data == value) return;
                _Data = value;
                NotifyPropertyChange("Data");
            }
        }
    }


    Model に INotifyPropertyChanged を実装したくないため、View や ViewModel の実装を複雑化させるのは本末転倒だと思うのですが、いかがでしょうか?


    「不適切な発言」の濫用はフォーラムの運営を妨げる行為です。ご遠慮ください https://social.msdn.microsoft.com/Forums/ja-JP/0f7b0966-0141-4c1b-b7b9-aed65abf60b6?forum=announceja



    2015年3月12日 8:41
    モデレータ
  • >ひらぽん様
    ありがとうございます。勉強になります。

    先に投稿したかずきさんご紹介のクラスを使用したタイプのをあげておきます。
    焦点以外のコードは省いてます。

    public class ViewModel : ViewModelBase
    {
        private Model_Repositry _repository;
    
        public ViewModel()
        {
            _repository = new Model_Repositry();
            var temp_List = new List<ModelVM>();
            foreach(var current in  _repository.Get_Model_List())
            {
                temp_List.Add(new ModelVM(current));
            }
            Model_List = temp_List;
            WireCommands();
        }
    
        private void WireCommands()
        {
            Data_Full_Command = new Command(Data_Full);
        }
    
        public Command Data_Full_Command { get; set; }
        public void Data_Full()
        {
            Current_Model.Data_FullWrapper();
            NotifyPropertyChange("Current_Model");
        }
    
        private List<ModelVM> _model_list;
        public List<ModelVM> Model_List
        {
            get { return _model_list; }
            set 
            {
                _model_list = value;
                NotifyPropertyChange("Model_List");
            }
        }
    
        private ModelVM _current_model;
        public ModelVM Current_Model
        {
            get { return _current_model; }
            set
            {
                if (_current_model != value)
                {
                    _current_model = value;
                    NotifyPropertyChange("Current_Model");
                    Data_Full_Command.IsEnabled = true;
                }
            }
        }
    
        #region ModelVM
        public class ModelVM : DynamicViewModelBase<Model>
        {
            public ModelVM(Model innerModel) : base(innerModel)
            {
                RefreshPersons();
            }
    
            public void Data_FullWrapper()
            {
                InnerModel.Data_Full();
                RefreshPersons();
            }
    
            private void RefreshPersons()
            {
                var temp_list = new List<PersonVM>();
                foreach (var current in InnerModel.Persons)
                {
                    temp_list.Add(new PersonVM(current));
                }
                PersonsVM = temp_list;
            }
    
            private List<PersonVM> _persons_vm;
            public List<PersonVM> PersonsVM 
            {
                get 
                {
                    return _persons_vm;
                }
                set 
                {
                    _persons_vm = value;
                    OnPropertyChanged("PersonsVM");
                }
            }
    
            public class PersonVM : DynamicViewModelBase<Model.Person>
            {
                public PersonVM(Model.Person innerModel) : base(innerModel) { }
            }
        }
        #endregion
    
        public class DynamicViewModelBase<T> : System.Dynamic.DynamicObject, INotifyPropertyChanged
        where T : class
        {
            #region かずきさんのやつ
            #endregion
        }
    }
    #endregion
    <DataGrid  CanUserAddRows="False" CanUserDeleteRows="False" Grid.Column="2"   AutoGenerateColumns="False" ItemsSource="{Binding Path=Current_Model.PersonsVM}" Grid.RowSpan="2">

    尚、Person.Dataはdouble型に変更しました。適宜コンバータなどを使って変換したほうが良いかもしれません。
    #もうちょっと色々やれないかなとごにょごにょやってたので汚いですね。スミマセン。

    私の実装方法が間違っている可能性もありますが、ひらぽんさんが仰っていたとおり複雑化しました・・・。


    2015年3月12日 11:03
    モデレータ

すべての返信

  • // 読みづらい。

    ええと、Sliderを持ってるDataGridはのItemsSourceがModel.Personsで、Slider.ValueはPerson.Dataにバインドされているということですね。で、Fullボタンを押すとコマンド経由でModel.Personsの各Person.Dataが設定されると。

    現状では、Person.Dataが変更されたことをDataGridが知るすべがありません。

    一般的には、データからビューへの通知には、INotifyPropertyChangedを実装させておくようにします(すでにいくつかのクラスでやられているように)。この場合ならPersonもINotifyPropertyChangedを実装してData変更時にPropertyChangedイベントを起こさせます。

    Model部分を変更できないのなら、ViewModel側のコマンド処理で、Data_Full()を呼び出した後、Current_Modelプロパティ全体の変更通知を投げるなりすることになるでしょうか。

    2015年3月12日 6:04
  • Hongliangさん

    ご回答ありがとうございます。

    非常に助かります。

    読みづらくて申し訳ないです。。

    MVVM法でのModelについていくつかサイトで調べたところ、Model内にInotifyPropertyChangedを実装してもよいという記事をいくつか見たのですが、MVVM方の私個人の考えとしては、できればModel内にInotifyは実装したくありません。(ただそれで今回問題にぶち当たっているのですが。。)

    ViewModel側でCurrent_Modelプロパティ全体の変更通知を投げるというのは、

    public void Data_Full()
            {
                Current_Model.Data_Full();
                NotifyPropertyChange("Current_Model");
            }

    ではなく、他の方法で変更通知を投げるということでしょうか?

    2015年3月12日 6:18
  • こんにちは。

    MVVMの考え(ドメインとUIの分離など?)からいくと、原則ModelをラップしたViewModelを作成するのかなと思います。

    (私は結構そうしてきたんですが、ベストプラクティスはわからない・・・)

    今回はモデル=画面構成な感じだと思うので単純にラップするだけで良さそうなので
    かずきさんが以前紹介されてました方法を取ると楽にできたりします。
    http://okazuki.hatenablog.com/entry/20100702/1278056325



    2015年3月12日 6:33
    モデレータ
  • Tak1waさん

    ご回答ありがとうございます。

    かずきさんの記事拝見しましたが、こんな便利な方法があるのですね!

    Medolが巨大化したときに、いちいちViewModel側でラップしてやるのは非常に手間だと思っていたので、非常に参考になりました。

    今回のソフトに実装して問題が解決できるか試しているのですが、ちょっと実装に苦戦しています。

    うまくいったら報告させていただきます。

    ありがとうございます!

    2015年3月12日 6:55
  • うーん、ちょっとうまくいかないです><

    そもそもスクロールバーを動かしたら隠れている部分は値が変わっているのは、スクロールバーを動かして隠れている行が表示されるたびに、データを取りに行っているからなのでしょうか?

    となるとやはりInotifyが正しく通知できていないのか。。。。

    変更通知はCurrent_Modelではなく何の変更通知をしてあげればよいのでしょうか?

    2015年3月12日 7:24
  • > そもそもスクロールバーを動かしたら隠れている部分は値が変わっているのは、スクロールバーを動かして隠れている行が表示されるたびに、データを取りに行っているからなのでしょうか?

    ですね。仮想化されてるため、再描画時にバインドされてるデータが反映されます。

    まず一番手っ取り早い解決策としては、Hongliang さんが仰られてるとおり、Person クラスに INotifyPropertyChanged を実装することでしょうね。以下のコードで動作確認できました。ちなみに名前空間等は適当なものに変えてます。また INotifyPropertyChanged の実装が手間なので、ViewModelBase を流用させて頂きました。

    public class Person : ViewModel.ViewModelBase {
        public int ID { get; set;}
        private int _Data;
        public int Data { 
            get { return _Data; }
            set {
                if (_Data == value) return;
                _Data = value;
                NotifyPropertyChange("Data");
            }
        }
    }


    Model に INotifyPropertyChanged を実装したくないため、View や ViewModel の実装を複雑化させるのは本末転倒だと思うのですが、いかがでしょうか?


    「不適切な発言」の濫用はフォーラムの運営を妨げる行為です。ご遠慮ください https://social.msdn.microsoft.com/Forums/ja-JP/0f7b0966-0141-4c1b-b7b9-aed65abf60b6?forum=announceja



    2015年3月12日 8:41
    モデレータ
  • モデレーターさん

    ご回答ありがとうございます。動作確認までしていただき非常に助かります。

    こちらでも動作確認しました。

    やはりModelにINotifyPropertyChangedを実装するのが確実なのですね。。

    なにかViewModel内で解決できる方法があるかもう少し検討してみます。

    解決するまでは、Modelでも必要なクラスにはINotifyPropertyChangedを実装する方針とします。

    ひとまず動くものができて非常に助かりました!

    ありがとうございますm(_ _)m

    2015年3月12日 9:13
  • そうですね。
    一番手っ取り早いのはプロパティ変更通知を実装することだと思います。

    Model に INotifyPropertyChanged を実装したくないため、View や ViewModel の実装を複雑化させるのは本末転倒だと思うのですが、いかがでしょうか?

    本末転倒と言われると少し思うところがあるのですが(こういった議論が本スレッドに不適切であればご指摘ください。)
    実装の簡略化とMVVMでの関心分離はトレードオフかなと私は思っておりました。
    Modelを汎用的なクラスライブラリとして考えた場合に通知の実装が本当に適切なのかということです。
    私はModelにはWPFなのかWinFormsなのかを意識させる必要は無いのではと思っていました。
    (間違っていることを申し上げているかもしれません。是非ご指摘頂ければと思います。)
    2015年3月12日 9:17
    モデレータ
  • Tak1waさん

    私がまだまだMVVM方について理解できていないので、こういった議論は非常に参考になります。

    私がこのソフトを作成するときに、なるべくWPFでもFormでも動く、Modelを作ってほしいていう依頼があり、今回はModelにINotifyPropertyChangedを実装しないソフト設計を目指していました。(以前はModelにガンガン実装していました。)

    できればViewModelだけで完結させたいのですが、今回は方法が分からないのでヒラポンさんの方法を参考にさせていただきます。

    2015年3月12日 9:31
  • もう一点。INotifyPropertyChanged を使用せず、ObservableCollection を使って要素の変更を通知する方法もあります。

    class Model {
        public Model() {
            this.Persons = 
                new ObservableCollection<Person>(Enumerable.Range(0, 512).Select(p => new Person() {ID = p}));
        }
    
        public int Group { get; set; }
    
        private ObservableCollection<Person> _Persons;
        public ObservableCollection<Person> Persons {
            get { return _Persons; }
            set {
                if (_Persons == value) return;
                _Persons = value;
                // NotifyPropertyChange("Persons");
            }
        }
    
        public void Data_Full() {
            for (int i = 0; i < 512; i++) {
                // Persons[i].Data = 255;
                Persons[i] = new Person() { Data = 255 };
            }
        }
    
        public void Data_Reset() {
            for (int i = 0; i < 512; i++) {
                // Persons[i].Data = 0;
                Persons[i] = new Person() { Data = 0 };
            }
        }
    }


    ただし、Data_Full メソッド等の実装を見て頂けばわかると思いますが、ObservableCollection を使い場合、各要素のプロパティを変更しただけでは通知せず、要素自体を入れ替える必要があります。よって今回のケースではあまり現実的な解ではないと思われます。

    「不適切な発言」の濫用はフォーラムの運営を妨げる行為です。ご遠慮ください https://social.msdn.microsoft.com/Forums/ja-JP/0f7b0966-0141-4c1b-b7b9-aed65abf60b6?forum=announceja

    2015年3月12日 9:34
    モデレータ
  • > Modelを汎用的なクラスライブラリとして考えた場合に通知の実装が本当に適切なのかということです。

    他のフレームワークでも使われる可能性が非常に高いなら INotifyPropertyChanged の実装を考慮する必要があるかも知れません。しかしクラスが WPF プロジェクト以外で使われる可能性が低いなら、Model に INotifyPropertyChanged を実装しても構わないのでは?というのが私の考えです。

    ASP.NET か ASP.NET.MVC か Forms か Windows アプリかetc・・・、そのクラスが一体どのフレームワークで使われるか(もしくは使われる可能性が非常に高いか)により、モデルの設計・・・いやモデルの概念自体も大きく変わるのではないかと思ってます。

    「不適切な発言」の濫用はフォーラムの運営を妨げる行為です。ご遠慮ください https://social.msdn.microsoft.com/Forums/ja-JP/0f7b0966-0141-4c1b-b7b9-aed65abf60b6?forum=announceja

    2015年3月12日 9:44
    モデレータ
  • ひらぽんさん

    ObservableCollectionを使うときは

    Public ViewModel(){
               _Persons.CollectionChanged += _Persons_Collectionchanged;
    }
    
    private ObservableCollection<Person> _Persons;
            public ObservableCollection<Person> Persons
            {
                get { return this._Persons; }
                set
                {
                    this._Persons= value;
                }
            }
    
    private void _Persons_Collectionchanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                NotifyPropertyChange("Persons");
            }

    という方法を使っていましたが、INotifyを使わずに変更を通知してあげる方法もあったんですね。

    参考になります。ありがとうございます。

    やはり、ModelがWPF以外でも使われる可能性がある場合と、そうでない場合で、設計思想を変えるのが必要なんですね。

    ご回答ありがとうございます。

    2015年3月12日 10:17
  • >ひらぽん様
    ありがとうございます。勉強になります。

    先に投稿したかずきさんご紹介のクラスを使用したタイプのをあげておきます。
    焦点以外のコードは省いてます。

    public class ViewModel : ViewModelBase
    {
        private Model_Repositry _repository;
    
        public ViewModel()
        {
            _repository = new Model_Repositry();
            var temp_List = new List<ModelVM>();
            foreach(var current in  _repository.Get_Model_List())
            {
                temp_List.Add(new ModelVM(current));
            }
            Model_List = temp_List;
            WireCommands();
        }
    
        private void WireCommands()
        {
            Data_Full_Command = new Command(Data_Full);
        }
    
        public Command Data_Full_Command { get; set; }
        public void Data_Full()
        {
            Current_Model.Data_FullWrapper();
            NotifyPropertyChange("Current_Model");
        }
    
        private List<ModelVM> _model_list;
        public List<ModelVM> Model_List
        {
            get { return _model_list; }
            set 
            {
                _model_list = value;
                NotifyPropertyChange("Model_List");
            }
        }
    
        private ModelVM _current_model;
        public ModelVM Current_Model
        {
            get { return _current_model; }
            set
            {
                if (_current_model != value)
                {
                    _current_model = value;
                    NotifyPropertyChange("Current_Model");
                    Data_Full_Command.IsEnabled = true;
                }
            }
        }
    
        #region ModelVM
        public class ModelVM : DynamicViewModelBase<Model>
        {
            public ModelVM(Model innerModel) : base(innerModel)
            {
                RefreshPersons();
            }
    
            public void Data_FullWrapper()
            {
                InnerModel.Data_Full();
                RefreshPersons();
            }
    
            private void RefreshPersons()
            {
                var temp_list = new List<PersonVM>();
                foreach (var current in InnerModel.Persons)
                {
                    temp_list.Add(new PersonVM(current));
                }
                PersonsVM = temp_list;
            }
    
            private List<PersonVM> _persons_vm;
            public List<PersonVM> PersonsVM 
            {
                get 
                {
                    return _persons_vm;
                }
                set 
                {
                    _persons_vm = value;
                    OnPropertyChanged("PersonsVM");
                }
            }
    
            public class PersonVM : DynamicViewModelBase<Model.Person>
            {
                public PersonVM(Model.Person innerModel) : base(innerModel) { }
            }
        }
        #endregion
    
        public class DynamicViewModelBase<T> : System.Dynamic.DynamicObject, INotifyPropertyChanged
        where T : class
        {
            #region かずきさんのやつ
            #endregion
        }
    }
    #endregion
    <DataGrid  CanUserAddRows="False" CanUserDeleteRows="False" Grid.Column="2"   AutoGenerateColumns="False" ItemsSource="{Binding Path=Current_Model.PersonsVM}" Grid.RowSpan="2">

    尚、Person.Dataはdouble型に変更しました。適宜コンバータなどを使って変換したほうが良いかもしれません。
    #もうちょっと色々やれないかなとごにょごにょやってたので汚いですね。スミマセン。

    私の実装方法が間違っている可能性もありますが、ひらぽんさんが仰っていたとおり複雑化しました・・・。


    2015年3月12日 11:03
    モデレータ
  • Tak1waさん

    サンプルコードありがとうございます。

    確かに多少複雑化していますが、ModelにINotifyを実装しないやり方はこのやり方が良さそうですね。

    非常に参考になりました、ありがとうございます。

    ModelとViewを切り離す必要がある時は、この方法を使わしていただきます。

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

    2015年3月13日 2:02
  • だいぶ議論が進んでしますが、私が良くやる方法は以下のパターンです。

    Model (プロパティのみのクラス)
        |
    UIObject (基本的にmodelにINotifyPropertyChangedを追加したもの)
        |
    ViewModel (UIobjectをObservableCollectionでラップしてプロパティで提供)
        |
    View (ViewModelのObservableCollectionをバインド)

    ModelとUIObjectはT4で自動生成していますので、そんなに手間ではありません。
    この辺りはフレームワーク化していますので、例えば単票でも1つのレコードのみ持つObservableCollectionがバインドしています。
    アプリケーションの作りが基本的に全て同じですので、メンテ等が楽になります。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2015年3月13日 2:05
    モデレータ