syncfusion-winforms-grouping について
Syncfusion Grouping Engineの実装を支援するスキルです。Windows Formsアプリの表形式データを扱う.NETフレームワークで、UIコンポーネントに依存せず、並べ替え、フィルタリング、階層グループ化、集計、計算フィールドを扱います。
主な機能
- 01標準または独自ルールでの並べ替え
- 02論理式や比較式によるフィルタリング
- 03入れ子グループの作成
- 04集計値の計算
- 05式によるフィールドの定義
活用シーン
- 01地域別売上の集計作成
- 02在庫不足商品の特定
- 03計算値を用いた顧客分析
- 04日付レコードの絞り込みとグループ化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ syncfusion/ winforms-ui-components-skills --skill syncfusion-winforms-groupingインストール後、Claude Code で/syncfusion-winforms-groupingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/data-binding.mdexpressions-and-summaries.mdfiltering.mdgetting-started.mdgrouping-data.mdsorting.md詳細
Syncfusion Grouping Engine の実装
Essential Grouping は、特定の UI コンポーネントに依存せず、表形式データの並べ替え、グループ化、フィルタリング、集計機能を提供する、100%ネイティブの .NET データ管理フレームワークです。Windows Forms アプリケーションでの使用を想定して設計されています。
このスキルを使用する場面
次のような場合に、このスキルを使用します。
- データを並べ替える:標準またはカスタムの比較ロジックを使用して、1 つまたは複数のプロパティで並べ替える
- データをグループ化する:プロパティで階層的にグループ化し、入れ子になったグループを作成する
- レコードをフィルタリングする:代数演算子および論理演算子を使った複雑な式に基づいて絞り込む
- データを集計する:集計計算(合計、平均、最小、最大、件数)を行う
- 計算フィールドを追加する:式ベースのプロパティを使用する
- 表形式データを管理する:任意の IList ソース(ArrayList、List、BindingList など)を使用する
- データ駆動型アプリケーションを構築する:柔軟なデータ整理を必要とする
- グループ化・集計した情報を表示する:UI コントロールに依存せずに表示する
コンポーネントの概要
Grouping Engine は、効率的なデータ操作の中核となるデータ構造として平衡二分木を使用します。主な利点は次のとおりです。
- 挿入、削除、移動操作を O(Log n) で実行
- 位置や集計値をすばやく検索できる 親情報のキャッシュ
- 複数レベルの入れ子グループに対応する 再帰的なグループ化
- 計算フィールドやフィルター条件に対応する 式のサポート
- Windows Forms アプリケーション向けに最適化された プラットフォームサポート
ドキュメントとナビゲーションガイド
はじめにとインストール
📄 参照先: references/getting-started.md
- インストール先とデプロイ要件
- Windows Forms アプリケーションの作成
- 必要なアセンブリ参照と依存関係
- デプロイ用の Copy Local プロパティの設定
- デプロイのシナリオと GAC のオプション
- Windows Forms のデプロイに必要な DLL
データバインディングの基本
📄 参照先: references/data-binding.md
- パブリックプロパティを持つカスタムオブジェクトの作成
- ArrayList および IList データソースの構築
- Engine.SetSourceList() によるデータソースの設定
- Table.Records コレクションの反復処理
- Record.GetData() によるレコードデータへのアクセス
- Engine と Table の関係の理解
データのグループ化
📄 参照先: references/grouping-data.md
- TableDescriptor.GroupedColumns コレクションの使用
- Table.TopLevelGroup によるグループへのアクセス
- Groups および Records を使った再帰的なナビゲーション
- キー値による特定のグループの取得
- グループ階層のパターンの理解
- グループ化の副作用(自動並べ替え)
- グループ内のレコードの表示
データの並べ替え
📄 参照先: references/sorting.md
- TableDescriptor.SortedColumns への列の追加
- 並べ替え方向(Ascending/Descending)の指定
- IComparer インターフェイスを使ったカスタム並べ替えの実装
- SortColumnDescriptor.Comparer プロパティの使用
- 数値と文字列の並べ替え方法
- 複数列の並べ替えシナリオ
レコードのフィルタリング
📄 参照先: references/filtering.md
- 式を指定した RecordFilterDescriptor の作成
- Table.FilteredRecords コレクションの使用
- フィルター式の構文と演算子
- 論理演算子(AND、OR、NOT)
- 比較演算子(=、<、>、<=、>=、<>)
- 文字列演算子(LIKE、MATCH、IN、BETWEEN)
- 複数条件を含む複雑なフィルターの構築
- フィルターコレクションのクリアと更新
式と集計
📄 読む: references/expressions-and-summaries.md
- 計算フィールド用の ExpressionFieldDescriptor の作成
- プロパティ名を使った代数式
- TableDescriptor.ExpressionFields コレクションへの追加
- Record.GetValue() を使った式の値の取得
- 集計用の SummaryDescriptor の作成
- 集計の種類(Int32Aggregate、DoubleAggregate など)
- GetSummary() を使ったグループごとの集計値の取得
- 代数演算子と評価の優先順位
- カスタム関数のサポートと制限
クイックスタートの例
using System;
using System.Collections;
using Syncfusion.Grouping;
// Define a custom data object
public class SalesRecord
{
public string Product { get; set; }
public string Category { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public SalesRecord(string product, string category, int qty, decimal price)
{
Product = product;
Category = category;
Quantity = qty;
Price = price;
}
}
// Create and populate datasource
ArrayList salesData = new ArrayList();
salesData.Add(new SalesRecord("Laptop", "Electronics", 5, 1200.00m));
salesData.Add(new SalesRecord("Mouse", "Electronics", 25, 15.00m));
salesData.Add(new SalesRecord("Desk", "Furniture", 10, 350.00m));
salesData.Add(new SalesRecord("Chair", "Furniture", 20, 150.00m));
// Create Grouping Engine and set datasource
Engine groupingEngine = new Engine();
groupingEngine.SetSourceList(salesData);
// Group by Category
groupingEngine.TableDescriptor.GroupedColumns.Add("Category");
// Add summary for Quantity
SummaryDescriptor quantitySummary = new SummaryDescriptor(
"TotalQuantity",
"Quantity",
SummaryType.Int32Aggregate
);
groupingEngine.TableDescriptor.Summaries.Add(quantitySummary);
// Iterate through groups and display summaries
foreach (Group group in groupingEngine.Table.TopLevelGroup.Groups)
{
Console.WriteLine($"Category: {group.Name}");
// Get summary value
var summary = group.GetSummary("TotalQuantity") as Int32AggregateSummary;
Console.WriteLine($"Total Items: {summary.Sum}");
// Display records in this group
foreach (Record rec in group.Records)
{
SalesRecord sale = rec.GetData() as SalesRecord;
Console.WriteLine($" {sale.Product}: {sale.Quantity} @ ${sale.Price}");
}
Console.WriteLine();
}
よくあるパターン
パターン 1:フィルターしてからグループ化するワークフロー
// 1. Create engine and set datasource
Engine engine = new Engine();
engine.SetSourceList(dataList);
// 2. Apply filter first
RecordFilterDescriptor filter = new RecordFilterDescriptor("[Price] > 100");
engine.TableDescriptor.RecordFilters.Add(filter);
// 3. Then group filtered results
engine.TableDescriptor.GroupedColumns.Add("Category");
// 4. Access filtered and grouped data
foreach (Group group in engine.Table.TopLevelGroup.Groups)
{
// Process each group
}
パターン 2:複数レベルのグループ化
// Group by multiple columns for nested hierarchy
engine.TableDescriptor.GroupedColumns.Add("Region");
engine.TableDescriptor.GroupedColumns.Add("Category");
engine.TableDescriptor.GroupedColumns.Add("SubCategory");
// Navigate nested groups recursively
void ProcessGroup(Group group, int level)
{
if (group.Records != null && group.Records.Count > 0)
{
// Terminal group with records
foreach (Record rec in group.Records)
{
// Process record
}
}
else if (group.Groups != null && group.Groups.Count > 0)
{
// Parent group with sub-groups
foreach (Group subGroup in group.Groups)
{
ProcessGroup(subGroup, level + 1);
}
}
}
パターン 3:動的な式フィールド
// Add calculated column for total value
ExpressionFieldDescriptor totalExpr = new ExpressionFieldDescriptor(
"TotalValue",
"[Quantity] * [Price]"
);
engine.TableDescriptor.ExpressionFields.Add(totalExpr);
// Use expression in filter
RecordFilterDescriptor highValueFilter = new RecordFilterDescriptor(
"[TotalValue] > 1000"
);
engine.TableDescriptor.RecordFilters.Add(highValueFilter);
// Retrieve expression values
foreach (Record rec in engine.Table.FilteredRecords)
{
decimal total = (decimal)rec.GetValue("TotalValue");
Console.WriteLine($"Total: ${total}");
}
パターン 4:IComparer を使ったカスタムソート
// Implement custom comparer
public class CustomComparer : IComparer
{
public int Compare(object x, object y)
{
// Custom comparison logic
string str1 = x.ToString();
string str2 = y.ToString();
// Example: Sort by length first, then alphabetically
int lengthCompare = str1.Length.CompareTo(str2.Length);
return lengthCompare != 0 ? lengthCompare : str1.CompareTo(str2);
}
}
// Apply custom sort
engine.TableDescriptor.SortedColumns.Add("Product");
engine.TableDescriptor.SortedColumns["Product"].Comparer = new CustomComparer();
主なプロパティとコレクション
Engine クラス
| プロパティ | 説明 |
|---|---|
Table | レコードとグループを含む Table オブジェクトを取得します |
TableDescriptor | スキーマ情報と構成を取得します |
SetSourceList(IList) | エンジンのデータソースを設定します |
TableDescriptor のプロパティ
| プロパティ | 説明 |
|---|---|
GroupedColumns | グループ化するプロパティのコレクション |
SortedColumns | ソートするプロパティのコレクション |
RecordFilters | フィルター条件のコレクション |
Summaries | 集計計算のコレクション |
ExpressionFields | 計算フィールドのコレクション |
Table のプロパティ
| プロパティ | 説明 |
|---|---|
Records | テーブル内のすべてのレコード |
FilteredRecords | フィルター適用後のレコード |
TopLevelGroup | 階層的にアクセスするためのルートグループ |
Group クラス
| プロパティ | 説明 |
|---|---|
Groups | 子グループ(親グループの場合) |
Records | このグループ内のレコード(末端グループの場合) |
Name | グループのキー/名前 |
GetSummary(string) | 名前で集計値を取得します |
Record クラス
| メソッド | 説明 |
|---|---|
GetData() | 基になるデータオブジェクトを返します |
GetValue(string) | 名前でプロパティまたは式の値を取得します |
よくあるユースケース
ユースケース 1:地域と製品別の売上レポート
// Group sales by region, then product category
engine.TableDescriptor.GroupedColumns.Add("Region");
engine.TableDescriptor.GroupedColumns.Add("ProductCategory");
// Add sales summaries
engine.TableDescriptor.Summaries.Add(
new SummaryDescriptor("TotalSales", "SalesAmount", SummaryType.DoubleAggregate)
);
engine.TableDescriptor.Summaries.Add(
new SummaryDescriptor("OrderCount", "OrderID", SummaryType.Count)
);
ユースケース 2:在庫不足商品の在庫フィルタリング
// Filter for items below reorder level
RecordFilterDescriptor lowStock = new RecordFilterDescriptor(
"[StockLevel] < [ReorderLevel] AND [Active] = 1"
);
engine.TableDescriptor.RecordFilters.Add(lowStock);
// Sort by urgency (stock level ascending)
engine.TableDescriptor.SortedColumns.Add("StockLevel", ListSortDirection.Ascending);
ユースケース 3:計算フィールドを使った顧客分析
// Add expression for customer value
ExpressionFieldDescriptor customerValue = new ExpressionFieldDescriptor(
"CustomerValue",
"[TotalOrders] * [AverageOrderAmount]"
);
engine.TableDescriptor.ExpressionFields.Add(customerValue);
// Filter for high-value customers
RecordFilterDescriptor premiumCustomers = new RecordFilterDescriptor(
"[CustomerValue] > 10000"
);
engine.TableDescriptor.RecordFilters.Add(premiumCustomers);
// Group by customer tier
engine.TableDescriptor.GroupedColumns.Add("Tier");
ユースケース 4:時系列データ分析
// Filter for date range using BETWEEN operator
RecordFilterDescriptor dateRange = new RecordFilterDescriptor(
"[OrderDate] BETWEEN {1/1/2024, 12/31/2024}"
);
engine.TableDescriptor.RecordFilters.Add(dateRange);
// Group by month (requires calculated field)
ExpressionFieldDescriptor monthField = new ExpressionFieldDescriptor(
"OrderMonth",
"Month([OrderDate])"
);
engine.TableDescriptor.ExpressionFields.Add(monthField);
engine.TableDescriptor.GroupedColumns.Add("OrderMonth");
エッジケースとトラブルシューティング
空のグループ
フィルター適用後にグループが空になることがあります。反復処理の前に、必ず Records.Count を確認してください。
式の型の不一致
プロパティ値は演算の型と一致している必要があります。
- 数値の比較には
=を使用します - 文字列の比較には
LIKEまたはMATCHを使用します - 適切な集計型(Int32Aggregate と DoubleAggregate)を使用します
再帰的なナビゲーション
グループを再帰的にたどる際は、group.Groups または group.Records のどちらに値が設定されているかを確認してください。各階層で項目を持つのはいずれか一方だけです。
フィルター式の構文
- プロパティ名は角括弧で囲む必要があります:
[PropertyName] - 文字列リテラルは一重引用符で囲む必要があります:
'value' - IN 演算子のリストでは、スペースが重要です:
{value1,value2}のように記述し、{value1, value2}とはしません
メモリ管理
Grouping Engine は内部データ構造を保持します。リソースを解放するため、処理が終わったら Dispose() を呼び出してください。
ベストプラクティス
- 設定の前にデータソースを指定する:グループ、並べ替え、フィルターを追加する前に、
SetSourceList()を呼び出します - フィルター適用後のデータには FilteredRecords を使用する:フィルターが有効な場合は、
Table.RecordsではなくTable.FilteredRecordsにアクセスします - 適切な集計タイプを選択する:集計タイプをプロパティのデータ型(Int32、Double、Boolean など)に合わせます
- 式を最適化する:複雑な式はパフォーマンスに影響するため、可能な限りシンプルにします
- null 値を処理する:
GetData()でレコードデータにアクセスする際は、null を確認します - 適切に破棄する:不要になった Engine オブジェクトを破棄します
- フィルター式をテストする:実行時エラーを避けるため、デプロイ前にフィルター構文を検証します
プラットフォーム固有の注意事項
Windows Forms
- Grouping Engine は単独でも GridGroupingControl と組み合わせても動作します
- UI への依存は不要です
- デスクトップアプリケーションのシナリオ向けに最適化されています
次のステップ
- インストールとセットアップについては、getting-started.md をお読みください
- データソースの要件を理解するには、data-binding.md をご確認ください
- 必要に応じて、各リファレンスファイルで特定の機能をご確認ください
- 実装の詳細については、各リファレンスのコード例をご確認ください