TPageControlには、OnChangingイベントがあり、これを使えばページを切り換えるタイミングがわかります。
では、今現在のタブはActivePage(Index)で取得可能ですが、選択しようとしているタブを取得する方法はないでしょうか?
TreeViewであれば、OnChangingイベントで、選択しようとしているNodeを参照できるのですが。
なぜこんなことが知りたいかというと、タブが複数あり「戻る」「進む」で、
タブを一つ一つ左右に移動するように、現在のタブの隣しか選択できないようにしたいわけです。
現在は、「戻る」「進む」のボタンでタブを移動させています。
タブを一つ進める場合、入力データのチェックが必要なので、OnChangingイベントで判定できれば理想なのですが。
Windowsのメッセージ処理でも、APIでも、取得できそうなものはなさそう。
実際に切り替わるまで、新しいページはわからないようです。
継承し、コンポーネント化したら、なんとかなるかもしれませんが、
そこまでの時間がないので、〆ます。
お騒がせしました。
でも、なにか情報があれば教えてください。
IndexOfTabAt(X, Y)でカーソル位置のタブが分かるよ。
> IndexOfTabAt(X, Y)でカーソル位置のタブが分かるよ。
ほぼ皆無とは思いますが、キーボード操作で、タブを変更することも可能なので、それだけでは不足なのです。
>現在のタブの隣しか選択できないようにしたいわけです。
上記間違って送信です。失礼
>現在のタブの隣しか選択できないようにしたいわけです
であれば,強引かもしれないけど
private
OldPage:integer;
procedure TForm1.PageControl1Change(Sender: TObject);
begin
if (OldPage + 1 <> PageControl1.ActivePageIndex) and
(OldPage - 1 <> PageControl1.ActivePageIndex) then
PageControl1.ActivePageIndex := OldPage
else
OldPage := PageControl1.ActivePageIndex;
end;
ってういうのはどうでしょか?
下記のような感じでいけるのでは無いでしょうか?
type
TForm1 = class(TForm)
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
TabSheet3: TTabSheet;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FDefProc: TWndMethod;
procedure PgCtrlProc(var Message: TMessage);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
Commctrl;
procedure TForm1.FormCreate(Sender: TObject);
begin
FDefProc:= PageControl1.WindowProc;
PageControl1.WindowProc:= PgCtrlProc;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
PageControl1.WindowProc:= FDefProc;
end;
procedure TForm1.PgCtrlProc(var Message: TMessage);
const
LastKey: DWORD = 0;
type
PTCKeyDown = ^TTCKeyDown;
var
I, NextTab: Integer;
Pt: TPoint;
begin
if (Message.Msg = CN_NOTIFY) then
with PNMHdr(Message.LParam)^, PageControl1 do
case code of
TCN_KEYDOWN:
LastKey:= PTCKeyDown(Message.LParam)^.wVKey;
TCN_SELCHANGING:
begin
NextTab:= -1;
case LastKey of
VK_LEFT:
for I:= ActivePage.PageIndex - 1 downto 0 do
if (Pages[I].TabIndex >= 0) then
begin
NextTab:= I;
Break;
end;
VK_RIGHT:
for I:= ActivePage.PageIndex + 1 to PageCount - 1 do
if (Pages[I].TabIndex >= 0) then
begin
NextTab:= I;
Break;
end;
VK_HOME:
for I:= 0 to PageCount - 1 do
if (Pages[I].TabIndex >= 0) then
begin
NextTab:= I;
Break;
end;
VK_END:
for I:= PageCount - 1 downto PageCount - 1 do
if (Pages[I].TabIndex >= 0) then
begin
NextTab:= I;
Break;
end;
else
GetCursorPos(Pt);
Pt:= ScreenToClient(Pt);
NextTab:= IndexOfTabAt(Pt.x, Pt.y);
end;
ShowMessage(Format('NextTabIndex:%d', [NextTab]));
end;
TCN_SELCHANGE:
LastKey:= 0;
end;
FDefProc(Message);
end;
end.
訂正です。
IndexOfTabAtの戻り値は TabIndex なので、
キー操作の場合は
× NextTab:= I;
○ NextTab:= Pages[I].TabIndex;
です。
Basser さん、ありがとうございます。
結論から言えばNGです。
Windows XP SP1 + Delphi 5 の環境下での結果です。
実はStyle = tsButtonsなので、左右キーでタブ移動にならず、移動候補としてボタンが凹んだ状態になります。
そして、Enterキーをたたいて初めてタブ移動になります。
したがって、マウスクリック同様、タブの飛び移動が可能になり、
LastKeyの判定だけでは、移動先タブが判定不能です。
ちなみに、Style = tsTabで試してみたところ、うまくいきそうな感じです。
Styleで動作が違うとは、思いもしませんでした。
最初に記載しておかなかった私のミスです。
すいませんでした。
画面構成は変えたくないので、このままがんばるなら、TCN_KEYDOWNで、
タブインデックスの移動量を計算して保持しておかないといけないようです(VK_LEFTで−1,Rightで+1する等)
そこまでして実装する価値があるか?となると、現時点では否です(^^ゞ
失礼、書き忘れました。
3Kさんの方法は確かに可能ですが、ちらつきが気になりまし、タブの移動時に
データチェックや、内部データの更新処理等も行っているため、移動して戻ってくるのは、
データの不整合につながる可能性もあるため、不採用です。
めんどくさい仕様になってます(^^ゞ
>画面構成は変えたくないので、このままがんばるなら、TCN_KEYDOWNで、
>タブインデックスの移動量を計算して保持しておかないといけないようです(VK_LEFTで−1,Rightで+1する等)
>そこまでして実装する価値があるか?となると、現時点では否です(^^ゞ
その必要は無いです。
VK_RETURN で TabCtrl_GetCurFocus マクロを使えば、
フォーカスのあるタブのインデックスが返ります。
OnChanging イベントは,新しいタブを選択する直前に発生します。
またもやご送信 失礼
上記のイベント使うと出来そうな気もするけど?
> その必要は無いです。
> VK_RETURN で TabCtrl_GetCurFocus マクロを使えば、
> フォーカスのあるタブのインデックスが返ります。
なんと!
そういうものがあったのですね。
奥が深い・・・「TabCtrl_GetCurFocus」でググっても、海外サイトしかみつかりません。
で、Basserさんの元のソースを、以下のように修正しました。
これで、概ね希望通りの動作のような感じです。
しばらく、これで様子を見てみます。
ありがとうございました。
[追加]
CanTabChange: Boolean; //True:タブ変更可能
OnMouseClick: Boolean; //True:マウスクリックである
[変更]
procedure TForm1.PgCtrlProc(var Message: TMessage);
type
PTCKeyDown = ^TTCKeyDown;
var
NextTab: Integer;
Pt: TPoint;
begin
if (Message.Msg = CN_NOTIFY) then begin
case PNMHdr(Message.LParam)^.code of
NM_CLICK:begin
OnMouseClick := True;
end;
TCN_SELCHANGE:begin
CanTabChange := False;
end;
TCN_SELCHANGING: begin
if OnMouseClick then begin
GetCursorPos(Pt);
Pt:= ScreenToClient(Pt);
NextTab:= PageControl1.IndexOfTabAt(Pt.x, Pt.y);
end
else begin
NextTab := TabCtrl_GetCurFocus(PageControl1.Handle);
end;
OnMouseClick := False;
CanTabChange := Abs(PageControl1.ActivePageIndex - NextTab) = 1;
end;
end;
end;
FDefProc(Message);
end;
procedure TForm1.PageControl1Changing(Sender: TObject;
var AllowChange: Boolean);
begin
AllowChange := CanTabChange;
//現在の選択タブを戻す、
//これがないと、移動しようとしたタブボタンが凹んだまま
TabCtrl_SetCurFocus(PageControl1.Handle,
PaeControl1.ActivePageIndex);
end;
3Kさん>
> OnChanging イベントは,新しいタブを選択する直前に発生します。
> 上記のイベント使うと出来そうな気もするけど?
最初の書き込みで
「選択しようとしているタブを取得する方法はないでしょうか?」
と書いています。
つまり、OnChanging イベントで、新しいタブを知る方法がなかったため質問しました。
変更後のタブがわからなければ、移動していいか判定できませんよね?
で、今気が付きましたが、メッセージ処理を置き換えたり、マウスクリックの判定しなくても、
単純にOnChangeでTabCtrl_GetCurFocusを使えばいいだけということに気がつきました。
たった、これだけで済んだ話のようです。
これなら、全style対応かもしれません。
procedure TForm1.PageControl1Changing(Sender: TObject; var AllowChange: Boolean);
var
newTabIdx: Integer;
begin
newTabIdx := TabCtrl_GetCurFocus(PageControl1.Handle);
AllowChange := (Abs(newTabIdx - PageControl1.ActivePageIndex) = 1);
if not AllowChange then begin
TabCtrl_SetCurFocus(PageControl1.Handle, PageControl1.ActivePageIndex);
end;
end;
Basserさん、3Kさん、ありがとうございました。
かなりすっきりしました。
補足
TabCtrl_GetCurFocus は、Windows NT 3.51, Windows 95 以降で使えるようです。
特にOSのバージョンは気にする必要はなさそうです。
ツイート | ![]() |