質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

2回答

2044閲覧

データ読込直後はDataGridに内容が表示されない

Q10

総合スコア12

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

1グッド

0クリップ

投稿2021/09/09 15:38

編集2021/09/10 12:58

前提

CSVデータをPostgreSQLデータベースに読み込み、その内容をDataGridに表示するプログラムです。
データの読み込みはImport CSVボタンで行います。

データは

1,AA,"{1,2,3}"

のようなCSV形式で、データの後半"{1,2,3}"をTest_Listと呼んでいます。

問題

DBが空の状態でデータを読み込むとTest_Listの内容が表示されません。
なぜか一度DBにデータが読み込まれた状態で一旦終了し再起動することで
初めてTest_Listの内容が表示されます。

実現したいこと

たとえDBが空の状態でデータを読み込んでも(一回目の起動で)
正しくTest_Listの内容を表示できるようにしたいです。

コード

MainWindow.xaml.cs

C#

1using Npgsql; 2using System; 3using System.Collections.Generic; 4using System.Data; 5using System.Linq; 6using System.Text; 7using System.Text.RegularExpressions; 8using System.Windows; 9 10namespace WpfApp1 11{ 12 public partial class MainWindow : Window 13 { 14 public DataTable dataTable { get; } = new DataTable(); 15 public MainWindow() 16 { 17 using (var conn = new NpgsqlConnection("Server=localhost; Port=5432; Database=postgres;User Id=postgres;Password=postgres;")) 18 { 19 conn.Open(); 20 using (var command = conn.CreateCommand()) 21 { 22 StringBuilder sb = new StringBuilder(); 23 24 sb.Clear(); 25 sb.Append("CREATE TABLE IF NOT EXISTS public.tblcat ("); 26 sb.Append(" num INTEGER NOT NULL"); 27 sb.Append(" , name VARCHAR(20) NOT NULL"); 28 sb.Append(" , test VARCHAR(40)"); 29 sb.Append(" , PRIMARY KEY (num, name)"); 30 sb.Append(")"); 31 32 command.CommandText = sb.ToString(); 33 command.ExecuteNonQuery(); 34 } 35 conn.Close(); 36 } 37 CreateDataTable(); 38 InitializeComponent(); 39 } 40 41 private void CreateDataTable() 42 { 43 DataColumnCollection columns = dataTable.Columns; 44 45 if (!columns.Contains(nameof(CatModel.Num))) 46 { 47 DataColumn dataColumn_Num = dataTable.Columns.Add(nameof(CatModel.Num), typeof(int)); 48 } 49 50 if (!columns.Contains(nameof(CatModel.Name))) 51 { 52 DataColumn dataColumn_Name = dataTable.Columns.Add(nameof(CatModel.Name), typeof(string)); 53 } 54 55 using (var context = new PgDbContext()) 56 { 57 var tblCat = context.Cats; 58 IQueryable<Cat> result; 59 result = from x in tblCat 60 select x; 61 62 int columnCount = 0; 63 64 foreach (Cat cat in result.ToList()) 65 { 66 var numbers = Regex.Match(cat.Test_0001, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 67 double?[] test_list = numbers.Split(',') 68 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 69 .ToArray<double?>(); 70 columnCount = test_list.Length; 71 } 72 73 for (int i = 0; i < columnCount; i++) 74 { 75 if (!columns.Contains($"Test_{i + 1}")) 76 { 77 dataTable.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(int))); 78 } 79 } // ここにブレークポイントを貼る 80 81 foreach (Cat cat in result.ToList()) 82 { 83 var row = dataTable.NewRow(); 84 row[0] = cat.Num; 85 row[1] = cat.Name; 86 87 var numbers = Regex.Match(cat.Test_0001, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 88 double?[] test_list = numbers.Split(',') 89 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 90 .ToArray<double?>(); 91 columnCount = test_list.Length; 92 93 for (int i = 0; i < columnCount; i++) 94 { 95 row[2 + i] = (test_list[i] == null) ? DBNull.Value : test_list[i]; 96 } 97 dataTable.Rows.Add(row); 98 } // ここにブレークポイントを貼る 99 } 100 } 101 102 private void imp_button_Click(object sender, RoutedEventArgs e) 103 { 104 List<Cat> list = new List<Cat>() { new Cat() { Num=1, Name="AA", Test_0001="{1,2,3}"} }; 105 using (var context = new PgDbContext()) 106 { 107 var table = context.Cats; 108 foreach (Cat cat in list) 109 { 110 if (table.SingleOrDefault(x => x.Num == cat.Num) == null) 111 { 112 context.Cats.Add(cat); 113 context.SaveChanges(); 114 } 115 } 116 } 117 CreateDataTable(); 118 } 119 } 120}

MainWindow.xaml

XAML

1<Window x:Class="WpfApp1.MainWindow" 2 x:Name="mainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 7 mc:Ignorable="d" 8 Title="List" Height="350" Width="750" 9 BorderThickness="1"> 10 11 <Grid Width="700" Height="700"> 12 <DataGrid AutoGenerateColumns="True" ItemsSource="{Binding dataTable, ElementName=mainWindow}" 13 HorizontalAlignment="Left" Margin="10,10,10,10"/> 14 <Button x:Name="imp_button" Content="Import CSV" HorizontalAlignment="Left" Margin="250,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="imp_button_Click"/> 15 </Grid> 16</Window>

Cat.cs

C#

1using System; 2using System.ComponentModel.DataAnnotations; 3using System.ComponentModel.DataAnnotations.Schema; 4 5namespace WpfApp1 6{ 7 [Table("tblcat")] 8 class Cat 9 { 10 [Key] 11 [DatabaseGenerated(DatabaseGeneratedOption.None)] 12 [Column("num", Order = 2)] 13 public int Num { get; set; } 14 [Key] 15 [DatabaseGenerated(DatabaseGeneratedOption.None)] 16 [Column("name", Order = 1)] 17 public String Name { get; set; } 18#nullable enable 19 [Column("test")] 20 public string? Test_0001 { get; set; } 21#nullable disable 22 } 23}

CatModel.cs

C#

1using System.Collections.ObjectModel; 2 3namespace WpfApp1 4{ 5 public class CatModel 6 { 7 public CatModel(int Num, string Name, ObservableCollection<double?> Test_List) 8 { 9 this.IsChecked = false; 10 this.Num = Num; 11 this.Name = Name; 12 this.Test_List = Test_List; 13 } 14 public bool IsChecked { get; set; } 15 public int Num { get; set; } 16 public string Name { get; set; } 17 public ObservableCollection<double?> Test_List { get; set; } 18 } 19}

PgDbContext.cs

C#

1using Npgsql; 2using System.Data.Entity; 3 4namespace WpfApp1 5{ 6 class PgDbContext : DbContext 7 { 8 private const string ConnectionString = "Server=localhost;User ID=postgres;Password=postgres;Database=postgres;port=5432"; 9 public PgDbContext() : base(new NpgsqlConnection(ConnectionString), true) { } 10 public DbSet<Cat> Cats { get; set; } 11 protected override void OnModelCreating(DbModelBuilder modelBuilder) 12 { 13 modelBuilder.HasDefaultSchema("public"); 14 Database.SetInitializer<PgDbContext>(null); 15 } 16 } 17}

試したこと

CreateDataTable()の呼び出し位置をInitializeComponent();の前に移動しました。
Test_Listの型をListからObservableCollectionに変更しました。

調べてみたところ、dataTableの列は正しく追加されて5つになっているにもかかわらず、DataGridは最初の2つの列{Num}, {Name}しか表示されず、残り3つの列{Test_1}, {Test_2}, {Test_3}は表示されていないようです。

dataTable.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(int)));の2行下の"}"にブレークポイントを貼り、DBを空にした状態で一回目に起動すると、自動変数タブのthis.dataTable.Columns.Results_Viewの[0][1][2][3][4]がそれぞれ{Num}, {Name}, {Test_1}, {Test_2}, {Test_3}と正しく入っていることが確認できました。

同様に、
dataTable.Rows.Add(row);の直後の"}"にブレークポイントを貼り、DBを空にした状態で一回目に起動すると、自動変数タブのthis.dataTable.Rows.Results_View.[0].ItemArrayの[0][1][2][3][4]がそれぞれ1, "AA", 1, 2, 3と正しく入っていることが確認できました。

しかし、そのままContinueすると、{Num}, {Name}の列は正しく表示されるのですが、Test_Listの部分の{Test_1}, {Test_2}, {Test_3}は表示されません。

つまり、dataTableには行・列ともに正しくデータが入っているにもかかわらず、Test_Listの部分がDataGridに表示されません。
これは何が原因と考えられますか?
どのように確認すればよいですか?

補足

Windows10
C# .NET Core5.0
VisualStudio2019

PostgreSQLのDB名、ユーザー名、パスワードは共にpostgres
スキーマはpublic

今回使ったTest_Listデータは"{1,2,3}"のように3つしかありませんが、
最終的には"{1,2,3,...,4998,4999,5000}"のように5,000列入力する予定です。
PostgreSQLの列数の上限は1,600列までであり、
通常のCSV形式"1,2,3,...,4998,4999,5000"では上限を超えてしまい、エラーになります。
そこで、Test_Listを1つの配列型"{1,2,3,...,4998,4999,5000}"で定義することで、
PostgreSQL上の列数は1つ(Excelで言うところのセル1つ)のみになり、上限を超えずに済みます。
その代わり、DataTable側で読み込む際に分解してやる必要がある訳です。

また、DataTableを導入した理由は、
実行時に任意の数の列を追加するためです。

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/09/09 22:28 編集

質問 (何がしたいか) を書いてください。 前のスレッドような変わったことをしてますか? 変わったことというのは、CSV, DataGrid, DB 形式が異なっていて、DataDrid に表示するとき他とは形を変えるという話だったと記憶。
Q10

2021/09/10 00:38

すみません、最初は質問を書いていたのですが、Test_Listの型をListからObservableCollectionに変更したことでTeratailの1万字の制約に引っ掛かり、元々の質問が書けなくなりました。 はい、CSV, (今回はDataGridではなく)DataTable, DB形式が異なっていて、DataTableに表示するとき形式を変えます。 ### 前提 CSVデータをPostgreSQL DBに読み込み、その内容をDataTableに表示するプログラムです。 データの読込みは`Import CSV`ボタンで行います。 データは > 1,AA,"{1,2,3}" のようなCSV形式で、データの後半"{1,2,3}"をTest_Listと呼んでいます。 ### 質問したいこと DBが空の状態でデータを読み込むとTest_Listの内容が表示されません。 一度DBにデータが読み込まれた状態で一旦終了し再起動することで 初めてTest_Listの内容が表示されます。 DBが空の状態でデータを読み込んでもTest_Listの内容を表示できるようにしたいです。 ### 試したこと CreateDataTable()の呼び出し位置をInitializeComponent();の前に移動しました。 Test_Listの型をListからObservableCollectionに変更しました。 ### 補足 Windows10 C# .NET Core5.0 VisualStudio2019 PostgreSQLのDB名、ユーザー名、パスワードは共にpostgres スキーマはpublic ※画面下に表示されているDataGridは、 今回のDataTableが正しく表示できた後に消す予定です。 DataTableを導入した理由は、任意の数の桁数を追加するためです。
退会済みユーザー

退会済みユーザー

2021/09/10 01:02 編集

質問欄を編集して何がしたいか・問題・解決したい課題を書いてください。 前のスレッドような変わったことをしていて、しかも、質問とは関係さそうな部分を含む、長いコードをベタっと張り付けて、それを赤の他人の第三者に見てもらって、何だか分からない質問に答えてもらうというのは、ここの回答者に期待しすぎだと思うのですが。 普通でない変わったことをしていると知った時点で少なくとも自分には無理です。 質問 (何がしたいか) を説明するのに必要最小限(あくまで必要最小限でお願いします)のところまでコードを削って、どこで躓いていて、何が分かれば解決するかを書いてもらえると、有用なレスが付きやすいと思います。
退会済みユーザー

退会済みユーザー

2021/09/10 01:30

上のコメントに「必要最小限のところまでコードを削って」と書いたことは、問題解決に非常に有用な手段なのでぜひやってみてください。その過程で問題点が分かって自己解決できるということはよくあることです。
Q10

2021/09/10 06:42

@SurferOnWwwさん ありがとうございます。自分が考え得る必要最小限のコードまで削りました。問題点はまだ分かっておりませんが、CreateDataTable()とInitializeComponent()の位置・タイミングの問題か、ObservableCollectionの変更通知が正しくできていないか、辺りが怪しいと思っています。
退会済みユーザー

退会済みユーザー

2021/09/10 08:01

質問の最初に書いてある「内容をDataTableに表示する」というのがそもそも分からないのですが。 DataTable というのはこれ ↓ のことですよね? https://docs.microsoft.com/ja-jp/dotnet/api/system.data.datatable そうだとすると、DataTable は表示に使うものではなくて、表示に使うのは DataGrid でそのデータソースに使うものだと思うのですが。 DataTable に DB からのデータを取得できないと言ってますか?
Q10

2021/09/10 08:16

では、DataGridに表示できない、と変更します。 それ以降は不明です。
Q10

2021/09/10 08:37

一回目の実行時は、test_list[i]には1,2,3とデータが入っていますが、dataTable.Rows.Add(row)後にdataTableに値が入っているようには見えません。Autosのthis.dataTableの下を見ています(具体的にどこを見ると良いのかもよく分かりませんが)。
Q10

2021/09/10 08:43

ちなみに、row[2+i]には1, 2, 3と値が入っています。
退会済みユーザー

退会済みユーザー

2021/09/10 09:20

> では、DataGridに表示できない、と変更します。 > それ以降は不明です。 何なのそれって感じですね。 どこまで期待通りにできているかあなたの方できちんと切り分けを行ってください。それまでは自分はお手伝いできそうもありません。
Q10

2021/09/10 11:26

それ以降のコメントにはどうですか?
Q10

2021/09/10 13:12

@SurferOnWwwさん 問題の切り分けは終わっています。 そろそろお願いできますでしょうか?
退会済みユーザー

退会済みユーザー

2021/09/10 14:20

今の情報では無理です。少なくとも自分には。
YAmaGNZ

2021/09/10 22:34

DataTableの構造変更→DataGridへのバインドとDataGridへのバインド→DataTableの構造変更の違いだったりしませんか?
退会済みユーザー

退会済みユーザー

2021/09/11 05:18 編集

コードはもっと削れるのではないですか。問題が再現できなくなるところまで削ってみませんか。 もしくは、逆のアプローチで、DataTable をコードで組み立てるところから始めるとか。 ところで、以前 null 許容参照型を使う必要はないと教えたけど、いまだに使っているのはなぜ? 【訂正】 先に、public DataTable dataTable { get; } = new DataTable(); がおかしいと書いたのは間違ってました。その記述は削除しました。
退会済みユーザー

退会済みユーザー

2021/09/11 00:22 編集

他に摩訶不思議なところは・・・ > データの読み込みはImport CSVボタンで行います。 それで動くのは imp_button_Click メソッド? だとするとそうはなってないように見えますけど?(そこはレコードの追加と DataTable の再生成に見える) 最初の読み込みはコンストラクターで行っているように見えます。 > なぜか一度DBにデータが読み込まれた状態で一旦終了し再起動することで初めてTest_Listの内容が表示されます。 「再起動」ってどういう操作ですか? 「再起動」しないと DB にデータがない? どういうこと? デバッグすると DataTable にデータは取得出来ているように書いてあったけど、どうなってるの?
YAmaGNZ

2021/09/11 03:23

>> public DataTable dataTable { get; } = new DataTable(); >プロパティにアクセスする度に空の DataTable を作っているように見えます。 これって初期化でアクセスする度ではないのでは? imp_button_Click メソッドでCSVから読み込み(サンプルでは変数で与えている)してDBに追加。 そしてCreateDataTableメソッドを呼び出してDataTableの構造変更→DBからデータを読み込んでDataTableへの格納って処理の流れですよね? んで、アプリの再起動を行うと、コンストラクタでCreateDataTableメソッドを呼び出してDataTableの構造変更、データ格納を行った後にInitializeComponentメソッドを呼び出しているから、そこでDataGridへバインドされてデータの表示が出来ているということなのではないですか?
YAmaGNZ

2021/09/11 03:26

質問とは別ですが、5000列のデータが表示されて誰が見るんですかね?
退会済みユーザー

退会済みユーザー

2021/09/11 05:16

> これって初期化でアクセスする度ではないのでは? そうですね、間違えてました。訂正しておきます。 > imp_button_Click メソッドでCSVから読み込み(サンプルでは変数で与えている)してDBに追加。そしてCreateDataTableメソッドを呼び出してDataTableの構造変更→DBからデータを読み込んでDataTableへの格納って処理の流れですよね? ということは、MainWindow のコンストラクタの CreateDataTable() で生成した DataTable は DataGrid に表示できているが、imp_button_Click メソッドで DataTable を再生成しても、DataGrid には再生成結果が反映されないという話なのかな?
YAmaGNZ

2021/09/11 05:49

DBにデータがない場合にコンストラクタからの処理で構築されたDataTableでDataGridにバインド この後imp_button_Click メソッドでCSVを読み込むとバインドされたDataTableに列が追加される。 この追加された列が表示されない。DataTableを見ても列は追加されているし、データはおかしくないはず ということかと思います。 最初からDBにデータがある場合は、見せたい構造のDataTableが得らた後にバインドしているからCSVを読み込んでもDataTableの構造が変化せずにデータの追加が行われでデータが表示されるのではないかと
退会済みユーザー

退会済みユーザー

2021/09/11 06:04

コードで明示的に DataGrid.ItemsSource に DataTable.DefaultView を設定すれば DataGrid にバインドされている DataTable を差し替えることができるはずなので、imp_button_Click でそのようにすれば DataTable 再生成結果は DataGrid に反映されると思います。回答欄にそのことを追記しておきました。
退会済みユーザー

退会済みユーザー

2021/09/12 05:07

話が通じてないように思います。上のコメントと私の回答の【追記】をよく読んでください。 MainWindow のコンストラクタの CreateDataTable() で生成した DataTable は DataGrid に表示できているが(多分、その状態では DataTable にできているのは Num と Name 列だけで中身はないと想像)、imp_button_Click メソッドで DB に CSV データを読み込んで DataTable を再生成しても、DataGrid には再生成結果が反映されないという話ですよね。 そして、それがこのスレッドであなたか問題にしているところ、解決すべき課題ですよね? それは、回答に書いたサンプルコードのように、明示的に DataGrid.ItemsSource に DataTable.DefaultView を設定したら解決できるはずと言っているのですが、話は通じてますか?
guest

回答2

0

ベストアンサー

データベースはなんの関係もないんですね^^;

DataGridDataTableの行の追加は動的に反映するが、列の追加は反映しないということですね?
これは仕様ということだと思います。

ではどうするかですが、Test_Listがどうなっているかによりそうです。

  • 個数が固定で数も事前にわかっている
    NumNameのように、無条件で列を作ってしまえばいいでしょう。
  • 個数は固定だが数はわからない
    この場合カラの状態からデータが入ったときの1回だけ、リフレッシュするような操作でよさそうです。
  • 個数はバラバラ
    読み込みごとにリフレッシュすると重そうな気がする(未確認)ので、直接列を追加してしまったほうがいいかもしれません。

xml

1<Window 2 x:Class="Questions358679.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="800" 6 Height="450"> 7 <DockPanel> 8 <Button 9 MinWidth="75" 10 Margin="10" 11 HorizontalAlignment="Left" 12 Click="Button_Click" 13 Content="Add" 14 DockPanel.Dock="Bottom" /> 15 16 <Grid Margin="10"> 17 <Grid.RowDefinitions> 18 <RowDefinition /> 19 <RowDefinition /> 20 <RowDefinition /> 21 </Grid.RowDefinitions> 22 23 <DataGrid ItemsSource="{Binding DataTable1}" /> 24 25 <DataGrid 26 x:Name="dataGrid2" 27 Grid.Row="1" 28 ItemsSource="{Binding DataTable2}" /> 29 30 <DataGrid 31 x:Name="dataGrid3" 32 Grid.Row="2" 33 AutoGenerateColumns="False" 34 ItemsSource="{Binding DataTable3}" /> 35 </Grid> 36 </DockPanel> 37</Window>

cs

1using System; 2using System.Data; 3using System.Globalization; 4using System.Linq; 5using System.Windows; 6using System.Windows.Controls; 7using System.Windows.Data; 8 9namespace Questions358679 10{ 11 public class Cat 12 { 13 public int Num { get; set; } 14 public string Name { get; set; } 15 public string Test0001 { get; set; } 16 } 17 18 public partial class MainWindow : Window 19 { 20 public DataTable DataTable1 { get; } = new(); // 個数が固定で数も事前にわかっている 21 public DataTable DataTable2 { get; } = new(); // 個数は固定だが数はわからない 22 public DataTable DataTable3 { get; } = new(); // 個数はバラバラ 23 24 25 public MainWindow() 26 { 27 InitializeComponent(); 28 DataContext = this; 29 30 _ = DataTable1.Columns.Add(nameof(Cat.Num), typeof(int)); 31 _ = DataTable1.Columns.Add(nameof(Cat.Name), typeof(string)); 32 for (var i = 0; i < 10; i++) 33 { 34 DataTable1.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(double))); 35 } 36 37 _ = DataTable2.Columns.Add(nameof(Cat.Num), typeof(int)); 38 _ = DataTable2.Columns.Add(nameof(Cat.Name), typeof(string)); 39 } 40 41 private void AddRow1(Cat cat) 42 { 43 var array = cat.Test0001.Trim('{', '}').Split(',') 44 .Select(x => string.IsNullOrEmpty(x) ? null : (double?)double.Parse(x, CultureInfo.InvariantCulture)) 45 .ToArray(); 46 47 var row = DataTable1.NewRow(); 48 (row[0], row[1]) = (cat.Num, cat.Name); 49 for (var i = 0; i < array.Length; i++) 50 { 51 row[2 + i] = (array[i] == null) ? DBNull.Value : array[i]; 52 } 53 DataTable1.Rows.Add(row); 54 } 55 56 private void AddRow2(Cat cat) 57 { 58 var array = cat.Test0001.Trim('{', '}').Split(',') 59 .Select(x => string.IsNullOrEmpty(x) ? null : (double?)double.Parse(x, CultureInfo.InvariantCulture)) 60 .ToArray(); 61 62 if (DataTable2.Columns.Count == 2) 63 { 64 for (var i = 0; i < array.Length; i++) 65 { 66 DataTable2.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(double))); 67 } 68 69 // ウソくさいが一番簡単にリフレッシュできそう 70 dataGrid2.AutoGenerateColumns = false; 71 dataGrid2.AutoGenerateColumns = true; 72 } 73 74 var row = DataTable2.NewRow(); 75 (row[0], row[1]) = (cat.Num, cat.Name); 76 for (var i = 0; i < array.Length; i++) 77 { 78 row[2 + i] = (array[i] == null) ? DBNull.Value : array[i]; 79 } 80 DataTable2.Rows.Add(row); 81 } 82 83 // AutoGenerateColumns="False" にして自分でカラム追加 84 private void AddRow3(Cat cat) 85 { 86 var columns = DataTable3.Columns; 87 if (!columns.Contains(nameof(Cat.Num))) 88 { 89 _ = DataTable3.Columns.Add(nameof(Cat.Num), typeof(int)); 90 91 var column = new DataGridTextColumn 92 { 93 Header = nameof(Cat.Num), 94 Binding = new Binding(nameof(Cat.Num)), 95 }; 96 dataGrid3.Columns.Add(column); 97 } 98 if (!columns.Contains(nameof(Cat.Name))) 99 { 100 _ = DataTable3.Columns.Add(nameof(Cat.Name), typeof(string)); 101 102 var column = new DataGridTextColumn 103 { 104 Header = nameof(Cat.Name), 105 Binding = new Binding(nameof(Cat.Name)), 106 }; 107 dataGrid3.Columns.Add(column); 108 } 109 110 var array = cat.Test0001.Trim('{', '}').Split(',') 111 .Select(x => string.IsNullOrEmpty(x) ? null : (double?)double.Parse(x, CultureInfo.InvariantCulture)) 112 .ToArray(); 113 114 for (var i = 0; i < array.Length; i++) 115 { 116 if (!columns.Contains($"Test_{i + 1}")) 117 { 118 DataTable3.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(double))); 119 120 var column = new DataGridTextColumn 121 { 122 Header = $"Test_{i + 1}", 123 Binding = new Binding($"Test_{i + 1}"), 124 }; 125 dataGrid3.Columns.Add(column); 126 127 } 128 } 129 130 var row = DataTable3.NewRow(); 131 (row[0], row[1]) = (cat.Num, cat.Name); 132 for (var i = 0; i < array.Length; i++) 133 { 134 row[2 + i] = (array[i] == null) ? DBNull.Value : array[i]; 135 } 136 DataTable3.Rows.Add(row); 137 } 138 139 private int i; 140 private void Button_Click(object sender, RoutedEventArgs e) 141 { 142 i++; 143 144 var s = $"{{{string.Join(",", Enumerable.Range(1, 10))}}}"; 145 var cat = new Cat { Num = i, Name = ((char)('A' + i - 1)).ToString(), Test0001 = s, }; 146 AddRow1(cat); 147 AddRow2(cat); 148 149 s = $"{{{string.Join(",", Enumerable.Range(1, i))}}}"; 150 cat = new Cat { Num = i, Name = ((char)('A' + i - 1)).ToString(), Test0001 = s, }; 151 AddRow3(cat); 152 } 153 } 154}

最終的には"{1,2,3,...,4998,4999,5000}"のように5,000列入力する予定です。

データベースやDataTableは大丈夫かもしれませんが、DataGridはどうでしょう?
列の生成時は500列くらいですでに激重でした(仮想化が効くので列ができてしまえば、あとの操作はそんなでもないですが)

投稿2021/09/11 03:06

編集2023/07/29 05:03
TN8001

総合スコア9315

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Q10

2021/09/12 04:32

データベースは原因ではなかったんですね。 確かにデータベースには毎回正しくデータが入っていました。 > DataGridはDataTableの行の追加は動的に反映するが、列の追加は反映しないということですね? その通りです。 Test_Listは、まずは ・個数は固定だが数はわからない で実装して、後々 ・個数はバラバラ に対応するつもりでした。 既に個数でバラバラに対応していただいて大変有難いです。 なるほど、第二引数はBinding = new Binding($"Test_{i + 1}")ですか。 あと、毎回リフレッシュではなく直接列を読み込むようにしてみます。 質問前のバージョンで10,000列(各データは8桁の数字)×3行のダミーデータを入力しまして、読み込みに1分程度かかりましたが、一度表示されてからはスムーズでしたので可能とは思います。実用上は条件で区切って表示するなど工夫するつもりです。 ご回答ありがとうございました。
退会済みユーザー

退会済みユーザー

2021/09/12 05:16

このスレッドの質問者さんの問題は、途中で話が変わってなければ、上の質問の 2021/09/11 15:04 の私のコメントと回答の【追記】に私が書いた、 MainWindow のコンストラクタの CreateDataTable() で生成した DataTable は DataGrid に表示できているが(多分、その状態では DataTable にできているのは Num と Name 列だけで中身はないと想像)、imp_button_Click メソッドで DB に CSV データを読み込んで DataTable を再生成しても、DataGrid には再生成結果が反映されない。 ・・・ということですよね。 そして、それがこのスレッドで解決すべき課題ですよね。上の TN8001 さんの回答はその解決策になっているでしょうか?
TN8001

2021/09/12 06:05

> つまり、dataTableには行・列ともに正しくデータが入っているにもかかわらず、Test_Listの部分がDataGridに表示されません。 > これは何が原因と考えられますか? いろいろ経緯があったようですが、これが質問の主題と考えています。 CreateDataTableの意味というかどう使おうとしているのかの、受け取り方の違いだと感じました。 SurferOnWwwさんは命名通りにDataTableを作ると受け取っていると思います。 作り直すのであれば当然バインドし直しになるでしょう(ItemsSourceに入れなおすような) 私はコード内容からDataTableに追加するつもりと受け取りました。 でないとif文等の意味がないからです。 問題は同じデータを何度も読んでしまうことです。 どちらと取っても中途半端な状態なわけですが、私は後者と判断して回答しました。 DataTableの行の追加は反映されるのに、列の追加はなぜか反映されません。 単にそれをどう解決するかを考えました(DBは全く分かりませんので) パターン2のAutoGenerateColumnsはおそらくItemsSource入れ直しと同じ時間はかかると思いますが、初回のみです。 パターン3は数列追加するだけなら、早いのではないかと想像しています(未確認) DataGridに毎回丸ごと読み直すのと、差分だけ読むのでは大きな違いが出ると思います。 DataTableに重複データを入れないような処理が必要でしょうが、その辺は全く分かりません^^;
退会済みユーザー

退会済みユーザー

2021/09/12 06:55 編集

質問者さんが出てこないところで回答者同士が想像ベースで話をしててもアレですけど・・・ 自分は最初は DB にはすでにデータが入っていて、それをコンストラクタの CreateDataTable で取得して DataTable を作り、作った DataTable を DataGrid に表示すればよいのだと思ってました。 でも、YAmaGNZ さんの質問欄のコメントを見て質問文を読み直すと、どうもそうではなさそうで、実はコンストラクタで DB にテーブルを生成するところから始まって(その時点では DB にテーブルとフィールドは生成されるが中身はカラ)、CreateDataTable で Num と Name 列だけの生身はカラの DataTable を作って DataGrid に表示したのであろうと思い直しました。 その後、imp_button_Click メソッドで CSV ファイルを読んできて、DB に INSERT し、CreateDataTable で DataTable を作り直すということだと思います。 そもそも、初期画面で Num と Name 列だけの生身がカラの DataTable を作って DataGrid に表示するというのがあり得ない話だと思いますので、そこを考え直すべきだとは思いますが。
guest

0

質問のコメント欄にも書きましたが、いろいろ謎で、何が原因で質問に書いてある問題を起こしているのかは想像を膨らませても分かりません。

元になるデータベースにデータが存在していれば、単にそれからデータを取り出して加工して表示するだけなので、Test が表示されないとか、「再起動」しないとダメとかはあり得ないと思うのですが、たぶん、私の想像の斜め上のことをしているのであろうと思います。

念のため、上に書いた「元になるデータベースにデータが存在していれば、単にそれからデータを取り出して加工して表示」は質問者さんのコードの致命的なところを直せば問題ないのは確認しました。

それを以下の書いておくので、自分のコードと見比べてください。

元になる SQL Server データベース

イメージ説明

TestTable2.cs

質問者さんの Cat.cs の Cat クラスに該当。VS2019 の Scaffold 機能を使って上の SQL Server データベースから自動生成したエンティティクラス。

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel.DataAnnotations; 4using System.ComponentModel.DataAnnotations.Schema; 5using Microsoft.EntityFrameworkCore; 6 7#nullable disable 8 9namespace WpfApp1.Entities 10{ 11 [Table("TestTable2")] 12 public partial class TestTable2 13 { 14 [Key] 15 [Column("num")] 16 public int Num { get; set; } 17 [Required] 18 [Column("name")] 19 [StringLength(50)] 20 public string Name { get; set; } 21 [Column("test")] 22 [StringLength(50)] 23 public string Test { get; set; } 24 } 25}

MainWindow.xaml

下のコードで、dataGrid が DB のテーブルをそのままの形で DataGrid に表示、dataGrid2 が質問者さんの希望する形で表示するためのもの。

XAML

1<Window x:Class="WpfApp1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp1" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top"/> 11 12 <DataGrid x:Name="dataGrid2" HorizontalAlignment="Left" Margin="10,200,10,10" VerticalAlignment="Top"/> 13 </Grid> 14</Window>

MainWindow.xaml.cs

MainWindow_Loaded メソッドで GetListAsync メソッドと GetDataTableAsync メソッドを呼び出しているところのみ注目。前者は DB のテーブルをそのままの形で DataGrid に表示、後者は質問者さんの希望する形で表示するためのもの。

C#

1using System.Windows; 2using Microsoft.Extensions.Configuration; 3using Microsoft.Extensions.DependencyInjection; 4using Microsoft.EntityFrameworkCore; 5using System.IO; 6using WpfApp1.Contexts; 7 8namespace WpfApp1 9{ 10 public partial class MainWindow : Window 11 { 12 // TestDatabaseService は SQL Server から EF Core を利用 13 // してデータを取得するクラス 14 private readonly TestDatabaseService testDatabaseService; 15 16 public MainWindow() 17 { 18 InitializeComponent(); 19 20 this.Loaded += MainWindow_Loaded; 21 22 // 中略 23 // Context のインスタンスをコンストラクタ DI するためのコード 24 25 testDatabaseService = provider.GetRequiredService<TestDatabaseService>(); 26 } 27 28 private async void MainWindow_Loaded(object sender, RoutedEventArgs e) 29 { 30 var list = await testDatabaseService.GetListAsync(); 31 this.dataGrid.ItemsSource = list; 32 33 var table = await testDatabaseService.GetDataTableAsync(); 34 this.dataGrid2.ItemsSource = table.DefaultView; 35 } 36 } 37}

TestDatabaseContext.cs

GetDataTableAsync メソッドは質問者さんのコードをほぼそのままコピペ(いろいろ気に入らないところはありますが)。dataTable プロパティの get アクセサで毎回 DataTable を生成するとか、Test 列が NULL だとエラーになるとかの致命的なところは修正。

C#

1using System.Collections.Generic; 2using System.Linq; 3using System.Threading.Tasks; 4using Microsoft.Extensions.Configuration; 5using WpfApp1.Contexts; 6using Microsoft.EntityFrameworkCore; 7using WpfApp1.Entities; 8using System.Data; 9using System.Text.RegularExpressions; 10using System; 11 12namespace WpfApp1 13{ 14 class TestDatabaseService 15 { 16 private readonly IConfiguration _configuration; 17 private readonly TestDatabaseContext _context; 18 19 // コンストラクタの引数経由で Configuration と 20 // TestDatabaseContext のインスタンスを DI する 21 public TestDatabaseService(IConfiguration configuration, 22 TestDatabaseContext context) 23 { 24 this._configuration = configuration; 25 this._context = context; 26 } 27 28 // List<TestTable2> 型のオブジェクトを返す 29 public async Task<List<TestTable2>> GetListAsync() 30 { 31 var list = from a in _context.TestTable2s 32 select a; 33 34 return await list.ToListAsync(); 35 } 36 37 // DataTable を返す 38 public async Task<DataTable> GetDataTableAsync() 39 { 40 var result = from x in _context.TestTable2s 41 select x; 42 43 List<TestTable2> list = await result.ToListAsync(); 44 var dataTable = new DataTable(); 45 46 DataColumnCollection columns = dataTable.Columns; 47 48 if (!columns.Contains(nameof(TestTable2.Num))) 49 { 50 DataColumn dataColumn_Num = dataTable.Columns.Add(nameof(TestTable2.Num), typeof(int)); 51 } 52 53 if (!columns.Contains(nameof(TestTable2.Name))) 54 { 55 DataColumn dataColumn_Name = dataTable.Columns.Add(nameof(TestTable2.Name), typeof(string)); 56 } 57 58 int columnCount = 0; 59 60 foreach (TestTable2 cat in list) 61 { 62 // DB の test フィールドには NULL があるはず。全行見る必要もないはず 63 if (!string.IsNullOrEmpty(cat.Test)) 64 { 65 var numbers = Regex.Match(cat.Test, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 66 double?[] test_list = numbers.Split(',') 67 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 68 .ToArray<double?>(); 69 columnCount = test_list.Length; 70 break; 71 } 72 } 73 74 for (int i = 0; i < columnCount; i++) 75 { 76 if (!columns.Contains($"Test_{i + 1}")) 77 { 78 dataTable.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(int))); 79 } 80 } 81 82 foreach (TestTable2 cat in list) 83 { 84 var row = dataTable.NewRow(); 85 row[0] = cat.Num; 86 row[1] = cat.Name; 87 88 // DB の test フィールドには NULL があるはず。 89 if (!string.IsNullOrEmpty(cat.Test)) 90 { 91 var numbers = Regex.Match(cat.Test, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 92 double?[] test_list = numbers.Split(',') 93 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 94 .ToArray<double?>(); 95 columnCount = test_list.Length; 96 97 for (int i = 0; i < columnCount; i++) 98 { 99 row[2 + i] = (test_list[i] == null) ? DBNull.Value : test_list[i]; 100 } 101 } 102 dataTable.Rows.Add(row); 103 } 104 105 return dataTable; 106 } 107 } 108}

結果は:

イメージ説明

【追記】

YAmaGNZ さんの質問欄のコメントを見て思ったのですが、MainWindow のコンストラクタの CreateDataTable() で生成した DataTable は DataGrid に表示できているが、imp_button_Click メソッドで DataTable を再生成しても、DataGrid には再生成結果が反映されないという話なのかな?

そうであれば、上の回答の MainWindow.xaml.cs のコードで書いたように、コードで明示的に DataGrid.ItemsSource に DataTable.DefaultView を設定してみたらどうなりますか?

先の MainWindow.xaml.cs のコードに手を加えて、コンストラクタでダミーの DataTable を作ってそれを DataGrid に表示した後、Button クリックで DataTable を差し替えるということを試してみましたが、期待通りに差し替えることができます。お試しください。

C#

1 // ・・・前略・・・ 2 3 testDatabaseService = provider.GetRequiredService<TestDatabaseService>(); 4 5 this.dataGrid2.ItemsSource = CreateDataTable().DefaultView; 6 } 7 8 private async void Button1_Click(object sender, RoutedEventArgs e) 9 { 10 var table = await testDatabaseService.GetDataTableAsync(); 11 this.dataGrid2.ItemsSource = table.DefaultView; 12 } 13 14 private async void MainWindow_Loaded(object sender, RoutedEventArgs e) 15 { 16 var list = await testDatabaseService.GetListAsync(); 17 this.dataGrid.ItemsSource = list; 18 19 //var table = await testDatabaseService.GetDataTableAsync(); 20 //this.dataGrid2.ItemsSource = table.DefaultView; 21 } 22 23 private DataTable CreateDataTable() 24 { 25 var table = new DataTable(); 26 table.Columns.Add(new DataColumn("Num", typeof(int))); 27 table.Columns.Add(new DataColumn("Name", typeof(string))); 28 for (int i = 0; i < 3; i++) 29 { 30 var row = table.NewRow(); 31 row["Num"] = i; 32 row["Name"] = $"Name{i}"; 33 table.Rows.Add(row); 34 } 35 return table; 36 } 37 } 38}

初期表示:

イメージ説明

Button クリックで:

イメージ説明

投稿2021/09/11 03:59

編集2021/09/11 05:59
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Q10

2021/09/12 04:30

DBのテーブルそのままとDataTableのテーブルの二つのパターンを作って下さったんですね。 ありがとうございます。 実はContext のインスタンスをコンストラクタ DI するところがよく分かりませんでした。 VS2019 の Scaffold 機能は知りませんでした。SQL Serverを使う際は使ってみます。 ご回答ありがとうございました。
退会済みユーザー

退会済みユーザー

2021/09/12 05:01

上の回答のポイントは理解してもらっているでしょうか? DI とかそんなことは関係ないです。【追記】の方をよく読んでください。 MainWindow のコンストラクタの CreateDataTable() で生成した DataTable は DataGrid に表示できているが(多分、その状態では DataTable にできているのは Num と Name 列だけで中身はないと想像)、imp_button_Click メソッドで DB に CSV データを読み込んで DataTable を再生成しても、DataGrid には再生成結果が反映されないという話ですよね。 それは、回答に書いたサンプルコードのように、明示的に DataGrid.ItemsSource に DataTable.DefaultView を設定したら解決できるはずと言っているのですが、話は通じてますか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問