C# PropertyGridの使い方完全ガイド|基本表示からカスタマイズ・属性設定までサンプルで解説

はじめに

C#でWindowsフォームアプリケーションを作っていると、設定値やオブジェクトのプロパティを一覧表示し、その場で編集したい場面があります。たとえば、アプリの設定画面、デバッグ用の内部状態表示、開発者向けツール、簡易的なプロパティ編集画面などです。

そのような場面で便利なのが、WinFormsのPropertyGridです。PropertyGridを使うと、対象オブジェクトをSelectedObjectに設定するだけで、publicプロパティを自動的に一覧表示できます。さらに、DisplayNameDescriptionCategoryBrowsableReadOnlyなどの属性を使えば、表示名、説明文、カテゴリ分け、非表示、読み取り専用なども簡単に制御できます。

この記事では、C#のPropertyGridの基本的な使い方から、属性による表示カスタマイズ、TypeConverterUITypeEditorを使った編集機能の拡張、イベント処理、実践的な設定画面の作成例まで、サンプルコード付きで解説します。

1. C#のPropertyGridとは?できることと利用シーン

1-1. PropertyGridの概要と役割

PropertyGridは、C#のWinFormsで利用できるコントロールのひとつです。指定したオブジェクトのプロパティを一覧表示し、値を編集できる画面を簡単に作成できます。

代表的な使い方は、次のようにSelectedObjectへ表示したいオブジェクトを設定する方法です。

C#
propertyGrid1.SelectedObject = targetObject;

これだけで、targetObjectが持つpublicプロパティがPropertyGrid上に表示されます。Microsoftの公式ドキュメントでも、PropertyGridは選択されたオブジェクトのプロパティを参照・編集するためのWinFormsコントロールとして説明されています。Microsoft Learn

PropertyGridはVisual Studioのプロパティウィンドウに近い見た目と操作感を持っています。そのため、アプリケーション内に「オブジェクトの設定値を編集する画面」を素早く実装したい場合に非常に便利です。

1-2. WinFormsでPropertyGridがよく使われる場面

PropertyGridは、特に次のような場面でよく使われます。

・アプリケーション設定画面
・開発者向けツールのプロパティ編集画面
・デバッグ用のオブジェクト状態確認画面
・エディタ系アプリの部品設定画面
・帳票、図形、部品、コントロールなどの属性編集
・プラグインや外部設定の編集画面

たとえば、アプリケーション設定を表すクラスを用意してPropertyGridに渡せば、設定項目の一覧表示と編集画面を短いコードで実装できます。

C#
public class AppSettings
{
public string UserName { get; set; } = "Guest";
public int FontSize { get; set; } = 12;
public bool DarkMode { get; set; } = false;
}
C#
var settings = new AppSettings();
propertyGrid1.SelectedObject = settings;

これだけで、UserNameFontSizeDarkModeを一覧表示し、画面上で編集できます。

1-3. PropertyGridで表示できるプロパティの種類

PropertyGridでは、さまざまな型のプロパティを表示できます。

C#
public class SampleSettings
{
public string Title { get; set; } = "Sample";
public int Width { get; set; } = 800;
public bool Enabled { get; set; } = true;
public DisplayMode Mode { get; set; } = DisplayMode.Normal;
}

public enum DisplayMode
{
Normal,
Compact,
FullScreen
}

このようなクラスをPropertyGridに設定すると、stringはテキストボックス、intは数値入力、boolTrue/Falseの選択、enumはドロップダウンのような形で編集できます。

また、ネストしたオブジェクト、配列、コレクション、色、フォント、ファイルパスなども、型や属性、コンバーター、エディターを組み合わせることで編集しやすくできます。

1-4. PropertyGridを使うメリット・注意点

PropertyGridを使うメリットは、画面作成の手間を大きく減らせることです。通常、設定画面を作る場合は、ラベル、テキストボックス、チェックボックス、コンボボックスなどを個別に配置し、それぞれの値を読み書きする処理を書く必要があります。

一方、PropertyGridでは、対象クラスを設計してSelectedObjectに設定するだけで、プロパティ一覧と編集機能をまとめて実装できます。

ただし、注意点もあります。

PropertyGridは便利ですが、一般ユーザー向けの洗練された設定画面を作る用途には必ずしも向いていません。表示形式はやや開発者向けで、複雑な入力補助や独自デザインを多用する画面には不向きです。

そのため、PropertyGridは「開発者向け」「管理者向け」「設定項目が多い画面」「短期間で作りたい内部ツール」に向いています。一般ユーザーが日常的に使う画面では、専用のUIを作った方がわかりやすい場合があります。

2. PropertyGridを表示する基本手順

2-1. WindowsフォームにPropertyGridを配置する方法

WinFormsアプリケーションでPropertyGridを使うには、フォームにPropertyGridコントロールを配置します。

Visual Studioのデザイナーを使う場合は、ツールボックスからPropertyGridを選び、フォーム上にドラッグ&ドロップします。配置後、名前をpropertyGrid1などにしておきます。

コードで配置する場合は、次のように記述します。

C#
using System;
using System.Windows.Forms;

public class MainForm : Form
{
private PropertyGrid propertyGrid1;

public MainForm()
{
propertyGrid1 = new PropertyGrid();
propertyGrid1.Dock = DockStyle.Fill;

Controls.Add(propertyGrid1);
}
}

フォーム全体に表示したい場合は、Dock = DockStyle.Fillを指定すると便利です。

2-2. SelectedObjectにオブジェクトを設定する基本サンプル

PropertyGridで最も重要なのがSelectedObjectプロパティです。ここに表示したいオブジェクトを設定します。

