4

コンテキストメニューで何時間も解決できない深刻な問題に遭遇しました。

問題を再現するために、Visual Studio 2012 で Windows Phone 8 用のアプリ テンプレートを使用してまったく新しい Panorama アプリを作成しました。ナゲット経由で Wi​​ndows 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 という名前の新しい項目がリストの一番下に表示されます。この新しい項目を保持すると、メニュー項目は「ランタイム 1」(削除された項目) にバインドされます。

これはエラーを再現できる最も単純なコードでしたが、実際のアプリでは、さまざまなメソッドやさまざまなページで追加および削除するためのより意味のあるコードで、ほぼ同じ問題が発生しています。コマンドをバインドしましたが、データ バインディングが間違っているため、コマンドは間違ったビュー モデルで間違ったパラメーターで実行されます。

なぜこれが起こっているのですか?

4

2 に答える 2

9

私のようにツールキットを再コンパイルしたくない場合は、パンタロンの回答に基づく簡単な回避策があります。Simpy add 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 を再バインドすると、既存の DataContext が正しいものであると想定されるため、_rootVisual で更新されないため、ここにバグがあることがわかります。修正は、そのチェックを次のように変更することです。

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 に答える