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

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

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

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

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

1285閲覧

WPF:DataGrid上で手入力した値をエクスポート時に反映させたい

syah

総合スコア16

C#

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

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2022/04/25 05:47

編集2022/04/25 07:25

前提

C#のWPFで、テキストファイルを読み込んで特定の文字列を検索し、CSVで表にするプログラムを作っています。
文字列を検索してCSVに出力するプログラムはできましたが、DataGrid上で手入力したキーワードを参照するところが上手くいきません。

実現したいこと

  • DataGrid上で手入力したデータをFileIO.csに値を渡したい。

発生している問題・エラーメッセージ

dataGrid1のItemSourceを参照し、DataGridで手入力で変更された内容を反映したいのですが、FileIO.csでnewしてWordListを参照してしまうと、新しくインスタンスを生成して参照してしまうので、初期値のデータを参照してしまいます。

該当のソースコード

MainWindow.xaml.cs

1// 2private void Button_Click_3(object sender, RoutedEventArgs e) 3 { 4 WordList wl = new WordList(); 5 wl.Show(); 6 } 7 private void Button_Click_4(object sender, RoutedEventArgs e) // エクスポート 8 { 9 // ボタン2のクリック・イベント 10 FileIO fileIo = new FileIO(); 11 12 fileIo.Export(tb.Text); 13 }

WordList.xaml

1 <Grid> 2 <Label Content="カラム名:" FontSize="12" Margin="10,29,0,192" Width="72" HorizontalAlignment="Left"/> 3 <TextBox Name="tb" Margin="67,29,115,0" Cursor="Arrow" VerticalAlignment="Top" /> 4 <DataGrid x:Name="dataGrid1" ItemsSource="{Binding DataTableView}" SelectedItem="{Binding SelectedRow}" Margin="3,68,0,116" Width ="300" Height ="200" >

WordList.xaml.cs

1using System; 2using System.Collections.ObjectModel; 3using System.Windows; 4using Microsoft.Win32; 5using System.IO; 6 7 8namespace LogCSVConverter 9{ 10 public partial class WordList : Window 11 { 12 13 public WordList() 14 { 15 16 InitializeComponent(); 17 18 var dataList = new ObservableCollection<Product>(); 19 for (int i = 0; i < 5; ++i) 20 { 21 var prefix = i + 1; 22 var data = CreateData(prefix); 23 dataList.Add(data); 24 } 25 dataGrid1.ItemsSource = dataList; 26 } 27 28 private void add_button_Click(object sender, RoutedEventArgs e) 29 { 30 var dataList = dataGrid1.ItemsSource as ObservableCollection<Product>; 31 var prefix = dataList.Count + 1; 32 var data = CreateData(prefix); 33 dataList.Add(data); 34 } 35 } 36}

Product.cs

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6 7namespace LogCSVConverter 8{ 9 public class Product 10 { 11 public string Keyword { get; set; } 12 public int Margin { get; set; } 13 public string Clip_char { get; set; } 14 15 public Product(string key, int margin, string clip_char) 16 { 17 Keyword = key; 18 Margin = margin; 19 Clip_char = clip_char; 20 } 21 } 22}

FileIO.cs

