我有一个WPF ListView
,其中包含CheckBox
列和自定义对象的其他列。我想将另一个CheckBox
放在此ListView
- 三态CheckBox
上 - 因此其状态将被绑定到选中的复选框。如果检查每个人,将检查上方复选框的状态。如果只有其中一些国家将是不确定的。否则其状态将被取消选中。 (比如在Gmail中)
答案 0 :(得分:1)
我找到了一些解决方案
<StackPanel>
<CheckBox Name="chbxAll" Checked="chbxAll_Checked" Unchecked="chbxAll_Unchecked" Indeterminate="chbxAll_Indeterminate" IsThreeState="True" >Select All</CheckBox>
<ListView Name="lstFoundedFiles" SelectionChanged="lstFoundedFiles_SelectionChanged" SelectionMode="Multiple" ItemsSource="{Binding Files}">
<ListView.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Aquamarine"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="Check">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chbxItem" IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="File">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" ></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Location">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
=============================================== ===========================
代码隐藏:
// True if we should ignore check change events.
private bool IgnoreCheckChangeEvents = false;
private void lstFoundedFiles_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (IgnoreCheckChangeEvents) return;
int temp = lstFoundedFiles.SelectedItems.Count;
IgnoreCheckChangeEvents = true;
if (temp == lstFoundedFiles.Items.Count)
{
chbxAll.IsChecked = true;
}
else if (temp == 0)
{
chbxAll.IsChecked = false;
}
else
{
chbxAll.IsChecked = null;
}
IgnoreCheckChangeEvents = false;
}
private void chbxAll_Checked(object sender, RoutedEventArgs e)
{
if (IgnoreCheckChangeEvents) return;
IgnoreCheckChangeEvents = true;
lstFoundedFiles.SelectAll();
IgnoreCheckChangeEvents = false;
}
private void chbxAll_Unchecked(object sender, RoutedEventArgs e)
{
if (IgnoreCheckChangeEvents) return;
IgnoreCheckChangeEvents = true;
lstFoundedFiles.UnselectAll();
IgnoreCheckChangeEvents = false;
}
private void chbxAll_Indeterminate(object sender, RoutedEventArgs e)
{
if (IgnoreCheckChangeEvents) return;
chbxAll.IsChecked = false;
IgnoreCheckChangeEvents = true;
lstFoundedFiles.UnselectAll();
IgnoreCheckChangeEvents = false;
}
答案 1 :(得分:0)
编辑:这比我的其他方法更糟糕,至少它不是那么混乱,也不需要那么多的事件处理。每当源发生变化时,此方法都会重建一个巨大的多重绑定,因此可能会有一点性能。
<GridViewColumn.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Is Active" />
<CheckBox IsThreeState="True"
local:AttachedProperties.SelectAllPath="IsActive"
local:AttachedProperties.SelectAllItemsSource="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=ItemsSource}" />
</StackPanel>
</GridViewColumn.Header>
public static readonly DependencyProperty SelectAllPathProperty =
DependencyProperty.RegisterAttached("SelectAllPath", typeof(string), typeof(AttachedProperties), new UIPropertyMetadata(null, OnAttached));
public static string GetSelectAllPath(DependencyObject obj)
{
return (string)obj.GetValue(SelectAllPathProperty);
}
public static void SetSelectAllPath(DependencyObject obj, string value)
{
obj.SetValue(SelectAllPathProperty, value);
}
public static readonly DependencyProperty SelectAllItemsSourceProperty =
DependencyProperty.RegisterAttached("SelectAllItemsSource", typeof(IEnumerable), typeof(AttachedProperties), new UIPropertyMetadata(null));
public static IEnumerable GetSelectAllItemsSource(DependencyObject obj)
{
return (IEnumerable)obj.GetValue(SelectAllItemsSourceProperty);
}
public static void SetSelectAllItemsSource(DependencyObject obj, IEnumerable value)
{
obj.SetValue(SelectAllItemsSourceProperty, value);
}
private static void OnAttached(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var cb = o as CheckBox;
if (cb.IsLoaded)
{
Attach(cb);
}
else
{
cb.Loaded += (s, _) => Attach(cb);
}
}
private static void Attach(CheckBox checkBox)
{
var itemsSource = GetSelectAllItemsSource(checkBox);
if (itemsSource is INotifyCollectionChanged)
{
var isAsIcc = itemsSource as INotifyCollectionChanged;
isAsIcc.CollectionChanged += (s, ccea) =>
{
RebuildBindings(checkBox);
};
}
RebuildBindings(checkBox);
checkBox.Click += (s, cea) =>
{
if (!checkBox.IsChecked.HasValue)
{
checkBox.IsChecked = false;
}
};
}
private static void RebuildBindings(CheckBox checkBox)
{
MultiBinding binding = new MultiBinding();
var itemsSource = GetSelectAllItemsSource(checkBox);
var path = GetSelectAllPath(checkBox);
foreach (var item in itemsSource)
{
binding.Bindings.Add(new Binding(path) { Source = item });
}
binding.Converter = new CheckedConverter();
checkBox.SetBinding(CheckBox.IsCheckedProperty, binding);
}
private class CheckedConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Length == 0)
{
return null;
}
else
{
bool first = (bool)values[0];
foreach (var item in values.Skip(1).Cast<bool>())
{
if (first != item)
{
return null;
}
}
return first;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
var output = (bool?)value;
var outarray = new object[targetTypes.Length];
if (output.HasValue)
{
for (int i = 0; i < outarray.Length; i++)
{
outarray[i] = output.Value;
}
}
return outarray;
}
}
( Bindingless Mess:)
以下(在XAML之后)有一些最丑陋的代码,它可能有事件处理和其他可怕缺陷的内存泄漏,但它确实在某种程度上起作用:
<GridViewColumn.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Is Active" />
<CheckBox IsThreeState="True"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=ItemsSource}"
local:AttachedProperties.SelectAllPath="IsActive"/>
</StackPanel>
</GridViewColumn.Header>
public static readonly DependencyProperty SelectAllPathProperty =
DependencyProperty.RegisterAttached("SelectAllPath", typeof(string), typeof(AttachedProperties), new UIPropertyMetadata(null, OnAttached));
public static string GetSelectAllPath(DependencyObject obj)
{
return (string)obj.GetValue(SelectAllPathProperty);
}
public static void SetSelectAllPath(DependencyObject obj, string value)
{
obj.SetValue(SelectAllPathProperty, value);
}
private static void OnAttached(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var cb = o as CheckBox;
// Needs more closures.
Action attach = () =>
{
IEnumerable itemsSource = cb.Tag as IEnumerable;
if (itemsSource == null) throw new Exception("ItemsSource for attached property 'SelectAllPath' not found.");
string path = e.NewValue as string;
cb.Checked += new RoutedEventHandler(cb_Checked);
cb.Unchecked += new RoutedEventHandler(cb_Unchecked);
PropertyChangedEventHandler propertyChangeHandler = (i, pcea) =>
{
if (pcea.PropertyName == path)
{
UpdateCb(cb, itemsSource.Cast<object>(), path);
}
};
Action<object> tryAttachHandlerAction = (item) =>
{
if (item is INotifyPropertyChanged)
{
var asInpc = item as INotifyPropertyChanged;
asInpc.PropertyChanged += propertyChangeHandler;
}
};
foreach (var item in itemsSource)
{
tryAttachHandlerAction(item);
}
if (itemsSource is INotifyCollectionChanged)
{
var asCC = itemsSource as INotifyCollectionChanged;
asCC.CollectionChanged += (s, cce) =>
{
if (cce.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in cce.NewItems)
{
tryAttachHandlerAction(item);
}
}
};
}
UpdateCb(cb, itemsSource.Cast<object>(), path);
};
if (cb.IsLoaded)
{
attach();
}
else
{
cb.Loaded += (s, esub) => attach();
}
}
private static void UpdateCb(CheckBox cb, IEnumerable<object> items, string path)
{
Type itemType = null;
PropertyInfo propInfo = null;
bool? previous = null;
bool indeterminate = false;
foreach (var item in items)
{
if (propInfo == null)
{
itemType = item.GetType();
propInfo = itemType.GetProperty(path);
}
if (item.GetType() == itemType)
{
if (!previous.HasValue)
{
previous = (bool)propInfo.GetValue(item, null);
}
else
{
if (previous != (bool)propInfo.GetValue(item, null))
{
indeterminate = true;
break;
}
}
}
}
if (indeterminate)
{
cb.IsChecked = null;
}
else
{
if (previous.HasValue)
{
cb.IsChecked = previous.Value;
}
}
}
static void cb_Unchecked(object sender, RoutedEventArgs e)
{
SetValues(sender, false);
}
static void cb_Checked(object sender, RoutedEventArgs e)
{
SetValues(sender, true);
}
private static void SetValues(object sender, bool value)
{
var cb = sender as CheckBox;
IEnumerable itemsSource = cb.Tag as IEnumerable;
Type itemType = null;
PropertyInfo propInfo = null;
foreach (var item in itemsSource)
{
if (propInfo == null)
{
itemType = item.GetType();
propInfo = itemType.GetProperty(GetSelectAllPath(cb));
}
if (item.GetType() == itemType)
{
propInfo.SetValue(item, value, null);
}
}
}
不要使用它(至少以其当前形式)。