TemplateStudio
TemplateStudio copied to clipboard
ListViewItemSelectionBehavior throws System.InvalidCastException when you click next to visible ListViewItem
Describe the bug
ListViewItemSelectionBehavior throws System.InvalidCastException when you click next to visible ListViewItem
To Reproduce Steps to reproduce the behavior:
- Create an application which contains ContentGridPage using Windows Template Studio.
- Launch the application.
- Navigate to ContentGrid.
- Click anywhere on empy area of the ContentGridPage.
- See error.
Expected behavior
ListViewItemSelectionBehavior does not throw any exceptions.
Screenshots
Additional context
Tested on WPF, Debug, AnyCPU.
confirmed with the following.
<genTemplate:Metadata>
<genTemplate:Item Name="generator" Value="Windows Template Studio"/>
<genTemplate:Item Name="wizardVersion" Version="v4.1.21179.1" />
<genTemplate:Item Name="templatesVersion" Version="v4.1.21179.1" />
<genTemplate:Item Name="projectType" Value="SplitView" />
<genTemplate:Item Name="framework" Value="MVVMToolkit" />
<genTemplate:Item Name="platform" Value="Wpf" />
</genTemplate:Metadata>
Does not happen with CodeBehind.
There are a few ways to address this. Just catching and suppressing the error is probably the simplest.
A slightly better way to handle this is to check the type of the DataContext of the selectedItem matches the type required by the command. Doing this requires a bit of reflection, so:
private void SelectItem(RoutedEventArgs args)
{
if (Command != null
&& args.OriginalSource is FrameworkElement selectedItem
&& Command.CanExecute(selectedItem.DataContext))
{
Command.Execute(selectedItem.DataContext);
}
}
becomes
private void SelectItem(RoutedEventArgs args)
{
if (Command != null
&& args.OriginalSource is FrameworkElement selectedItem
+ && selectedItem.DataContext.GetType() == Command.GetType().GetGenericArguments()[0]
&& Command.CanExecute(selectedItem.DataContext))
{
Command.Execute(selectedItem.DataContext);
}
}
As a workaround, this doesn't account for non-generic types and so if the app needed to handle those too, they'd need to be considered as well.
The reason this is necessary is because the original code didn't appreciate that SelectItem will be called on whichever control in the UI stack is under the mouse when the left button is clicked. The naming of the variable suggests that it assumes the click will only ever be on an item but this issue highlights that this might not always be the case.
A different approach to the above would be to check that the source of the item passed to SelectItem
isn't the container of the items. So:
private void SelectItem(RoutedEventArgs args)
{
if (Command != null
+ && !(args.OriginalSource is ScrollViewer)
&& args.OriginalSource is FrameworkElement selectedItem
&& Command.CanExecute(selectedItem.DataContext))
{
Command.Execute(selectedItem.DataContext);
}
}
This second option is probably a better solution for implementing in the actual templates, but I'm open to discussion of options.