Правильный способ найти TabItem из команды ContextMenu - PullRequest
1 голос
/ 18 января 2009

У меня есть TabControl, на котором я устанавливаю ContextMenu.

Все элементы ContextMenu имеют установленные команды.

<ContextMenu x:Key="tabMenu">
  <MenuItem Command="{x:Static tabs:TabCommands.Close}" />
  <MenuItem Command="{x:Static tabs:TabCommands.CloseAllButThis}" />
  <MenuItem Command="{x:Static tabs:TabCommands.CloseAll}" />
</ContextMenu>

Все команды маршрутизируются, а привязки команд определяются на несколько уровней выше TabControl.

Таким образом, вопрос заключается в следующем: в обработчиках событий CommandBinding CanExecute / Execute, как правильно узнать, для какого TabItem было вызвано меню? Под правильным я подразумеваю тот, который не сломался бы, если бы я изменил что-то вроде шаблона TabItem.

Или, может быть, весь подход неверен, и я не должен использовать для этого маршрутизируемые команды? Первоначально я использовал маршрутизацию для команды Add New Tab, для которой требуются горячие клавиши.

Заранее спасибо.

UPDATE

Решение Игоря чище от архитектурного POV (за исключением того, что я бы удалил _ в ViewModel), но я хочу иметь многоразовую команду Close, которая не зависит от того, к чему привязан TabControl (так как Close / Close All для вкладок существует во всех виды приложений и не семантически связаны с конкретной моделью).

Кроме того, я не могу использовать собственный DataTemplate, так как у меня уже есть собственный шаблон, и создание его подкласса сделало бы решение немного более сложным.

Ответы [ 2 ]

1 голос
/ 06 февраля 2009

Конечно, есть лучший ответ. Вам нужно работать с Model / ViewModel, а не с представлением. Вот упрощенный пример из моего кода:

        <TabControl Margin="3" Grid.Column="1" Name="tbPages"
                    ItemsSource="{Binding DsmProject.Pages}" 
                    ItemTemplate="{DynamicResource TabItemTemplate}"
                    IsSynchronizedWithCurrentItem="True">
        </TabControl>
<DataTemplate x:Key="TabItemTemplate">
    <StackPanel Orientation="Horizontal" ContextMenu="{DynamicResource cmPages}">
        <ContentPresenter Content="{Binding Path=Name}"/>
    </StackPanel>
</DataTemplate>
<ContextMenu x:Key="cmPages">
    <MenuItem Header="Close" Command="cmd:DSM2100Commands.ClosePage" CommandParameter="{Binding}" />
</ContextMenu>

Вот код, который обрабатывает эту команду.

Регион "Закрыть страницу"

    Private Sub ClosePageCmd(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
        ViewModel_.History.TakeCommmand(New cmdRemovePage(ViewModel_, e.Parameter))
    End Sub

    Private Sub CanClosePageCmd(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
        e.CanExecute = ViewModel_.DsmProject IsNot Nothing AndAlso ViewModel_.DsmProject.Pages.Count > 1
    End Sub

Конечный регион

Как видите, моему коду не нужно знать, какой TabItem был нажат, нужно просто знать, какой объект данных был связан с этим TabItem. В любом случае, если вам нужно знать TabItem, по которому щелкнули, вы можете найти его по привязанному к нему объекту Data, используя объект ContainerGenerator и вашу табличку с данными.

С наилучшими пожеланиями из России!

1 голос
/ 19 января 2009

Кажется, я нашел ответ сам, однако он крайне не элегантен:

<Style TargetType="MenuItem">
  <Setter Property="CommandTarget">
    <Setter.Value>
      <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}"
               Path="(ContextMenu.PlacementTarget)" />
    </Setter.Value>
  </Setter>
</Style>

<Style TargetType="TabItem">
  <Setter Property="ContextMenu" Value="{StaticResource tabMenu}" />
</Style>

Поэтому я добавляю ContextMenu в TabItem вместо TabControl и привязываю CommandTarget к TabItem.

Интересно, есть ли лучший ответ.

...