SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

グリッドレイアウト用のライブラリ GridExtraを更新しました~Grid子要素の自動レイアウト機能などの追加~

この記事は、XAML Advent Calendar 2017の22日目の記事です。

以前↓に書いた、各種グリッドレイアウトを補助するGridExtraというライブラリを更新しました。
WPF/UWP向けに、グリッドレイアウト補助ライブラリを作ってみました~GridExtra~ - SourceChord

ソースコードリポジトリはこちら

Nugetから以下のコマンドでインストールできます。

Install-Package GridExtra

主に以下のような変更が入っています。

  • GridExクラスの更新
    • GridEx.AutoFillChildren添付プロパティ
      • Gridパネル内の子要素を、UniformGridやWrapPanelみたいに各要素を自動で順々に並べる機能
  • WrapPanelExクラスの追加
    • WrapPanelEx.AdaptiveLayout添付プロパティ
      • WrapPanelの子要素がOrientation方向の幅いっぱいに広がるようにレイアウトを自動調整する機能

ということで変更点などを、ざっとご紹介したいと思います。

GridExの更新内容

GridEx.AutoFillChildren添付プロパティ

gridへの、要素の自動流し込み

今回の更新のメイン!! GridEx.AutoFillChildrenという添付プロパティをTrueにすると、子要素をGrid内の各領域へと自動で順番に流し込んでいきます。

Gridなんだけど、WrapPanelみたいに要素を順番に流し込んでいけるイメージ。

f:id:minami_SC:20171222232439g:plain

使い方

f:id:minami_SC:20171222234049p:plain
こういう風にグリッド内に順番にコントロールを配置したい場合、
以下のように、子要素でGrid.Row/Grid.Columnを個別に設定せず、自動で配置することができます。

    <Grid ge:GridEx.ColumnDefinition="*, *"
        ge:GridEx.RowDefinition="Auto, Auto, Auto"
        ge:GridEx.AutoFillChildren="True"
        ShowGridLines="True">
        <TextBlock Text="Name:" />
        <TextBox VerticalAlignment="Top" Margin="5"/>

        <TextBlock Text="Age:" />
        <TextBox VerticalAlignment="Top" Margin="5"/>

        <Button ge:GridEx.Area="2, 1, 1, 1"
            Margin="5" Width="60"
            HorizontalAlignment="Right"
            Content="OK" />
    </Grid>
Orientationの指定

GridEx.AutoFillOrientation添付プロパティを指定することで、自動レイアウトする方向を指定できます。
こんな風に、縦方向の並びで順番に並べることもできます。

    <Grid ge:GridEx.ColumnDefinition="*, *, *"
              ge:GridEx.RowDefinition="*, *"
              ge:GridEx.AutoFillChildren="True"
              ge:GridEx.AutoFillOrientation="Vertical">
        <Button Content="1" />
        <Button Content="2" />
        <Button Content="3" />
        <Button Content="4" />
        <Button Content="5"  />
        <Button Content="6" />
    </Grid>

f:id:minami_SC:20171222234236p:plain

自動レイアウトに、固定要素を混ぜる

AutoFillChildrenを用いたレイアウトをする際、GridEx.AreaGridEx.AreaNameなど、GridExでの位置指定を行う添付プロパティが設定された子要素があると、自動レイアウトの対象から除外し、指定された位置に固定配置します。

    <Grid ge:GridEx.ColumnDefinition="*, *, *"
          ge:GridEx.RowDefinition="*, *"
          ge:GridEx.AutoFillChildren="True">
        <Button Content="1" />
        <Button Content="2" />
        <Button Content="3" />
        <Button Content="4" />
        <Button Content="Fixed Item" ge:GridEx.Area="0,1,1,1"/>
    </Grid>

f:id:minami_SC:20171222234350p:plain

まぁ、これはこれでどこかで用途あるんじゃないかな、と。
AutoFill絡みのプロパティは、今後もう少し追加していきたいなと思います。

GridEx.Row/Column Definitionで、行・列サイズのmin/max指定に対応

GridExでは、GridEx.RowDefinitionGridEx.ColumnDefinitionという添付プロパティで、Gridの行・列数を定義する機能を作ってました。
この定義では、行・列の各要素のサイズ指定はできたのですが、MinWidth/MinHeightなどの値を設定できませんでした。

行・列定義を行う文字列の文法に少し仕様を追加し、 以下のような形式で、複数の行や列のサイズをMin/Max条件含めてまとめて定義できるようにしました。 ge:GridEx.ColumnDefinition="50, *(50-200), 2*(80-), 2*(-300)"

こんな風にグリッド定義をすることができます。

    <Grid ge:GridEx.RowDefinition="*, *, *, *"
          ge:GridEx.ColumnDefinition="50, *(50-200), 2*(80-), 2*(-300)"
          ShowGridLines="True">
        <Button Grid.Row="1"
                Grid.Column="2"
                Margin="5"
                Content="Button" />
    </Grid>

これは、以下のようなコードと同じレイアウトとなります。

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50"/>
            <ColumnDefinition Width="*" MinWidth="50" MaxWidth="200"/>
            <ColumnDefinition Width="2*" MinWidth="80"/>
            <ColumnDefinition Width="2*" MaxWidth="300"/>
        </Grid.ColumnDefinitions>

地味ですが、これで一応GridExクラスの機能だけで、Gridの行・列 定義を一通りできるようになったかと思います。

WrapPanelExクラスの追加

WrapPanelのレイアウト調整を行うヘルパークラス、WrapPanelExを追加しました。
WrapPanelでWrapPanelEx.AdaptiveLayout="True"という添付プロパティを設定すると、通常のWrapPanelのように各要素を等間隔で配置するのではなく、指定幅以上でパネルを埋め尽くすようにレイアウトします。
UWP Community ToolkitのAdaptiveGridViewのようなレイアウト方法です。

ちょっと言葉で表現しにくいので、動きは↓のgif見てください。
f:id:minami_SC:20171222232502g:plain

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <WrapPanel ItemWidth="200" ItemHeight="70"
                   Orientation="Horizontal"
                   ge:WrapPanelEx.AdaptiveLayout="{Binding IsChecked, ElementName=chkIsAdaptive}">
            <Button />
            <Button />
            <Button />
            <Button />
            <Button />
            <Button />
            <Button />
        </WrapPanel>
        <CheckBox x:Name="chkIsAdaptive"
                  Grid.Row="1" Margin="5"
                  Content="WrapPanelEx.AdaptiveLayout"/>
    </Grid>

今回の更新内容は以上ですが、今後も面白そうなレイアウト手法があれば、色々と取り入れながら更新していきたいと思ってます。