C#
using System;
using System.Windows.Forms;

public class MainForm : Form
{
private PropertyGrid propertyGrid1;
private AppSettings settings;

public MainForm()
{
propertyGrid1 = new PropertyGrid
{
Dock = DockStyle.Fill
};

Controls.Add(propertyGrid1);

settings = new AppSettings
{
UserName = "Taro",
FontSize = 14,
DarkMode = true
};

propertyGrid1.SelectedObject = settings;
}
}

public class AppSettings
{
public string UserName { get; set; }
public int FontSize { get; set; }
public bool DarkMode { get; set; }
}

このコードを実行すると、UserNameFontSizeDarkModePropertyGridに表示され、値を編集できます。

2-3. クラスのプロパティをPropertyGridに表示する仕組み

PropertyGridは、対象オブジェクトのプロパティ情報を取得し、一覧として表示します。基本的には、publicなプロパティが表示対象になります。

C#
public class Person
{
public string Name { get; set; } = "山田太郎";
public int Age { get; set; } = 30;

private string InternalCode { get; set; } = "ABC";
}

この場合、NameAgeは表示されますが、privateInternalCodeは通常表示されません。

また、フィールドは基本的に表示対象ではありません。

C#
public class BadExample
{
public string Name = "山田太郎"; // フィールドなのでPropertyGridの標準表示対象にはしない
}

PropertyGridに表示したい値は、フィールドではなくプロパティとして定義するのが基本です。

C#
public class GoodExample
{
public string Name { get; set; } = "山田太郎";
}

2-4. 実行時に表示対象オブジェクトを切り替える方法

SelectedObjectを変更すれば、実行中に表示対象のオブジェクトを切り替えられます。

C#
private AppSettings appSettings = new AppSettings();
private UserSettings userSettings = new UserSettings();

private void ShowAppSettingsButton_Click(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = appSettings;
}

private void ShowUserSettingsButton_Click(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = userSettings;
}

複数のオブジェクトを切り替えて編集したい場合は、リストボックスやツリービューと組み合わせると便利です。

C#
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = listBox1.SelectedItem;
}

オブジェクトを切り替えた後、表示を明示的に更新したい場合はRefreshを呼び出します。

C#
propertyGrid1.SelectedObject = selectedObject;
propertyGrid1.Refresh();

PropertyGridでは、対象オブジェクトの値をコード側で変更しても、画面表示がすぐに更新されない場合があります。その場合はRefreshなどで再表示します。公式ドキュメントでも、実行時にコードで変更された値は、操作が行われるまで表示に反映されない場合があることが説明されています。Microsoft Learn

3. PropertyGridに表示するクラスの作り方

3-1. publicプロパティを表示する基本ルール

PropertyGridに表示するクラスでは、編集したい項目をpublicプロパティとして定義します。

C#
public class ProductSettings
{
public string ProductName { get; set; } = "サンプル商品";
public int Price { get; set; } = 1000;
public bool IsPublished { get; set; } = true;
}

このクラスをPropertyGridに設定します。

C#
propertyGrid1.SelectedObject = new ProductSettings();

すると、ProductNamePriceIsPublishedが表示されます。

表示名を日本語にしたい場合は、後述するDisplayName属性を使います。

3-2. get/setの有無による表示・編集可否の違い

PropertyGridでは、プロパティのアクセサによって表示や編集の可否が変わります。

C#
public class AccessorSample
{
public string EditableName { get; set; } = "編集可能";

public string ReadOnlyName { get; } = "読み取り専用";

public string PrivateSetName { get; private set; } = "外部からは変更不可";
}

getsetの両方がpublicであれば、通常は表示も編集もできます。

C#
public string EditableName { get; set; }

getのみの場合は、値の表示はできても編集できない扱いになります。

C#
public string ReadOnlyName { get; }

setがprivateの場合も、外部から値を設定できないため、編集不可として扱われることがあります。

C#
public string PrivateSetName { get; private set; }

明示的に編集不可にしたい場合は、ReadOnly属性を使う方が意図が伝わりやすくなります。

C#
[ReadOnly(true)]
public string Version { get; set; } = "1.0.0";

3-3. int・string・bool・enumなど基本型の表示例

基本型は、特別な実装をしなくてもPropertyGrid上で編集できます。

C#
public class BasicTypeSettings
{
public string Title { get; set; } = "タイトル";
public int Count { get; set; } = 10;
public double Opacity { get; set; } = 0.8;
public bool Enabled { get; set; } = true;
public WindowMode Mode { get; set; } = WindowMode.Normal;
}

public enum WindowMode
{
Normal,
Minimized,
Maximized
}

enumは選択肢として表示されるため、ユーザーに固定値から選ばせたい場合に便利です。

C#
public WindowMode Mode { get; set; } = WindowMode.Normal;

日本語の選択肢にしたい場合は、enumの表示変換をTypeConverterでカスタマイズする方法があります。

3-4. ネストしたクラスや複合オブジェクトを表示する方法

ネストしたクラスを表示する場合、そのままではオブジェクト名だけが表示され、内部のプロパティが展開されないことがあります。

C#
public class AppSettings
{
public DatabaseSettings Database { get; set; } = new DatabaseSettings();
}

public class DatabaseSettings
{
public string Host { get; set; } = "localhost";
public int Port { get; set; } = 5432;
}

このような複合オブジェクトを展開表示したい場合は、ExpandableObjectConverterを使います。

C#
using System.ComponentModel;

[TypeConverter(typeof(ExpandableObjectConverter))]
public class DatabaseSettings
{
public string Host { get; set; } = "localhost";
public int Port { get; set; } = 5432;

public override string ToString()
{
return $"{Host}:{Port}";
}
}

