Click here to Skip to main content
15,891,749 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi I have a WPF application.
I have a tab control. inside this tab control I have several tab pages. When the user right clicks on a tab page, a context menu appears. one of the items of this menu is "remove". by clicking on this item the right clicked tab page should be removed.
Now, the problem is that how to identify which tab page was clicked. inside the event handler there is no proof of showing which tab page was removed.
please consider that I'm new in WPF.

thanks

C#
private void MenuItem_Remove_Click(object sender, RoutedEventArgs e)
{
//sender does not give us enough information to know which tab page was clicked on
}



The Image[^]

if my WPF source is needed:
XML
<Window x:Class="InfoManager.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
      <TabControl Name="tabControlMain" Margin="1,1,48,1">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title}" MouseRightButtonDown="TextBlock_MouseRightButtonDown">
                        <TextBlock.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="Remove" Click="MenuItem_Remove_Click">
                                    <MenuItem.Icon>
                                        <Image Width="20" Height="20"  Source="..\Resources\remove.png" Stretch="Fill" />
                                    </MenuItem.Icon>
                                </MenuItem>
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabItem Header="tabItem1" Name="tabItem1">
            </TabItem>
        </TabControl>
        <Button Width="30" Height="30" HorizontalAlignment="Right"  Name="buttonAddTab" VerticalAlignment="Top" Margin="0,5,5,0" Click="buttonAddTab_Click">
        <Image Source="..\resources\add.png" Stretch="UniformToFill"></Image>
        </Button>
    </Grid>
</Window>
Posted
Updated 23-Jun-11 23:17pm
v2

*Edit*

After thinking about this again, my original reply was wrong because the user doesn't necessarily right-click on the selected tab. I see your dilemma, but there's a solution (well, at least one)...

In the MenuItem Click handler, the sender is a MenuItem. Its DataContext will be a reference to the object providing the data item for the tab item. You just need to remove that object from the collection that is providing data to the tab control. For example, here's how I tested it...
<UserControl x:Class="WPFTester.TabControlTestPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TabControl x:Name="tabControlMain" Loaded="tabControlMain_Loaded" >
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title}">
                        <TextBlock.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="Remove" Click="MenuItem_Remove_Click">
                                    <!--<MenuItem.Icon>
                                        <Image Width="20" Height="20"  Source="..\Resources\remove.png" Stretch="Fill" />
                                    </MenuItem.Icon>-->
                                </MenuItem>
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Content}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</UserControl>

public partial class TabControlTestPage : UserControl
{
    public TabControlTestPage()
    {
        InitializeComponent();
    }


    private TabDataTestList tabDataTestList = new TabDataTestList();

    private void tabControlMain_Loaded(object sender, RoutedEventArgs e)
    {
        tabControlMain.ItemsSource = tabDataTestList;
    }

    private void MenuItem_Remove_Click(object sender, RoutedEventArgs e)
    {
        tabDataTestList.Remove(((sender as MenuItem).DataContext) as TabData);
    }

}


public class TabData
{
    public string Title { get; set; }
    public string Content { get; set; }
}

public class TabDataTestList : ObservableCollection<TabData>
{
    public TabDataTestList()
    {
        this.Add(new TabData() { Title = "Item 1", Content = "Content for item 1" });
        this.Add(new TabData() { Title = "Item 2", Content = "Content for item 2" });
        this.Add(new TabData() { Title = "Item 3", Content = "Content for item 3" });
        this.Add(new TabData() { Title = "Item 4", Content = "Content for item 4" });
        this.Add(new TabData() { Title = "Item 5", Content = "Content for item 5" });
    }
}
 
Share this answer
 
