TPageControlで選択されようとしているタブは?

解決


ハレ  2006-01-19 20:24:07  No: 19764

TPageControlには、OnChangingイベントがあり、これを使えばページを切り換えるタイミングがわかります。
では、今現在のタブはActivePage(Index)で取得可能ですが、選択しようとしているタブを取得する方法はないでしょうか?

TreeViewであれば、OnChangingイベントで、選択しようとしているNodeを参照できるのですが。

なぜこんなことが知りたいかというと、タブが複数あり「戻る」「進む」で、
タブを一つ一つ左右に移動するように、現在のタブの隣しか選択できないようにしたいわけです。
現在は、「戻る」「進む」のボタンでタブを移動させています。

タブを一つ進める場合、入力データのチェックが必要なので、OnChangingイベントで判定できれば理想なのですが。


ハレ  2006-01-19 21:43:14  No: 19765

Windowsのメッセージ処理でも、APIでも、取得できそうなものはなさそう。
実際に切り替わるまで、新しいページはわからないようです。

継承し、コンポーネント化したら、なんとかなるかもしれませんが、
そこまでの時間がないので、〆ます。

お騒がせしました。

でも、なにか情報があれば教えてください。


MouseMove  2006-01-19 21:53:23  No: 19766

IndexOfTabAt(X, Y)でカーソル位置のタブが分かるよ。


ハレ  2006-01-20 00:13:52  No: 19767

> IndexOfTabAt(X, Y)でカーソル位置のタブが分かるよ。
ほぼ皆無とは思いますが、キーボード操作で、タブを変更することも可能なので、それだけでは不足なのです。


3K  2006-01-20 08:40:39  No: 19768

>現在のタブの隣しか選択できないようにしたいわけです。


3K  2006-01-20 08:45:55  No: 19769

上記間違って送信です。失礼

>現在のタブの隣しか選択できないようにしたいわけです
であれば,強引かもしれないけど

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;

ってういうのはどうでしょか?


Basser  2006-01-20 23:27:41  No: 19770

下記のような感じでいけるのでは無いでしょうか?

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.


Basser  2006-01-20 23:34:05  No: 19771

訂正です。
IndexOfTabAtの戻り値は TabIndex なので、
キー操作の場合は

× NextTab:= I;
○ NextTab:= Pages[I].TabIndex;

です。


ハレ  2006-01-21 02:15:38  No: 19772

Basser さん、ありがとうございます。

結論から言えばNGです。

Windows XP SP1 + Delphi 5 の環境下での結果です。

実はStyle = tsButtonsなので、左右キーでタブ移動にならず、移動候補としてボタンが凹んだ状態になります。
そして、Enterキーをたたいて初めてタブ移動になります。
したがって、マウスクリック同様、タブの飛び移動が可能になり、
LastKeyの判定だけでは、移動先タブが判定不能です。

ちなみに、Style = tsTabで試してみたところ、うまくいきそうな感じです。
Styleで動作が違うとは、思いもしませんでした。
最初に記載しておかなかった私のミスです。
すいませんでした。

画面構成は変えたくないので、このままがんばるなら、TCN_KEYDOWNで、
タブインデックスの移動量を計算して保持しておかないといけないようです(VK_LEFTで−1,Rightで+1する等)
そこまでして実装する価値があるか?となると、現時点では否です(^^ゞ


ハレ  2006-01-21 02:23:56  No: 19773

失礼、書き忘れました。

3Kさんの方法は確かに可能ですが、ちらつきが気になりまし、タブの移動時に
データチェックや、内部データの更新処理等も行っているため、移動して戻ってくるのは、
データの不整合につながる可能性もあるため、不採用です。

めんどくさい仕様になってます(^^ゞ


Basser  2006-01-21 03:05:55  No: 19774

>画面構成は変えたくないので、このままがんばるなら、TCN_KEYDOWNで、
>タブインデックスの移動量を計算して保持しておかないといけないようです(VK_LEFTで−1,Rightで+1する等)
>そこまでして実装する価値があるか?となると、現時点では否です(^^ゞ

その必要は無いです。
VK_RETURN で TabCtrl_GetCurFocus マクロを使えば、
フォーカスのあるタブのインデックスが返ります。


3K  2006-01-21 04:35:32  No: 19775

OnChanging イベントは,新しいタブを選択する直前に発生します。


3K  2006-01-21 04:37:13  No: 19776

またもやご送信  失礼
上記のイベント使うと出来そうな気もするけど?


ハレ  2006-01-21 04:55:28  No: 19777

> その必要は無いです。
> 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;


ハレ  2006-01-21 05:12:27  No: 19778

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さん、ありがとうございました。
かなりすっきりしました。


ハレ  2006-01-21 05:15:05  No: 19779

補足

TabCtrl_GetCurFocus は、Windows NT 3.51, Windows 95 以降で使えるようです。
特にOSのバージョンは気にする必要はなさそうです。

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/tab/macros/tabctrl_getcurfocus.asp


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加