【WPF】ここまで出来る!DataGridで多段表示!(UserControl編)

当ページのリンクには広告が含まれています。

DataGridの多段表示ですが、【WPF】ここまで出来る!DataGridで多段表示!(DataGridTemplateColumn編)の記事ではXAMLのレイアウトのみで実現しました。

1つのセルに複数の項目を表示する場合、単純なものであればXAMLだけでも良いのですが、複雑なロジックが伴う場合や、もっと凝ったレイアウトにしたい場合、XAMLだけではコードが複雑になってしまいます。

そこで、今回はセルにユーザーコントロールを張り付けて多段表示を実現してみました。

ユーザーコントロールにするメリットは、そのセルに関するC#のコードが分離できるため、修正や機能追加がやりやすくなるという点です。

今回の記事が、みなさんの開発のヒントにでもなれば幸いです。

目次

UserControlを使ったDataGridの多段表示画面サンプル

今回は次の様なデザインになります。

商品データのセルにユーザーコントロールが張り付けられています。

ユーザーコントロールにすることで、例えば入力時に入力チェックを行ったり、ドロップダウンリストで入力候補を選ばせるといった処理が、メインのコードから切り離して開発できるようになります。

デメリットとしては、DataGridで選択した行の文字色が変わらない事です。

通常、DataGridは選択行の文字色が白になりますが、ユーザーコントロール側は独立しているので変化しません。

DataGridのデフォルトの選択色は比較的濃い青色なので、白文字でないと見難くなります。

フォーカスされたことをユーザーコントロール側に通知して、ユーザーコントロール側で文字色を白にするなどの方法が考えられますが、それも面倒なので、ここではDataGrid側の文字色を黒、選択色を薄い黄色に設定することで見難さを回避しています。

DataGridのレイアウト構造

前回の記事では、DataGridTemplateColumnに複数のコントロールを張り付けて多段表示を実現していましたが、今回はここにユーザーコントロールを張り付けます。

XAMLのコード

多段表示のセルをユーザーコントロールに替えたことで、XAMLのコードはかなりシンプルになりました。

9行目から始まる <Windows.Resource>のタグで、DataGridRowの背景色と文字色、選択セルの枠線の色を定義しています。

また、セルに使ったユーザーコントロールは通常のセルと同様、Binding に対応しています。

<Window x:Class="DataGridLayoutSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridLayoutSample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style TargetType="{x:Type DataGridRow}">
            <Style.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightYellow" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightYellow" />
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
            </Style.Resources>
        </Style>
    </Window.Resources>
    <Grid>
        <DataGrid x:Name="uxDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding}" Margin="10,36,10,10">
            <DataGrid.Columns>
                <DataGridTextColumn Header="日付" Width="100" Binding="{Binding Path=日付}">
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="TextBlock">
                            <Setter Property="VerticalAlignment" Value="Center"/>
                            <Setter Property="HorizontalAlignment" Value="Center"/>
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>

                <DataGridTemplateColumn Header="商品データ">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <local:CellUserControl Maker="{Binding メーカー}" ProductName="{Binding 商品名}" ProductCode="{Binding 商品コード}" Padding="0"></local:CellUserControl>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="uxShowData" Content="表示" HorizontalAlignment="Right" Height="21" Margin="0,8,17,0" VerticalAlignment="Top" Width="101" Click="uxShowData_Click"/>

    </Grid>
</Window>

C#のコード

C#側のコードは「【WPF】ここまで出来る!DataGridで多段表示!(DataGridTemplateColumn編)」とまったく同じです。

つまり、ユーザーコントロールを作成したあと、XAML側でユーザーコントロールにBindingするよう記述を変えるだけで済んでしまいます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data;

namespace DataGridLayoutSample
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void uxShowData_Click(object sender, RoutedEventArgs e)
        {
            uxDataGrid.DataContext = CreateDataTable();
        }

        private DataTable CreateDataTable()
        {
            var dt = new DataTable();
            dt.Columns.Add("日付");
            dt.Columns.Add("メーカー");
            dt.Columns.Add("商品コード");
            dt.Columns.Add("商品名");
            dt.Columns.Add("単価");
            dt.Columns.Add("数量");

            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/10/11", "キャノン", "4147C001", "EOS Ra", "150000", "3" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/10/12", "キャノン", "4147C012", "EOS R5", "160000", "6" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/11/15", "ニコン", "4960759127693", "COOLPIX S9700 ", "20000", "1" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/11/23", "ニコン", "4960759127716", "COOLPIX S6000", "20000", "4" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/11/09", "SONY", "S26347C001", "Cyber-shotHX400V", "123000", "8" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/12/01", "SONY", "S30447C004", "Cyber-shotWX700", "45000", "12" });
            dt.Rows.Add(dt.NewRow().ItemArray = new string[] { "2020/12/11", "富士フィルム", "FJ55505", "FUJIFILM X-S10", "120000", "3" });

            return dt;
        }

    }
}

ユーザーコントロールのXAML

ユーザーコントロールは次の様なレイアウトになっています。

単純にテキストボックス、コンボボックス、在庫区分の3つを張り付けただけであるため、XAMLもシンプルになっています。

