LoginSignup
11
12

More than 1 year has passed since last update.

WPFのGridを動的に作成する

Last updated at Posted at 2021-09-23

はじめに

WPFのxamlの基本といえばGridですが、基本的にはデザイン時にrow, columnを設定して使用します。

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="1"/> // Grid.Row, ColumnでTextBlockの位置を指定して表示させる
    </Grid>

ただ、アプリを作っていると、たまにGridのRow, Columnを動的に設定したいというニーズがたまに出てくることがあります。

自分が開発していた時は、ListBoxのItemsPanelTemplateで動的にGridを設定したいということがありました。
カレンダーやスケジューラーのような月によって行数が変わるといった場合に、それをListBoxを使って実現しようと思うと、動的にGridを変更できるといいなということです。
以前 Drag&Dropの記事で、以下のような画面を紹介しましたが、この日にちごとのコマ割りで使用しています。
image.png
こんな感じでListBoxを作っています
見ていただけば、なんとなく構造がわかっていただけるのではと思います。

    <!--CustomGridを使用して、ListBoxItemを動的配置しています-->
    <ListBox Grid.Row="1"
            Visibility="{Binding CalendarVisitility.Value}"
            ItemsSource="{Binding CaseCalendarDatas}"
            Style="{StaticResource MaterialDesignCardsListBox}"
            ScrollViewer.CanContentScroll="False"
            dd:DragDrop.IsDropTarget="True">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <customgrid:CustomGrid GridColumnCount="{Binding GridColumnCount.Value}"
                                ColumnDefinitionWidth="0"
                                GridRowCount="{Binding GridRowCount.Value}"
                                RowDefinitionHeight="0"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem"
                BasedOn="{StaticResource MaterialDesignCardsListBoxItem}">
                <Setter Property="Margin" Value="5"/>
                <Setter Property="Grid.Row" Value="{Binding GridRowIndex}"/>
                <Setter Property="Grid.Column" Value="{Binding GridColumnIndex}"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <views:RegisterCaseControl DataContext="{Binding ControlViewModel}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

このxaml内でGridを継承したカスタムコントロール(CustomGrid)を使っています。
今回の記事は、このCustomGridのコードをご紹介させていただこうと思います。

カスタムGrid (AutoGrid)

Gridを継承して、AutoGridというカスタムコントロールを作成します。

public class AutoGrid : Grid

添付プロパティ

Columnの「幅」「数」
Rowの「高さ」「数」
 にあたる添付プロパティ(Dependency Property)を作成します。

        #region ColumnDefinitionWidth
        /// <summary>
        /// Row.Width
        ///  負数:固定高(50pixel)
        ///   0 :可変(1*)
        ///  正数:指定高
        /// </summary>
        public static readonly DependencyProperty ColumnDefinitionWidthProperty =
                                    DependencyProperty.Register("ColumnDefinitionWidth",
                                        typeof(int),
                                        typeof(AutoGrid),
                                        new PropertyMetadata(50));
        public int ColumnDefinitionWidth
        {
            get { return (int)GetValue(ColumnDefinitionWidthProperty); }
            set { SetValue(ColumnDefinitionWidthProperty, value); }
        }
        #endregion

        #region ColumnCount
        public static readonly DependencyProperty GridColumnCountProperty =
                                    DependencyProperty.RegisterAttached("GridColumnCount",
                                        typeof(int),
                                        typeof(AutoGrid),
                                        new PropertyMetadata(0, new PropertyChangedCallback(OnGridColumnCountChanged)));
        public int GridColumnCount
        {
            get { return (int)GetValue(GridColumnCountProperty); }
            set { SetValue(GridColumnCountProperty, value); }
        }

        #endregion

        #region RowDefinitionHeight
        /// <summary>
        /// Row.Height
        ///  負数:固定高(14pixel)
        ///   0 :可変(1*)
        ///  正数:指定高
        /// </summary>
        public static readonly DependencyProperty RowDefinitionHeightProperty =
                                    DependencyProperty.Register("RowDefinitionHeight",
                                        typeof(int),
                                        typeof(AutoGrid),
                                        new PropertyMetadata(-1));
        public int RowDefinitionHeight
        {
            get { return (int)GetValue(RowDefinitionHeightProperty); }
            set { SetValue(RowDefinitionHeightProperty, value); }
        }
        #endregion

        #region RowCount 
        public static readonly DependencyProperty GridRowCountProperty =
                                    DependencyProperty.RegisterAttached("GridRowCount",
                                        typeof(int),
                                        typeof(AutoGrid),
                                        new PropertyMetadata(1, new PropertyChangedCallback(OnGridRowCountChanged)));
        public int GridRowCount
        {
            get { return (int)GetValue(GridRowCountProperty); }
            set { SetValue(GridRowCountProperty, value); }
        }

        #endregion

PropertyChangedCallback

Row, Columnの数を設定した際のイベントを作成します。

OnGridRowCountChanged

Rowを指定された「数」で、指定された「高さ」で作成します

        /// <summary>
        /// GridRowCountで指定された数のRowDefinitionsを作成
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnGridRowCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is Grid) || (int)e.NewValue < 0)
                return;

            Grid grid = (Grid)d;

            grid.RowDefinitions.Clear();

            int heightProperty = (int)d.GetValue(RowDefinitionHeightProperty);

            for (int i = 0; i < (int)e.NewValue; i++)
            {

                RowDefinition rowDefinition = new RowDefinition();
                if (heightProperty < 0)
                {
                    rowDefinition.Height = new GridLength(14, GridUnitType.Pixel);
                }
                else if (heightProperty > 0)
                {
                    rowDefinition.Height = new GridLength(heightProperty, GridUnitType.Pixel);
                }
                else if (heightProperty == 0)
                {
                    rowDefinition.Height = new GridLength(1, GridUnitType.Star);
                }
                grid.RowDefinitions.Add(rowDefinition);
            }
        }

OnGridColumnCountChanged

Columnを指定された「数」で、指定された「幅」で作成します

        /// <summary>
        /// GridColumnCountで指定された数のColumnDefinitionsを作成
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnGridColumnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is Grid) || (int)e.NewValue < 0)
                return;

            Grid grid = (Grid)d;

            grid.ColumnDefinitions.Clear();

            int widthProperty = (int)d.GetValue(ColumnDefinitionWidthProperty);

            for (int i = 0; i < (int)e.NewValue; i++)
            {

                ColumnDefinition columnDefinition = new ColumnDefinition();
                if (widthProperty < 0)
                {
                    columnDefinition.Width = new GridLength(50, GridUnitType.Pixel);
                }
                else if (widthProperty > 0)
                {
                    columnDefinition.Width = new GridLength(widthProperty, GridUnitType.Pixel);
                }
                else if (widthProperty == 0)
                {
                    columnDefinition.Width = new GridLength(1, GridUnitType.Star);
                }
                grid.ColumnDefinitions.Add(columnDefinition);
            }
        }

実装

あとは、作成したカスタムコントロールを参照指定して、xaml内で以下のような感じで記述すればOKです。
.ValueがついているのはReactivePropertyを使用しているためです。

xmlns:customgrid="clr-namespace:..(参照先)..."

    <customgrid:CustomGrid GridColumnCount="{Binding GridColumnCount.Value}"
                        ColumnDefinitionWidth="0"
                        GridRowCount="{Binding GridRowCount.Value}"
                        RowDefinitionHeight="0"/>
11
12
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12