Multiselect Listview с флажком выбора всех. Datatrigger на связанной собственности
У меня есть представление списка с видом сетки, которая имеет два столбца. Первый столбец содержит флажки, связанные с выбранным свойством listviewitem, второй столбец - текст. В заголовке для столбца флажков у меня есть флажок, который я хочу функционировать как кнопка выбора / отмены выбора всех. Я использовал для этого триггеры данных, но это работает только тогда, когда я удаляю привязку между флажками и выбранным свойством. Могу ли я использовать триггер данных для установки связанного свойства?
отделенный код
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>
3 ответа
Используя предложение AjS, я придумал это решение.
Я также смог придумать решение, которое детализировало визуальное дерево списка, пока не получил VirtualizingStackPanel, который содержит все элементы ListviewItems, и прошел через них, устанавливая свойство 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));
}
}
}
}
Для всех, кто интересуется вторым решением, которое я упомянул, это событие должно быть прикреплено к отмеченным и непроверенным событиям флажка 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;
}
Я считаю, что лучший способ реализовать это - создать свойство IsSelected для вашего класса и связать его с флажком (IsChecked) и списком (isSelected). Обработайте событие флажка заголовка и вручную переключите свойство IsSelected элементов в коллекции, чтобы флажки и элементы списка были обновлены. Пожалуйста, дайте мне знать, если у вас есть другие вопросы