WP8 LongListSelector and not correctly updating ContextMenu’s

These last days I have been working on a simple WP8 app that uses TvDb.com to keep track of the next/upcoming series episode to watch.

I made extensively use of the LongListSelector combined with a ContextMenu from the WP8 Toolkit found at CodePlex. I want to be able to short tap (navigate) and long tap (context menu). The DataContext supplied is a Dictionary hence the Key, Value and  KeyValuePAir stuff present in the code.

For the xaml I used code like this to make sure my C# code would be able to know which episode to mark as watched when a user long taps a list item (note: I removed all non essential attributes)

   1: <phone:PivotItem Header="upcoming">

   2: <phone:LongListSelector ItemsSource="{Binding UpcomingEpisodes}" >

   3:     <phone:LongListSelector.ItemTemplate>

   4:     <DataTemplate>

   5:         <StackPanel Tag="{Binding Value.Id}" Tap="Upcoming_Tap">

   6:         <toolkit:ContextMenuService.ContextMenu>

   7:             <toolkit:ContextMenu DataContext="{Binding Value.Id}" >

   8:             <toolkit:MenuItem Header="mark as watched" Click="UpcomingWatched_Click"/>

   9:             </toolkit:ContextMenu>

  10:         </toolkit:ContextMenuService.ContextMenu>

  11:         <TextBlock Text="{Binding Key.SeriesName}" />

  12:         <StackPanel Orientation="Horizontal">

  13:             <TextBlock Text="{Binding Value.EpisodeAndSeason}" />

  14:             <TextBlock Text="{Binding Value.EpisodeName}" />

  15:         </StackPanel>

  16:         </StackPanel>

  17:     </DataTemplate>

  18:     </phone:LongListSelector.ItemTemplate>

  19: </phone:LongListSelector>

note: I removed all non essential attributes.

The C# code is quite simple:

a) For the short tap I use:

   1: private void Upcoming_Tap(object sender, System.Windows.Input.GestureEventArgs e)

   2: {

   3:     if (sender is FrameworkElement && (sender as FrameworkElement).Tag != null)

   4:     {

   5:     Int32 id = Int32.Parse((sender as FrameworkElement).Tag.ToString());

   6:

   7:     // etc

   8:     }

   9: }

 

b) For the long tap I use:

   1: private void UpcomingWatched_Click(object sender, RoutedEventArgs e)

   2: {

   3:     if (sender is FrameworkElement && (sender as FrameworkElement).DataContext != null)

   4:     {

   5:         KeyValuePair<Serie, Episode> dc = (KeyValuePair<Serie, Episode>)((sender as FrameworkElement).DataContext);

   6:

   7:         //etc

   8:     }

   9: }

note: My DataContext is a KeyValuePair so I need to do some typecasting here.

The problem is that after marking a couple of episodes as read, the DataContext of the ContextMenu is not update correctly anymore and I keep marking things watched  I do not see in my LongListSelector.

After using Google for two days and found a ‘çomplex’ workaround I did not  get working at the one following links ‘we-secretly-have-changed’ or ‘dlaa’ I stumbled across an article at codeproject that led to the solution (I did not get the codeproject code to work in my project but searching for it at msdn did).

I modified my code a tiny bit at three places.

a) I added a

x:name=”UpcomingItem”

attribute to the topmost StackPanel element that defines an LongListSelector Item.

b) I changed the binding of the ContextMenu from”

{binding Value.Id}

into

{Binding ElementName=UpcomingItem},

effectively binding the ContextMenu to it’s parent StackPanel named UpcomingItem (so NOT to it’s DataContext anymore).

   1: <phone:PivotItem Header="upcoming">

   2: <phone:LongListSelector ItemsSource="{Binding UpcomingEpisodes}" >

   3:     <phone:LongListSelector.ItemTemplate>

   4:     <DataTemplate>

   5:         <StackPanel Tag="{Binding Value.Id}" Tap="Upcoming_Tap" x:Name="UpcomingItem">

   6:         <toolkit:ContextMenuService.ContextMenu>

   7:             <toolkit:ContextMenu  DataContext="{Binding ElementName=UpcomingItem}" >

   8:             <toolkit:MenuItem Header="mark as watched" Click="UpcomingWatched_Click"/>

   9:             </toolkit:ContextMenu>

  10:         </toolkit:ContextMenuService.ContextMenu>

  11:         <TextBlock Text="{Binding Key.SeriesName}" />

  12:         <StackPanel Orientation="Horizontal">

  13:             <TextBlock Text="{Binding Value.EpisodeAndSeason}" />

  14:             <TextBlock Text="{Binding Value.EpisodeName}" />

  15:         </StackPanel>

  16:         </StackPanel>

  17:     </DataTemplate>

  18:     </phone:LongListSelector.ItemTemplate>

  19: </phone:LongListSelector>

note: the phone:PivotItem has nothing to do with the problem described in this post.

c) Finally in the C# code I had to modify the retrieval of the DataContext dc variable as the (sender as FrameworkElement).DataContext is now  a StackPanel object instead of the KeyValuePair of the  original code.

So I changed the line 5 of the C# code piece above to read:

   1: StackPanel sp = (StackPanel)(sender as FrameworkElement).DataContext;

   2: KeyValuePair<Serie, Episode> dc = (KeyValuePair<Serie, Episode>)(sp.DataContext);

Finally the ContextMenu nicely works on the LongListSelector Item when long tapped, even when the underlying DataSource is updated.

Advertisement
This entry was posted in C#, Programming, Windows. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s