<UserControl x:Class="DataGridLayoutSample.CellUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DataGridLayoutSample"
             mc:Ignorable="d" 
             d:DesignHeight="56" Width="524" Height="57">
   
    <Grid>
        <CheckBox Content="在庫区分" HorizontalAlignment="Left" Margin="425,6,0,0" VerticalAlignment="Top" Width="95" RenderTransformOrigin="0.979,1.2"/>
        <Label Content="商品名" HorizontalAlignment="Left" Margin="10,25,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.348,0.115"/>
        <TextBox x:Name="uxProductName" IsReadOnly="True" HorizontalAlignment="Left" Margin="58,29,0,0" TextWrapping="Wrap" Width="146" Height="19" VerticalAlignment="Top"/>
        <Label Content="メーカー" HorizontalAlignment="Left" Margin="10,2,0,0" VerticalAlignment="Top"/>
        <TextBox  IsReadOnly="True" x:Name="uxMaker"  HorizontalAlignment="Left" Height="19" Margin="58,6,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="146"/>
        <Label Content="商品コード" HorizontalAlignment="Left" Margin="209,2,0,0" VerticalAlignment="Top"/>
        <TextBox  IsReadOnly="True" x:Name="uxProductCode" HorizontalAlignment="Left" Height="19" Margin="273,6,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="130"/>
        <Label Content="支店名" HorizontalAlignment="Left" Margin="222,25,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.348,0.115"/>
        <ComboBox Text=""  HorizontalAlignment="Left"  Margin="273,29,0,0" Width="130" Height="22" VerticalAlignment="Top">
            <ComboBoxItem>北海道支店</ComboBoxItem>
            <ComboBoxItem>東京支店</ComboBoxItem>
            <ComboBoxItem>大阪支店</ComboBoxItem>
            <ComboBoxItem>愛知支店</ComboBoxItem>
            <ComboBoxItem>京都支店</ComboBoxItem>
            <ComboBoxItem>兵庫支店</ComboBoxItem>
            <ComboBoxItem>四国支店</ComboBoxItem>
            <ComboBoxItem>九州支店</ComboBoxItem>
            <ComboBoxItem>沖縄支店</ComboBoxItem>
        </ComboBox>
    </Grid>
</UserControl>

ユーザーコントロールのC#コード

ユーザーコントロールのC#コードは次の通りです。

特に中身は無いのですが、バインド(Binding) に対応するためのソースコードが大半を占めてしまいました。

普通のプロパティ(public string Maker {get;set;})のように記述するだけでは、Binding出来ません。

Binding に対応するためのプロパティには特別な書き方があり、これを「依存関係プロパティ」と呼んでいます。

今回はメーカー名、商品名、商品コードの3つが Binding できるようになっています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DataGridLayoutSample
{
    /// <summary>
    /// CellUserControl.xaml の相互作用ロジック
    /// </summary>
    public partial class CellUserControl : UserControl
    {
        //===================================================================================
        //メーカーの依存関係プロパティの定義
        //===================================================================================
        public static readonly DependencyProperty MakerProperty =
        DependencyProperty.Register("Maker",
                                            typeof(string),
                                            typeof(CellUserControl),
                                            new FrameworkPropertyMetadata("Maker", new PropertyChangedCallback(OnMakerChanged)));
        public string Maker   { get { return (string)GetValue(MakerProperty); } set { SetValue(MakerProperty, value); }  }
        private static void OnMakerChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = (CellUserControl)obj;
            control.uxMaker.Text = (obj != null) ? control.Maker : control.uxMaker.Text;
        }

        //===================================================================================
        //商品コードの依存関係プロパティの定義
        //===================================================================================
        public static readonly DependencyProperty ProductCodeProperty =
               DependencyProperty.Register("ProductCode",
                                            typeof(string),
                                            typeof(CellUserControl),
                                            new FrameworkPropertyMetadata("ProductCode", new PropertyChangedCallback(OnProductCodeChanged)));
        public string ProductCode { get { return (string)GetValue(ProductCodeProperty); } set { SetValue(ProductCodeProperty, value); } }
        private static void OnProductCodeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = (CellUserControl)obj;
            control.uxProductCode.Text = (obj != null) ? control.ProductCode : control.uxProductCode.Text;
        }

        //===================================================================================
        //商品名の依存関係プロパティの定義
        //===================================================================================
        public static readonly DependencyProperty ProductNameProperty =
               DependencyProperty.Register("ProductName",
                                            typeof(string),
                                            typeof(CellUserControl),
                                            new FrameworkPropertyMetadata("ProductName", new PropertyChangedCallback(OnProductNameChanged)));
        public string ProductName { get { return (string)GetValue(ProductNameProperty); } set { SetValue(ProductNameProperty, value); } }
        private static void OnProductNameChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = (CellUserControl)obj;        
            control.uxProductName.Text = (obj != null) ? control.ProductName : control.uxProductName.Text;
        }


        /// <summary>
        /// コンストラクタ
        /// </summary>
        public CellUserControl()
        {
            InitializeComponent();

        }
    }
}

まとめ

以上でユーザーコントロールを使ったDataGridの多段表示の解説は終了です。

<DataGridTemplateColumn> タグに複数のコントロールを記述できますが、今回はそこにユーザーコントロールを記述することで多段表示を実現しました。

ユーザーコントロールにすることで、複雑なUI部分をメイン処理から独立させられるため、機能追加や改修などが行いやすくなるというメリットがあります。

また、通常のセルと同様、ユーザーコントロールを Binding に対応させることが出来ますが、その場合は通常のプロパティではなく、「依存関係プロパティ」という特別な記述が必要となります。

「依存関係プロパティ」については、「【WPF】依存関係プロパティでユーザーコントロールをバインド対応する!」の記事で詳しく触れていますので、興味のある方がご覧ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次