DelphiのUIレイアウト管理のための2つの新しいコンポーネント(TFlowPanelとTGridPanel)を検証します。はじめにDelphiは、常に、すばらしいユーザーインターフェイスを作成する開発環境の主役の座を務めてきました。良いユーザーインターフェイスのために必要とされる要素は多数ありますが、フォーム上のコントロールのビジュアルレイアウトは、最も基本的な要素のひとつです。2006では、新たにTFlowPanelとTGridPanelが導入されました。この記事のサンプルプロジェクトは、こちら.からダウンロードできます。TFlowPanelTFlowPanelは、非常に単純なコントロールです。このパネルは、その中に置かれたコントロールを自動的に位置合わせします。FlowStyleプロパティは、コントロールの配置順を制御します。デフォルトでは、fsLeftRightTopBottomに設定されており、コンポーネントは、左から右、上から下に、余白なしで配置されます。image大きさを変更すると、配置は自動的に更新されます。image他のレイアウトは以下のとおりです。imageひとつ留意しておくべきポイントは、フローパネルがスクロールバーをサポートしていないという点です。そのため、いくつかのコンポーネントは、一部しか表示されなかったり、完全に隠れてしまうかもしれません。imageボタンに限らず、どのようなコントロールも使用できます。これは、ボタンとエディットボックス、チェックボックスを使用した例です。imageもし、コントロールを隣接させたくない場合には、MarginsとAlignWithMarginsプロパティによって、コントロールの間隔を調整します。設計時にコントロールを再配置したいときには、どうすればよいでしょうか?これは、そう簡単ではありません。マウスでコントロールをドラッグしてもだめです。コントロールは、最初の位置に戻ってしまいます。コントロールをカットアンドペーストすれば、最後の位置に移動できますが、これではあまり柔軟性がありません。幸いなことに、ControlIndexプロパティというマジックがあります。これは、面白い機能です。TButtonコントロールをフローパネルに配置すると、ControlIndexプロパティがオブジェクトインスペクタの下の方にリストされます。同じTButtonをフォームに直接配置すると、このControlIndexプロパティは表示されません。そのため、TButtonは(あるいは、同じような他のどのコントロールも)、フローパネル上に置かれているかどうかに依存するControlIndexプロパティを持っているような、いないようなといった状態です。このControlIndexプロパティに関するリファレンスが、VCLソースコードのどこにもありません。TFlowPanelは、GetControlIndexとSetControlIndexという2つの関数を持っており、これらは、コントロールの現在のインデックスを返したり、変更したりします。IDEは、必要に応じて自動的にこのプロパティを追加しています。同じテクニックがTGridPanelでも見ることができます。ボタンのControlIndexプロパティを変更することが、フローパネルのコントロールの位置を変更する方法です。値を0に設定すると先頭の場所へ、1に変更すると2番目の場所へと移動します。TGridPanelグリッドパネルは、コントロールをグリッド(格子状)に配置するために用います。各々のコントロールは、グリッドのセルの中央に配置されます。各セルには、最大で1つのコントロールが配置されます。このパネルをリサイズすると、コントロールは、セルの中央に位置するように配置変更されます。必要に応じて、コントロールの大きさは縮小されます。image大きさを変更すると次のようになります。imageここでも、コントロールの位置を変更するためのマジックプロパティがあります。ColumnプロパティとRowプロパティは、TFlowPanelのControlIndexプロパティと同じように働きます。違いは、ColumnプロパティかRowプロパティに新しい値を設定すると、これが、対象となるセルに位置するコントロールとの入れ替えを引き起こすという点です。3に位置していた)Edit2と位置を入れ替えます(下図参照)。image他にも2つマジックプロパティがあります。ColumnSpanとRowSpanです。これらは、HTMLテーブルと同じように働きます。下の画面ショットでは、Button2とButton5のColumnSpanプロパティを2に、Button4とButton11のRowSpanプロパティを2に設定しています。Button2とButton5、Button11は、予想通りの動作をします。Button4は、若干問題があります。これは正しく配置されますが、結果としてButton8とButton12の位置がおかしくなります。imageButton4の下のセルが空ならば、すべては正しく動作します。image行と列のレイアウトグリッドパネルは、ColumnCollectionプロパティによって、列の追加、削除、幅の変更ができます。グリッドの列または行は、現在のセル数以上のコントロールを追加しようとすると、自動的に追加されます。グリッドパネルのExpandStyleプロパティをemAddColumnsかemAddRowsに設定すれば、列または行の追加が決定されますが、emFixedSizeに設定すると、コントロールが追加されても、これを抑止します。列幅を設定するオプションは3つあります。ssAbsoluteスタイルは、列幅をピクセル単位で指定します。ssAutoスタイルは、各行の対象列をスキャンして、最大幅を持つコントロールを見つけ、これを列幅に設定します。imagessPercentスタイルは、全体の幅の割合(パーセント)で幅を指定します。そのようなスタイルであると考えるでしょう。実際には、まだそのようには働きません。ssAutoあるいはssAbsoluteタイルを指定したカラムの幅を無視します。100ピクセルの幅を持つグリッドパネルを思い浮かべてください。最初の列は、30ピクセルの絶対値で指定します。2つの残りの列は、25%と75%の幅に設定します。この2つの列の実際の幅は、それぞれ17ピクセルと52ピクセルになるはずです(100–30=70ピクセルの25%と75%)。トリッキーなのは、パーセントで指定している部分があることです。2つの列を持つグリッドのそれぞれの列幅が50%と指定されており、最初の列を25%に設定したとしましょう。すると、その幅は33.33%と66.67%になってしまいます。25%と75%になることを期待していたはずです。問題はここにあります。最初の列幅を25%に設定したとき、TColumnItemオブジェクトはアイテムが変更されたことをコレクションに通知します。コレクションは、グリッドパネルにコレクションが(個々のアイテムではありません)変更されたことを通知します。この時点で、グリッドパネルは、ひとつの列が25%に、そしてもうひとつが50%に設定されていることを知ることができますが、どちらが変更されたかを知ることはできません。どのようにこの値が33.33%になるのでしょうか?すべてのカラムのパーセンテージの合計で、列幅を分割するのです。何度も25%を入力し続ければ、やがてその値は25%に近づいていきます。これは、たしかに有効ですが、ちょっと厄介です。幸いにも、もっと簡単な方法があります。BeginUpdate/EndUpdateの間に列幅を指定するコードを記述した、以下のコードを用います。end;列幅は、期待通りに、RowCollectionプロパティを通してまったく同じ方法でハンドルされます。以下は、25%、25%と50%に設定された列を表示した画面ショットです。imageセルのレイアウトセルの中にコントロールをどのように配置するかを制御する方法は、いくつもあります。コントロールのAlignプロパティを使うのは、通常のやり方です。これには、コントロールのMarginsプロパティとAlignWithMarginsプロパティを使うこともできます。Anchorぷ炉パティを使って、Alignプロパティを使って実現できるのとは若干異なる方法で、セル端にコントロールを位置合わせすることができます。ボタンをグリッドパネルに配置してください。設定されていることが分かります。これは、コントロールを中央に配置します。Anchorプロパティを[alLeft]に設定すると、最初、何も変わらないように見えるでしょう。グリッドパネルをリサイズするか、ボタンを移動させようと試みると、ボタンは直ちにその高さや幅を変更することなく、その幅の左端に位置合わせされます。MarginプロパティもAlignWithMarginが設定するのと同様に動きます。以下の画面ショットでは、Button1がTop-Leftにアンカーされています。Button2はTop-Bottomへ、Button3はTop-Rightにというように。注意点は、Button9では、グリッドパネルの端からボタンまでの距離に、そのMarginプロパティとAlignWithMarginsプロパティを使用していることです。imageなお、TopプロパティとLeftプロパティを使って、レイアウトを変更することはできません。これらのプロパティは、コントロールをグリッドパネルに配置したときに、オブジェクトインスペクタからマジックのように隠されます。さらなるマジックすでにControlIndex、Column、ColumnSpan、Row、RowSpan、Top、Leftといったマジックプロパティを見てきました。筆を置く前に、もうひとつのマジックプロパティについて触れておきます。グリッドパネルの上にもうひとつのグリッドパネルを埋め込むと、埋め込まれたグリッドパネルは、その上に配置される各々のコントロールであるTControlItemのコレクションへのアクセスを提供する、ControlCollectionプロパティを使えるようになります。TControlItemのインスタンスは、Column、ColumnSpan、Row、RowSpanプロパティを持っています。これは、組み込まれたコントロールにあるプロパティと同じ機能を提供するものです。コレクションエディタウィンドウを用いれば、TControlItemのインスタンスの順序を変更できますが、グリッド上のコントロールの順序には影響がありません。実践編へ...さて、それではグリッドパネルの実践的なサンプルを紹介して、終りにしたいと思います。以下の2つの画面ショットは、同じフォームですが、サイズが違います。それぞれのケースで、コントロールは適当な大きさになっています。コードは一行も記述していません。