none
ListViewでデータにバインドしたCheckBoxを表示したとき、選択された項目のCheckBoxをクリックすると表示がおかしくなる RRS feed

  • 質問

  • INotifyPropertyChangedインターフェイスを使用して、ListViewに表示したCheckBoxにデータをバインドし、ListViewの項目をクリックする度にCheckBoxの表示が変更されるは確認できました。ところが、選択されている項目のCheckBoxをクリックすると表示が期待した動き(CheckからUncheck、またはその逆)にならず、元に戻って(Check→Uncheck→Check、または、Uncheck→Check→Uncheck)しまいます。

    ちなみに、デバッガにてlistView_PreviewMouseDownの中にブレークポイントを張り、一度停止させてから続行すると期待した表示になります。

    どう対処すればいいのかご教示頂けたら幸いです。

    参考までに、コードは以下の通りです。

    XAML

            <ListView x:Name="listView" HorizontalAlignment="Left" Height="143" Margin="10,10,0,0" VerticalAlignment="Top" Width="139" ItemsSource="{Binding}" SelectionChanged="listView_SelectionChanged">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="チェック">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding Path=Selected}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="内 容" DisplayMemberBinding="{Binding Path=Comment}" />
                    </GridView>
                </ListView.View>
                <ListView.ItemContainerStyle>
                    <Style TargetType="{x:Type ListViewItem}">
                        <EventSetter Event="PreviewMouseDown" Handler="listView_PreviewMouseDown" />
                    </Style>
                </ListView.ItemContainerStyle>
            </ListView>

    C#

            public ObservableCollection<MyData> myData = new ObservableCollection<MyData>();
    
            public MainWindow()
            {
                InitializeComponent();
                myData.Add(new MyData(true, "Sample1"));
                myData.Add(new MyData(false, "Sample2"));
                myData.Add(new MyData(true, "Sample3"));
                listView.DataContext = myData;
            }
    
            public class MyData : INotifyPropertyChanged
            {
                public event PropertyChangedEventHandler PropertyChanged;
                public virtual void OnPropertyChanged(string name)
                {
                    var h = PropertyChanged;
                    if (h == null) return;
                    h(this, new PropertyChangedEventArgs(name));
                }
    
                private bool _Selected;
                public bool Selected
                {
                    get { return _Selected; }
                    set
                    {
                        _Selected = value;
                        OnPropertyChanged("Selected");
                    }
                }
    
                private string _Comment;
                public string Comment
                {
                    get { return _Comment; }
                    set
                    {
                        _Comment = value;
                        OnPropertyChanged("Comment");
                    }
                }
    
                public MyData (bool bBool, string sComment)
                {
                    _Selected = bBool;
                    _Comment = sComment;
                }
            }
    
            private void button_Click(object sender, RoutedEventArgs e)
            {
                MyData dd = sender as MyData;
            }
    
            private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                ListView md = sender as ListView;
                MyData ss = (MyData)md.SelectedItem;
                ss.Selected = ss.Selected ? false : true;
            }
    
            private void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
            {
                ListViewItem lvi = sender as ListViewItem;
    
                if (lvi == null) return;
                if (lvi.IsSelected)
                {
                    MyData td = (MyData)lvi.Content;
                    if (td != null)
                    {
                        td.Selected = td.Selected ? false : true;
                    }
                }
            }
        }


    • 編集済み tjshin 2015年12月10日 2:22
    2015年12月10日 2:21

回答

  • 簡単なのは

    <CheckBox IsChecked="{Binding Path=Selected}" IsHitTestVisible="False" />

    としてCheckBoxがマウスに反応しないようにしてしまうといいです

    #ListViewのSelectionModeをSingle以外にしてると複数行選択で悲しいことに…


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

    • 編集済み gekkaMVP 2015年12月10日 3:07
    • 回答としてマーク tjshin 2015年12月10日 3:33
    2015年12月10日 2:50
  • こんにちは。

    PreviewMouseDownが処理された時は処理済みとしてマーク(e.Handled = true)してしまってはどうですか。

    private void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        ListViewItem lvi = sender as ListViewItem;
    
        if (lvi == null) return;
        if (lvi.IsSelected)
        {
            MyData td = (MyData)lvi.Content;
            if (td != null)
            {
                td.Selected = td.Selected ? false : true;
                e.Handled = true;
            }
        }
    }

    • 回答としてマーク tjshin 2015年12月11日 7:43
    2015年12月10日 3:29
    モデレータ

すべての返信

  • 簡単なのは

    <CheckBox IsChecked="{Binding Path=Selected}" IsHitTestVisible="False" />

    としてCheckBoxがマウスに反応しないようにしてしまうといいです

    #ListViewのSelectionModeをSingle以外にしてると複数行選択で悲しいことに…


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

    • 編集済み gekkaMVP 2015年12月10日 3:07
    • 回答としてマーク tjshin 2015年12月10日 3:33
    2015年12月10日 2:50
  • こんにちは。

    PreviewMouseDownが処理された時は処理済みとしてマーク(e.Handled = true)してしまってはどうですか。

    private void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        ListViewItem lvi = sender as ListViewItem;
    
        if (lvi == null) return;
        if (lvi.IsSelected)
        {
            MyData td = (MyData)lvi.Content;
            if (td != null)
            {
                td.Selected = td.Selected ? false : true;
                e.Handled = true;
            }
        }
    }

    • 回答としてマーク tjshin 2015年12月11日 7:43
    2015年12月10日 3:29
    モデレータ
  • gekkaさん

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

    ご教示頂いたプロパティを設定した所、期待した動作になりました。

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

    2015年12月10日 3:32
  • Tak1waさん

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

    こちらの方法でも期待した動作になりました。

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

    2015年12月11日 7:43
  • そもそも、SelectionChangedイベントハンドラは要らないんじゃないかなぁ?

    private void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        ListViewItem lvi = sender as ListViewItem;
    
        if (lvi == null) return;
    
        var dc = (MyData)lvi.DataContext;
    
        dc.Selected = !dc.Selected;
    
        e.Handled = true;
    
        //if (lvi.IsSelected)
        //{
        //    MyData td = (MyData)lvi.Content;
        //    if (td != null)
        //    {
        //        td.Selected = td.Selected ? false : true;
        //    }
        //}
    }
    #好みとしては、e.Handled = trueよりは、gekkaさんのIsHitTestVisible="False"が良いですね。 CheckBoxを表示専用にしてしまう方がわかりやすいので。

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

    2015年12月11日 8:30
    モデレータ