I have a PageControl with a couple of pages. I want in OnChanging event of
PageControl to set up different conditions depending on what page the user
tries to select. How to find out which page was clicked being in the
OnChanging event?
Thank you very much
Janusz
That is one of life's great mysteries.
With the way TPageControl is currently implemented, getting the identity of
the tab that the user just clicked on is not possible. My programming team
looked for a solution to the problem (including trying various DejaNews
power searches) but did not meet with success.
This limitation pretty much makes the AllowChange parameter in the control's
OnChanging event completely useless, if you want to restrict which pages the
user can change to.
There are third party controls that you can resort to to get around this
glaring limitation.
There is one very tacky solution, however. With a hack you may be able to
determine which tab the user clicked by examining the results of
GetCursorPos(), if you know the coordinates and size of each page's
clickable tab.
-Gary
Janusz Cyran <lob...@silesia.top.pl> wrote in message
news:e28M3.27218$F5.4...@news.tpnet.pl...
> Hi,
>
> I have a PageControl with a couple of pages. I want in OnChanging event of
> PageControl to set up different conditions depending on what page the user
> tries to select. How to find out which page was clicked being in the
> OnChanging event?
>
>Janusz Cyran wrote in message ...
>>Hi,
>>
>>I have a PageControl with a couple of pages. I want in OnChanging event of
>>PageControl to set up different conditions depending on what page the user
>>tries to select. How to find out which page was clicked being in the
>>OnChanging event?
>
>That is one of life's great mysteries.
>
>With the way TPageControl is currently implemented, getting the identity of
>the tab that the user just clicked on is not possible.
The impossible just takes a little longer - and is more complicated <g>
My previous solution is not immediately implementable because the OnChanging is
fired before the OnMouseDown. However the OnMouseMove _is_ fired before the
OnChanging and can be used to store the mouse X and Y. These values can then be
tested in the OnChanging. Obviously one should have only the minimum code in
TPageControl.OnMouseMove, even though it is fired only in the page control tab
area.
var
PageCtlX, PageCtlY : integer;
procedure TForm1.PageControl1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
PageCtlX := X;
PageCtlY := Y;
end;
procedure TForm1.PageControl1Changing(Sender: TObject;
var AllowChange: Boolean);
var
HitTestInfo : TTCHitTestInfo; // data structure, put CommCtrl in uses
begin
HitTestInfo.Pt := point(PageCtlX, PageCtlY); // mouse position from client
origin
{stop selection of TabPage 2}
if PageControl1.Perform(TCM_HitTest, 0, integer(@HitTestInfo)) = 2 then
AllowChange := false;
end;
Alan Lloyd
alang...@aol.com
>I have a PageControl with a couple of pages. I want in OnChanging event of
>PageControl to set up different conditions depending on what page the user
>tries to select. How to find out which page was clicked being in the
>OnChanging event?
>
. . . and to support Multi-Option Felicide (more ways than one to kill a cat
<g>) following my previous post here's another way :-
procedure TForm1.PageControl1Changing(Sender: TObject;
var AllowChange: Boolean);
var
MousePt : TPoint;
HittestInfo : TTCHitTestInfo; // uses CommCtrl
NextSheetIndex : integer;
Key : Word;
const
KeyDown : Word = $8000;
begin
{next sheet by mouse click}
GetCursorPos(MousePt);
MousePt := PageControl1.ScreenToClient(MousePt);
HitTestInfo.Pt := MousePt;
NextSheetIndex := PageControl1.Perform(TCM_HITTEST, 0,
longint(@HitTestInfo));
if NextSheetIndex = -1 then begin
{next sheet by left or right arrow
note that Changing is not called if sheet is lowest and Left Arrow,
or sheet is highest and Right Arrow}
Key := GetAsyncKeyState(VK_LEFT);
if Key and KeyDown <> 0 then
with PageControl1 do
NextSheetIndex := ActivePage.PageIndex - 1;
Key := GetAsyncKeyState(VK_RIGHT);
if Key and KeyDown<> 0 then
with PageControl1 do
NextSheetIndex := ActivePage.PageIndex + 1;
end; {if NextSheetIndex = -1}
{...
Now do what you want with nextSheetIndex
...}
end;
Alan Lloyd
alang...@aol.com
Hmm. What I always did was to:
(1) Use "OnChanging" to detect that a new page is being selected;
remember the old page number now, set a boolean flag.
(2) In the handler for the next event that's coming ("Arrive", as I
recall) detect that the flag has been raised. Reset the flag and
process the fact that you've just finished moving from one page to the
next.
If you have to prohibit certain page-transitions from occurring, you
must do it by disabling and enabling individual pages -- before the user
attempts to select them. (Users should always know what options are or
are not available to them now.)
The problem is that the OnChanging code can establish the tab sheet to go to
either from the mouse or from the key-press, but it needs to know _what_ has
caused the change so that it can use the mouse position or the key-press. Both
are readable with a system call. Because the OnMouseDown event handler occurs
after OnChanging, it cannot be used. And the TPageControl does not have a
OnKeyDown or OnKeyUp to use as the primary check.
But TCustomTabControl (which is an ancestor of TPageControl) has such event
handlers but they are protected. So one has to expose them by casting them to a
descendant which one declares in the form's type clause.
These handlers then store a value of -1 for a left key press, a value of +1 for
a right key press, and zero for any other. As the key value is only needed in
the OnChanging event handler it is set to Zero in the OnKeyUp event handler.
Then the OnChanging event handler checks the stored key value and uses it if it
is non-zero. Note that the key value is that needed to increment the ActivePage
to get the next page - saves a conditional test there.
Otherwise it knows that a mouse must have caused its call, and gets and uses
the mouse position and translates it to the page control origin. It calls the
API to do a client-hit-test on the page control to return the tab clicked on.
type
TKeyPageCtrl = class(TPageControl); // to use to expose the OnKey event
handlers
var
PageKey : integer; // non-zero between VK_LEFT or VK_RIGHT key-down and
key-up
procedure TForm1.FormCreate(Sender: TObject);
{exposes the OnKeyDown and OnKeyUp event
handlers and allocates the handlers}
begin
TKeyPageCtrl(PageControl1).OnKeyDown := KeyPageCtrlKeyDown;
TKeyPageCtrl(PageControl1).OnKeyUp := KeyPageCtrlKeyUp;
end;
procedure TForm1.KeyPageCtrlKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
{store the page increment appropriate to the key which has been pressed}
begin
case Key of
VK_LEFT : PageKey := -1;
VK_RIGHT : PageKey := +1;
else
PageKey := 0;
end;
end;
procedure TForm1.KeyPageCtrlKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
{sets the key increment to zero to be a flag that it
is not a key-press which has changed the tab sheet}
begin
if Key in [VK_Left, VK_RIGHT] then
PageKey := 0;
end;
procedure TForm1.PageControl1Changing(Sender: TObject;
var AllowChange: Boolean);
var
HitTestInfo : TTCHitTestInfo; // data structure, put CommCtrl in uses
NextTabId : integer;
MousePt : TPoint;
begin
if (PageKey <> 0) then // OnChanging is due to a key press
NextTabId := PageControl1.ActivePage.TabIndex + PageKey
else begin // OnChanging is due to a mouse click
GetCursorPos(MousePt);
MousePt := PageControl1.ScreenToClient(MousePt); // transform to the page
control
HitTestInfo.Pt := MousePt; // mouse position for client hit-test
NextTabId := PageControl1.Perform(TCM_HitTest, 0, integer(@HitTestInfo));
end;
{now control the sheet change here}
case NextTabId of
0 : ;
1 : ;
2 : ;
3 : ;
{more if you need them}
end;
end;
Sorry there's been three bites at the cherry, but I now feel satisfied with the
code <g>
Alan Lloyd
alang...@aol.com