LoginSignup
8

More than 5 years have passed since last update.

[WPF] DataGridの列幅を保存・リセットするコントロール

Last updated at Posted at 2017-04-25

はじめに

WPF標準の DataGrid は、マウスで列幅や順番の変更ができます。
この変更値を、保存したりリセットする、ユーザ コントロールを作成しました。

GridColumnSetting.gif

環境

  • .NET Framework 4.6.1

※デザインテーマは、Material Design In XAML Toolkitを使っています。
導入例

準備

今回はテストなので、設定の保存には簡便な Visual Studio の ユーザ設定を使います。
プロジェクトの「設定」に、保存するキー名をstringで登録しておきます。

image

"dataGrid1Columns"という名前を使います。

実際の現場では、このVSのユーザ設定を使ったことは無いです。
設定ファイルの保存先が、C:\Users\(UserName)\AppData\Local\(CompanyName)\(appdomainname)_(eid)_(hash)\(version)\user.config などと凶悪なうえ、任意の場所にするには難しいので。

コントロールのコード

Material Design Toolkit の PopupBox を作り、子要素に

  • 現在の列状態を保存
  • 列の設定をリセット

というメニューを作ります。
それぞれのClickイベントと、コントロールのLoadedイベントを定義します。

image

GridColumnSetting.xaml
<UserControl x:Class="WpfApplication1.Controls.GridColumnSetting"
             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:materialDesign="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="30"
             Loaded="GridColumnSetting_Loaded">
    <UserControl.Resources>
        <materialDesign:PackIcon x:Shared="False" x:Key="GridSettingPopupBoxIcon" Kind="TableLarge" Width="24" Height="24"
                                 Foreground="{DynamicResource AccentColorBrush}"/>
    </UserControl.Resources>

    <materialDesign:PopupBox ToggleContent="{StaticResource GridSettingPopupBoxIcon}"
                             ToolTip="グリッド列幅設定"
                             ToolTipService.VerticalOffset="-70"
                             VerticalAlignment="Bottom"
                             PlacementMode="BottomAndAlignRightEdges"
                             StaysOpen="False">
        <StackPanel>
            <Button Click="ButtonSave_Click">
                <StackPanel Orientation="Horizontal">
                    <materialDesign:PackIcon Kind="ContentSave" Width="24" Height="24"/>
                    <TextBlock Text="現在の列状態を保存"
                               Margin="8 0 0 0"/>
                </StackPanel>
            </Button>
            <Separator/>
            <Button Click="ButtonReset_Click">
                <StackPanel Orientation="Horizontal">
                    <materialDesign:PackIcon Kind="UndoVariant" Width="24" Height="24"/>
                    <TextBlock Text="列の設定をリセット"
                               Margin="8 0 0 0"/>
                </StackPanel>
            </Button>
        </StackPanel>
    </materialDesign:PopupBox>

</UserControl>

partial コードに、依存プロパティとイベントの内容を記述します。

