質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

トリガーイベントをMVVMで実装したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,942

score 19

発生している問題

WPFアプリケーションを作っておりMVVMで実装しようと考えています。
ListBoxのSelectionChagedイベントを実装しようとしていますが、想定通りの動きにならず困っています。

該当のソースコード

Viewのコードに一部コメント箇所があります。
別の質問で「ListBoxではSelectedItemの問題でできないため、CheckListBoxを使ってください」というご回答をいただき、
CheckListBoxを使った場合がコメントアウト箇所になります。
結果としてはどちらでも上手くいきませんでした。

//View
        xmlns:xcad="http://schemas.xceed.com/wpf/xaml/toolkit"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox HorizontalAlignment="Left" Height="56" Margin="64,67,0,0" VerticalAlignment="Top" Width="380" SelectedItem="{Binding SelectColorList}" ItemsSource="{Binding ColorList}"/>
        <ListBox HorizontalAlignment="Left" Height="56" Margin="64,182,0,0" VerticalAlignment="Top" Width="380" ItemsSource="{Binding ItemList}"/>

        <!--<xcad:CheckListBox HorizontalAlignment="Left" Height="56" Margin="64,67,0,0" VerticalAlignment="Top" Width="380" SelectedItemsOverride="{Binding SelectColorList}" ItemsSource="{Binding ColorList}"/>
        <xcad:CheckListBox HorizontalAlignment="Left" Height="56" Margin="64,182,0,0" VerticalAlignment="Top" Width="380" ItemsSource="{Binding ItemList}"/>-->

        <Label Content="色" HorizontalAlignment="Left" Height="24" Margin="65,38,0,0" VerticalAlignment="Top" Width="65"/>
        <Label Content="アイテム" HorizontalAlignment="Left" Height="24" Margin="64,153,0,0" VerticalAlignment="Top" Width="65"/>

        <Button Content="Button" HorizontalAlignment="Left" Height="37" Margin="354,260,0,0" VerticalAlignment="Top" Width="90" Click="Button_Click"/>
    </Grid>
//ViewModel
    public class MainViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<string> ColorListVal;
        public ObservableCollection<string> ColorList
        {
            get { return ColorListVal; }
            set
            {
                ColorListVal = value;
                NotifyPropertyChanged("ColorList");

            }
        }

        private ObservableCollection<string> SelectColorListVal = new ObservableCollection<string>();
        public ObservableCollection<string> SelectColorList
        {
            get { return SelectColorListVal; }
            set
            {
                SelectColorListVal = value;
                NotifyPropertyChanged("SelectColorList");

                ItemListVal.Clear();

                for (int i = 0; i < SelectColorList.Count; i++)
                {
                    switch (SelectColorList[i])
                    {
                        case "赤":
                            ItemListVal.Add("りんご");
                            ItemListVal.Add("太陽");
                            ItemListVal.Add("チューリップ");
                            break;

                        case "青":
                            ItemListVal.Add("海");
                            ItemListVal.Add("ブルーハワイ");
                            ItemListVal.Add("空");
                            break;

                        case "黄":
                            ItemListVal.Add("レモン");
                            ItemListVal.Add("パイナップル");
                            ItemListVal.Add("チューリップ");
                            break;

                        case "緑":
                            ItemListVal.Add("青りんご");
                            ItemListVal.Add("野菜");
                            ItemListVal.Add("お茶");
                            break;
                    }
                }
            }
        }

        private ObservableCollection<string> ItemListVal;
        public ObservableCollection<string> ItemList
        {
            get { return ItemListVal; }
            set
            {
                ItemListVal = value;
                NotifyPropertyChanged("ItemList");
            }
        }



        public MainViewModel()
        {
            ColorListVal = new ObservableCollection<string>(){ "赤", "青", "黄", "緑" };
            ItemListVal = new ObservableCollection<string>() { "何も選択されていません"};
        }



        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
//コードビハインド
    public partial class MainWindow : Window
    {
        private MainViewModel viewmodel;
        public MainWindow()
        {
            InitializeComponent();
            viewmodel = new MainViewModel();
            this.DataContext = viewmodel;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string s1 = "";

            for (int i = 0; i < viewmodel.SelectColorList.Count; i++)
            {
                s1 += viewmodel.SelectColorList[i] + "\r\n";
            }

            MessageBox.Show(s1);
        }
    }

試したこと

本来であれば「色」リストの項目を選択すると「アイテム」リストに対応するアイテムが反映されるのですが、
初期値である「何も選択されていません」から変わりません。

プロパティのデータバインドが上手くいっていないのかと思い、
ボタンを追加して現在選択されている「色」リストの項目を確認するようにもしましたが、そこは正常に反映されています。

やはりプロパティのセッターを使うのではなく、コマンドをバインドさせる形になるのでしょうか?
ご助言いただけますと幸いです。

補足

