none
WPFのBindingのグループ化について RRS feed

  • 質問

  • WPFを勉強して1カ月の初心者です。

    必要な情報をいろいろと勉強してきましたが、以下の方法を実現しようと思うと、

    良い方法が思いつかずアドバイスを頂きたいと思い投稿させて頂きました。

    例えば、担当者情報をコード検索または検索用のダイアログを作成しておき、一意の情報を返却するViewModelを作成してみました。

    public class BaseViewModel1 : ViewModel { public void Initialize() { } #region txtTantoCd変更通知プロパティ private string _txtTantoCd; public string txtTantoCd { get { return _txtTantoCd; } set { if (_txtTantoCd == value) return; _txtTantoCd = value; RaisePropertyChanged("txtTantoCd"); } } #endregion #region txtTantoNm変更通知プロパティ private string _txtTantoNm; public string txtTantoNm { get { return _txtTantoNm; } set { if (_txtTantoNm == value) return; _txtTantoNm = value; RaisePropertyChanged("txtTantoNm"); } } #endregion #region getTantoCommand private ViewModelCommand _getTantoCommand; public ViewModelCommand getTantoCommand { get { if (_getTantoCommand == null) { _getTantoCommand = new ViewModelCommand(getTanto); } return _getTantoCommand; } } public void getTanto() { /*

    担当者情報を取得する処理・・・

    */

      this.txtTantoNm = "担当者の結果をセット"; } #endregion }

    これを継承したViewModelクラスを作成しViewとバインドさせることで、結果をセットする処理はできました。

    次に、1画面に複数の担当者を入力する必要がある場合が出てくるかと思います。

    Viewを修正し、グループ化することで同ロジックで複数の処理をする場合にも対応出来るかと思いました。

    これを回避するためには、

         	<TextBox
                        Path=txtTantoCd,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>
    
            <TextBox
                        Path=txtTantoNm,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>
    
            <Button
                Content="検索"
                Command="{Binding getTantoCommand}">
            </Button>

    上記の記述を、

           <TextBox
                        Path=group1/txtTantoCd,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>

            <TextBox
                        Path=group1/txtTantoNm,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>

            <Button
                Content="検索"
                Command="{Binding group1/getTantoCommand}">
            </Button>

           <TextBox
                        Path=group2/txtTantoCd,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>

            <TextBox
                        Path=group2/txtTantoNm,
                        UpdateSourceTrigger=PropertyChanged}">
            </TextBox>

            <Button
                Content="検索"
                Command="{Binding group2/getTantoCommand}">
            </Button>

    上記のようにする必要があると思います。

    これに対するViewModelを上手く作成することができません。

    そもそも、基底クラスにバインド変数を宣言しておくことが良くないのでしょうか?

    やはり、取得処理を行うメソッドと変数だけを基底クラスにし、バインド変数は各ViewModelで宣言していくパターンで設計した方が賢明なのでしょうか?



    2015年8月7日 0:52

回答

  • アプリ全体の設計を見ないと決められないけど、

    例として、担当者要素のViewModel(ViewModelという名前は議論の余地はあるが)を作って、画面用のViewModelでそれを使う、というパターンもあります。Xamlのバインドは子孫プロパティを指定できますので。


    //担当者要素のViewModel
    class TantoViewModel : BindableBase
    {
        public int TantoCd { get; }//おきまりの処理は省略
        public string TantoName { get; }//おきまりの処理は省略
        public ICommand SearchCommand { get; }//おきまりの処理は省略
    }
    
    //画面にバインドするViewModel
    class GamenViewModel : BindableBase
    {
        public TantoViewModel Tanto1 { get; }//おきまりの処理は省略
        public TantoViewModel Tanto2 { get; }//おきまりの処理は省略
    }

    <TextBlock Text="{Binding Tanto1.TantoName}" />
    <Button Command="{Binding Tanto1.SearchCommand}" />
            
    <TextBlock Text="{Binding Tanto2.TantoName}" />
    <Button Command="{Binding Tanto2.SearchCommand}" />



    2015年8月7日 15:23

