WPF Binding IsSelected to ViewModel doesn't set items that haven't been shown in the List
I have a ViewModel
that has an IsSelected
property which I bind in my ListView.ItemContainerStyle
XAML to an IsSelected
property in my view model.
I bring up the application and populate the view model collection (which is shown in my ListView
) with a lot of items, say about 2000. Then I select everything in the list via Ctrl-A. The items in my view model collection only get the IsSelected
set for the items that are visible in the ListView
. If I page down through the list the IsSelected
gets set for whatever items get shown. If I page through all the items then all the items in my view model have the IsSelected
property set to true.
Here is my XAML for binding the IsSelected
in the list view to my view model:
<ListView Margin="5" ItemsSource="{Binding FilteredComparisonList}" 开发者_如何学JAVAx:Name="comparisonListView">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Source filename" DisplayMemberBinding="{Binding ImageFile.BaseFilename}" Width="Auto" />
</GridView>
</ListView.View>
</ListView>
Why isn't IsSelected
for all the items in my view model set to true when I select all the items in the ListView
?
The MVVM way to do this would be to override the Ctrl-A shortcut with your own SelectAll command (Create a SellectAll command with a shortcut of Ctrl-A). The implementation will set IsSelected on the view models.
Your IsSelected property in your view needs to have a two way binding to your view model so that the items appear selected in your view.
I dont think turning off virtualisation is nessesary.
Hope this helps.
This is happening because of the ListView's built-in virtualization. If you're not familiar with that, what it basically means is that the items don't become real until they are in view. You can turn off the ListView's virtualization with the following property:
VirtualizingStackPanel.IsVirtualizing="False"
But beware this will have an adverse effect on your ListView's performance. For 2,000 items it won't be severe, but it might be noticeable.
The ListBoxItem's are virtualised, they do not exist until they are generated (when they are brought into view). However the underlying ListView is derived from ListBox which has a SelectedItems property. The SelectedItems are a list of all the selected items not a list of the ListBoxItems that are selected. When the ListBoxItem is brought into view its IsSelected property is set based on if it's item (the item it displays) is in the collection of SelectedItems.
This is a propblem for your view model, especially if you need the virtualisation to be on (which you probably do for 2000 or so items). I would be interested in the answer to the question "How do i bind my view models IsSelected property based on if an item is in a ListBox's SelectedItems collection?"
the answer probably involves a converter
I had the same problem and that's what helped me finally:
Put in your ListView
:
VirtualizingStackPanel.VirtualizationMode="Standard"
I'll post this answer that I found on a different forum since it helped me with the issue.
I have defined an interface ISelectable for my ViewModels to implement
public interface ISelectable
{
bool IsSelected { get; set; }
}
Then in MyCustomListView I have done this:
public class MyCustomListView : ListView
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<ISelectable>())
item.IsSelected = true;
foreach (var item in e.RemovedItems.OfType<ISelectable>())
item.IsSelected = false;
base.OnSelectionChanged(e);
}
}
Alternatively, you could do subscribe to the SelectionChanged event of the ListView and use the same code above.
精彩评论