问题
I have UWP app with a XAML listview in a user control as seen below.
<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>
The listview's item template is as follows. You can see the item template has a check box and it is bound to my view model using two way binding.
<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>
The listview's datacontext is set to my view model.
ucDeviceInfo.DataContext = iobj_DevicesViewModel;
So what I want is when the user clicks on anitem in the listview, the checkbox for that item updated with a check mark or have its check mark removed. I figured the best way to do that would be to set the 'Selected' property in the ViewModel to true or false when the user clicks on the row then notify the view that the property has changed. (The list of items is an observable collection.).
I try to accomplish this in the following manner:
In the UserControl - when the user clicks on a row I call a method back in the base page using this code:
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);
}
In the base page I call a method in my ViewModel using this code:
public void SendNotifyPropertyChanged(int pi_DeviceID)
{
iobj_DevicesViewModel.DeviceSelectedInListView(pi_DeviceID);
}
Then in the view model I update the selected property for the device and call the notify property changed function using this code:
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");
}
The challenge is that when I do this and call the notify property changed method, the checkbox of the item in the listview I just clicked on is not updated. Any idea how I can get the checkbox to update correctly when a user clicks on a row in the listview?
Link to sample project that illustrates this issue is Example Code
回答1:
As for your code - you have almost done it right. Your collection reference Devices
doesn't change so binding won't update the whole collection, also you don't add/remove element, so ObservableCollection could have updated. In this case you need to call property changed on item's class. To do it just implement INotifyPropertyChanged at Device class:
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
After this you also don't need to call NotifyPropertyChanged("Devices");
every time something change inside the item, the binding will do the job. As you have ObservableCollection I don't think you will have to call it at all.
The modified sample you have shared.
I'm not quite sure why you don't use the default ListView's checkboxes. If you set the selection mode to Multiple then you will see a checkbox at every item. If you want to handle some more actions you can subscribe to SelectionChanged event. A sample ListView with standard checkboxes:
<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>
In your code it's hard for me to say what is wrong as I cannot see your item class, I don't know if you have property Selected, does it implement INotifyPropertyChanged and more minor things.
来源:https://stackoverflow.com/questions/33553691/how-to-have-the-click-event-of-a-listview-item-turn-on-and-off-the-checkbox-in-t