ExpandableObjectConverterは、PropertyGridでオブジェクトの内部プロパティを展開表示したい場合によく使われるTypeConverterです。公式ドキュメントでも、プロパティグリッドで型を展開可能にする用途が説明されています。Microsoft Learn

4. 属性を使ったPropertyGridの表示カスタマイズ

4-1. DisplayName属性でプロパティ名を変更する

DisplayName属性を使うと、PropertyGridに表示されるプロパティ名を変更できます。

C#
using System.ComponentModel;

public class UserSettings
{
[DisplayName("ユーザー名")]
public string UserName { get; set; } = "Taro";

[DisplayName("フォントサイズ")]
public int FontSize { get; set; } = 12;
}

プロパティ名は英語のまま保ち、画面表示だけ日本語にしたい場合に便利です。

C#
[DisplayName("ダークモードを使用する")]
public bool DarkMode { get; set; } = false;

コード上の名前とUI上の名前を分けられるため、保守性と見やすさを両立できます。

4-2. Description属性で説明文を表示する

Description属性を使うと、プロパティの説明文を設定できます。

C#
public class UserSettings
{
[DisplayName("フォントサイズ")]
[Description("画面に表示する文字の大きさを指定します。")]
public int FontSize { get; set; } = 12;
}

PropertyGridの説明エリアが表示されている場合、項目を選択すると説明文が表示されます。

説明文を表示するには、PropertyGrid側のHelpVisibletrueになっている必要があります。

C#
propertyGrid1.HelpVisible = true;

Descriptionは、利用者が各項目の意味を理解しやすくするために非常に重要です。特に設定項目が多い画面では、説明文を丁寧に書いておくと操作ミスを減らせます。

4-3. Category属性で項目をグループ分けする

Category属性を使うと、プロパティをカテゴリごとにグループ化できます。

C#
public class AppSettings
{
[Category("表示")]
[DisplayName("テーマ")]
public string Theme { get; set; } = "Light";

[Category("表示")]
[DisplayName("フォントサイズ")]
public int FontSize { get; set; } = 12;

[Category("通信")]
[DisplayName("サーバーURL")]
public string ServerUrl { get; set; } = "https://example.com";

[Category("通信")]
[DisplayName("タイムアウト秒数")]
public int TimeoutSeconds { get; set; } = 30;
}

カテゴリ表示を有効にするには、PropertySortCategorizedまたはCategorizedAlphabeticalにします。

C#
propertyGrid1.PropertySort = PropertySort.Categorized;

カテゴリ分けを行うと、設定項目が多い場合でも見やすく整理できます。

4-4. Browsable属性で特定のプロパティを非表示にする

Browsable属性を使うと、PropertyGridに表示したくないプロパティを非表示にできます。

C#
public class AppSettings
{
[DisplayName("ユーザー名")]
public string UserName { get; set; } = "Taro";

[Browsable(false)]
public string InternalToken { get; set; } = "secret";
}

InternalTokenはpublicプロパティですが、[Browsable(false)]を付けているためPropertyGridには表示されません。

内部処理用の値、ユーザーに変更されたくない値、セキュリティ上見せたくない値などにはBrowsable(false)を指定します。

4-5. ReadOnly属性で編集不可にする

ReadOnly属性を使うと、プロパティを表示しつつ編集不可にできます。

C#
public class AppInfo
{
[DisplayName("アプリ名")]
public string AppName { get; set; } = "Sample App";

[ReadOnly(true)]
[DisplayName("バージョン")]
public string Version { get; set; } = "1.0.0";
}

Versionは表示されますが、PropertyGrid上では編集できません。

「ユーザーには見せたいが変更はさせたくない」という項目に向いています。

4-6. DefaultValue属性で既定値を指定する

DefaultValue属性を使うと、プロパティの既定値をメタデータとして指定できます。

C#
using System.ComponentModel;

public class AppSettings
{
[DefaultValue(12)]
[DisplayName("フォントサイズ")]
public int FontSize { get; set; } = 12;

[DefaultValue(false)]
[DisplayName("ダークモード")]
public bool DarkMode { get; set; } = false;
}

注意点として、DefaultValue属性はプロパティに自動で値を代入するものではありません。初期値はコンストラクタやプロパティ初期化子で設定する必要があります。

C#
public int FontSize { get; set; } = 12;

DefaultValueは、デザイナーやシリアライズ、リセット処理などで「この値が既定値である」と判断するための情報として使われます。

5. PropertyGridの編集機能をカスタマイズする

5-1. enumをドロップダウンで選択させる方法

enum型のプロパティは、PropertyGrid上で選択肢として表示されます。

C#
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}

public class LogSettings
{
[DisplayName("ログレベル")]
public LogLevel Level { get; set; } = LogLevel.Info;
}

この場合、Levelを選択すると、DebugInfoWarningErrorから選べます。

enumは、入力値を限定したい場合に非常に便利です。文字列で自由入力させるよりも、入力ミスを防ぎやすくなります。

5-2. TypeConverterで表示文字列や入力値を変換する

TypeConverterを使うと、値の表示方法や文字列からの変換方法をカスタマイズできます。TypeConverterは、値を別の型へ変換したり、標準値やサブプロパティを提供したりするための仕組みです。Microsoft Learn

たとえば、独自型を文字列として表示したい場合を考えます。

C#
using System;
using System.ComponentModel;
using System.Globalization;

[TypeConverter(typeof(SizeTextConverter))]
public class SizeText
{
public int Width { get; set; }
public int Height { get; set; }

public override string ToString()
{
return $"{Width} x {Height}";
}
}

