如何让 ListView 项目的单击事件打开和关闭列表视图的项目模板中的复选框

How to have the click event of a ListView item turn on and off the checkbox in the item template for the listview

我的 UWP 应用在用户控件中带有 XAML 列表视图,如下所示。

<ListView x:Name="lvDevices"
          Margin="12,8,0,0"
          Grid.Row="4"
          HorizontalContentAlignment="Stretch"
          ItemTemplate="{StaticResource DeviceTemplate}"
          Width="Auto"
          ItemsSource="{Binding Devices}"
          IsItemClickEnabled="True"
          ItemClick="lvDevices_ItemClick"
          d:DataContext="{d:DesignData /SampleData/DevicesVMSampleData.xaml}" >
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

列表视图的项目模板如下。您可以看到项目模板有一个复选框,它使用两种方式绑定到我的视图模型。

<DataTemplate x:Key="DeviceTemplate">
        <Grid Margin="0" HorizontalAlignment="Stretch" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <CheckBox x:Name="chkSelected" IsChecked="{Binding Selected, Mode=TwoWay}" MinWidth="32" Grid.Column="0" Checked="chkSelected_Changed" Unchecked="chkSelected_Changed" />
            <StackPanel Grid.Column="1" VerticalAlignment="Center" Margin="10,0,0,0">
                <TextBlock Text="{Binding DeviceName}" Style="{StaticResource TitleTextBlockStyle}" Tag="{Binding ID}" TextWrapping="Wrap" MaxWidth="500"/>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="User:" Margin="0,0,10,0" Style="{StaticResource CaptionTextBlockStyle}" FontWeight="Bold" />
                    <TextBlock Text="{Binding User}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
                </StackPanel>
            </StackPanel>
            <Button x:Name="btnInfo" Grid.Column="2" Background="Transparent" HorizontalAlignment="Right">
                <Image x:Name="imgInfo" HorizontalAlignment="Right" VerticalAlignment="Center" Source="/Assets/InfoIcon-100.png" Height="50" Width="50" />
            </Button>
        </Grid>
    </DataTemplate>

列表视图的数据上下文设置为我的视图模型。

ucDeviceInfo.DataContext = iobj_DevicesViewModel;

所以我想要的是当用户单击列表视图中的某个项目时,该项目的复选框更新为带有复选标记或删除其复选标记。我认为最好的方法是在用户单击该行时将 ViewModel 中的 'Selected' 属性 设置为 true 或 false,然后通知视图 属性 已更改. (项目列表是一个可观察的集合。)。

我尝试通过以下方式完成此操作:

在 UserControl 中 - 当用户单击一行时,我使用以下代码在基页中调用一个方法:

private void lvDevices_ItemClick(object sender, ItemClickEventArgs e)
    {
        StartPage lobj_ParentForm;         

        lobj_ParentForm = ((StartPage)((Frame)Window.Current.Content).Content);
        lobj_ParentForm.SendNotifyPropertyChanged( ((Device)e.ClickedItem).ID);
    }

在基本页面中,我使用以下代码在我的 ViewModel 中调用了一个方法:

public void SendNotifyPropertyChanged(int pi_DeviceID)
    {
        iobj_DevicesViewModel.DeviceSelectedInListView(pi_DeviceID);
    }

然后在视图模型中,我为设备更新了选定的 属性 并使用以下代码调用通知 属性 已更改的函数:

public void DeviceSelectedInListView(int pi_DeviceID)
    {
        Device lobj_SelectedDevice;

        lobj_SelectedDevice = (from lobj_Device in Devices
                       where lobj_Device.ID == pi_DeviceID
                       select lobj_Device).ToList<Device>()[0];


        lobj_SelectedDevice.Selected = !lobj_SelectedDevice.Selected;
        NotifyPropertyChanged("Devices");
        SelectionsChanged();
    }

    public void SelectionsChanged()
    {
        NotifyPropertyChanged("SelectedDeviceCount");
    }

挑战在于,当我这样做并调用 notify 属性 changed 方法时,我刚刚单击的列表视图中项目的复选框没有更新。知道当用户单击列表视图中的一行时如何让复选框正确更新吗?

Link 说明此问题的示例项目是 Example Code

至于你的代码 - 你几乎已经做对了。您的集合引用 Devices 不会更改,因此绑定不会更新整个集合,您也不会 add/remove 元素,因此 ObservableCollection 可能已经更新。在这种情况下,您需要调用 属性 changed on item's class。为此,只需在设备 class:

上实施 INotifyPropertyChanged
public class Device : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    private bool selected;
    public bool Selected
    {
        get { return selected; }
        set { selected = value; RaiseProperty(nameof(Selected)); }
    }
    // rest of the class

在此之后,您也不需要每次在项目内部发生变化时调用 NotifyPropertyChanged("Devices");,绑定将完成这项工作。因为你有 ObservableCollection 我认为你根本不需要调用它。

The modified sample you have shared.


我不太清楚您为什么不使用默认的 ListView 复选框。如果您将选择模式设置为 Mu​​ltiple 那么您将在每个项目上看到一个复选框。如果您想处理更多操作,您可以订阅 SelectionChanged 事件。带有标准复选框的示例 ListView:

<ListView Name="myList" SelectionChanged="myList_SelectionChanged" SelectionMode="Multiple">
    <ListView.Items>
        <TextBlock Text="First item"/>
        <TextBlock Text="Second item"/>
        <TextBlock Text="Third item"/>
        <TextBlock Text="Fifth item"/>
    </ListView.Items>
</ListView>

在你的代码中,我很难说出什么问题,因为我看不到你的项目 class,我不知道你是否 属性 Selected,它是否实现了 INotifyPropertyChanged 和更多小东西。