0

I created a context menu for a tabcontrol which changes the name of the tab. However, if the mouse over on an unselected tab and mouse right clicked on context menu pops up. If I click the the menu item it changes the selected tab's name. For instance, I right clicked on Favorite 4 tab and tried to change its name but it changed first tab's name (selected tab) as shown below.

enter image description here

I would like to select the tab with right mouse click as well as with left mouse click so it will not cause confusion or not intentional tab name change.

XAML

 <TabControl x:Name="FavoritesTabs" HorizontalAlignment="Stretch" Height="23" 
 Initialized="FavoritesTabs_Initialized" Margin="8,0,7,0" 
 MouseRightButtonDown="FavoritesTabs_MouseRightButtonDown" MouseEnter="FavoritesTabs_MouseEnter" >

   <TabControl.ContextMenu>
      <ContextMenu Name="tabContextMenu">
         <MenuItem Header="Change Tab Name" Click="MenuItem_Click" />
         <MenuItem Header="Save Favorite Layers" />
      </ContextMenu>
   </TabControl.ContextMenu>

</TabControl>
                

C#

private void FavoritesTabs_Initialized(object sender, EventArgs e)
{
    FavoritesList.Add("Favorite 1");
    FavoritesList.Add("Favorite 2");
    FavoritesList.Add("Favorite 3");
    FavoritesList.Add("Favorite 4");
    FavoritesTabs.ItemsSource = FavoritesList;
}

private void FavoritesTabs_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{

}

private void MenuItem.Click(object sender, RoutedEventArgs e)
{
    int index = FavoritesTabs.SelectedIndex;
    FavoritesList[index] = "New tab";            
}

I tried this answer but it did not work.

Amadeus
  • 157
  • 10

1 Answers1

1

You have to style your TabControl adding event for MouseDown then select the tab item accordingly.

This is the code:

XAML

At fist, you need to define reosurces for TabControl style and also a style for a Grid. The latter is needed because you can't define an event handler within the TabControl style directly.

<Window x:Class="WpfApp7.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp7"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.Resources>
    <Style x:Key="GridStyle" TargetType="Grid">
        <EventSetter Event="Mouse.MouseDown" Handler="UIElement_OnMouseDown"/>
    </Style>

    <Style x:Key="TabcontrolStyle"  TargetType="{x:Type TabControl}">
    <Style.Resources>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid Height="20"
                              Background="{TemplateBinding Background}"
                              SnapsToDevicePixels="true"
                              Style="{StaticResource GridStyle}">
                            <ContentPresenter Margin="10,0"
                                              HorizontalAlignment="Center"
                                              VerticalAlignment="Center"
                                              ContentSource="Header" >
                            </ContentPresenter>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="false">
                                <Setter Property="Background" Value="Transparent" />
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" Value="{x:Static SystemColors.ControlBrush}" />
                            </Trigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter Property="Background" Value="{x:Static SystemColors.ActiveCaptionBrush}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid KeyboardNavigation.TabNavigation="Local">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                        <TabPanel Name="HeaderPanel"
                                  Panel.ZIndex="1"
                                  IsItemsHost="True"
                                  KeyboardNavigation.TabIndex="1" />
                    <ContentPresenter Name="PART_SelectedContentHost"
                                      Margin="10"
                                      Grid.Row="1"
                                      ContentSource="SelectedContent" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    </Style>
    
</Window.Resources>
<Grid>
    <TabControl x:Name="MyTabControl" Style="{StaticResource TabcontrolStyle}">
        
        <TabControl.ContextMenu>
            <ContextMenu Name="tabContextMenu">
                <MenuItem Header="Change Tab Name" />
                <MenuItem Header="Save Favorite Layers" />
            </ContextMenu>
        </TabControl.ContextMenu>

        <TabItem Header="First">First Content</TabItem>
        <TabItem Header="Second">Second Content</TabItem>
        <TabItem Header="Third">Third Content</TabItem>
    </TabControl>
</Grid>

Code behind

In the code behind you can equals the TabItem header to selected the TabItem accordingly.

private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        ContentPresenter contentPresenter = null;

        if (e.Source is Grid)
            contentPresenter = (ContentPresenter) ((Grid) e.Source).Children[0];
        else if (e.Source is ContentPresenter)
            contentPresenter = ((ContentPresenter) e.Source);

        if (contentPresenter == null) return;

        var header = contentPresenter.Content.ToString();

        var selectedIndex = -1;
        foreach (var item in this.MyTabControl.Items)
        {
            selectedIndex++;

            var tabItem = item as TabItem;

            if (tabItem?.Header != null && tabItem.Header.ToString().Equals(header, StringComparison.InvariantCultureIgnoreCase))
            {
                this.MyTabControl.SelectedIndex = selectedIndex;
            }
        }
    }
trix
  • 878
  • 6
  • 14
  • thank you for your answer. Where should I place these styles in XAML? Inside the TabControl.Resources under the TabControl? I don't get any error but it does not change the tab name which the mouse is over. It still changes the name of previously selected tab or do nothing if none of them selected. In the code behind, I think it is supposed to go through tabs and select the tab where mouse is over right? contentPresenter stays null somehow because I placed several MessageBox.Show inside the UIElement_OnMouseDown and none of them shows up. – Amadeus Nov 09 '20 at 15:42
  • @Amadeus I added the full XAML code for further explanation – trix Nov 09 '20 at 15:49