public class SizeTextConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}

return base.CanConvertTo(context, destinationType);
}

public override object ConvertTo(
ITypeDescriptorContext context,
CultureInfo culture,
object value,
Type destinationType)
{
if (destinationType == typeof(string) && value is SizeText size)
{
return $"{size.Width} x {size.Height}";
}

return base.ConvertTo(context, culture, value, destinationType);
}
}

このようにTypeConverterを使うと、PropertyGrid上での見え方を制御できます。

5-3. ExpandableObjectConverterでオブジェクトを展開表示する

複合オブジェクトを展開して編集したい場合は、ExpandableObjectConverterを使います。

C#
using System.ComponentModel;

public class AppSettings
{
[DisplayName("ウィンドウ設定")]
public WindowSettings Window { get; set; } = new WindowSettings();
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public class WindowSettings
{
[DisplayName("幅")]
public int Width { get; set; } = 800;

[DisplayName("高さ")]
public int Height { get; set; } = 600;

[DisplayName("最大化")]
public bool Maximized { get; set; } = false;

public override string ToString()
{
return $"{Width} x {Height}";
}
}

これにより、Windowプロパティを展開して、WidthHeightMaximizedを個別に編集できます。

ToStringをオーバーライドしておくと、折りたたまれているときの表示もわかりやすくなります。

5-4. UITypeEditorで独自の編集画面を表示する

UITypeEditorを使うと、PropertyGrid上で独自の編集UIを表示できます。公式ドキュメントでも、UITypeEditorは値を表示・編集するためのユーザーインターフェイスを提供する値エディターの基底クラスとして説明されています。Microsoft Learn

たとえば、文字列を専用ダイアログで編集したい場合は、次のようなイメージです。

C#
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;

public class MessageSettings
{
[Editor(typeof(MultilineTextEditor), typeof(UITypeEditor))]
[DisplayName("メッセージ")]
public string Message { get; set; } = "Hello";
}

public class MultilineTextEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}

public override object EditValue(
ITypeDescriptorContext context,
IServiceProvider provider,
object value)
{
string text = value as string ?? string.Empty;

using (var form = new Form())
using (var textBox = new TextBox())
using (var okButton = new Button())
{
form.Text = "メッセージ編集";
form.Width = 400;
form.Height = 300;

textBox.Multiline = true;
textBox.Dock = DockStyle.Fill;
textBox.Text = text;

okButton.Text = "OK";
okButton.Dock = DockStyle.Bottom;
okButton.DialogResult = DialogResult.OK;

form.Controls.Add(textBox);
form.Controls.Add(okButton);
form.AcceptButton = okButton;

if (form.ShowDialog() == DialogResult.OK)
{
return textBox.Text;
}
}

return value;
}
}

GetEditStyleUITypeEditorEditStyle.Modalを返すと、プロパティ欄に「...」ボタンが表示され、クリック時にモーダルダイアログを開くような編集ができます。UITypeEditorEditStyleには、対話UIなしのNone、ダイアログ系のModal、ドロップダウン系のDropDownなどがあります。Microsoft Learn

5-5. コレクションや配列を編集する方法

配列やコレクションをプロパティにすると、PropertyGrid上でコレクションエディターを使って編集できる場合があります。

C#
using System.Collections.Generic;
using System.ComponentModel;

public class MenuSettings
{
[DisplayName("メニュー項目")]
public List<MenuItemSetting> Items { get; set; } = new List<MenuItemSetting>
{
new MenuItemSetting { Text = "ファイル", Command = "File" },
new MenuItemSetting { Text = "編集", Command = "Edit" }
};
}

public class MenuItemSetting
{
public string Text { get; set; } = "";
public string Command { get; set; } = "";

public override string ToString()
{
return Text;
}
}

コレクション編集をより細かく制御したい場合は、CollectionEditorや独自のUITypeEditorを使います。

C#
[Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(UITypeEditor))]
public List<MenuItemSetting> Items { get; set; } = new List<MenuItemSetting>();

ただし、コレクション編集は一般ユーザーには少しわかりにくい場合があります。アプリの利用者が開発者でない場合は、専用の編集画面を用意した方が親切です。

6. PropertyGridのイベント処理

6-1. PropertyValueChangedで値の変更を検知する

PropertyValueChangedイベントを使うと、PropertyGrid上でプロパティ値が変更されたタイミングを検知できます。公式ドキュメントでも、このイベントはプロパティ値が変更されたときに発生すると説明されています。Microsoft Learn

C#
propertyGrid1.PropertyValueChanged += PropertyGrid1_PropertyValueChanged;

private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
string propertyName = e.ChangedItem.Label;
object oldValue = e.OldValue;
object newValue = e.ChangedItem.Value;

MessageBox.Show(
$"{propertyName} が変更されました。\n" +
$"変更前: {oldValue}\n" +
$"変更後: {newValue}");
}

設定値が変更されたら即座に画面へ反映したい場合や、保存ボタンを有効化したい場合に便利です。

C#
private bool isDirty = false;

private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
isDirty = true;
saveButton.Enabled = true;
}

6-2. SelectedGridItemChangedで選択項目の変更を取得する

SelectedGridItemChangedイベントを使うと、PropertyGrid上で選択されている項目が変わったことを検知できます。公式ドキュメントでも、選択されたGridItemが変更されたときのイベントとして説明されています。Microsoft Learn

C#
propertyGrid1.SelectedGridItemChanged += PropertyGrid1_SelectedGridItemChanged;

