Code to remove nested TabPage doesn't always work
I wanted to implement MiddleClick-To-Delete functionality like most tabbed environment in my application that looks like this:
This is just one form with TabControl
which has 2x TabPage
: ActiveItems and archived items.
Both of these pages contain their own TabControl
, which the user can add as many objects as possible TabPage
.
If the user has Active Items selected and they remove the tab, it removes the correct one. If they remove the tab from the archived items, it also removes the correct one. If they move the Active tab to Archive and then delete it, it removes the wrong tab. So in the screenshot above, you cannot see the Archived Items, but there are already 2 tabs. If I move 13571 from Active -> Archive and then delete it (tab # 3, index # 2), it removes tab # 2, index # 1.
// Both the tcActive and tcArchived, the TabControls on tcRoot two Active Items
// and Archived Items tab pages, subscribe to this event
private void tc_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != System.Windows.Forms.MouseButtons.Left)
tabPageClicked = e.Location;
if (e.Button == MouseButtons.Middle)
DeleteTab();
}
// Passes the TabControl and TabPageIndex to be deleted
private void DeleteTab()
{
int tpi = GetTabIndex(tabPageClicked);
if (tcActiveArchive.SelectedIndex.Equals(0))
DeleteTab(tcActive, tpi);
else
DeleteTab(tcArchived, tpi);
}
// Gets the TabPage index from either Active Items page or Archived Items page
private int GetTabIndex(Point p)
{
if (tcActiveArchive.SelectedIndex.Equals(0))
return tcActive.TabPages.IndexOf(tcActive.TabPages.Cast<TabPage>().Where((t, i) => tcActive.GetTabRect(i).Contains(p)).First());
else
return tcArchived.TabPages.IndexOf(tcArchived.TabPages.Cast<TabPage>().Where((t, i) => tcArchived.GetTabRect(i).Contains(p)).First());
}
// Actual removal of TabPage at index tabIndex from TabControl tc
private void DeleteTab(TabControl tc, int tabIndex)
{
lastDeleteWasActiveTab = (tcActiveArchive.SelectedIndex.Equals(0));
//Tab object just stores a string, string[] and bool
Tab deletedTab = new Tab(tc.TabPages[tabIndex].Text, ((TextBox)tc.TabPages[tabIndex].Controls[0]).Lines, lastDeleteWasActiveTab);
if (lastDeleteWasActiveTab)
{
deletedActiveTabs.Push(deletedTab);
filesToDelete.Add(mantisDir + tc.TabPages[tabIndex].Text + ".txt");
}
else
{
deletedArchiveTabs.Push(deletedTab);
filesToDelete.Add(archiveDir + tc.TabPages[tabIndex].Text + ".txt");
}
tc.TabPages.RemoveAt(tabIndex);
//Try to select the tab to the right of the one that was deleted
if (tc.TabPages.Count >= tabIndex + 1)
tc.SelectedIndex = tabIndex;
else
tc.SelectedIndex = tabIndex - 1;
undoQueue.Push((lastDeleteWasActiveTab) ? UndoEventType.DeleteActive : UndoEventType.DeleteArchive);
}
Executing the code for the above condition:
-
MouseDown
the event is triggered -
DeleteTab()
-
GetTabIndex
returns 2 -
DeleteTab(tcArchived, 2);
called -
Tab deletedTab
has all the meanings it should contain, indicating that we are looking at the correct contributor / index. -
tc.RemoveAt(2)
called
But it removes the second item, not index 2. Absolutely can't figure out what happened.
source to share
Changing this line:
DeleteTab(TabControl tc, int tabIndex)
{
//other code
tc.TabPages.RemoveAt(tabIndex);
}
To:
TabPage tp = tc.TabPages[tabIndex];
tc.TabPages.Remove(tp);
It looks like it works for the use case described in my original post with minimal testing. But I would love to know why it RemoveAt(tabIndex)
doesn't work.
source to share