Summary
A bug in Delphi XE7’s Vcl.ComCtrls.pas causes TPageControl.PageIndexFromTabIndex to return an incorrect page index when multiple tabs have TabVisible = False. The routine only skips the first hidden tab, leading to off‑by‑one errors that surface in custom drawing and other internal operations.
Root Cause
- The original loop iterates from
0toTabIndexand increments the result for every hidden tab it encounters. - When more than one hidden tab appears before the target tab, the function stops after adjusting for the first hidden tab, leaving the result short by the number of additional hidden tabs.
- The algorithm assumes at most one hidden tab in the range, which is false in real‑world usage.
Why This Happens in Real Systems
- Dynamic UI: Applications frequently hide tabs based on user permissions, feature flags, or runtime conditions.
- Custom drawing:
OnDrawTabhandlers need the correct mapping between visible (tab) and underlying (page) indices. - Internal VCL logic: Several VCL methods rely on
PageIndexFromTabIndex; an incorrect mapping propagates subtle UI glitches.
Real-World Impact
- Mismatched event data –
OnDrawTabreceives the wrong page index, causing drawing artifacts or crashes. - Navigation bugs – Programmatic page switches (
PageIndex := …) land on the wrong sheet. - State loss – Hidden tabs may be mistakenly treated as visible, leading to incorrect persistence of UI state.
- Hard‑to‑debug – The error appears only when multiple tabs are hidden, a scenario developers may not test.
Example or Code (if necessary and relevant)
function TPageControl.FPageIndexFromTabIndex(TabIndex: Integer): Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to TabIndex do
while (not Pages[I + Result].TabVisible) and (I + Result < PageCount) do
Inc(Result);
Inc(Result, TabIndex);
end;
How Senior Engineers Fix It
- Replace the buggy method with the corrected implementation above (or a similar logic that counts all hidden tabs before the target).
- Add unit tests that cover:
- No hidden tabs.
- A single hidden tab.
- Multiple hidden tabs before, after, and interleaved with visible tabs.
- Encapsulate the fix in a subclass (
TFixedPageControl) to avoid modifying VCL source directly, then use the subclass throughout the project. - Review all call sites (
OnDrawTab, internal VCL uses) to ensure they reference the fixed version.
Why Juniors Miss It
- Assume single‑hidden‑tab logic: Junior developers often test only the simplest case and overlook complex UI states.
- Limited familiarity with VCL internals: The private nature of
PageIndexFromTabIndexhides the bug from casual inspection. - Focus on “works for now”: Without a regression suite, the off‑by‑one error goes unnoticed until a feature that hides multiple tabs is added.
Key takeaway: Always verify index‑translation functions against edge cases involving multiple hidden elements; a single‑pass adjustment is rarely sufficient in dynamic UI components.