Only capture tap event from button contained in ListViewItem without triggering item click - UWP Windows 10 - xaml

In my UWP app which uses MVVMLight, I have a button contained in my ListViewItem and when clicked I want to display a flyout and provide additional actions for the specific ListViewItem but whenever I tap on the button, I get an error i.e.
System.InvalidCastException: Unable to cast object of
type 'Windows.UI.Xaml.Input.TappedRoutedEventArgs' to
type 'MyApp.ViewModels.ItemViewModel'.
at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
at Microsoft.Xaml.Interactions.Core.InvokeCommandAction.Execute(Object
sender, Object parameter) at
Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender,
ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTr
I understand to some extend why it's happening but how I can I fix this? The button contained in my ListViewItem data template is obviously being passed to the parent which is the listview and the DataTrigger that's defined in the Listview is capturing it.
All I want to do is display a flyout when this button is clicked to provide additional options.
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource SystemControlBackgroundAccentBrush5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Width="90"
Height="90"
VerticalAlignment="Center"
Margin="5,5,0,5">
<Image Width="90"
Height="90"
Source="{Binding Logo}" />
</Border>
<Grid Grid.Row="0" Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock Text="Id:" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Subject}" Margin="10,0,0,0" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<Button Width="30" Height="30"
Command="{Binding AdditionalInfoCommand}"
Grid.RowSpan="4"
Grid.Column="1"
Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stroke="{ThemeResource SystemControlBackgroundAccentBrush}"
Fill="{ThemeResource SystemControlBackgroundAccentBrush}"
StrokeThickness="2">
</Ellipse>
<Image Source="ms-appx:///Assets/Information.png" Width="30" Height="30" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
and my ListView has the following code:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
.... as defined above
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
Any ideas on how I can resolve this?
UPDATE 1:
I'll re-phrase my question slightly. I've managed to bind the button displayed on the ListViewItem to a specific click event by introducing a RelayCommand (AdditionalInfoCommand) in the item's ViewModel. So now my main problem is the actual error mentioned above which is triggered after AdditionalInfoCommand and I assume it's because it is being captured by the ItemClickCommand. Strange as you would assume that it is using the same ViewModel.
Is there a way to no trigger the itemclick when this button is tapped?
Thanks.
Thierry

I figured it out.
When I tap the button inside my ListView DataTemplate, the button is being passed the Item's ViewModel which is correct but then the tap event is bubbled up back to the parent which is captured by:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
While there may be a solution to stop the event bubbling, to circumvent the problem, I changed my ItemClickCommand.
It was defined as follows:
private RelayCommand<MyItemViewModel> _itemClickCommand;
public RelayCommand<MyItemViewModel> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<MyItemViewModel>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I simply changed it to:
private RelayCommand<object> _itemClickCommand;
public RelayCommand<object> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<object>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I then changed the ItemClickCommandAction to take in an object instead of MyItemViewModel and now, within the method, I check the type of the ViewModel being passed:
private void ItemClickCommandAction(object currentObject)
{
if (currentObject is MyItemViewModel)
{
//go to next page
}
}

Related

UWP - adding items to ListView dynamically cause crash

