C++ Builder 10.2 Tokyo > Forms > フォームを並べたときに空白がないように配置する (Windows 7, 8.1, 10) > FormShow後の実行

```txt:実装環境
RAD Studio 10.2 Tokyo Update 3
```

### 関連

- [C++ Builder 10.2 Tokyo > Forms > Bug > 8個の子フォームを親フォームの下に4行 x 2列で配置する > 空白があいてしまう > 10.2 Tokyoの問題ではなさそう](https://qiita.com/7of9/items/426360c66715d276bc0d)
- [C++ Builder XE4 > Windows 7, 8.1, 10 のウィンドウサイズ情報を見る](https://qiita.com/7of9/items/0d18eac464a9ef1f5eb7)

### 処理概要

- Windows 7, 8.1, 10 すべてにおいてフォームを並べたときに空きが生じないようにする

### code

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
#include <Vcl.ComCtrls.hpp>
#include <VCLTee.Chart.hpp>
#include <VCLTee.Series.hpp>
#include <VclTee.TeeGDIPlus.hpp>
#include <VCLTee.TeEngine.hpp>
#include <VCLTee.TeeProcs.hpp>

#include "Unit2.h"
//---------------------------------------------------------------------------

// hogehoge

class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *B_matrix;
    void __fastcall B_createChildClick(TObject *Sender);
    void __fastcall B_matrixClick(TObject *Sender);
private:    // ユーザー宣言
    static const int kNumChild = 8;
    TForm2 *m_childForms[kNumChild];
    void __fastcall putChildrenInMatrix();
    void __fastcall GetVisibleWindowSize(TForm *formPtr, TRect *rectPtr);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <DateUtils.hpp>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}

//---------------------------------------------------------------------------
void __fastcall TForm1::B_createChildClick(TObject *Sender)
{
    for(int idx=0; idx < kNumChild; idx++) {
        m_childForms[idx] = new TForm2(this);
        m_childForms[idx]->Show();
    }
}
//---------------------------------------------------------------------------

static void calcLeftTopOfChildren(int parentTop, int parentHeight, int childIdx, int childWidth, int childHeight, int *dstTop, int *dstLeft)
{
    int topSize, leftSize;

    if (childIdx < 4) {
        topSize = childIdx * childHeight;
        leftSize = 0;
    } else {
        topSize = (childIdx - 4) * childHeight;
        leftSize = childWidth;
    }

    int marginTop = parentTop + parentHeight;

    *dstTop = marginTop + topSize;
    *dstLeft = leftSize;
}

void __fastcall TForm1::putChildrenInMatrix()
{
    TForm2 *formPtr;
    int topPos, leftPos;

    TRect parGeo; // parent
    TRect chlGeo; // child

    for(int chlIdx=0; chlIdx < kNumChild; chlIdx++) {
        formPtr = m_childForms[chlIdx];

        // parent
        GetVisibleWindowSize(this, &parGeo);
        int parhei = parGeo.Bottom - parGeo.top;
        // child
        GetVisibleWindowSize(formPtr, &chlGeo);
        int chlwid = chlGeo.Right - chlGeo.Left;
        int chlhei = chlGeo.Bottom - chlGeo.top;

        // calcLeftTopOfChildren(this->Top, this->Height, chlIdx, formPtr->Width, formPtr->Height, &topPos, &leftPos);
        calcLeftTopOfChildren(parGeo.top, parhei, chlIdx, chlwid, chlhei, &topPos, &leftPos);
        formPtr->Top = topPos;
        formPtr->Left = leftPos;
    }
}
void __fastcall TForm1::B_matrixClick(TObject *Sender)
{
    putChildrenInMatrix();
}

void __fastcall TForm1::GetVisibleWindowSize(TForm *formPtr, TRect *rectPtr)
{
    TRect arect_inv;
    RECT arect_nrm;

    if (formPtr == NULL || rectPtr == NULL) {
        return; // error
    }

    GetWindowRect(formPtr->Handle, &arect_nrm); // (1)
    DwmGetWindowAttribute(formPtr->Handle, DWMWA_EXTENDED_FRAME_BOUNDS, &arect_inv, sizeof(TRect)); // (2)

    // for Windows 7
    if (arect_inv.left == 0 && arect_inv.top == 0 && arect_inv.Bottom == 0 && arect_inv.right == 0) {
        // DwmGetWindowAttribute()が0を返すためGetWindowRect()の結果を使う
        rectPtr->left = arect_nrm.left;
        rectPtr->top = arect_nrm.top;
        rectPtr->right = arect_nrm.right;
        rectPtr->bottom = arect_nrm.bottom;
        return;
    }

    // for Windows 8.1, Windows 10
    //   Windows 8.1では(1)と(2)は同じ定義
    //   Windows 10ではinvisible borderにより(1)と(2)の結果が異なる
    //   invisible borderのサイズを含まない(2)の方を返す
    //
    // !!!ただしWindows 10の場合、FormShow時には正しい値を返さない!!!
    // FormShow後に実行すること
    rectPtr->left = arect_inv.left;
    rectPtr->top = arect_inv.top;
    rectPtr->right = arect_inv.right;
    rectPtr->bottom = arect_inv.bottom;
}
```

## 実行結果

### Windows 7

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/d3f18e12-f26f-64a9-bda9-034358982b4e.png)

### Windows 8.1

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/fa963fde-d437-a3e3-b30f-20ad156b59d9.png)

### Windows 10

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/b993f2e2-b5f8-6f26-2228-a3279da6f496.png)

### FormShow後の実行
(追記 2018/05/07)

[C++ Builder 10.2 Tokyo > Forms > Height > Windows 10: 実行タイミングによって異なる | Windows 7, 8.1: 実行タイミングによらず同じ > FormShow時とFormShow後](https://qiita.com/7of9/items/fcd4e85d87857e513393)
にも記載の通り、

**上記の実装はFormShow時に実行すると希望の動作にはならない**。

FormShowの処理後でのみ有効であるようだ。