Контекстные меню WPF в Caliburn Micro

Я пытаюсь получить контекстное меню в ListBox ItemTemplate, чтобы вызвать метод в родительской модели представления, передав элемент, на который был нажат параметр. У меня это работает для других кнопок в шаблоне элемента, но для контекстного меню он, кажется, терпит неудачу.

У меня есть следующий xaml (сокращенно для ясности):

<ListBox>
    <ListBox.GroupStyle>
        <GroupStyle>
            ...
        </GroupStyle>
    </ListBox.GroupStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ContextMenu>
                    <ContextMenu Name="cm">
                        <MenuItem Header="Open" 
                                  cal:Message.Attach="Open($dataContext)">

                        </MenuItem>
                </Grid.ContextMenu>

                <TextBlock VerticalAlignment="Center" >
                    .. text..
                </TextBlock>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

У меня такое чувство, что это связано с тем, что визуальное дерево отличается от других, поэтому Caliburn неспособно надежно разрешить метод. Я уверен, что это обычная проблема, и я пробовал несколько вещей, которые я нашел в Интернете, но ничего не работает.

Любые идеи?

Ответ 1

Используя информацию, которую я нашел на сайте Caliburn Micro, я изменил ваш XAML таким образом:

  <Grid Background="White" HorizontalAlignment="Stretch" Height="200" Name="GridLayout">       
    <ListBox x:Name="ListBoxItems">            
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid Tag="{Binding DataContext, ElementName=GridLayout}">
                    <Grid.ContextMenu>
                        <ContextMenu Name="cm" cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Open" 
                              cal:Message.Attach="Open($dataContext)">
                            </MenuItem>
                        </ContextMenu>
                    </Grid.ContextMenu>

                    <TextBlock VerticalAlignment="Center" >
                .. text..
                    </TextBlock>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

И моя модель взгляда:

    public List<string> ListBoxItems { get; set; }
    public ShellViewModel()
    {
        ListBoxItems = new List<string> {"One", "Two", "Three"};          
    }

    public void Open(object source)
    {
        MessageBox.Show((string) source);
    }

Open был успешно вызван с соответствующими строками из списка.

Ответ 2

В дополнение к ответу Jason, если вы собираетесь использовать тот же контекст данных, что и элемент управления, то вы можете просто связать DataContext вместо Tag

<Grid>
    <Grid.ContextMenu>
        <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
            <MenuItem Header="Open" 
                      cal:Message.Attach="Open($source)">
            </MenuItem>
        </ContextMenu>
    </Grid.ContextMenu>
</Grid>

$source Фактический FrameworkElement, который вызвал ActionMessage быть

Вы можете увидеть больше информации о соглашении $source здесь: https://caliburnmicro.com/documentation/cheat-sheet