WPF - Extend ListView with checkable AND selectable ListViewItems
I already read many examples on extending ListViews with checkboxes bound with IsSelected. But I want something more.
I want a seperation between the checked and selected state, so i get a ListBox that has a single selected item, but can have multiple checked items. Unfortunately ListViewItem does not have a property for checked and I dont see a possibility to get the ListView to work with a custom CheckableListViewItem.
Of course i could use a List of objects with a checked property as ItemSource, but I dont think thats a good way to go. Checked or not is a matter of the list or item-container, not of the object listed in it. Beside that I dont want all my classes like user, role, group to have counterparts like checkableUser, checkableRole and checkabl开发者_运维问答eGroup.
The behaviour i want can be easyly accomblished for the UI with a
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox />
</StackPanel>
</DataTemplate>
and a
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}" Width="30"/>
But without a binding on the checkbox i cant check if it is checked or not.
Is there any way to accomplish something like that? The perfect solution for me would be to have listView1.SelectedItem, listView1.CheckedItems and maybe a listView1.UncheckedItems and of course listView1.CheckItem and listView1.UncheckItem.
Thanks for any help.
Ok, I got it. Wasn't much to do, but becouse I'm new to the whole WPF stuff, its been some work to figure out. Here is the solution:
public class CheckableListViewItem : ListViewItem
{
[Category("Appearance")]
[Bindable(true)]
public bool IsChecked { get; set; }
}
public class CheckableListView : ListView
{
public IList CheckedItems
{
get
{
List<object> CheckedItems = new List<object>();
for (int i=0;i < this.Items.Count; ++i)
{
if ((this.ItemContainerGenerator.ContainerFromIndex(i) as CheckableListViewItem).IsChecked)
CheckedItems.Add(this.Items[i]);
}
return CheckedItems;
}
}
public bool IsChecked(int index)
{
if (index < this.Items.Count) return (this.ItemContainerGenerator.ContainerFromIndex(index) as CheckableListViewItem).IsChecked;
else throw new IndexOutOfRangeException();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
if (item is CheckableListViewItem) return true;
else return false;
}
protected override DependencyObject GetContainerForItemOverride()
{
return new CheckableListViewItem();
}
}
Insert into your XAML under Window.Resources (clr = my class namespace):
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsChecked,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type clr:CheckableListViewItem}}}" />
</StackPanel>
</DataTemplate>
And this as your CheckableListView:
<clr:CheckableListView SelectionMode="Single" [...] >
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}"
Width="30"/>
[...]
</GridView>
</ListView.View>
</clr:CheckableListView>
Maybe this helps someone with the same problem.
In order to do this, you will have to create a custom ListBox
and custom ListBoxItem
controls to use within your application. Otherwise, you would have to add it to the items in your lists as a generic object ICheckable<T>
(where T is User or Role) and have an ICheckableCollection<ICheckable<T>>
for your items instead of adding a checkable to your model objects.
精彩评论