4

我刚刚遇到了一个严重的上下文菜单问题,几个小时都无法解决。

为了重现该问题,我在 Visual Studio 2012 中使用 Windows Phone 8 的应用模板创建了一个全新的全景应用。我通过 nugget 安装了 Windows Phone 工具包,并在绑定到 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>

我将标头设置为 LineOne 属性以便于调试。我附上了以下事件:

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" });
}

我运行应用程序并使用上下文菜单删除第一项。第一项消失了,一个名为 Test 的新项按预期出现在列表底部。如果我持有这个新项目,则菜单项将绑定到“运行时之一”(已删除的项目)。

这是我可以重现错误的最简单的代码,但在我的真实应用程序中,我遇到了几乎相同的问题,在不同的方法甚至不同的页面中添加和删除更有意义的代码。我有一个命令绑定,但由于数据绑定错误,因此命令在错误的视图模型中运行,参数错误。

知道为什么会这样吗?

4

2 回答 2

9

对于像我这样不想重新编译工具包的人来说,这是一个基于 pantaloons 答案的简单解决方法。简单添加 Opened 事件处理程序:

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

事件处理程序代码:

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;

}
于 2013-04-01T16:36:00.587 回答
5

LongListSelector 是一个虚拟化控件,这意味着它会实例化您指定固定次数(我认为是 20 次)的 DataTemplate,然后在您向下滚动列表时重用这些 Item 容器,只需移动它们并重新绑定它们的 DataContext。这样,您就可以拥有非常大的列表,而无需将所有列表都放在可视树中。

http://phone.codeplex.com/SourceControl/changeset/view/80797#1335947的 ContextMenu 代码在函数中有以下几行OpenPopup()

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

您会看到这里有一个错误,因为当虚拟化容器重新绑定 DataContext 时,它不会在 _rootVisual 上得到更新,因为它假定现有的 DataContext 是正确的。解决方法是将检查更改为:

if (ReadLocalValue(DataContextProperty) == DependencyProperty.UnsetValue ||
    (DataContext != dataContextSource.DataContext))
{
    DependencyObject dataContextSource = Owner ?? _rootVisual;
    SetBinding(DataContextProperty, new Binding("DataContext") { Source = dataContextSource });
}
于 2013-03-03T02:25:33.460 回答