private void PropertyGrid1_SelectedGridItemChanged(
object sender,
SelectedGridItemChangedEventArgs e)
{
if (e.NewSelection != null)
{
Console.WriteLine($"選択項目: {e.NewSelection.Label}");
}
}

独自のヘルプ表示、ステータスバー表示、ログ出力などに利用できます。

C#
private void PropertyGrid1_SelectedGridItemChanged(
object sender,
SelectedGridItemChangedEventArgs e)
{
statusLabel.Text = e.NewSelection?.Label ?? "";
}

6-3. PropertySortChangedで並び順変更を検知する

PropertySortChangedイベントを使うと、PropertyGridの並び順が変更されたことを検知できます。

C#
propertyGrid1.PropertySortChanged += PropertyGrid1_PropertySortChanged;

private void PropertyGrid1_PropertySortChanged(object sender, EventArgs e)
{
Console.WriteLine($"並び順が変更されました: {propertyGrid1.PropertySort}");
}

ツールバーからカテゴリ表示やアルファベット順表示を切り替えたときに、現在の表示モードを保存したい場合などに使えます。

C#
private void PropertyGrid1_PropertySortChanged(object sender, EventArgs e)
{
Properties.Settings.Default.PropertySort =
propertyGrid1.PropertySort.ToString();

Properties.Settings.Default.Save();
}

6-4. 値変更後に画面や設定ファイルへ反映するサンプル

PropertyGridで変更された値を、アプリ画面に即時反映する例です。

C#
private AppSettings settings = new AppSettings();

private void MainForm_Load(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = settings;
propertyGrid1.PropertyValueChanged += PropertyGrid1_PropertyValueChanged;

ApplySettings();
}

private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
ApplySettings();
}

private void ApplySettings()
{
BackColor = settings.DarkMode
? System.Drawing.Color.FromArgb(40, 40, 40)
: System.Drawing.SystemColors.Control;

Font = new System.Drawing.Font(Font.FontFamily, settings.FontSize);
}

設定ファイルに保存する場合は、JSONなどにシリアライズします。

C#
using System.IO;
using System.Text.Json;

private void SaveSettings()
{
string json = JsonSerializer.Serialize(
settings,
new JsonSerializerOptions { WriteIndented = true });

File.WriteAllText("settings.json", json);
}

値変更時に自動保存するなら、次のように呼び出します。

C#
private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
ApplySettings();
SaveSettings();
}

ただし、変更のたびに保存するとファイルアクセスが多くなるため、実際のアプリでは「保存」ボタンを用意する方法もよく使われます。

7. PropertyGridの見た目と操作性を調整する

7-1. ToolbarVisibleでツールバー表示を切り替える

ToolbarVisibleを使うと、PropertyGrid上部のツールバー表示を切り替えられます。

C#
propertyGrid1.ToolbarVisible = false;

ツールバーには、カテゴリ表示やアルファベット順表示などを切り替えるボタンが表示されます。

ユーザーに表示順を変更させたくない場合や、シンプルな見た目にしたい場合は非表示にします。

C#
propertyGrid1.ToolbarVisible = false;
propertyGrid1.PropertySort = PropertySort.Categorized;

7-2. HelpVisibleで説明エリアの表示を切り替える

HelpVisibleを使うと、下部の説明エリアを表示または非表示にできます。

C#
propertyGrid1.HelpVisible = true;

説明エリアには、Description属性で指定した説明文が表示されます。

C#
[Description("画面の文字サイズを指定します。")]
public int FontSize { get; set; } = 12;

説明文を活用する場合は、HelpVisibletrueにしておくのがおすすめです。

画面スペースを優先したい場合は、非表示にできます。

C#
propertyGrid1.HelpVisible = false;

7-3. PropertySortでカテゴリ順・名前順を変更する

PropertySortを使うと、プロパティの表示順を変更できます。

C#
propertyGrid1.PropertySort = PropertySort.Categorized;

主な指定値は次のとおりです。

C#
propertyGrid1.PropertySort = PropertySort.NoSort;
propertyGrid1.PropertySort = PropertySort.Alphabetical;
propertyGrid1.PropertySort = PropertySort.Categorized;
propertyGrid1.PropertySort = PropertySort.CategorizedAlphabetical;

カテゴリごとに見せたい場合は、CategorizedまたはCategorizedAlphabeticalが便利です。

C#
propertyGrid1.PropertySort = PropertySort.CategorizedAlphabetical;

設定項目が少ない場合はアルファベット順、多い場合はカテゴリ順が見やすくなります。

7-4. CommandsVisibleIfAvailableでコマンド表示を制御する

CommandsVisibleIfAvailableを使うと、利用可能なコマンド領域の表示を制御できます。

C#
propertyGrid1.CommandsVisibleIfAvailable = false;

通常の設定編集画面では、コマンド領域を使わないことも多いため、必要に応じて非表示にします。

C#
propertyGrid1.ToolbarVisible = false;
propertyGrid1.HelpVisible = true;
propertyGrid1.CommandsVisibleIfAvailable = false;

不要な要素を減らすと、ユーザーが編集項目に集中しやすくなります。

7-5. BackColorやLineColorなど外観を変更する

PropertyGridは、背景色や線の色などを変更できます。

C#
propertyGrid1.BackColor = System.Drawing.Color.White;
propertyGrid1.LineColor = System.Drawing.Color.LightGray;
propertyGrid1.ViewBackColor = System.Drawing.Color.White;
propertyGrid1.ViewForeColor = System.Drawing.Color.Black;

ヘルプエリアの色も調整できます。

C#
propertyGrid1.HelpBackColor = System.Drawing.Color.WhiteSmoke;
propertyGrid1.HelpForeColor = System.Drawing.Color.Black;