1using System; 2using System.IO; 3using System.Collections.ObjectModel; 4using System.Windows; 5 6using Microsoft.Win32; 7 8 9namespace LogCSVConverter 10{ 11 class FileIO 12 { 13 public void Export(string Path) 14 { 15 if (Path != string.Empty) 16 { 17 string FileContents = String.Join("\r\n", File.ReadAllLines(Path)); 18 19 // ダイアログのインスタンスを生成 20 var dialog = new SaveFileDialog(); 21 22 // ファイルの種類を設定 23 dialog.Filter = "CSVファイル (*.csv)|*.csv|全てのファイル (*.*)|*.*"; 24 25 // ダイアログを表示する 26 dialog.ShowDialog(); 27 28 if (dialog.FileName != string.Empty) //保存パスを選択してないときは通さない 29 { 30 using (StreamWriter writer = new StreamWriter(dialog.FileName)) 31 { 32 // ファイルに書き込む 33 writer.WriteLine(Search(FileContents)); 34 } 35 } 36 } 37 else 38 { 39 // 選択されたファイル名 (ファイルパス) をメッセージボックスに表示 40 MessageBox.Show("ファイルパスを指定してください"); 41 } 42 } 43 44 public string Search(string str)//検索対象のファイルの中身、検索キーワード、 抽出文字列 45 { 46 var dataList = new ObservableCollection<Product>(); 47 WordList wl = new WordList();// newしたら参照できなくなる 48 dataList = wl.ReadData();//アイテムソースのデータを読み込む 49 50 int primary_num = 0;//主キーの番号 51 52 MessageBox.Show("検索キーワード:" + dataList[primary_num] + "\n文字列の長さ:"+str.Length); 53 54 string result = ""; 55 Console.WriteLine(); 56 57 58 for (int j=0;j< 98; j++) 59 { 60 int[] pos = new int[dataList.Count]; //検索キーワードの位置([]はデータテーブルの行の数で決める) 61 string p_key = dataList[primary_num].Keyword; 62 pos[primary_num] = str.IndexOf(p_key, pos[primary_num] + 1); //主キー位置を先に検索 63 if (pos[primary_num] == -1) 64 { 65 MessageBox.Show("全ての検索処理が終了しました"); 66 break; 67 } 68 for(int i = 0; i < dataList.Count; i++) 69 { 70 if (i != primary_num) //主キー以外は主キー位置を基準に検索 71 { 72 pos[i] = str.IndexOf(dataList[i].Keyword, pos[primary_num] + 1); //現在の位置から次のキーの位置を検索 73 } 74 75 int key_length = dataList[i].Keyword.Length; 76 int clp_length = dataList[i].Clip_char.Length; 77 int margin = dataList[i].Margin; 78 result += string.Concat(str.AsSpan(pos[i] + key_length + margin, clp_length), "\t"); // 抽出したい文字列を出力する 79 } 80 result += "\n"; 81 } 82 return result; 83 } 84 } 85}

試したこと

ItemControll.ItemSourceをWordListをnewで無理やり参照したり、Staticにして参照を試みましたが、どれも上手くいきませんでした。
Staticを使わずに参照できる方法があれば、何卒ご教授お願い致します。

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

C#のWPFで、テキストファイルを読み込んで特定の文字列を検索し、CSVで表にするプログラムを作っています。

コードが不完全なのでよくわかりませんが、

  1. あるテキストファイルを読み込む
  2. dataListProductのリスト)を使い何らかの加工
  3. CSV(タブ区切り)で出力する

それらは(dataListを事前に準備できてるときは)全部できている。と

FileIO.csでnewしてWordListを参照してしまうと、新しくインスタンスを生成して参照してしまうので、初期値のデータを参照してしまいます。

dataListをどうやり取りすればいいのか」って話ですかね?

本質的には理解されていると思うのですが、newすればしただけインスタンスが増えていきます。

cs

1private void Button_Click_3(object sender, RoutedEventArgs e) 2{ 3 WordList wl = new WordList(); 4 wl.Show(); 5}

例えば↑の処理はこのボタンを押すたびに、WordListウィンドウが何個も出てきます。
当然dataListも同じ数だけ作られることになり、それぞれは全く無関係に編集・追加できてしまいます。

手抜きな解決策はstaticにすることですが、

cs

1public static ObservableCollection<Product> DataList { get; } = new ObservableCollection<Product>();

それは無しということになると、何らかの手でnewを1回だけにする必要があります。

  • MainWindowで初めに1回だけdataListを作り、WordListFileIO)に渡す
    MainWindowは普通1個しか作らないので、データ置き場にはちょうどいいです。
  • WordListを都度newしない
    別ウィンドウを使うときのテクニックですが、バツボタンで閉じても実際にはHide()するだけという手があります。
    状態が保存される(ウィンドウ位置とかTextBoxの入力内容とか)のがうれしい場合はおすすめです。

文字数制限もあるのでObservableCollectionをtsvに出すような感じに、大幅に内容を変えさせていただきました^^;
データの共有に関してはご理解いただけるかと思います。

面倒なのでObservableCollection自体を継承してしまいましたが、実際はFileIOのプロパティでObservableCollectionを持つ感じでしょうか(FileIOnewを1回だけにする)

ダイアログを出したりするのはView(Window)側でやって、ロジック(FileIO?)側では出さないようにするとテストしやすく見通しもよくなります。

.NET6です^^
MainWindow

xml

1<Window 2 x:Class="Q6fg0h19dpimexx.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Title="MainWindow" 6 Width="400" 7 Height="200"> 8 <Grid> 9 <Grid.ColumnDefinitions> 10 <ColumnDefinition /> 11 <ColumnDefinition /> 12 </Grid.ColumnDefinitions> 13 <GroupBox Header="WordListWindow1"> 14 <StackPanel> 15 <TextBlock Text="データを渡すパターン" /> 16 <Button Click="Open1_Button_Click" Content="open" /> 17 <TextBlock Text="いくつも開ける" /> 18 <Button Click="Save1_Button_Click" Content="save" /> 19 </StackPanel> 20 </GroupBox> 21 <GroupBox Grid.Column="1" Header="WordListWindow2"> 22 <StackPanel> 23 <TextBlock Text="閉じたふりパターン" /> 24 <Button Click="Open2_Button_Click" Content="open" /> 25 <TextBlock Text="" /> 26 <Button Click="Save2_Button_Click" Content="save" /> 27 </StackPanel> 28 </GroupBox> 29 </Grid> 30</Window>

cs

1using System.Collections.ObjectModel; 2using System.IO; 3using System.Linq; 4using System.Windows; 5using Microsoft.Win32; 6 7namespace Q6fg0h19dpimexx 8{ 9 public class Product 10 { 11 public string Keyword { get; set; } 12 public int Margin { get; set; } 13 public string ClipChar { get; set; } 14 } 15 16 public class ProductList : ObservableCollection<Product> 17 { 18 public ProductList() 19 { 20 AddProduct(); AddProduct(); AddProduct(); 21 } 22 23 public void AddProduct() 24 { 25 var i = Count + 1; 26 Add(new Product { Keyword = $"Keyword{i}", Margin = i, ClipChar = $"ClipChar{i}", }); 27 } 28 29 public void Save(string path) 30 => File.WriteAllLines(path, this.Select(x => $"{x.Keyword}\t{x.Margin}\t{x.ClipChar}")); 31 } 32 33 public partial class MainWindow : Window 34 { 35 private readonly ProductList productList = new ProductList(); 36 // WordListWindow2が先にできちゃうと、MainWindow扱いになるのでここではnewしない 37 private readonly WordListWindow2 wordListWindow2; // = new WordListWindow2(); 38 39 public MainWindow() 40 { 41 InitializeComponent(); 42 43 // WordListWindow2は閉じれないWindowなので、OnLastWindowClose(デフォルト) 44 // では終われないのでMainWindowを閉じたら終了モードに 45 Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; 46 47 // ここなら大丈夫 48 wordListWindow2 = new WordListWindow2(); 49 // まあこうやればどこでnewしててもいいんだけど^^; 50 //Application.Current.MainWindow = this; 51 } 52 53 private void Open1_Button_Click(object sender, RoutedEventArgs e) 54 => new WordListWindow1(productList).Show(); 55 56 private void Save1_Button_Click(object sender, RoutedEventArgs e) 57 { 58 var dialog = new SaveFileDialog { Filter = "CSVファイル (*.csv)|*.csv", }; 59 if (dialog.ShowDialog() == true) 60 { 61 productList.Save(dialog.FileName); 62 } 63 } 64 65 private void Open2_Button_Click(object sender, RoutedEventArgs e) 66 { 67 wordListWindow2.Show(); 68 wordListWindow2.WindowState = WindowState.Normal; 69 } 70 71 private void Save2_Button_Click(object sender, RoutedEventArgs e) 72 { 73 var dialog = new SaveFileDialog { Filter = "CSVファイル (*.csv)|*.csv", }; 74 if (dialog.ShowDialog() == true) 75 { 76 wordListWindow2.ProductList.Save(dialog.FileName); 77 } 78 } 79 } 80}

WordListWindow1

xml

1<Window 2 x:Class="Q6fg0h19dpimexx.WordListWindow1" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Title="WordListWindow1" 6 Width="600" 7 Height="400"> 8 <DockPanel> 9 <Button 10 Click="Button_Click" 11 Content="add" 12 DockPanel.Dock="Top" /> 13 <DataGrid x:Name="dataGrid1" /> 14 </DockPanel> 15</Window>

cs

1using System.Windows; 2 3namespace Q6fg0h19dpimexx 4{ 5 public partial class WordListWindow1 : Window 6 { 7 public WordListWindow1(ProductList productList) 8 { 9 InitializeComponent(); 10 dataGrid1.ItemsSource = productList; 11 } 12 13 private void Button_Click(object sender, RoutedEventArgs e) 14 { 15 if (dataGrid1.ItemsSource is ProductList productList) 16 { 17 productList.AddProduct(); 18 } 19 } 20 } 21}

WordListWindow2

xml

1<Window 2 x:Class="Q6fg0h19dpimexx.WordListWindow2" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Title="WordListWindow2" 6 Width="600" 7 Height="400"> 8 <DockPanel> 9 <Button 10 Click="Button_Click" 11 Content="add" 12 DockPanel.Dock="Top" /> 13 <DataGrid x:Name="dataGrid1" /> 14 </DockPanel> 15</Window>

cs

1using System.ComponentModel; 2using System.Windows; 3 4namespace Q6fg0h19dpimexx 5{ 6 public partial class WordListWindow2 : Window 7 { 8 public ProductList ProductList { get; } = new ProductList(); 9 10 public WordListWindow2() 11 { 12 InitializeComponent(); 13 dataGrid1.ItemsSource = ProductList; 14 } 15 16 private void Button_Click(object sender, RoutedEventArgs e) => ProductList.AddProduct(); 17 18 protected override void OnClosing(CancelEventArgs e) 19 { 20 e.Cancel = true; 21 Hide(); 22 } 23 } 24}

投稿2022/04/25 12:20

編集2023/07/30 07:22
TN8001

総合スコア9321

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

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

syah

2022/04/27 03:24 編集

とても分かりやすい回答有難うございます! 独学で知識が偏っている部分があるので、アドバイスしていただけるととても助かります。 コードを試してから返信しようかと思いましたが、遅くなりそうだったので先に返信させていただきます。 >コードが不完全なのでよくわかりませんが すいません、今回は素のコードが長めだったので必要ない箇所は削りました。 >それらは(dataListを事前に準備できてるときは)全部できている。と そうです。コードから直接初期値を設定すれば、CSVで出力するところまで問題なく動きます。 >「dataListをどうやり取りすればいいのか」って話ですかね? そうです。具体的にはWordListにあるdataListをFileIOで値をやりとりしたいです、 >ダイアログを出したりするのはView(Window)側でやって、ロジック(FileIO?)側では出さないようにするとテストしやすく見通しもよくなります。 元々WPFのテストプログラムから作ったのでファイル名が分かりずらくなっていました。いずれリファクタリングをしたいと思います。 後でコードを試したときに再度コメントをするので宜しくお願いします。
TN8001

2022/04/27 08:42

> 後でコードを試したときに再度コメントをするので宜しくお願いします。 はい。こちらは急ぎませんのでごゆっくりどうぞ。 回答コードに不明な点があれば、お気軽にコメントください^^
syah

2022/05/31 01:14

一か月も返信遅れてすみません。 結果として状態を保持して欲しいこと、複数のウィンドウを表示して欲しいことから2個目のWordListをHide()するやり方を採用し、無事データの受け渡しもできるようになりました。 また、以前はFileIOでファイルの入出力処理、WordList読み込み処理、検索処理を同時に担っていたのですが、 ボタン入力処理、ファイル入出力処理、WordList読み込み処理はMainWindowの方で行い、 検索処理はFileIOで処理をするようにし、KeySearchと名前を改めました。 別ウィンドウを生成した際のデータのやり取りは、別クラス間ではなくMainWindow内でやった方がいいということが分かり、勉強になりました! これからWPFで設計する際はViewとロジックを分けることを意識しようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問