【问题标题】:Multiselect Listview with select all check box. Datatrigger on binded property带有全选复选框的多选 Listview。绑定属性上的数据触发器
【发布时间】:2017-07-03 08:50:07
【问题描述】:

我有一个带有两列网格视图的列表视图。第一列包含绑定到 listviewitem selected 属性的复选框,第二列是文本。在复选框列的标题中,我有一个复选框,我想用作全选/取消全选按钮。我已经使用数据触发器来执行此操作,但它仅在我删除复选框和选定属性之间的绑定时才有效。我应该能够使用数据触发器来设置绑定属性吗?

Screenshot

代码隐藏

namespace ListviewWCheckboxes
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        List<string> listItems = new List<string>() { "foo", "bar", "blah" };
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            pdflistView.ItemsSource = listItems;
        }
    }
}

xaml

<Window x:Class="ListviewWCheckboxes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListviewWCheckboxes"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        Loaded="Window_Loaded">
    <Grid>
        <ListView x:Name="pdflistView" HorizontalAlignment="Left" Height="300" Margin="5" VerticalAlignment="Top" Width="240"
                      SelectionMode="Extended"
                      >
            <ListView.View>
                <GridView>
                    <GridViewColumn>
                        <GridViewColumn.Header>
                            <CheckBox x:Name="ckbxSelectAll"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=IsSelected, 
                                                RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}, Mode=FindAncestor},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
                                    <CheckBox.Style>
                                        <Style TargetType="CheckBox">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding ElementName=ckbxSelectAll, Path=IsChecked}" Value="True">
                                                    <Setter Property="IsChecked" Value="True" />
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding ElementName=ckbxSelectAll, Path=IsChecked}" Value="False">
                                                    <Setter Property="IsChecked" Value="False" />
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </CheckBox.Style>

                                </CheckBox>
                                <DataTemplate.Triggers>

                                </DataTemplate.Triggers>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>

                    </GridViewColumn>
                    <GridViewColumn Header="Pdf" DisplayMemberBinding="{Binding}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

【问题讨论】:

    标签: c# wpf binding datatrigger selectall


    【解决方案1】:

    根据 AjS 的建议,我想出了这个解决方案。

    我还能够提出一个解决方案,该解决方案向下钻取列表框的可视化树,直到获得包含所有 ListviewItems 的 VirtualizingStackPanel 并在它们中循环设置 IsSelected 属性,但这看起来更简洁。

    xaml

     <Window x:Class="ListviewWCheckboxes.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ListviewWCheckboxes"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ListView x:Name="pdflistView" HorizontalAlignment="Left" Height="300" Margin="5" VerticalAlignment="Top" Width="240"
                          SelectionMode="Extended" ItemsSource="{Binding Path=listItems}">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="IsSelected" Value="{Binding is_Selected}"/>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.View>
                    <GridView>
                        <GridViewColumn>
                            <GridViewColumn.Header>
                                <CheckBox x:Name="ckbxSelectAll" Checked="ckbxSelectAll_Checked" Unchecked="ckbxSelectAll_Checked"/>
                            </GridViewColumn.Header>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding is_Selected}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Pdf" DisplayMemberBinding="{Binding Path=text}"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>
    </Window>
    

    代码

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace ListviewWCheckboxes
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public ObservableCollection<someobjects> listItems { get; set; } = new ObservableCollection<someobjects>();
    
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = this;
                listItems.Add(new someobjects("foo"));
                listItems.Add(new someobjects("bar"));
                listItems.Add(new someobjects("blah"));
            }
    
            private void ckbxSelectAll_Checked(object sender, RoutedEventArgs e)
            {
                foreach (someobjects item in listItems)
                {
                    item.is_Selected = (bool)((CheckBox)sender).IsChecked;
                }
            }
    
            public class someobjects : INotifyPropertyChanged
            {
                private string _text;
    
                public string text
                {
                    get { return _text; }
                    set { _text = value; OnPropertyChanged(); }
                }
    
                private bool _is_Selected;
    
                public bool is_Selected
                {
                    get { return _is_Selected; }
                    set { _is_Selected = value; OnPropertyChanged(); }
                }
    
                public someobjects(string t)
                {
                    text = t;
                    is_Selected = false;
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
    
                protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
                {
                    var handler = PropertyChanged;
                    if (handler != null)
                        handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      对于任何想知道我提到的第二个解决方案的人,此事件必须附加到 selectall 复选框的选中和未选中事件

      private void ckbxSelectAll_Checked(object sender, RoutedEventArgs e)
          {
              DockPanel dp = FindVisualChild<DockPanel>(pdflistView, 0);
              ScrollContentPresenter scp = FindVisualChild<ScrollContentPresenter>(dp, 1);
              VirtualizingStackPanel vsp = FindVisualChild<VirtualizingStackPanel>(scp, 0);
      
              foreach (ListViewItem item in vsp.Children)
              {
                  item.IsSelected = (bool)((CheckBox)sender).IsChecked;
              }
          }
      
          private static T FindVisualChild<T>(UIElement element, int childindex) where T : UIElement
          {
              UIElement child = element;
              while (child != null)
              {
                  T correctlyTyped = child as T;
                  if (correctlyTyped != null)
                  {
                      return correctlyTyped;
                  }
      
                  child = VisualTreeHelper.GetChild(child, childindex) as UIElement;
              }
              return null;
          }
      

      【讨论】:

        【解决方案3】:

        我相信实现这一点的最佳方法是在您的类上创建一个属性“IsSelected”并将其绑定到复选框 (IsChecked) 和列表视图 (isSelected)。处理标题复选框的选中事件并手动切换集合中项目的 IsSelected 属性,以便更新复选框和列表视图项。如果您有任何其他问题,请告诉我

        【讨论】:

        • 只要我在使用全选按钮之前不使用复选框来选择行,它就可以工作。如果我用复选框选择项目 foo 然后取消选择它并使用全选复选框 foo 将不会被选中,但其他项目会。如果手动选择,每个项目都会中断。关于如何解决这个问题的任何想法?
        • @WIllCruz 是的,我看到它破坏了选择;抱歉,我回复得太早了。我已经更新了帖子。
        猜你喜欢
        • 2018-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-27
        • 2010-10-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多