ただし、PropertyGridは細かいデザイン変更に強いコントロールではありません。見た目を大幅にカスタマイズしたい場合は、専用の設定画面を作る方が適しています。

8. 実践サンプル:アプリ設定画面をPropertyGridで作る

8-1. 設定用クラスを作成する

ここでは、アプリ設定画面をPropertyGridで作る実践サンプルを紹介します。

まず、設定を表すクラスを作成します。

C#
using System.ComponentModel;

public enum AppTheme
{
Light,
Dark,
System
}

public class AppSettings
{
[Category("ユーザー")]
[DisplayName("ユーザー名")]
[Description("アプリ内で表示するユーザー名です。")]
public string UserName { get; set; } = "Guest";

[Category("表示")]
[DisplayName("テーマ")]
[Description("アプリケーションの表示テーマを選択します。")]
public AppTheme Theme { get; set; } = AppTheme.System;

[Category("表示")]
[DisplayName("フォントサイズ")]
[Description("画面に表示する文字のサイズを指定します。")]
[DefaultValue(12)]
public int FontSize { get; set; } = 12;

[Category("通信")]
[DisplayName("サーバーURL")]
[Description("接続先サーバーのURLを指定します。")]
public string ServerUrl { get; set; } = "https://example.com";

[Category("通信")]
[DisplayName("タイムアウト秒数")]
[Description("通信タイムアウトまでの秒数を指定します。")]
[DefaultValue(30)]
public int TimeoutSeconds { get; set; } = 30;

[Category("内部情報")]
[DisplayName("設定バージョン")]
[Description("設定ファイルのバージョンです。")]
[ReadOnly(true)]
public string Version { get; set; } = "1.0";

[Browsable(false)]
public string InternalId { get; set; } = "internal";
}

このクラスでは、表示名、カテゴリ、説明文、既定値、読み取り専用、非表示を属性で設定しています。

8-2. PropertyGridに設定オブジェクトをバインドする

次に、フォーム側でPropertyGridに設定オブジェクトを割り当てます。

C#
private AppSettings settings = new AppSettings();

private void MainForm_Load(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = settings;
}

フォームのコンストラクタで設定しても構いません。

C#
public MainForm()
{
InitializeComponent();

settings = new AppSettings();
propertyGrid1.SelectedObject = settings;
}

コードでPropertyGridを作る場合は、次のようにします。

C#
public MainForm()
{
InitializeComponent();

propertyGrid1 = new PropertyGrid
{
Dock = DockStyle.Fill,
ToolbarVisible = true,
HelpVisible = true,
PropertySort = PropertySort.CategorizedAlphabetical
};

Controls.Add(propertyGrid1);

settings = new AppSettings();
propertyGrid1.SelectedObject = settings;
}

8-3. 属性で表示名・カテゴリ・説明文を整える

PropertyGridを使う場合、属性による調整が非常に重要です。

属性を使わない場合、プロパティ名がそのまま表示されます。

C#
public int TimeoutSeconds { get; set; }

このままだと、一般ユーザーにはややわかりにくい場合があります。

属性を付けると、表示がわかりやすくなります。

C#
[Category("通信")]
[DisplayName("タイムアウト秒数")]
[Description("通信タイムアウトまでの秒数を指定します。")]
public int TimeoutSeconds { get; set; } = 30;

PropertyGridで使うクラスは、単なるデータ保持クラスではなく「UIに表示される設定定義」として設計すると扱いやすくなります。

8-4. 変更された設定値を保存・読み込みする

設定をJSONファイルに保存する例です。

C#
using System.IO;
using System.Text.Json;

private const string SettingsFilePath = "settings.json";

private void SaveSettings()
{
string json = JsonSerializer.Serialize(
settings,
new JsonSerializerOptions
{
WriteIndented = true
});

File.WriteAllText(SettingsFilePath, json);
}

読み込み処理は次のように書けます。

C#
private AppSettings LoadSettings()
{
if (!File.Exists(SettingsFilePath))
{
return new AppSettings();
}

string json = File.ReadAllText(SettingsFilePath);

return JsonSerializer.Deserialize<AppSettings>(json)
?? new AppSettings();
}

フォーム起動時に読み込み、PropertyGridへ設定します。

C#
private void MainForm_Load(object sender, EventArgs e)
{
settings = LoadSettings();
propertyGrid1.SelectedObject = settings;
}

保存ボタンを用意する場合は、次のようにします。

C#
private void saveButton_Click(object sender, EventArgs e)
{
SaveSettings();
MessageBox.Show("設定を保存しました。");
}

値が変更されたタイミングで自動保存する場合は、PropertyValueChangedを使います。

C#
private void MainForm_Load(object sender, EventArgs e)
{
settings = LoadSettings();

propertyGrid1.SelectedObject = settings;
propertyGrid1.PropertyValueChanged += PropertyGrid1_PropertyValueChanged;
}

private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
SaveSettings();
}

8-5. 実装コード全体

以下は、PropertyGridを使った簡単な設定画面の全体例です。

C#
using System;
using System.ComponentModel;
using System.IO;
using System.Text.Json;
using System.Windows.Forms;