すべての返信

  • やはり、取得処理を行うメソッドと変数だけを基底クラスにし、バインド変数は各ViewModelで宣言していくパターンで設計した方が賢明なのでしょうか?

    私はそのように思います。ViewModelはViewとModelの架け橋となるものですから、基底のViewModelは両方の最小の共通事項のみを持つのが良いように思います。
    Viewによって担当者の数が変わるのであれば、そのViewに対応したViewModel、つまり派生したViewModelで対応するようにした方が良いように思います。

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

    2015年8月7日 1:13
    モデレータ
  • アプリ全体の設計を見ないと決められないけど、

    例として、担当者要素のViewModel(ViewModelという名前は議論の余地はあるが)を作って、画面用のViewModelでそれを使う、というパターンもあります。Xamlのバインドは子孫プロパティを指定できますので。


    //担当者要素のViewModel
    class TantoViewModel : BindableBase
    {
        public int TantoCd { get; }//おきまりの処理は省略
        public string TantoName { get; }//おきまりの処理は省略
        public ICommand SearchCommand { get; }//おきまりの処理は省略
    }
    
    //画面にバインドするViewModel
    class GamenViewModel : BindableBase
    {
        public TantoViewModel Tanto1 { get; }//おきまりの処理は省略
        public TantoViewModel Tanto2 { get; }//おきまりの処理は省略
    }

    <TextBlock Text="{Binding Tanto1.TantoName}" />
    <Button Command="{Binding Tanto1.SearchCommand}" />
            
    <TextBlock Text="{Binding Tanto2.TantoName}" />
    <Button Command="{Binding Tanto2.SearchCommand}" />



    2015年8月7日 15:23
  • >trapemiyaさん

    ご意見ありがとうございます。

    基底クラスとは別クラスで作成して、バインドさせたいと思います。

    2015年8月10日 2:51
  • >アドマイヤコジーンさん

    教えて頂いたソースを参考にしたところ上手くいきました!!

    基底クラスから分離して、担当者情報を取得する別クラスを作成する方針で検討したいと思います。

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

    2015年8月10日 2:57
  • ちなみに、ある1つの画面に入力する担当者数は固定なのでしょうか? もし固定でない場合、GamenViewModelでTantoViweModelをObservableCollectionとして公開するようにすると便利です。こうしておくと、例えば担当者一覧をListViewやDataGridで管理できるようになります。今回はTextBoxを使用されるようですが、将来的な参考として覚えておかれると良いと思います。

    #今回は必須ではないかもしれませんが、最初からObservableCollectionで実装しておいても良いです。例えば担当者が1人と固定の場合でも、ObservableCollectionの1番目の要素を使うだけで済みます。その場合はTextBoxにバインドさせても良いでしょう。このようにObservableCollectionで統一しておくと、他の似たような画面や、将来の改修が楽になります。なぜなら、初めからプログラムの構造が統一されて頭に入っているからです。

    #アドマイヤコジーンさんも書かれていますが、TantoViewModelという名前はちょっと違和感があります。ViewModelが公開してバインディングするオブジェクトなので、例えば私ならTantoUIObjectみたいな名前にすると思います。


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


    • 編集済み trapemiyaModerator 2015年8月10日 5:05 わかりやすい言い回しに修正
    2015年8月10日 4:40
    モデレータ
  • >trapemiyaさん

    コメント頂きありがとうございます。

    ObservableCollectionについて勉強してみました。

    TantoViewModelから、コマンドでメッセンジャーを利用して担当者一覧を表示するためのDataGridを有する画面を呼び出し、選択した結果をTantoViewModelのtxtTantoCdやtxtTantoNmに返却することを想定しています。

    これにObservableCollectionを適用することで、担当者一覧の画面のバインド時にも活用出来そうですね。

    担当者情報はデータベースから取得するのですが、SQLの結果をObservableCollectionで取れそうな記事もみつけたので試してみます。

    また、TantoViewModelをカプセル化し、ObservableCollectionを持たせたクラスを作成して公開すると便利そうな気がしますので、そこも試してみたいと思います。

    >#アドマイヤコジーンさんも書かれていますが、TantoViewModelという名前はちょっと違和感があります。ViewModelが公開してバインディングするオブジェクトなので、例えば私ならTantoUIObjectみたいな名前にすると思います。

    確かに、TantoViewModelには対応するViewが無いのにViewModelというのは不自然と思っていました。

    値だけならParameterって感じですが、コマンドもあるのでObjectという表現の方が良さそうですね。

    いろいろと貴重なご意見頂きありがとうございました。


    2015年8月11日 9:30