プログラミング初心者です。
csvにて受け取った内容を、自作クラスでオブジェクト化したいのですが
プロパティ名が可変する場合、どのようにクラスを作ればよいのでしょうか。
複数のcsvが与えられ
各csvには下記のように情報が入っています。---
項目名1,内容,項目名2,内容,項目名3,内容・・・
例:csv1
場所,東京,取引先,株式会社A,商品,食品・・・
項目名は、決まったものではなく、どんな項目が入ってくるかわかりません。
また項目数も、ファイルによってマチマチです。
項目名の順序もマチマチという場合があります。
例:csv2
実績,あり,商品,食品,取引先,株式会社A,場所,東京,実店舗,あり・・・
これを、例えば"csv1"の”場所”の情報が欲しい時に
csv1.場所
とすれば
東京という値が取れるように
自作のクラスを使ってオブジェクト化したいのですが
具体的なコードを書くことができません。
どのような方法論が良いのか、ヒントだけでも頂けないでしょうか。
尚、”場所”や”取引先”といった項目名は、日本語で入っています。
このような場合に、クラスを使うのは適切ではないのでしょうか。
アドバイス頂ければ幸いです。
宜しくお願い致します。
<追記>
ご指摘を頂きましたので、目的を追記します。
質問の意図は私が思いついた範囲の手段であり
目的は上記にあるような形態のcsvファイルが複数供給され
それをDataGridViewに表示し、DataGridViewの編集結果を保存したい
といったことになります。
VisualStudio2022
言語はC#
WindowsForms
フレームワークは.NET Framework4.7.2
になります。
<05.25追記>
回答者様方のご親切で、当初の質問の意図そのものと少しずれ始めているにも関わらず、会話をさせて頂けております為、質問を整理させてください。
・インプットとなるcsvファイルは複数ファイルです。
・アウトプットしたいcsvファイルは1ファイル(DataGridViewに表示されているイメ―ジの通り)
・DataGridViewに表示したデータに対して、編集する場合があり、編集されたデータをアウトプットしたいです。
・DataGridViewの表示イメージを下記に示します。
C#
1using System; 2using System.Collections.Generic; 3using System.Collections; 4using System.Data; 5using System.Linq; 6using System.Windows.Forms; 7 8namespace csvtest 9{ 10 public partial class Form1 : Form 11 { 12 public Form1() 13 { 14 InitializeComponent(); 15 } 16 17 private void Form1_Load(object sender, EventArgs e) 18 { 19 20 //データテーブルを作成 21 DataTable dt = new DataTable("sample"); 22 23 24 //コレクションを作成 25 var dict_col = new List<Dictionary<string, string>>(); 26 27 28 //csvデータを用意(csvの要素が配列になっているとして) 29 string[] csvdata = { "場所", "東京", "取引先", "株式会社A", "商品", "食品" }; 30 string[] csvdata2 = { "実績", "あり", "商品", "文具", "取引先", "株式会社B", "場所", "大阪", "実店舗", "あり" }; 31 32 33 34 //csvのリストを作成 35 List<string[]> csvall = new List<string[]>(); 36 37 //リストにcsvデータを追加 38 csvall.Add(csvdata); 39 csvall.Add(csvdata2); 40 41 42 43 //dictionary型に入れ込む 44 45 foreach (var a in csvall) 46 { 47 var csv_dict = new Dictionary<string, string>(); 48 49 for (int i = 0; i < a.Length; i += 2) 50 { 51 csv_dict.Add(a[i], a[i + 1]); 52 } 53 54 dict_col.Add(csv_dict); 55 56 } 57 58 //datatableのカラムにcsv_dictからキーを取り出して一通り入れる 59 60 foreach (var b in dict_col) 61 { 62 List<string> keysList = b.Keys.ToList(); //keyの一覧リストを作成して 63 foreach (string key in keysList) //データテーブルのカラムに追加していく 64 { 65 66 //データテーブルのカラムに既に存在しないか(重複)を 67 //確認。重複していた場合追加しない 68 if (0 <= Array.IndexOf(dt.Columns.list,key) //※1うまくいっていません 69 70 71 { 72 dt.Columns.Add(key); 73 74 } 75 } 76 } 77 78 //未実装:データテーブルにValueを入れていく 79 80 81 dataGridView1.DataSource = dt; 82 83 84 } 85 } 86} 87
現状困っている点
・※1 のところですが
dt.Columns.Addで例外処理(すでにkeyが存在している)が発生してしまうので
https://dobon.net/vb/dotnet/programing/arraycontains.html
こちらを参考に
重複していないときのみ、keyを追加するようにしたいのですが
using System.Data;
を記述しているのですが
DataColumnCollection
に対して、.Listがインテリセンスされません。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
回答5件
0
私はデータグリッドビューをバインドせずに使用することも多いのでバインドしないで行う方法を回答してみます。
基本的にはCSVファイルを読み込んでDictionaryでキーと値を管理してデータグリッドビューに直接設定してます。
CSV出力時もデータグリッドビューに設定されている値から直接CSV出力を行ってます。
※注意点
細かい部分は省いたり勝手に想像して作ってます。
例えばCSVの文字コードであったり、CSV出力時に空の値に対する処理であったりとか色々です。
また、バインドしない方法だと大量データを扱うような場合、データ設定時にバインドの方法に比べてパフォーマンスが悪くなります。(この辺りは共有行(sharedRow)という仕組みがデータグリッドビューにはあり、手動で行を追加する方法だと全行にアクセスするタイミングでsharedRow関連の処理が走るため。列数にもよりますが1,000行程度なら気になるほどではないと思ってます)
C#
1using System; 2using System.Collections.Generic; 3using System.Collections; 4using System.Data; 5using System.Linq; 6using System.Windows.Forms; 7using Microsoft.VisualBasic.FileIO; 8 9namespace csvtest { 10 public partial class Form1 : Form { 11 12 public Form1() { 13 InitializeComponent(); 14 15 dataGridView1.AutoGenerateColumns = false; 16 dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; 17 dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; 18 } 19 20 private void ButtonRead_Click(object sender, EventArgs e) { 21 22 var dialog = new OpenFileDialog() { 23 Title = "CSVファイルを選択してください", 24 DefaultExt = "csv", 25 Filter = "CSVファイル|*.csv", 26 FilterIndex = 1 27 }; 28 29 if (dialog.ShowDialog(this) != DialogResult.OK) { 30 return; 31 } 32 33 var columns = new Dictionary<string, int>(); 34 var rows = new List<Dictionary<string, string>>(); 35 36 using (var parser = new TextFieldParser(dialog.FileName)) { 37 38 parser.Delimiters = new string[] { "," }; 39 40 while (true) { 41 42 if (parser.EndOfData) { 43 break; 44 } 45 46 var fields = parser.ReadFields(); 47 var row = new Dictionary<string, string>(); // CSV1行分のデータ 48 49 for (int i = 0; i < fields.Length; i += 2) { 50 51 var column = fields[i]; 52 var value = fields[i + 1]; 53 54 // 列名が存在しなかった場合は何番目に追加したかの情報とともに列一覧に追加 55 if (columns.ContainsKey(column) == false) { 56 columns.Add(column, columns.Count); 57 } 58 59 // 行の情報に値を追加 60 row.Add(column, value); 61 } 62 63 rows.Add(row); 64 } 65 66 } 67 68 DisplayCsvData(columns, rows); 69 } 70 71 private void DisplayCsvData(Dictionary<string, int> columns, List<Dictionary<string, string>> rows) { 72 73 dataGridView1.SuspendLayout(); 74 75 // 前回情報をクリア 76 dataGridView1.Rows.Clear(); 77 dataGridView1.Columns.Clear(); 78 79 // 列の作成(列の出現順で作成) 80 foreach (var csvColumn in columns.OrderBy(x => x.Value)) { 81 82 dataGridView1.Columns.Add(new DataGridViewTextBoxColumn() { 83 HeaderText = csvColumn.Key, 84 ValueType = typeof(string) 85 }); 86 87 } 88 89 // 一旦、全体の行数を作成 90 if (rows.Count > 0 || dataGridView1.AllowUserToAddRows) { 91 dataGridView1.RowCount = rows.Count + (dataGridView1.AllowUserToAddRows ? 1 : 0); 92 } 93 94 // CSVデータを設定 95 for (var i = 0; i < rows.Count; i++) { 96 97 var csvRow = rows[i]; 98 var gridRow = dataGridView1.Rows[i]; 99 100 foreach (var keyValue in csvRow) { 101 gridRow.Cells[columns[keyValue.Key]].Value = keyValue.Value; 102 } 103 104 } 105 106 // グリッドの列幅・行高さの調整とレイアウトロジックの再開 107 dataGridView1.AutoResizeColumns(); 108 dataGridView1.AutoResizeRows(); 109 dataGridView1.ResumeLayout(); 110 } 111 112 private void ButtonWrite_Click(object sender, EventArgs e) { 113 114 if (dataGridView1.RowCount == 0 || dataGridView1.ColumnCount == 0) { 115 MessageBox.Show("出力対象データがありません。"); 116 return; 117 } 118 119 // セルが編集中の場合は強制終了 120 if (dataGridView1.IsCurrentCellInEditMode) { 121 dataGridView1.EndEdit(); 122 } 123 124 var dialog = new SaveFileDialog() { 125 Title = "保存先選択", 126 DefaultExt = "csv", 127 Filter = "CSVファイル|*.csv", 128 FilterIndex = 1 129 }; 130 131 if (dialog.ShowDialog(this) != DialogResult.OK) { 132 return; 133 } 134 135 // CSV出力 136 var outputCount = 0; 137 138 using (var writer = new System.IO.StreamWriter(dialog.FileName, false)) { 139 140 foreach (DataGridViewRow gridRow in dataGridView1.Rows) { 141 142 // 追加用の行は除外 143 if (gridRow.IsNewRow) { 144 continue; 145 } 146 147 var csvRow = new List<string>(); 148 149 for (var i = 0; i < dataGridView1.ColumnCount; i++) { 150 151 var gridCell = gridRow.Cells[i]; 152 var value = gridCell.Value as string; 153 154 // 値が空の場合は出力しない 155 if (string.IsNullOrEmpty(value)) { 156 continue; 157 } 158 159 // キーと値を出力データに追加 160 csvRow.Add(gridCell.OwningColumn.HeaderText); 161 csvRow.Add(value); 162 } 163 164 if (csvRow.Count == 0) { 165 continue; 166 } 167 168 // 行を出力 169 writer.WriteLine(string.Join(",", csvRow)); 170 outputCount++; 171 } 172 173 } 174 175 if (outputCount == 0) { 176 MessageBox.Show("出力対象データがありませんでした。"); 177 return; 178 } 179 180 } 181 } 182}
投稿2022/05/28 15:31
総合スコア292
0
Dictionaryクラスが参考になるかと。
Dictionary<TKey,TValue> クラス
C#
1Dictionary<string,string> csv = new Dictionary<string,string>; 2string[] line; 3// CSVの1行を項目単位で取得する関数を作ったとして 4line= GetCsvLine(); 5for (int i=0; i<line.Count; i+=2) 6{ 7 csv.Add(line[i], line[i+1]); 8} 9 10// csv2をセットした場合、場所のデータを取得したい場合 11string locationName = csv["場所"];
これを基本に基底クラスを作成し、各々の形式用を継承クラスで作成するのが、わかりやすいかと。
*環境がないため、上記コードはコンパイルも含め未検証です。
投稿2022/05/21 15:16
総合スコア1866
退会済みユーザー
2022/05/24 03:00 編集
退会済みユーザー
2022/05/30 11:50
0
CSV からカスタムクラスのオブジェクトを作りたいと言うのは手段であって目的ではなく、目的は CSV ファイルの内容をユーザーに表示しユーザーがそれを編集して編集結果を CSV ファイルに書き出すというようなことではなかろうかと想像してます。
その想像が当たっていれば、CSV ファイルから DataTabe を作成し、それを Windows Forms アプリの DataGridView にバインドして表示してはいかがですか?
ユーザーが DataGridView を操作して編集した結果は自動的に DataTable に反映されるので、DataTable からデータを読んで CSV ファイルに書き出すこともできます。
具体例は以下の記事を見てください。
CSV ファイルを DataGridView に表示
http://surferonwww.info/BlogEngine/post/2020/09/11/show-date-in-csv-file-on-datagridview.aspx
紹介した記事には TextFieldParser を使う案と ADO.NET + OelDb + JET を使う案が書いてありますが、CSV ファイルの列が不定ということであれば前者のコードが使えると思います。
【追記】
質問者さんの言う「CVS ファイル」の形式を以下のように(いわゆる一般的に言う CVS 形式に)見直す余地はありませんか?
場所,取引先,商品
東京,株式会社A,食品
...他の行(もしあれば)...
実績,商品,取引先,場所,実店舗
あり,文具,株式会社B,大阪,あり
...他の行(もしあれば)...
上の形にすることができるなら、それぞれの CSV の DataTable を作ってそれを Merge メソッドで合体する方法を取ることをお勧めします。そうすれば Dictionary がどうのこうのと悩まずに簡単に DataTable が得られます。
サンプルを載せておきます。
CSV ファイルは以下の通り。(質問者さんのサンプルに一行追加)
コードは以下の通り。先に紹介した記事とほぼ同じです。違うのは 2 つの CSV ファイルから 2 つの DataTable を作って、それを Merge してから DataDridView に表示しているところです。
C#
1using System; 2using System.ComponentModel; 3using System.Data; 4using System.Text; 5using System.Windows.Forms; 6using Microsoft.VisualBasic.FileIO; 7 8namespace WindowsFormsApp1 9{ 10 public partial class Form4 : Form 11 { 12 private BindingSource bindingSource1; 13 private DataTable table; 14 15 public Form4() 16 { 17 InitializeComponent(); 18 19 this.components = new Container(); 20 this.bindingSource1 = 21 new BindingSource(this.components); 22 this.dataGridView1.DataSource = this.bindingSource1; 23 } 24 25 private void Form4_Load(object sender, EventArgs e) 26 { 27 // CVS ファイルのパス 28 string path1 = @"C:\Users...\CsvData1.csv"; 29 string path2 = @"C:\Users...\csvData2.csv"; 30 31 // CSV ファイルを読む際の文字エンコーディングを指定 32 Encoding encoding = new UTF8Encoding(); 33 34 this.table = CreateDataTable(path1, encoding); 35 DataTable table2 = CreateDataTable(path2, encoding); 36 37 this.table.Merge(table2); 38 this.table.AcceptChanges(); 39 40 this.bindingSource1.DataSource = this.table; 41 } 42 43 // ================ 以下ヘルパメソッド ================ 44 45 // TextFieldParser 版 46 // 指定されたパスから CSV ファイルを読んできて DataTable 47 // を作成。CSV ファイルのカラム数とその型は不定という前提 48 // なので、全カラムを string 型として扱わざるを得ない。 49 // TextFieldParser は CSV ファイルに BOM が付与されていれ 50 // ばそれからエンコーディングを自動判別。BOM 無しの場合は 51 // 引数の encoding の指定に従う 52 protected DataTable CreateDataTable(string path, 53 Encoding encoding) 54 { 55 // TextFieldParser は Microsoft が提供している Visual 56 // Basic .NET 用のクラスライブラリ。C# のアプリでも 57 // Microsoft.VisualBasic.dll を参照に追加すれば利用可 58 using (TextFieldParser tfp = 59 new TextFieldParser(path, encoding)) 60 { 61 //フィールドがデリミタで区切られている 62 tfp.TextFieldType = FieldType.Delimited; 63 64 // デリミタを , とする 65 tfp.Delimiters = new string[] { "," }; 66 67 // フィールドを " で囲み、フィールド内に改行文字、 68 // デリミタを含めることができるか 69 tfp.HasFieldsEnclosedInQuotes = true; 70 71 // 空白文字をトリム 72 tfp.TrimWhiteSpace = true; 73 74 // CSV ファイルは 1 行目がヘッダであることが条件 75 string[] headers = tfp.ReadFields(); 76 int fieldCount = headers.Length; 77 78 DataTable dt = new DataTable(); 79 DataRow dr; 80 DataColumn dc; 81 82 // DataTable の列の設定 83 for (int i = 0; i < fieldCount; i++) 84 { 85 dc = new DataColumn(headers[i], typeof(String)); 86 dt.Columns.Add(dc); 87 } 88 89 // CSV のデータから DataRow を作り DataTable 90 // に追加していく 91 while (!tfp.EndOfData) 92 { 93 string[] fields = tfp.ReadFields(); 94 95 dr = dt.NewRow(); 96 for (int i = 0; i < fieldCount; i++) 97 { 98 dr[headers[i]] = fields[i]; 99 } 100 dt.Rows.Add(dr); 101 } 102 103 return dt; 104 } 105 } 106 } 107}
結果は以下のようになります。
投稿2022/05/21 00:31
編集2022/05/26 02:28退会済みユーザー
総合スコア0
退会済みユーザー
2022/05/22 01:38
退会済みユーザー
2022/05/22 11:43
0
ベストアンサー
項目名は、決まったものではなく、どんな項目が入ってくるかわかりません。
また項目数も、ファイルによってマチマチです。
項目名の順序もマチマチという場合があります。
普通csvって
csv
1場所,取引先,商品 2東京,株式会社A,食品 3大阪,B株式会社,飲料
こうだと思うんですけど、扱うファイルはなぜか「2個ずつ組になった1行のファイル」ということでしょうか?
単純にやるならDictionaryかなんかでいいんじゃないでしょうか。
Dictionary<TKey,TValue> クラス (System.Collections.Generic) | Microsoft Docs
どうしてもプロパティっぽく書きたいのであればExpandoObjectが使えますが、便利かどうかはわかりません。
ExpandoObject クラス (System.Dynamic) | Microsoft Docs
cs
1using System; 2using System.Collections.Generic; 3using System.Dynamic; 4using System.IO; 5using System.Linq; 6 7namespace Q8aid4d1edch6jx 8{ 9 class Program 10 { 11 static void Main() 12 { 13 File.WriteAllText("csv1.csv", "場所,東京,取引先,株式会社A,商品,食品"); 14 File.WriteAllText("csv2.csv", "実績,あり,商品,食品,取引先,株式会社A,場所,東京,実店舗,あり"); 15 16 17 var csv1 = new Dictionary<string, string>(); // 辞書(Dictionary)を作る 18 var line1 = File.ReadLines("csv1.csv").Single(); // 1行読み込む(1行でなかったらエラーFirstのほうがいいかも?) 19 var split1 = line1.Split(new char[] { ',' }); // カンマで区切る 20 for (var i = 0; i < split1.Length; i += 2) // 2個セットで使うので+=2 21 { 22 csv1.Add(split1[i], split1[i + 1]); // 2個セットの前をキー後ろを値に 23 } 24 25 Console.WriteLine(csv1["場所"]); // 東京 キーから値を取得(キーがないとKeyNotFoundException) 26 Console.WriteLine(csv1.TryGetValue("実績", out var value) ? value : "項目なし"); // 項目なし キーがあるかどうかわからない場合はTryGetValue 27 28 29 dynamic csv2 = new ExpandoObject(); 30 31 var line2 = File.ReadLines("csv2.csv").Single(); 32 var split2 = line2.Split(new char[] { ',' }); 33 for (var i = 0; i < split2.Length; i += 2) 34 { 35 var dic = (IDictionary<string, object>)csv2; // ExpandoObjectはIDictionaryにキャストできる 36 dic.Add(split2[i], split2[i + 1]); // Dictionaryと同じようにAddするとプロパティが生えてくるように見える 37 } 38 39 Console.WriteLine(csv2.場所); // 東京 プロパティのようにアクセスできるが、インテリセンスが効くわけでもないので便利かどうかはわからない 40 Console.WriteLine(csv2.実績); // あり 41 //Console.WriteLine(csv2.ほげ); // RuntimeBinderException 42 } 43 } 44}
DataGridViewがマストとなると、Datatableを選択すべきでしょう。
当初の回答とは方向性が変わりましたが、やること自体は同じような感じです(Dictionaryの代わりにDatatableになっただけ)
どうせなのでより実践的?(エンコードやまともなSplit)にしました。
CsvHelperでもなんでもいいのですが、変則csvを扱いやすかったので↓を使用しました。
NuGet Gallery | Csv 2.0.84
出番がないのはかわいそうなので、PropertyGridもついでに付けましたw
DataGridViewで複数行選択すると、PropertyGridで値を一括変更できます(なぜか単一選択と複数選択で項目の並び順が変わる。なんで??)
cs
1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Diagnostics; 6using System.IO; 7using System.Linq; 8using System.Text; 9using System.Windows.Forms; 10using Csv; 11 12namespace QQ8aid4d1edch6jx 13{ 14 public partial class Form1 : Form 15 { 16 private readonly DataTable dataTable = new DataTable(); 17 18 public Form1() 19 { 20 InitializeComponent(); 21 openFileDialog1.Filter = "CSVファイル|*.csv"; 22 dataGridView1.DataSource = dataTable; 23 dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; 24 propertyGrid1.ToolbarVisible = false; 25 26 File.WriteAllText("csv1.csv", "場所,東京,取引先,株式会社A,商品,食品", Encoding.GetEncoding("shift_jis")); 27 File.WriteAllText("csv2.csv", "実績,あり,商品,文具,取引先,株式会社B,場所,大阪,実店舗,あり", Encoding.GetEncoding("shift_jis")); 28 } 29 30 // 変則csvの読み込み 31 private void LoadButton_Click(object sender, EventArgs e) 32 { 33 if (openFileDialog1.ShowDialog() == DialogResult.OK) 34 { 35 DataRow row = dataTable.NewRow(); 36 37 // 値にカンマや改行が含まれることも考慮しちゃんとパースする 38 string csv = File.ReadAllText(openFileDialog1.FileName, Encoding.GetEncoding("shift_jis")); 39 var options = new CsvOptions { HeaderMode = HeaderMode.HeaderAbsent, AllowNewLineInEnclosedFieldValues = true, }; 40 string[] split = CsvReader.ReadFromText(csv, options).First().Values; 41 42 // カラム数が奇数の場合はどうすべきか?(とりあえずハブったものは無視) 43 for (var i = 0; i < split.Length - 1; i += 2) 44 { 45 string key = split[i]; 46 string value = split[i + 1]; 47 48 if (!dataTable.Columns.Contains(key)) // まだ該当カラムがなければ... 49 { 50 dataTable.Columns.Add(key); // カラム追加 51 } 52 row[key] = value; 53 } 54 55 dataTable.Rows.Add(row); 56 } 57 } 58 59 // DataTableをまともなcsvに出力 60 private void SaveButton_Click(object sender, EventArgs e) 61 { 62 var header = new List<string>(); // csvヘッダー部分 63 foreach (DataColumn column in dataTable.Columns) 64 { 65 header.Add(column.Caption); // カラムヘッダーを順番に追加 66 } 67 68 var data = new List<string[]>(); // csvデータ部分(2次元配列のような状態) 69 foreach (DataRow row in dataTable.Rows) 70 { 71 var items = new List<string>(); // 1行分 72 foreach (object item in row.ItemArray) 73 { 74 items.Add(item.ToString()); // 1セル分 75 } 76 data.Add(items.ToArray()); 77 } 78 79 // ダブルクォート処理等いい感じにやってくれる 80 var csv = CsvWriter.WriteToText(header.ToArray(), data, ','); 81 82 // 上記foreachをワンライナーで書くなら 83 //var header = dataTable.Columns.Cast<DataColumn>().Select(x => x.Caption).ToArray(); 84 //var data = dataTable.Rows.Cast<DataRow>().Select(x => x.ItemArray.Select(y => y.ToString()).ToArray()); 85 //var csv = CsvWriter.WriteToText(header, data, ','); 86 87 File.WriteAllText("out.csv", csv, Encoding.GetEncoding("shift_jis")); 88 Process.Start("out.csv"); 89 } 90 91 private void DataGridView1_SelectionChanged(object sender, EventArgs e) 92 { 93 propertyGrid1.SelectedObjects = dataGridView1.SelectedRows 94 .Cast<DataGridViewRow>() 95 .Select(x => new RowWrapper((DataRowView)x.DataBoundItem)) 96 .ToArray(); 97 } 98 99 private void PropertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e) 100 { 101 dataGridView1.Refresh(); 102 } 103 } 104 105 106 // DataRowをいい感じにPropertyGridに表示する 107 // [C#/winforms: how to best bind a propertygrid and a System.Data.DataRow - Stack Overflow](https://stackoverflow.com/questions/943621/c-winforms-how-to-best-bind-a-propertygrid-and-a-system-data-datarow) 108 [TypeConverter(typeof(RowWrapperConverter))] 109 class RowWrapper 110 { 111 public List<string> Exclude { get; } = new List<string>(); 112 private readonly DataRowView rowView; 113 public RowWrapper(DataRowView rowView) => this.rowView = rowView; 114 private static DataRowView GetRowView(object component) => ((RowWrapper)component).rowView; 115 private class RowWrapperConverter : TypeConverter 116 { 117 public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; 118 public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) 119 { 120 var rw = (RowWrapper)value; 121 var props = TypeDescriptor.GetProperties(GetRowView(value), attributes); 122 var result = new List<PropertyDescriptor>(props.Count); 123 foreach (PropertyDescriptor prop in props) 124 { 125 if (rw.Exclude.Contains(prop.Name)) continue; 126 result.Add(new RowWrapperDescriptor(prop)); 127 } 128 return new PropertyDescriptorCollection(result.ToArray()); 129 } 130 } 131 private class RowWrapperDescriptor : PropertyDescriptor 132 { 133 private static Attribute[] GetAttribs(AttributeCollection value) 134 { 135 if (value == null) return null; 136 var result = new Attribute[value.Count]; 137 value.CopyTo(result, 0); 138 return result; 139 } 140 private readonly PropertyDescriptor innerProp; 141 public RowWrapperDescriptor(PropertyDescriptor innerProperty) : base(innerProperty.Name, GetAttribs(innerProperty.Attributes)) => innerProp = innerProperty; 142 public override bool ShouldSerializeValue(object component) => innerProp.ShouldSerializeValue(GetRowView(component)); 143 public override void ResetValue(object component) => innerProp.ResetValue(GetRowView(component)); 144 public override bool CanResetValue(object component) => innerProp.CanResetValue(GetRowView(component)); 145 public override void SetValue(object component, object value) => innerProp.SetValue(GetRowView(component), value); 146 public override object GetValue(object component) => innerProp.GetValue(GetRowView(component)); 147 public override Type PropertyType => innerProp.PropertyType; 148 public override Type ComponentType => typeof(RowWrapper); 149 public override bool IsReadOnly => innerProp.IsReadOnly; 150 } 151 } 152}
投稿2022/05/20 23:02
編集2022/05/25 10:04総合スコア10336
退会済みユーザー
2022/05/24 03:00 編集
退会済みユーザー
2022/05/24 23:04
退会済みユーザー
2022/05/25 01:12 編集
退会済みユーザー
2022/05/25 02:19 編集
0
項目名が不定だとクラスを動的に生成することになります。
参考になりそうな記事を紹介しますが、はっきり言って無理筋じゃないでしょうか。
「クラスの動的生成」
https://horiuchi.hateblo.jp/entry/20081109/1226214664
DataTable に格納するのが現実的かなと思います。
「基本 CSVファイルをDataTableに読み込んで表示する」
https://www.oborodukiyo.info/Forms/VS2008/F-ReadCSVToDataTable
投稿2022/05/20 16:49
総合スコア2760
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。






2022/05/30 11:36