namespace PropertyGridSample
{
public enum AppTheme
{
Light,
Dark,
System
}

public class AppSettings
{
[Category("ユーザー")]
[DisplayName("ユーザー名")]
[Description("アプリ内で表示するユーザー名です。")]
public string UserName { get; set; } = "Guest";

[Category("表示")]
[DisplayName("テーマ")]
[Description("アプリケーションの表示テーマを選択します。")]
public AppTheme Theme { get; set; } = AppTheme.System;

[Category("表示")]
[DisplayName("フォントサイズ")]
[Description("画面に表示する文字のサイズを指定します。")]
[DefaultValue(12)]
public int FontSize { get; set; } = 12;

[Category("通信")]
[DisplayName("サーバーURL")]
[Description("接続先サーバーのURLを指定します。")]
public string ServerUrl { get; set; } = "https://example.com";

[Category("通信")]
[DisplayName("タイムアウト秒数")]
[Description("通信タイムアウトまでの秒数を指定します。")]
[DefaultValue(30)]
public int TimeoutSeconds { get; set; } = 30;

[Category("内部情報")]
[DisplayName("設定バージョン")]
[Description("設定ファイルのバージョンです。")]
[ReadOnly(true)]
public string Version { get; set; } = "1.0";

[Browsable(false)]
public string InternalId { get; set; } = "internal";
}

public partial class MainForm : Form
{
private const string SettingsFilePath = "settings.json";

private PropertyGrid propertyGrid1;
private Button saveButton;
private AppSettings settings;

public MainForm()
{
InitializeComponent();
InitializeUi();

settings = LoadSettings();

propertyGrid1.SelectedObject = settings;
propertyGrid1.PropertyValueChanged += PropertyGrid1_PropertyValueChanged;
}

private void InitializeUi()
{
propertyGrid1 = new PropertyGrid
{
Dock = DockStyle.Fill,
ToolbarVisible = true,
HelpVisible = true,
PropertySort = PropertySort.CategorizedAlphabetical
};

saveButton = new Button
{
Text = "保存",
Dock = DockStyle.Bottom,
Height = 40
};

saveButton.Click += SaveButton_Click;

Controls.Add(propertyGrid1);
Controls.Add(saveButton);

Text = "PropertyGrid 設定画面サンプル";
Width = 600;
Height = 500;
}

private AppSettings LoadSettings()
{
if (!File.Exists(SettingsFilePath))
{
return new AppSettings();
}

try
{
string json = File.ReadAllText(SettingsFilePath);

return JsonSerializer.Deserialize<AppSettings>(json)
?? new AppSettings();
}
catch
{
return new AppSettings();
}
}

private void SaveSettings()
{
string json = JsonSerializer.Serialize(
settings,
new JsonSerializerOptions
{
WriteIndented = true
});

File.WriteAllText(SettingsFilePath, json);
}

private void SaveButton_Click(object sender, EventArgs e)
{
SaveSettings();
MessageBox.Show("設定を保存しました。");
}

private void PropertyGrid1_PropertyValueChanged(
object sender,
PropertyValueChangedEventArgs e)
{
Text = $"PropertyGrid 設定画面サンプル - 変更あり";
}
}
}

このサンプルでは、PropertyGridに設定クラスを表示し、ユーザーが編集した内容をJSONファイルに保存できます。

9. PropertyGridでよくあるトラブルと解決方法

9-1. プロパティが表示されない原因

プロパティがPropertyGridに表示されない場合、まず確認したいのは、publicプロパティになっているかどうかです。

表示されない例です。

C#
public class Sample
{
private string Name { get; set; } = "Taro";
}

privateなので表示されません。

修正例です。

C#
public class Sample
{
public string Name { get; set; } = "Taro";
}

また、フィールドは標準的なプロパティ表示の対象として扱わないため、プロパティに変更します。

C#
public class Sample
{
public string Name = "Taro"; // フィールド
}

修正例です。

C#
public class Sample
{
public string Name { get; set; } = "Taro";
}

Browsable(false)が付いている場合も表示されません。

C#
[Browsable(false)]
public string Name { get; set; } = "Taro";

この場合は、属性を削除するかBrowsable(true)に変更します。

9-2. 値を編集できない原因

値が編集できない場合は、setアクセサがpublicかどうかを確認します。

C#
public string Name { get; } = "Taro";

このようにgetのみの場合、値は表示できても編集できません。

編集可能にするには、setを追加します。

C#
public string Name { get; set; } = "Taro";

また、ReadOnly(true)が付いている場合も編集できません。

C#
[ReadOnly(true)]
public string Name { get; set; } = "Taro";

編集可能にしたい場合は、属性を削除するかReadOnly(false)にします。

C#
[ReadOnly(false)]
public string Name { get; set; } = "Taro";

9-3. カテゴリが表示されない原因

Category属性を付けているのにカテゴリ表示されない場合は、PropertySortを確認します。

C#
propertyGrid1.PropertySort = PropertySort.Alphabetical;

この設定ではカテゴリ表示ではなく名前順表示になります。

カテゴリ表示にするには、次のようにします。

C#
propertyGrid1.PropertySort = PropertySort.Categorized;

または、カテゴリ内でアルファベット順にしたい場合は次のようにします。

C#
propertyGrid1.PropertySort = PropertySort.CategorizedAlphabetical;

カテゴリを使う場合は、プロパティ側にCategory属性を付けます。

C#
[Category("表示")]
public int FontSize { get; set; } = 12;

9-4. 日本語の表示名や説明文が反映されない原因

日本語の表示名や説明文が反映されない場合は、属性の名前空間を確認します。

C#
using System.ComponentModel;

DisplayNameDescriptionCategoryBrowsableReadOnlyDefaultValueなどはSystem.ComponentModel名前空間にあります。

C#
using System.ComponentModel;

public class AppSettings
{
[DisplayName("ユーザー名")]
[Description("画面に表示する名前です。")]
public string UserName { get; set; } = "Taro";
}

また、すでにPropertyGridにオブジェクトを設定した後で属性やクラス定義を変更した場合、アプリを再ビルドして実行し直してください。