v4
Comments
[no name] 24-Jun-11 23:25pm    
Thanks for helping me
I could solve the problem.
but still something is unclear for me.
on this line:
tabDataTestList.Remove(((sender as MenuItem).DataContext) as TabData);
TabData is the binded data. I can have access to the Control by:
((sender as MenuItem).Parent as ContextMenu).PlacementTarget as TextBlock
but I cannot find index order of this control. and parent of this control is null!!! and I dont know how to find out which tabPage was triggered. I just know that which binded object is selected.
Mark Salsbery 24-Jun-11 23:46pm    
If you need the index you can find the TabData instance (((sender as MenuItem).DataContext) as TabData) in the TabControl's ItemsSource collection. For example, in my code I could use tabDataTestList .IndexOf(((sender as MenuItem).DataContext) as TabData); to find the tab index. If you run that code you'll see it knows which TabItem is clicked on because it removes the tab. You can't rely on the content of the TabItem, however, because WPF will delete it and re-create it as tabs are selected.
[no name] 28-Jun-11 4:55am    
my problem is that the type "TabData" is not recognized by my C#
Mark Salsbery 28-Jun-11 15:24pm    
TabData is what I used in my example. You need to use the type you are using to supply data to the tabitems.
this is for winform as im not sure about wpf but hpefully this will point you in the right diretion

C#
protected TabPage ActiveTab
        {
            get
            {
                if (this.tabControl1.SelectedTab != null)
                {
                    return this.tabControl1.SelectedTab;
                }
                return null;
            }
        }


to use just use this

C#
TabPage MySelectedTab = ActiveTab;
if (MySelectedTab != null)
{
// do what you need with the active TabPage here
}
 
Share this answer
 
v2
Comments
[no name] 24-Jun-11 23:26pm    
Thanks
but the problem is that the right clicked tab is not the active tab necessarily.
My workaround for this is the following:

C#
public TabItem Clicked;

public void AddTab(string Name)
{
TabControl1.Items.Add(new TabItem{Header = c.Name, Name = c.Name});
((TabItem)TabControl1.Items[TabControl1.Items.Count - 1]).MouseDown += new MouseButtonEventHandler((sender, e) =&gt; { if (e.RightButton == MouseButtonState.Pressed) Clicked = (TabItem)TabControl1.Items[TabControl1.Items.Count - 1]; });
}

//Window Mouse Down Event Handler
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.RightButton == MouseButtonState.Pressed)
        Clicked = null;
}

Then you can use the Clicked variable anywhere to find out the Right Clicked TabItem.

Good Luck!
 
Share this answer
 
An alternative solution is shown below.
HTML
<window x:class="WPF_SampleApp.MainWindow" xmlns:x="#unknown">
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Sample" Height="250" Width="435">
    <grid>
        <tabcontrol height="169" horizontalalignment="Left" margin="10,10,0,0" name="tabControl1" verticalalignment="Top" width="389">
            <tabitem header="tabItem1" name="tabItem1" isselected="False" mouserightbuttondown="TabItem_MouseRightButtonDown">
                <grid />
            </tabitem>
            <tabitem header="tabItem2" mouserightbuttondown="TabItem_MouseRightButtonDown" />
        </tabcontrol>
    </grid>
</window>

C#
namespace WPF_SampleApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private TabItem selectedTabItem = new TabItem();

        private void TabItem_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            ContextMenu mainMenu = new ContextMenu();

            MenuItem removeTab = new MenuItem();
            removeTab.Header = "Remove";
            removeTab.Foreground = Brushes.Black;
            removeTab.Background = Brushes.Transparent;
            removeTab.Click += new RoutedEventHandler(removeTab_Click);
            mainMenu.Items.Add(removeTab);

            MenuItem changeTabBGColor = new MenuItem();
            changeTabBGColor.Header = "Change background color";
            changeTabBGColor.Foreground = Brushes.Black;
            changeTabBGColor.Background = Brushes.Transparent;
            changeTabBGColor.Click += new RoutedEventHandler(changeTabBGColor_Click);
            mainMenu.Items.Add(changeTabBGColor);

            TabItem tabItem = (TabItem)sender;
            selectedTabItem = tabItem; // Set seleted tab
            tabItem.ContextMenu = mainMenu;
        }

        void changeTabBGColor_Click(object sender, RoutedEventArgs e)
        {
            if (selectedTabItem != null) selectedTabItem.Background = Brushes.Blue;
        }

        void removeTab_Click(object sender, RoutedEventArgs e)
        {
            if (selectedTabItem != null) tabControl1.Items.Remove(selectedTabItem);
        }
    }
}
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900