I have a listview defined like this:
<ListView
x:Name="phonesListView"
Grid.Row="5"
Background="Black"
IsItemClickEnabled="True"
Foreground="Gray" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid
Grid.Row="0">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox
Grid.Column="0"
VerticalAlignment="Center"
Background="Black"
Foreground="White">
<ComboBoxItem Content="Home" IsSelected="True"/>
<ComboBoxItem Content="{Binding Type}"/>
<ComboBoxItem Content="Office"/>
<ComboBoxItem Content="Fax"/>
</ComboBox>
<Button
Grid.Column="1"
Height="30"
Width="30"
Foreground="Black"
Margin="0, 5, 0, 5"
HorizontalAlignment="Center" Click="RemovePhone">
<Button.Background>
<ImageBrush Stretch="Fill" ImageSource="Assets/appbar.close.png" />
</Button.Background>
</Button>
</Grid>
<TextBox
Grid.Row="1"
Background="White"
Foreground="Black"
FontSize="20"
InputScope="TelephoneNumber"
Text="{Binding Number}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have an private ObservableCollection<Phone> numbers = new ObservableCollection<Phone>();
In constructor I call phonesListView.ItemSource = numbers;
And on some button click I want to add new item to the listView so I call a method:
private void AddPhone(object sender, RoutedEventArgs e) {
Phone phone = new Phone("", Types.HOME);
numbers.Add(phone);
}
But after button click to add a item the app crashes and App.g.i.cs is called and global::System.Diagnostics.Debugger.Break(); is highlighted
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
I'm really new to Universal Windows App, and I read that this is called when something is wrong with XAML code. Could you help me with that? Thanks.
I tried to use ItemsSource="{x:Bind Mode=TwoWay}"
The only way (but not the best, I think) where I could dynamically update a listview was making a method that handle a button event where I add a new element (calling the addmethod) and updating the ItemsSource property of the list view.
private void AnadeDoctorAppBarButton_Click(object sender, RoutedEventArgs e)
{
Doctor A = new Doctor("Mapache", "dentista", "La calle", "La vida", "Lo que sea", "Direccion", "olos");
ViewModel.AnadeDoctor = A;
ListaDePrueba.ItemsSource = ViewModel.ColeccionDoctores;
}
It Works, but I think that is not true Databinding.

WinRT hardcoded ListViewItem vs. DataTemplate, why do they not look/work the same?

I'm new to WinRT so apologies if this is a silly question. I've created the following ListView:
<ListView x:Name="MyListView1"
Grid.Row="1"
Margin="120,300,0,0"
Width="500"
HorizontalAlignment="Left">
<ListViewItem Background="DodgerBlue">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<TextBlock Text="hardcoded value 1" Grid.Column="0"></TextBlock>
<TextBlock Text="hardcoded value 2" Grid.Column="1"></TextBlock>
</Grid>
</ListViewItem>
</ListView>
This looks the way I want it to look, and if you click an item it will select the whole row. However, if I move this into a DataTemplate it doesn't look the same, and you can no longer click the whole row. (If I add an ItemContainerStyle with the target type ListViewItem and set the background to yellow, it will fill it up so it's the same size as the hardcoded ListItem, but you can only click the yellow outline to select it.) This is the code:
<ListView x:Name="MyListView2"
ItemTemplate="{StaticResource MyTemplate}"
ItemsSource="{Binding MyData}"
Grid.Row="1"
Margin="120,0,0,0"
Width="500"
HorizontalAlignment="Left">
</ListView>
And in StandardStyles.xaml:
<DataTemplate x:Key="MyTemplate">
<ListViewItem Background="DodgerBlue">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MyDataOne}" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding MyDataTwo}" Grid.Column="1"></TextBlock>
</Grid>
</ListViewItem>
</DataTemplate>
I don't understand why they don't look/work the same - shouldn't it get populated with the exact same code when you bind it? What do I need to do to make it work?
The problem there is the way you created the datatemplate. Remember that ListViewItem is a wrapper added to any object that needs to be displayed in the list, so when you create a datatemplate that contains a listviewitem you are basically wrapping the object twice. All you need to do is remove the listviewitem element from the datatemplate
<DataTemplate x:Key="MyTemplate">
<Grid Background="DodgerBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MyDataOne}" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding MyDataTwo}" Grid.Column="1"></TextBlock>
</Grid></DataTemplate>

Windows Phone Context Menu Item Text Not Appearing

