问题
I have a ListView
of CheckBox
controls in which a check mark is mysteriously applied to other items within the ListView
when the page containing the ListView
has navigation returned to it.
Specifically, I apply check marks to some items within the ListView
that contains roughly 300 items. I then tap a Button
that invokes another app to launch, and then when I return back to my original app with the ListView
and scroll down the ListView
, I observe a pattern of mysteriously checked checkboxes that follow the pattern of the original checked locations that I have made within the ListView
.
NOTE:
I am using virtualization within my ListView
.
The mysterious checked checkboxes only occur when navigation is returned to the page containing the ListView
.
Checkboxes should not get checked mysteriously. XAML:
<ListView x:Name="ContactList"
ItemsSource="{Binding SelectedCategory.Contacts}"
SelectedItem="{Binding SelectedContact, Mode=TwoWay}"
ScrollViewer.VerticalScrollMode="Enabled"
Height="600"
Width="425"
Margin="58,175,0,0" Canvas.ZIndex="99"
Background="Transparent" Foreground="#FF333747"
VerticalAlignment="Top" HorizontalAlignment="Left">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="checkbox" Style="{StaticResource CheckBoxStyle1}"
Visibility="{Binding ElementName=grid, Path=DataContext.BroadcastActivated, Converter={StaticResource BoolToVisibilityConverter}, Mode=TwoWay}"
Margin="0,-8" BorderBrush="#FF4E58BC" Checked="ContactChecked" Unchecked="ContactUnchecked">
</CheckBox>
<TextBlock Text="{Binding DisplayName}" Width="425">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Holding">
<behaviors:MoveContactAction />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Family" Command="{Binding ElementName=grid, Path=DataContext.MoveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
<MenuFlyoutItem Text="Friend" Command="{Binding ElementName=grid, Path=DataContext.MoveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
<MenuFlyoutItem Text="Business" Command="{Binding ElementName=grid, Path=DataContext.MoveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
<MenuFlyoutItem Text="Met" Command="{Binding ElementName=grid, Path=DataContext.MoveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
<MenuFlyoutItem Text="Others" Command="{Binding ElementName=grid, Path=DataContext.MoveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border>
<ScrollViewer>
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ListView.Template>
<Interactivity:Interaction.Behaviors>
<behaviors:ContactSelectionBehavior />
<Core:DataTriggerBehavior Binding="{Binding DataContext.BusinessRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="BusinessSelectedState"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding DataContext.FriendsRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="FriendsSelectedState"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding DataContext.FamilyRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="FamilySelectedState"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding DataContext.OthersRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="OthersSelectedState"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding DataContext.AllRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="AllSelectedState"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding DataContext.MetRequested, ElementName=grid}" Value="True">
<Core:GoToStateAction StateName="MetSelectedState"/>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="FontSize" Value="26" />
<Setter Property="Margin" Value="0,10" />
<Setter Property="Foreground" Value="#FF333747" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
Code Behind:
private void ContactChecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
var control = sender as CheckBox;
var viewModel = this.DataContext as HomeViewModel;
var contact = control.DataContext as Contact;
viewModel.SelectedContacts.Add(contact);
if (CallButton.IsEnabled)
{
CallButton.IsEnabled = false;
}
SetMessageContactOptionsEnabledState(viewModel);
}
private void ContactUnchecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
var control = sender as CheckBox;
var viewModel = this.DataContext as HomeViewModel;
var contact = control.DataContext as Contact;
viewModel.SelectedContacts.Remove(contact);
}
Update: This issue is reproducible without navigating to another app.
回答1:
I managed to reproduce your issue in a new Windows Phone 8.1 project (the same should apply for WPF and etc):
MainPage.xaml
<Page
x:Class="App27.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App27"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded">
<Grid>
<ListView x:Name="listview">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
class Model
{
public string Name { get; set; }
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var items = new List<Model>();
for (var i = 0; i < 100; i++)
items.Add(new Model { Name = i.ToString() });
listview.ItemsSource = items;
}
}
This is an extremely basic example that creates a page with a list view containing check boxes for each item. If I check the first three items and scroll down, some of the subsequent check boxes are checked, as in this picuture:
My guess is that since virtualization is enabled (by default), the list view is reusing existing ListViewItem
s when you scroll down, some of which contain a checkbox element that I had previously checked. This checkbox's state will not be cleared when the container is reused.
Let's say I have 100 items in my listview, and due to virtualization there will only ever be, say, 20 ListViewItem
s created in memory. Then how is the app supposed to remember which of the 100 items should have a checked checkbox if there only exists 20 checkboxes in memory?
You should store the checked state of each item in your view model and databind to it. When the listview needs to reuse a list view item, it will set its data context to the new item, causing the checkbox to update its state due to the binding it has.
You should make this change to your checkbox:
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
and add the IsChecked
property to your item's model:
class Model
{
public string Name { get; set; }
public bool IsChecked { get; set; }
}
Note: This is only necessary if you have virtualization enabled. If your list won't contain too many items, then you can just disable virtualization instead by using a non-virtualizing panel such as a StackPanel
:
<ListView x:Name="listview">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
...
</ListView>
Now of course, these examples are based on my simplified example, but are still applicable to your code.
It looks like you have some specific behavior attached to each checkbox, otherwise I would have suggested to just set SelectionMode="Multiple"
on your listview to get checkboxes out of the box.
来源:https://stackoverflow.com/questions/25510762/listview-applies-a-check-to-other-listview-items-having-a-checkbox-when-page-nav