Listview将检查应用于返回页面导航时具有复选框的其他列表视图项

时间:2014-08-26 16:29:28

标签: wpf winrt-xaml windows-8.1 windows-phone-8.1

我有ListViewCheckBox个控件,当包含ListView的网页返回到导航时,ListView内的其他项目会神秘地应用复选标记它

具体来说,我对包含大约300个项目的ListView中的某些项目应用了复选标记。然后我点按Button调用另一个应用启动,然后当我使用ListView返回原始应用并向下滚动ListView时,我会观察到一种神秘检查的模式复选框遵循我在ListView内创建的原始核对位置的模式。

注意: 我在ListView内使用虚拟化。 仅当导航返回到包含ListView的页面时才会出现神秘复选框。

不应该神秘地检查复选框。 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>

代码背后:

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);
}

更新 此问题可重现,无需导航到其他应用程序。

1 个答案:

答案 0 :(得分:0)

我设法在新的Windows Phone 8.1项目中重现您的问题(同样适用于WPF等):

<强> 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;
    }
}

这是一个非常基本的示例,它创建一个包含列表视图的页面,其中包含每个项目的复选框。如果我检查前三项并向下滚动,则会检查一些后续复选框,如下图所示:

screenshot

我的猜测是,由于启用了虚拟化(默认情况下),列表视图在向下滚动时会重用现有的ListViewItem,其中一些包含我之前检查过的复选框元素。重新使用容器时,不会清除此复选框的状态。

假设我的listview中有100个项目,并且由于虚拟化,在内存中只会创建20个ListViewItem。那么,如果内存中只有20个复选框,应用程序应该如何记住100个项目中的哪一个应该有一个复选框?

您应该在视图模型中存储每个项目的已检查状态,并将数据绑定到该模型。当listview需要重用列表视图项时,它会将其数据上下文设置为新项,从而导致复选框由于其具有绑定而更新其状态。

您应该对您的复选框进行此更改:

<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />

并将IsChecked属性添加到商品的模型中:

class Model
{
    public string Name { get; set; }
    public bool IsChecked { get; set; }
}

注意:仅在启用虚拟化时才需要这样做。如果您的列表不包含太多项目,那么您可以通过使用非虚拟化面板(例如StackPanel

来禁用虚拟化
<ListView x:Name="listview">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    ...
</ListView>

当然,这些示例基于我的简化示例,但仍适用于您的代码。

您似乎在每个复选框上都附加了一些特定的行为,否则我建议您只在列表视图中设置SelectionMode="Multiple"以获取开箱即用的复选框。