26: VisualC++で複数のフォームでタブ切り替え
概要
前回の記事 から作成しているGUIチートクライアントにおいて、サイドバーの項目をクリックする事でコンテンツウィンドウに表示されるフォームを切り替えます。 これを実現するには、各項目用のフォームをそれぞれ用意し、ボタンクリックイベントでそのフォームを切り替えます。今回は、GUIチートクライアントのサイドバーの項目をクリックする事により、コンテンツウィンドウに表示されるフォームを切り替える事を目標にやっていきます。
チート対象のゲームは、x86でチートの練習用に作られた、Pwn Adventure 3を使います。
Windows対象で、Visual Studio 2019を使用し
C++/CLIで記述する。ソースコードはまとめてページの一番下にある。
            
            三つの項目用のフォームを作成
現状「Player」「Warp」「Item」の三つの項目があるので、それぞれ用のフォームを作成する。以下のようにして新しい項目を追加する画面を出しましょう。
 そして、以下のように
            そして、以下のようにUI > Windowsフォームから追加しましょう。
             同様の手順で「Warp」「Item」のフォームも作成しましょう。
            同様の手順で「Warp」「Item」のフォームも作成しましょう。作り終えたら、今度は見た目を整えます。
全てのフォームのプロパティにおいて、以下を設定しましょう。
※ 注意としてSizeは他のプロパティ項目を変える事で値が変わる場合があるので、一番最後に設定するのが良いです。
- 配置
- AutoScaleMode: None
- Size: 745, 488 (※ MainWindowのコンテンツウィンドウのSizeと同じにする)
- 表示
- BackColor: 20, 20, 20
- Font: Microsoft Sans Serif, 10.2pt
- ForeColor: Lime
- FormBorderStyle: None
フォームの切り替え
では、サイドバーのボタンクリックイベントによりフォームを切り替えるコードを書いていきます。MainWindow.hを開くと、既に色々なコードが自動生成されていると思います。 自動生成されたコードは
#pragma region ~ #pragma endregion の間に書かれているので、
            これから追加するコードは自動生成されたコードの下、すなわち #pragma endregion の下に書いていきます。でも、まずはMainWindow.h内で上記で作成した各フォームを扱えるように、各フォームのヘッダファイルをincludeしましょう。
 そして、
            そして、#pragma endregion の下に以下のように書きましょう。
             まず、最初の三行で各フォームのインスタンスを生成しています。
            まず、最初の三行で各フォームのインスタンスを生成しています。その後の
switchForm関数はコンテンツウィンドウに表示するフォームを切り替えるための関数です。activeFormが現在表示されてるフォームを格納するものです。
            この関数が呼ばれた場合、現在表示されてるフォームをactiveForm->Hide()で隠し、
            新しいフォームをactiveForm = fでactiveFormにセットします。その後、そのフォームを表示するための色々な設定をしていますが、その各種コードの意味を以下に示します。
- f->TopLevel=false
- トップレベルウィンドウ: ウィンドウは親子関係を持たせる事ができるが、これは一番の親にあたるウィンドウ。
- 今回はMainWindowの中に(子として)上記の三つのフォームを表示するのでfalse
- f->FormBorderStyle=System::Windows::Forms::FormBorderStyle::None
- プロパティでも指定してるが一応ウィンドウの枠を消す
- f->Dock = DockStyle::Fill
- コンテンツウィンドウのパネル全体にピッタリ入るようにこうしてる
- MainWindowのサイズが変ってもそれに応じてリサイズされるようになる
- ContentPanel->Controls->Add(f)
- フォームをコンテンツウィンドウのパネル(ContentPanel)のコントロールに加えている
- コントロール: ツールボックスにあるボタンとかのオブジェクトの事をコントロールと呼ぶ
- 例えばDockのFillはパネルA内のオブジェクトにかければパネルA全体に広がるし、パネルB内のオブジェクトにかければパネルB全体に広がる。 この "パネル内の" とはパネルのコントロールであるオブジェクトという事で、今回はContentPanel内全体にFillしたいのでこうしてる。
- ContentPanel->Tag = f
- Tagには任意のオブジェクトを格納する事ができるっぽい
- 正直何のためにこれ書くのかわからないが、とりあえずパネルとフォームを関連付けるという意味で置いてる
- f->BringToFront()
- パネルとかよりも前面に持ってくる
- f->Show()
- 文字通りこのフォームを表示する
書けたら、一旦MainWindow.hのデザインビューの方に行き、サイドバーにあるPlayerボタンをダブルクリックしましょう。
そうすると、MainWindow.hのソースコードの方に飛び、以下のようなボタンクリックのイベントハンドラーが自動生成されるはずです。
 同様にして「Warp」「Item」の方のイベントハンドラーも追加し、中にこのように書きましょう。
            同様にして「Warp」「Item」の方のイベントハンドラーも追加し、中にこのように書きましょう。
            
これで実際に試してみたいですが、現状フォームは全部同じデザインでこれだと切り替わってるかどうかわからないので、 適当にボタンとか置いて切り替わってるかどうかを確認しましょう。切り替わったら成功です。
ソースコード
MainWindow.hに追加するフォーム切り替え用のコード
private:
    Form^ warpwnd   = gcnew WarpWindow();
    Form^ playerwnd = gcnew PlayerWindow();
    Form^ itemwnd   = gcnew ItemWindow();
    Form^ activeForm = nullptr;
    void switchForm(Form^ f) {
        if (activeForm != nullptr)
            activeForm->Hide();
        activeForm = f;
        f->TopLevel = false;
        f->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None;
        f->Dock = DockStyle::Fill;
        ContentPanel->Controls->Add(f);
        ContentPanel->Tag = f;
        f->BringToFront();
        f->Show();
    }
private: System::Void PlayerSideBarButton_Click(System::Object^ sender, System::EventArgs^ e) {
    switchForm(playerwnd);
}
private: System::Void WarpSideBarButton_Click(System::Object^ sender, System::EventArgs^ e) {
    switchForm(warpwnd);
}
private: System::Void ItemSideBarButton_Click(System::Object^ sender, System::EventArgs^ e) {
    switchForm(itemwnd);
}
};