Windows Phone Toolkit Context Menu Items have the wrong object attached to them when the item is deleted and then added

I ran into a serious problem in the context menu, which I cannot solve for hours.

To reproduce the problem, I created a completely new Panorama application with application templates for Windows Phone 8 in Visual Studio 2012. I installed the Windows Phone toolkit via the nugget and added a context menu to the data template of the first long list that is associated with the items

<StackPanel Margin="0,-6,0,12"> <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/> <toolkit:ContextMenuService.ContextMenu> <toolkit:ContextMenu> <toolkit:MenuItem Header="{Binding LineOne}" Click="MenuItem_Click_1" Tag="{Binding}"> </toolkit:MenuItem> </toolkit:ContextMenu> </toolkit:ContextMenuService.ContextMenu> </StackPanel> 

I set the title to the LineOne property for easier debugging. I attached the following event:

 private void MenuItem_Click_1(object sender, RoutedEventArgs e) { var itemViewModel = (ItemViewModel)((MenuItem)sender).Tag; App.ViewModel.Items.Remove(itemViewModel); App.ViewModel.Items.Add(new ItemViewModel { LineOne = "Test", LineTwo = "Test", LineThree = "Test" }); } 

I launched the application and used the context menu to delete the first item. The first item disappears, and a new item named Test appears at the bottom of the list, as expected. If I hold this new item, the menu item is bound to "runtime one" (the item that was deleted).

It was the simplest code that I could reproduce, but in my real application I have almost the same problem with more meaningful code to add and remove in different methods and even on different pages. I had a command, but since the data binding is incorrect, the command runs in the wrong view model with the wrong parameter.

Any idea why this is happening?

+4
source share
2 answers

LongListSelector is a virtualized control, which means that it creates an instance of a DataTemplate that you specify a fixed number of times (20, I think), and then reuses these Item containers when scrolling down the list, simply moving them and interlacing their DataContext's. That way, you can have extremely large lists without requiring the entire batch to be in a visual tree.

The ContextMenu code at http://phone.codeplex.com/SourceControl/changeset/view/80797#1335947 has the following lines in the OpenPopup() function;

 if (ReadLocalValue(DataContextProperty) == DependencyProperty.UnsetValue) { DependencyObject dataContextSource = Owner ?? _rootVisual; SetBinding(DataContextProperty, new Binding("DataContext") { Source = dataContextSource }); } 

You will see that there is an error because when the virtualized container double-checks the DataContext, it will not be updated to _rootVisual, as it assumed that the existing DataContext is correct. The fix will be to change this check:

 if (ReadLocalValue(DataContextProperty) == DependencyProperty.UnsetValue || (DataContext != dataContextSource.DataContext)) { DependencyObject dataContextSource = Owner ?? _rootVisual; SetBinding(DataContextProperty, new Binding("DataContext") { Source = dataContextSource }); } 
+5
source

For people like me who do not want to recompile the toolkit, this is not an easy workaround based on answers to drawers. Simpy add Open event handler:

 <toolkit:ContextMenu Opened="ContextMenu_Opened"> ... </toolkit:ContextMenu> 

Event Handler Code:

 private void ContextMenu_Opened(object sender, RoutedEventArgs e) { var menu = (ContextMenu)sender; var owner = (FrameworkElement)menu.Owner; if (owner.DataContext != menu.DataContext) menu.DataContext = owner.DataContext; } 
+9
source

All Articles