I have a Windows Phone 8 app using XAML/C#. My app has an ItemsControl that relies on a data template. My DataTemplate looks like the following:
<DataTemplate x:Key="myTemplate">
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding DisplayName}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextLargeStyle}" TextTrimming="WordEllipsis" >
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem x:Name="customerMenuItem" Foreground="White" Header="View Customer Profile" Click="customerMenuItem_Click" Tag="{Binding Path=CustomerName}" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
<TextBlock Text="{Binding Summary}" TextWrapping="NoWrap" Grid.Row="1" Style="{StaticResource PhoneTextSmallStyle}" />
</Grid>
<StackPanel Orientation="Horizontal" Grid.Column="1"><!-- Stuff here --></StackPanel>
</Grid>
</DataTemplate>
This DataTemplate is referenced in the main part of my XAML as shown here:
<Grid x:Name="ContentPanel" Grid.Row="1" Grid.ColumnSpan="2" Margin="12,0,12,0">
<ScrollViewer>
<ItemsControl x:Name="myItemsControl" ItemTemplate="{StaticResource myTemplate}" ItemsSource="{Binding Customers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
Please note, the "toolkit" namespace comes from clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit. When I hold my finger (or mouse) on the TextBlock, a context menu appears. However, I never see the words "View Customer Profile". I just see a block box that represents the context menu itself. I know that the item is there though. I know because the customerMenuItem_Click event successfully fires. I have a MessageBox in there that shows the value of the Tag. That value is always correct. For some reason though the menu item text is not appearing. What am I doing wrong?
You put Foreground = "White". Context menu is on white background. That is why you don't see your menu item.

XAML element inside of ItemsControl DataTemplate sometimes has null parent (WinRT)

I'm making a win8 app. I have an ItemsControl with a DataTemplate. Inside this DataTemplate, there is a Grid. Inside this Grid there is a WebView and a ProgressRing. I have an event for LoadCompleted on the WebView, at which time I want to disable the ProgressRing. My thought was to use the following:
ProgressRing pr = ((sender as FrameworkElement).Parent as Grid).Children.First(t => t.GetType().Equals(typeof(ProgressRing))) as ProgressRing;
pr.IsActive = false;
And this works sometimes, but sometimes it claims that ((sender as FrameworkElement).Parent) is null. I verified from debugging that in these cases, the sender is still the correct WebView. The WebView is being shown on the screen and is formatted correctly - so how could its parent be null?
Here's the XAML:
<ItemsControl Margin="40,0,40,0" x:Name="resultsItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="50,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" MaxWidth="150"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="100" MaxWidth="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="viewInBrowser" IsEnabled="False" Style="{StaticResource PreviewLinkAppBarButtonStyle}" Margin="0,0,40,0" Click="viewInBrowser_Click_1"></Button>
<TextBlock HorizontalAlignment="Left" TextAlignment="Left" Grid.Column="1" FontSize="30" Margin="0,20,0,20" FontWeight="Bold" FontFamily="Georgia" VerticalAlignment="Center" Text="{Binding Name}"></TextBlock>
<ProgressRing x:Name="loadingProgressRing" Margin="20,0" Grid.Column="2" Foreground="White" IsActive="True" HorizontalAlignment="Left" VerticalAlignment="Center"></ProgressRing>
<WebView ScriptNotify="WebView_ScriptNotify_1" Grid.Column="0" Grid.ColumnSpan="14" Grid.Row="1" Source="{Binding CurrentSearchUrl}" Width="0" LoadCompleted="WebView_LoadCompleted_1"></WebView>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I wouldn't use a WebView in an ItemsControl at all, but you could try putting your entire template in a UserControlm then you handle the event inside of the UserControl and don't need to care about sender, since you can just name the Grid and access a named Grid.

WP7 - ContextMenu listbox item click event

Say I have the following data template:
<DataTemplate x:Key="ItemTemplate">
<StackPanel>
<Grid Height="95" Width="446" HorizontalAlignment="Left" ShowGridLines="false" RenderTransformOrigin="0.3,0.526">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding categoryimage}" Height="100" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="{Binding categoryname}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="8,0,-46,0" FontSize="26.667"/>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Name="MyContextMenu">
<toolkit:MenuItem Header="Delete console" Click="contextMenuAction_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</Grid>
</StackPanel>
</DataTemplate>
I want to handle when the context menu item is clicked. How would I access the list item as a class object (if this makes sense?)
I've tried the following code, however it's giving me a NullReferenceException exception:
Private Sub contextMenuAction_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Dim c As classes.consoles = listBoxview.SelectedItem
MessageBox.Show(c.categoryname)
End Sub
You might be able to get the parent item using the Visual Tree Helper, as detailed in the following thread :-
Context menu selected item wp7