This isn't just in regards to tab pages, although we use a tab page derived in a similar fashion. Sending events willy-nilly through the UI causes me much confusion (keeping the order in which the various events trigger and so on). Instead, I create a controller class which is passed to the various UI components and those components notify the controller with changes and the UI elements subscribe to events on the controller to receive information about the environment.
To make this concrete, each of the derived Tab Pages is passed a reference to the controller. They may change the state of the controller based on user actions. Say the current record is changed, the UI calls a method on the controller telling it the new record.
The other UI elements on the page are notified of this change because they subscribe to the controller's OnCurrentRecordChange
event.
While this introduces another class into the mix, the advantage is that you have a single controller orchestrating the changes to the UI, rather than a bunch of events percolating and passing information around. I find that is also breaks dependencies on UI elements collaborating: I can add, remove or change UI elements and as long as they all speak to the controller for updates there is far less code rework.
If you have ever found yourself debugging UI "loops" (where changes in one control are triggering changes in other controls which trigger yet more changes which eventually affect the original component) then the extra work of a controller class will pay off immediately.
Update: Answering your comment... A first stop would be to read up on the Model View Controller architecture: http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller
For a concrete example: http://www.dotnetheaven.com/Uploadfile/rmcochran/MVC_intro02012006001723AM/MVC_intro.aspx
When working with Windows Forms many people get stuck in a two tier design: UI + Data Layer. The binding system makes this very natural as there is an easy way to get at data (Entity Framework, LINQ) and an easy way to wire data to the UI (the designers). Adding a controller between them isn't as hard as it may seem though.
In my work I use LLBLGen (http://www.llblgen.com/defaultgeneric.aspx) for my low level data layer, but you could substitute LINQ or Entity Framework or any other data access tool and the general overview would be the same.
Above this layer I build my business objects. Many of them are nothing more than facades for the LLBLGen objects (if my business rules don't say much about the entity), while others have a lot of validation built in and they aggregate several low level objects into more usable business objects. Finally there are the business objects that don't directly have entities behind them (communications objects between systems, for example).
The controller object I mention lives alongside my business objects in that it knows about these objects and it even hands them out to the UI for data binding purposes. When the UI wants a change make, it notifies the controller and it uses the business objects to ensure the updates are permitted and if so passes the changes back down the the data layer.
In the diagram on Wikipedia, the View is my UI. The Controller is my coordination object which is mediating changes in both directions while the Model is my business object layer (which has a low level below this, but that is an implementation detail that is hidden from the higher layers).
Although going from "View <-> Model" (classic data binding) to "View <-> Controller <-> Model" seems to be adding complexity, the major benefit is that the controller becomes the one stop shopping location for "truth" about the application. The UI requests data from the controller, so the controller knows about all the UI elements that have a given data binding. If things change, an event notifies all the UI elements and they change visually for the user. The nice thing is that there is one "truth" about system state, and that is the truth the controller is managing for the UI.
When data needs to be persisted, the request goes to the controller to update the model. Again, we have a single place for the coordination of the save and subsequent updates. All of the data validation integrity rules are in the business logic layer (the Model) so the controller's code is kept light.
By separating your UI concerns, your coordination concerns and your business logic concerns
you end up with each having very "lightweight" methods and properties. More importantly, each subsystem can take a very simplistic view of the application as they focus on that piece of the puzzle instead of threading events, UI updates and data updates in one monolithic piece of code.
For further reading I would recommend any of the articles or books about ASP.NET MVC. While this is not Winforms, the basic ideas underlying the MVC can be applied to winforms. There are some good comments here as well: http://stackoverflow.com/questions/2406/looking-for-a-mvc-sample-for-winforms
I should be clear that what I am doing with my projects is probably not considered "pure" MVC, but simply a separation of concerns along the natural fault-lines found in the applications we develop. We don't use a "framework" for this (although some code generation is involved).