ちなみに本番アプリケーションでは複数選択をするので SelectionMode="Extended" プロパティを設定します。(ListBoxの場合)
ListBox、ChechListBoxどちらを使うかは大きな問題ではないのですが、
複数選択した時にも「アイテム」リストに複数の色アイテムが表示されるようにできればと思っています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

その作りかただと、MainViewModelにてSelectColorListのCollectionChangedイベントをリスニングする必要があります。
リスニングには IWeakEventListener を実装したクラスを用いることが多いですが、Livet(MVVMインフラ)以外でやると結構面倒です。
また、変更はItemListValではなく、ItemListに対して行ってください。

Model が階層構造を持っていれば、それをそのまま表示するだけなのでVMでのリスニングは不要になります。
こんな風に

    // ViewModel
    public class MyViewModel
    {
        public MyViewModel()
        {
            Groups = MyGroupPresenter.GetMaterials();
            SelectedGroups = new ObservableCollection<MyGroup>();
        }

        public IEnumerable<MyGroup> Groups { get; private set; }

        public ObservableCollection<MyGroup> SelectedGroups { get; set; }
    }

    // 以下Model
    public enum ColorTypes
    {
        赤, 青, 黄, 緑
    }

    public class Item
    {
        public string Name { get; set; }
    }

    public class MyGroup
    {
        public ColorTypes Color { get; set; }
        public ICollection<Item> Items { get; set; }
    }

    public class MyGroupPresenter
    {
        public static List<MyGroup> GetMaterials()
        {
            return new List<MyGroup>
            {
                new MyGroup
                {
                    Items = new Item []
                    {
                        new Item {Name = "リンゴ"},
                        new Item {Name = "太陽"},
                        new Item {Name = "チューリップ"}
                    },
                    Color = ColorTypes.赤
                }
            };
        }
    }
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <tk:CheckListBox ItemsSource="{Binding Groups}"
                     SelectedItemsOverride="{Binding SelectedGroups}"
                     DisplayMemberPath="Color"/>
    <ListBox Grid.Column="1"
             ItemsSource="{Binding SelectedGroups}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding Color}"/>
                    <ListBox Grid.Row="1" ItemsSource="{Binding Items}"
                             DisplayMemberPath="Name"/>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/18 15:57

    hihijiji様
    ご回答ありがとうございます。ご教示いただいたコード自体は動いたのですが2つだけ質問させてください。
    ①色とアイテムのリストが動的に追加される(中身が可変で数も可変ある)場合、どのように書いたらいいのでしょうか。
    ②ItemSource="{Binding Items}"のListBoxをTextBlockにすることは可能でしょうか。

    キャンセル

  • 2019/01/18 16:11 編集

    1.Modelが変わるだけです。動的に変化するModelを書いてください。
    2.出来ないことは無いですが、Textを扱うととても面倒で厄介なのでまずそんな書きかたはしません。

    キャンセル

  • 2019/01/18 17:03

    hihijiji様 ご回答ありがとうございます。試しにTextBlockにしたらすごく大変でしかもやっぱり解決できなかったので、手は出さないでおこうと思います...。前回に引き続きありがとうございました。

    キャンセル

+1

一つ目の ListBox の SelectedItem に SelectColorList が設定されているからでは?

SelectedItem には ListBox で選択されている内容、この場合"赤"やら"青"といった文字列が設定されるべきなのに、Bind されているのが ObservableCollection<string> だからではないですか?
※デバッガに「ObservableCollectionに変換できません云々」のエラーが表示されていないですか?

以下でどうでしょう。

        private string SelectColorListVal = "";
        public string SelectColorList
        {
            get { return SelectColorListVal; }
            set
            {
                if( SelectColorListVal == value )
                    return;

                SelectColorListVal = value;
                NotifyPropertyChanged("SelectColorList");

                ItemListVal.Clear();

                switch (SelectColorListVal)
                {
                    case "赤":
                        ItemListVal.Add("りんご");
                        ItemListVal.Add("太陽");
                        ItemListVal.Add("チューリップ");
                        break;
                    case "青":
                        ItemListVal.Add("海");
                        ItemListVal.Add("ブルーハワイ");
                        ItemListVal.Add("空");
                        break;
                    case "黄":
                        ItemListVal.Add("レモン");
                        ItemListVal.Add("パイナップル");
                        ItemListVal.Add("チューリップ");
                        break;
                    case "緑":
                        ItemListVal.Add("青りんご");
                        ItemListVal.Add("野菜");
                        ItemListVal.Add("お茶");
                        break;
                }
            }
        }

なお複数選択を考慮するということであれば、別途違やり方を考える必要があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/18 17:03

    ebiryo様 ご回答ありがとうございます。複数選択は絶対外せない仕様なので、他に考えてみます。ちなみに「ObservableCollectionに変換できません云々」は出ていませんでした。それにSelectionChangeイベントとしてコードビハインドにゴリゴリ書いていた時は上手くできていたので...。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る