GridColumnSetting.xaml.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1.Controls
{
    /// <summary>
    /// DataGrid 列設定ボタンコントロール
    /// </summary>
    public partial class GridColumnSetting : UserControl
    {
        #region Dependency Properties

        /// <summary>依存プロパティ定義 - 対象のDataGrid</summary>
        /// <summary></summary>
        public static readonly DependencyProperty TargetGridProperty =
            DependencyProperty.Register("TargetGrid",
                typeof(DataGrid),
                typeof(GridColumnSetting),
                new FrameworkPropertyMetadata(null));

        /// <summary>対象のDataGrid</summary>
        public DataGrid TargetGrid
        {
            get { return (DataGrid)GetValue(TargetGridProperty); }
            set { SetValue(TargetGridProperty, value); }
        }

        /// <summary>依存プロパティ定義 - 設定ファイルのキー</summary>
        public static readonly DependencyProperty SettingsKeyProperty =
            DependencyProperty.Register("SettingsKey",
                typeof(string),
                typeof(GridColumnSetting),
                new FrameworkPropertyMetadata(null));

        /// <summary>設定ファイルのキー</summary>
        public string SettingsKey
        {
            get { return (string)GetValue(SettingsKeyProperty); }
            set { SetValue(SettingsKeyProperty, value); }
        }
        #endregion

        // 元(デザイン状態)の列設定
        private List<DataGridColumnInfo> _defaultSettings;

        /// <summary>
        /// constractor
        /// </summary>
        public GridColumnSetting()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Loaded event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GridColumnSetting_Loaded(object sender, RoutedEventArgs e)
        {
            // デフォルトの並び順・幅を確保する
            _defaultSettings = this.TargetGrid.Columns
                                    .Select((x, index) => new DataGridColumnInfo()
                                    {
                                        ColumnIndex = index,
                                        DisplayIndex = x.DisplayIndex,
                                        Width = x.Width.Value,
                                    })
                                    .ToList();

            // ユーザ設定から保存値を取得し、DataGridに反映する
            var saveValue = Properties.Settings.Default[SettingsKey].ToString();
            if (!string.IsNullOrEmpty(saveValue))
            {
                var settings = xmlDeserialize<List<DataGridColumnInfo>>(saveValue);
                setColumns(settings);
            }
        }

        /// <summary>
        /// 保存ボタン押下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonSave_Click(object sender, RoutedEventArgs e)
        {
            // 列情報コレクションに現在のDisplayIndexとWidthを確保
            var newSettings = this.TargetGrid.Columns
                                    .Select((x, index) => new DataGridColumnInfo()
                                    {
                                        ColumnIndex = index,
                                        DisplayIndex = x.DisplayIndex,
                                        Width = x.Width.Value,
                                    })
                                    .ToList();

            // ユーザ設定へ保存
            Properties.Settings.Default[SettingsKey] = xmlSerialize<List<DataGridColumnInfo>>(newSettings);
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// リセットボタン押下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonReset_Click(object sender, RoutedEventArgs e)
        {
            // 最初にとっておいた_defaultSettingsで設定する
            setColumns(_defaultSettings);

            // ユーザ設定へはnullを保存する
            Properties.Settings.Default[SettingsKey] = null;
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// DataGrid列設定
        /// </summary>
        /// <param name="settings"></param>
        private void setColumns(List<DataGridColumnInfo> settings)
        {
            if (settings != null && settings.Count > 0)
            {
                for (var i = 0; i < this.TargetGrid.Columns.Count; i++)
                {
                    var setting = settings[i];
                    this.TargetGrid.Columns[i].DisplayIndex = setting.DisplayIndex;
                    this.TargetGrid.Columns[i].Width = new DataGridLength(setting.Width);
                }
            }
        }

        #region XAMLシリアライズ(テスト用)
        /// <summary>
        /// XMLシリアライズ
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        private string xmlSerialize<T>(object obj)
        {
            var sb = new StringBuilder();
            using (var w = new System.IO.StringWriter(sb))
            {
                var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                serializer.Serialize(w, obj);
            }
            return sb.ToString();
        }

        /// <summary>
        /// XMLデシリアライズ
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml"></param>
        /// <returns></returns>
        private T xmlDeserialize<T>(string xml)
        {
            T result;
            using (var tr = new System.IO.StringReader(xml))
            {
                var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                result = (T)serializer.Deserialize(tr);
            }
            return result;
        }
        #endregion

        /// <summary>
        /// 列情報クラス
        /// </summary>
        public class DataGridColumnInfo
        {
            /// <summary>列index</summary>
            public int ColumnIndex { get; set; }
            /// <summary>表示index</summary>
            public int DisplayIndex { get; set; }
            /// <summary>横幅</summary>
            public double Width { get; set; }
        }
    }
}

コードの説明

依存プロパティ

操作対象となる DataGrid と、設定保存のキー名を受け取るためのプロパティ(DependencyProperty)を定義しています。

Loadedイベント

XAMLに定義されている列順と列幅を取得し、メンバ変数に確保します。
この値が、デフォルトに戻すときの値になります。

ユーザ設定から、保存されている設定値を読み取ります。
設定値は、DataGridColumnInfoクラスのコレクション(List<DataGridColumnInfo>)をXMLシリアライズした文字列になっています。
こいつをオブジェクトに戻した後、DataGrid の列プロパティを変更していきます。

保存ボタン押下イベント

表示中の DataGrid の列情報を List<DataGridColumnInfo> に格納し、XMLシリアライズした値を、ユーザ設定に保存します。

リセットボタン押下イベント

Loadedイベントで確保したデフォルト値で DataGrid の列を更新します。
ユーザ設定には、nullを保存します。

使い方

MainWindow.xaml
<!--
コントロールへの名前空間を定義しておきます
xmlns:controls="clr-namespace:WpfApplication1.Controls"
-->

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <TextBlock Text="TextBox"/>
            <TextBox MinWidth="120"/>
            <TextBlock Text="ComboBox"/>
            <ComboBox MinWidth="120"/>
            <Button Content="Button" Width="100"/>
        </StackPanel>

        <!-- 列設定コントロール -->
        <Grid Grid.Row="1" HorizontalAlignment="Right" Margin="0 0 10 0">
            <controls:GridColumnSetting TargetGrid="{Binding ElementName=dataGrid1}"
                                        SettingsKey="dataGrid1Columns"/>
        </Grid>

        <!-- 設定対象の DataGrid -->
        <DataGrid x:Name="dataGrid1"
                  Grid.Row="2"
                  Margin="10">
            <DataGrid.Columns>
                <DataGridTextColumn Header="氏名" Binding="{Binding Name}"
                                    Width="100"/>
                <DataGridTextColumn Header="ふりがな" Binding="{Binding Ruby}"
                                    Width="100"/>
                <DataGridTextColumn Header="性別" Binding="{Binding Gender}"
                                    Width="40"/>
                <DataGridTextColumn Header="誕生日" Binding="{Binding Birthday, StringFormat={}{0:yyyy/MM/dd}}"
                                    Width="80"/>
                <DataGridTextColumn Header="カレーの食べ方" Binding="{Binding Curry}"
                                    Width="200"/>
            </DataGrid.Columns>
        </DataGrid>

    </Grid>

作成したコントロール「GridColumnSetting」には、TargetGrid(対象DataGrid)と、SettingsKey(設定キー名)のプロパティを指定します。

これだけで、DataGrid の列設定の保存・リセットが動くようになります。

まとめとか

この例では、列幅と順序だけの操作に留まっていますが、列の非表示や、固定列の設定なども、簡単に行えると思います。
(そのような設定の場合、コントロールのUIを作るのが大変だけど)

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
8