views:

502

answers:

6

I need to determine which tab the user is coming from, and going to, when they switch tabs, and possibly cancel the switch. I have tried the Deselecting, Deselected, Selecting, Selected events, and all of them show the e.TabPageIndex to be the same as the sender.SelectedIndex.

Is there an event, or property, that I can use so that I can determine both sides of this, or do I have to hack something together with caching it from one event and using that value in the new event.

I am trying to avoid handling the Deselecting/Deselected events and caching the value to use in the Selecting event. I already know I can do this, so I am asking if there is a cleaner way, without doing this.

I have tried in both C# and VB, with the same results (no surprise).

Thanks.

+1  A: 

You can subscribe to TabControl.Deselecting. When it fires, the tab index in the event args will be the old index.

You can then subscribe to TabControl.Selected. When this event fires, the tab index will be the index of the newly selected tab.

Reed Copsey
Yeah, I am trying to avoid having to cache the value like this. It is looking like this is the only way though.
Rick Mogstad
Yes. There is no single event that has both tab indices available, unfortunately.
Reed Copsey
+1  A: 

It doesn't look like any one event argument will carry the details of both the previous and current tabs, so you'll need to handle a couple events to keep track.

At a minimum you'd need to use the Deselected event to store a reference to the previously-selected tab, you can always query the TabControl for it's current tab. To stretch a little further you can also handle the Selected event to track the current tab.

Option Strict On
Option Explicit On

Public Class Form1

 Private PreviousTab As TabPage
 Private CurrentTab As TabPage

 Private Sub TabControl1_Deselected(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlEventArgs) Handles TabControl1.Deselected
  PreviousTab = e.TabPage
  Debug.WriteLine("Deselected: " + e.TabPage.Name)
 End Sub

 Private Sub TabControl1_Selected(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlEventArgs) Handles TabControl1.Selected
  CurrentTab = e.TabPage
  Debug.WriteLine("Selected: " + e.TabPage.Name)
 End Sub

 Private Sub TabControl1_Selecting(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles TabControl1.Selecting
  If CurrentTab Is Nothing Then Return
  Debug.WriteLine(String.Format("Proposed change from {0} to {1}", CurrentTab.Name, e.TabPage.Name))
 End Sub

End Class
STW
Yeah, that is the solution I have, but I am trying to avoid doing that. Also, I need to use the Selecting event, so that I can cancel, but that is beside the point.
Rick Mogstad
@Rick: It doesn't look like the stock WinForms TabControl supports everything you're after, you could extend it to provide the info you're after, or you could use a 3rd party TabControl (I think Infragistics supports the behavior you're looking at)
STW
+1  A: 

You can get the index of the tab the user is moving away from with the Deselecting event and store it in a variable for later use:

Private Sub TabControl1_Deselecting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles TabControl1.Deselecting
    someClassLevelVariable = e.TabPageIndex
End Sub

You want put code to prevent the switch in the Selecting event. Here's an example in VB.NET that will prevent you from selecting tab 2 on a tab control:

Private Sub TabControl1_Selecting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles TabControl1.Selecting
    If (e.TabPageIndex = 1) Then
        e.Cancel = True
    End If
End Sub
raven
This doesn't really help, though, since I want to know the previous tab before deciding whether to cancel or not.
Rick Mogstad
I updated my answer, but I then read in another comment you don't want to have to cache the value of the current tab. I don't know of any other way.
raven
+1  A: 

You could do something like that :

    private int _oldIndex = -1;

    private void tabControl1_Deselected(object sender, TabControlEventArgs e)
    {
        _oldIndex = e.TabPageIndex;
    }

    private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
    {
        if (_oldIndex != -1)
        {
            if (CanChangeTab(_oldIndex, e.TabPageIndex))
            {
                e.Cancel = true;
            }
        }
    }

    private bool CanChangeTab(int fromIndex, int toIndex)
    {
        // Put your logic here
    }
Thomas Levesque
A: 

I believe the e.Cancel flag in the selecting event should do this trick. This sends the selected tab back to the original on cancel:

    private void tabControl1_Selecting( object sender, TabControlCancelEventArgs e )
    {
        int badIndex = 0;

        if ( tabControl1.SelectedIndex == badIndex )
            e.Cancel = true;            
    }

If you really need more complex beharvior, subclassing the tab control will work, Then override the onSelecting method. This shouldn't be done lightly. If the control is changed in the future you will have broken code. And you have to confirm that all the behavior of the tab control are met (ie constructors ...)

EDIT: based on feedback. This will internally track the original tab before selection.

public class MyTab : System.Windows.Forms.TabControl
{
    int _previousTab;

