#はじめに
WPF標準の DataGrid は、マウスで列幅や順番の変更ができます。
この変更値を、保存したりリセットする、ユーザ コントロールを作成しました。
#環境
- .NET Framework 4.6.1
※デザインテーマは、Material Design In XAML Toolkitを使っています。
(導入例)
#準備
今回はテストなので、設定の保存には簡便な Visual Studio の ユーザ設定を使います。
プロジェクトの「設定」に、保存するキー名をstringで登録しておきます。
"dataGrid1Columns"という名前を使います。
実際の現場では、このVSのユーザ設定を使ったことは無いです。
設定ファイルの保存先が、C:\Users\(UserName)\AppData\Local\(CompanyName)\(appdomainname)_(eid)_(hash)\(version)\user.config
などと凶悪なうえ、任意の場所にするには難しいので。
#コントロールのコード
Material Design Toolkit の PopupBox を作り、子要素に
- 現在の列状態を保存
- 列の設定をリセット
というメニューを作ります。
それぞれのClickイベントと、コントロールのLoadedイベントを定義します。
<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 コードに、依存プロパティとイベントの内容を記述します。
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を保存します。
#使い方
<!--
コントロールへの名前空間を定義しておきます
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を作るのが大変だけど)