実行中に値や表示を変更した場合は、必要に応じてRefreshします。

C#
propertyGrid1.Refresh();

9-5. 変更した値がオブジェクトに反映されない原因

通常、PropertyGridで編集した値は、SelectedObjectに設定したオブジェクトのプロパティに反映されます。

C#
var settings = new AppSettings();
propertyGrid1.SelectedObject = settings;

編集後は、同じsettingsインスタンスの値が変わっています。

C#
MessageBox.Show(settings.UserName);

反映されていないように見える場合は、別のインスタンスを参照していないか確認します。

悪い例です。

C#
propertyGrid1.SelectedObject = new AppSettings();

// 別のインスタンスを見ている
var settings = new AppSettings();
MessageBox.Show(settings.UserName);

正しい例です。

C#
private AppSettings settings = new AppSettings();

private void MainForm_Load(object sender, EventArgs e)
{
propertyGrid1.SelectedObject = settings;
}

private void showButton_Click(object sender, EventArgs e)
{
MessageBox.Show(settings.UserName);
}

また、コード側でオブジェクトの値を変更した場合、PropertyGridの表示がすぐ更新されないことがあります。その場合はRefreshを呼び出します。

C#
settings.UserName = "Hanako";
propertyGrid1.Refresh();

10. PropertyGridを使う際の設計ポイント

10-1. ユーザーに見せる項目と内部用プロパティを分ける

PropertyGridを使うと、publicプロパティが簡単に表示されます。そのため、ユーザーに見せる項目と内部処理用の項目をきちんと分けることが重要です。

内部用プロパティを表示したくない場合は、Browsable(false)を付けます。

C#
[Browsable(false)]
public string InternalToken { get; set; }

表示はしたいが変更させたくない場合は、ReadOnly(true)を使います。

C#
[ReadOnly(true)]
public string Version { get; set; } = "1.0.0";

ユーザーが編集してよい項目だけを明確に表示することで、誤操作を防ぎやすくなります。

10-2. 属性を使って保守しやすくする

PropertyGridを使う場合、属性を積極的に使うと保守しやすくなります。

C#
[Category("表示")]
[DisplayName("フォントサイズ")]
[Description("画面に表示する文字のサイズを指定します。")]
[DefaultValue(12)]
public int FontSize { get; set; } = 12;

属性を使えば、表示名、説明、カテゴリ、編集可否などをプロパティ定義の近くにまとめられます。

別の場所にUI制御コードを大量に書くよりも、設定クラスを見れば画面表示の意図がわかるため、後から修正しやすくなります。

10-3. 設定画面・デバッグ画面・開発ツールでの使い分け

PropertyGridは、特に次のような用途に向いています。

・設定項目が多い管理者向け画面
・開発中のデバッグ用画面
・社内ツール
・エディタ系アプリの部品プロパティ編集
・プロトタイプ開発

一方で、一般ユーザー向けの画面では、項目の並び、説明、入力補助、バリデーション、デザイン性などが重要になります。その場合は、専用のフォームを作る方が適しています。

たとえば、ユーザー登録画面や購入フォームのように、入力体験を細かく設計したい画面にはPropertyGridはあまり向いていません。

10-4. PropertyGridが向いているケース・向いていないケース

PropertyGridが向いているケースは、次のような場合です。

・短時間で設定画面を作りたい
・対象が開発者や管理者である
・プロパティ数が多い
・カテゴリ分けして一覧編集したい
・見た目より機能性を重視する
・オブジェクトの状態を確認、編集したい

逆に、向いていないケースは次のような場合です。

・一般ユーザー向けにわかりやすい画面を作りたい
・独自デザインを重視したい
・入力フローを細かく制御したい
・複雑なバリデーションや入力補助が必要
・スマートフォン風、Webアプリ風のUIにしたい

PropertyGridは非常に便利なコントロールですが、万能ではありません。アプリの目的や利用者に合わせて、専用UIと使い分けることが大切です。

まとめ

C#のPropertyGridを使うと、WinFormsアプリケーションでオブジェクトのプロパティを簡単に一覧表示・編集できます。基本的な使い方は、PropertyGridをフォームに配置し、SelectedObjectに対象オブジェクトを設定するだけです。

C#
propertyGrid1.SelectedObject = settings;

表示対象のクラスでは、編集したい項目をpublicプロパティとして定義します。

C#
public class AppSettings
{
public string UserName { get; set; } = "Guest";
public int FontSize { get; set; } = 12;
public bool DarkMode { get; set; } = false;
}

さらに、属性を使うことで表示をわかりやすく調整できます。

C#
[Category("表示")]
[DisplayName("フォントサイズ")]
[Description("画面に表示する文字のサイズを指定します。")]
[DefaultValue(12)]
public int FontSize { get; set; } = 12;

DisplayNameで表示名を変更し、Descriptionで説明文を表示し、Categoryでグループ分けし、Browsable(false)で非表示にし、ReadOnly(true)で編集不可にできます。

また、TypeConverterExpandableObjectConverterを使えば、独自型やネストしたオブジェクトの表示をカスタマイズできます。さらに、UITypeEditorを使えば、独自の編集ダイアログやドロップダウンUIを実装できます。

値の変更を検知したい場合は、PropertyValueChangedイベントを使います。

C#
propertyGrid1.PropertyValueChanged += PropertyGrid1_PropertyValueChanged;

PropertyGridは、設定画面、デバッグ画面、社内ツール、開発者向けツールなどで特に効果を発揮します。少ないコードでプロパティ編集画面を作れるため、WinFormsで効率よく設定画面を実装したい場合には非常に有力な選択肢です。