    protected override void OnSelecting( TabControlCancelEventArgs e )
    {
        // Some logic here to do cool UI things, perhaps use _previousTab

        // Call the base method
        base.OnSelecting( e );           
    }
    protected override void OnDeselecting( TabControlCancelEventArgs e )
    {
        // Store the value for use later in the chain of events
        _previousTab = this.SelectedIndex;

        // Call the base method
        base.OnDeselecting( e );
    }

}
MarcLawrence
This doesn't do anything to address the actual problem, where I need to know which tab you are coming from. Inheriting from the control also won't fix the problem, unfortunately.
Rick Mogstad
+1  A: 

To provide an alternative to my initial answer.... here's a sample of an extended TabControl which modifies the event arguments to include some more details.

Disclaimer This is slapped together, it'll need some adjustments for sure!

Option Strict On
Option Explicit On

Imports System
Imports System.Windows.Forms

Public Class ExtendedTabControl
 Inherits TabControl

 Public Shadows Event Selecting As EventHandler(Of SelectedTabChangingEventArgs)
 Public Shadows Event Selected As EventHandler(Of SelectedTabChangedEventArgs)

 Private _PreviousTab As TabPage
 Public Property PreviousTab() As TabPage
  Get
   Return _PreviousTab
  End Get
  Private Set(ByVal value As TabPage)
   _PreviousTab = value
  End Set
 End Property

 Private _CurrentTab As TabPage
 Public Property CurrentTab() As TabPage
  Get
   Return _CurrentTab
  End Get
  Private Set(ByVal value As TabPage)
   _CurrentTab = value
  End Set
 End Property

 Protected Overrides Sub OnDeselected(ByVal e As System.Windows.Forms.TabControlEventArgs)
  PreviousTab = e.TabPage
  MyBase.OnDeselected(e)
 End Sub

 Protected Overrides Sub OnSelected(ByVal e As System.Windows.Forms.TabControlEventArgs)
  CurrentTab = e.TabPage
  Dim selectedArgs As New SelectedTabChangedEventArgs(e, PreviousTab)
  RaiseEvent Selected(Me, selectedArgs)
 End Sub

 Protected Overrides Sub OnSelecting(ByVal e As System.Windows.Forms.TabControlCancelEventArgs)
  Dim selectedArgs As New SelectedTabChangingEventArgs(e, CurrentTab)
  RaiseEvent Selecting(Me, selectedArgs)
 End Sub

End Class

Public Class SelectedTabChangingEventArgs
 Inherits TabControlCancelEventArgs

 Public Sub New(ByVal selectingEventArgs As TabControlCancelEventArgs, ByVal previousTabPage As TabPage)
  MyBase.New(selectingEventArgs.TabPage, selectingEventArgs.TabPageIndex, selectingEventArgs.Cancel, selectingEventArgs.Action)
  Me.CurrentTab = previousTabPage
 End Sub

 Private _CurrentTab As TabPage
 Public Property CurrentTab() As TabPage
  Get
   Return _CurrentTab
  End Get
  Set(ByVal value As TabPage)
   _CurrentTab = value
  End Set
 End Property

End Class

Public Class SelectedTabChangedEventArgs
 Inherits TabControlEventArgs

 Public Sub New(ByVal selectedEventArgs As TabControlEventArgs, ByVal previousTabPage As TabPage)
  MyBase.New(selectedEventArgs.TabPage, selectedEventArgs.TabPageIndex, selectedEventArgs.Action)
  Me.PreviousTab = previousTabPage
 End Sub

 Private _PreviousTab As TabPage
 Public Property PreviousTab() As TabPage
  Get
   Return _PreviousTab
  End Get
  Set(ByVal value As TabPage)
   _PreviousTab = value
  End Set
 End Property

End Class

...and a sample Form using this control...

Option Strict On
Option Explicit On

Public Class Form1

 Private Sub TabControl1_Selecting(ByVal sender As Object, ByVal e As SelectedTabChangingEventArgs) Handles TabControl1.Selecting
  If e.CurrentTab Is Nothing Then Return
  Console.WriteLine(String.Format("Proposed change from {0} to {1}", e.CurrentTab.Name, e.TabPage.Name))
 End Sub

 Private Sub TabControl1_Selected(ByVal sender As Object, ByVal e As SelectedTabChangedEventArgs) Handles TabControl1.Selected
  Console.WriteLine(String.Format("Changed from {0} to {1}", e.PreviousTab.Name, e.TabPage.Name))
 End Sub

End Class
STW
Unfortunately, I can't shadow the events, because it is passed as a System.Windows.Forms.Tabcontrol into other methods. I appreciate the answers, but it looks like my initial conculsion was correct.
Rick Mogstad
@Rick: It *might* still work since the shadowed event types extend the types used by the base type; the methods that work with it as a TabControl just wouldn't see the extra properties.
STW
The code that this is being used in is a framework to support other developers, so I am a bit limited in what I can do without breaking existing code. Thanks again for your answers.
Rick Mogstad