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

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

新規登録して質問してみよう
ただいま回答率
85.46%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

WPF

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

Q&A

解決済

1回答

4099閲覧

WPF ObservableCollection<myData>の修正更新手順

退会済みユーザー

退会済みユーザー

総合スコア0

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

WPF

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

1グッド

0クリップ

投稿2021/05/31 06:41

色々なサイトを参考にさせて頂いてます。
最終的には、WPFでCSVファイルを読込み、DataTableに表示させたいと思っています。

初歩的な事かもしれず大変申し訳ないのですが、教えてください。

【知りたい事】
・クリックされたセルの修正(プログラム側で)
⇒ObservableCollectionの修正方法
ObservableCollection<myData>
これを myData[row][col]="A"みたいに編集したりしたいです。

<DataGrid Name="dataGrid" ItemsSource="{Binding Path=myData}" CanUserSortColumns="False" IsReadOnly="True" AutoGenerateColumns="False" CanUserReorderColumns="False" MouseDoubleClick="dataGrid_MouseDBLCLK"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Path=Title,TargetNullValue=''}" Header="タイトル" MinWidth="100"/> <DataGridTextColumn Binding="{Binding Path=CurrentFol,TargetNullValue=''}" Header="カレントフォルダ" /> <DataGridTextColumn Binding="{Binding Path=Pic,TargetNullValue=''}" Header="画像" /> </DataGrid.Columns> </DataGrid> <Button Content="Button" HorizontalAlignment="Left" Height="72" Margin="311,620,0,0" VerticalAlignment="Top" Width="157" Click="Button_Click"/>
public MainWindow() { InitializeComponent(); UpdateDispList(); } //表示用リストを再設定 private void UpdateDispList() { //dataGrid.ItemsSource = myData; dataGrid.ItemsSource = new ObservableCollection<myData> { new myData { Title="化粧品", CurrentFol="/pic" }, new myData { Title="洗剤", CurrentFol="/pic" }, new myData { Title="パン", CurrentFol="/pic" }, }; } //座標を出すもの private void dataGrid_MouseDBLCLK(object sender, MouseButtonEventArgs e) { // データグリッドでマウスがクリックされた位置を取得します。 var dataGrid = sender as DataGrid; var point = e.GetPosition(dataGrid); // データグリッドでマウスがクリックされた位置の行オブジェクトを取得します。 var row = GetDataGridObject<DataGridRow>(dataGrid, point); if (row == null) { return; } // 行オブジェクトから行インデックス(0起算)を取得します。 var rowIndex = row.GetIndex(); // データグリッドでマウスがクリックされた位置のセルオブジェクトを取得します。 var cell = GetDataGridObject<DataGridCell>(dataGrid, point); if (cell == null) { return; } // セルオブジェクトから列インデックス(0起算)を取得します。 var columnIndex = cell.Column.DisplayIndex; MessageBox.Show($"行={rowIndex} 列={columnIndex}"); switch (columnIndex) { case 0: break; case 1: //ファイル選択画面 string exeFolder = AppDomain.CurrentDomain.BaseDirectory; var uriBase = new Uri(exeFolder);//カレントディレクトリ var Cell = GetCellText(dataGrid,columnIndex, rowIndex);//dataGridのCell値取得 var dlg = new MSAPI::Dialogs.CommonOpenFileDialog(); // フォルダ選択ダイアログ(falseにするとファイル選択ダイアログ) dlg.IsFolderPicker = true; // タイトル dlg.Title = "フォルダを選択してください"; // 初期ディレクトリ dlg.InitialDirectory = exeFolder + Cell; if (dlg.ShowDialog() == MSAPI::Dialogs.CommonFileDialogResult.Ok) { // MessageBox.Show($"{dlg.FileName}が選択されました。"); var uriTarget = new Uri(dlg.FileName); // MakeRelativeUriメソッドで相対パスを取得する // 同時にURIに変換する際にエスケープされた文字列をアンエスケープする var relativePath = Uri.UnescapeDataString(uriBase.MakeRelativeUri(uriTarget).ToString()); // var img = new BitmapImage(new Uri("フォルダー/Test.png", UriKind.Relative)); } break; case 2: break; default: return; } } //座標を出すもの private T GetDataGridObject<T>(DataGrid dataGrid, Point point) { T result = default(T); var hitResultTest = VisualTreeHelper.HitTest(dataGrid, point); if (hitResultTest != null) { var visualHit = hitResultTest.VisualHit; while (visualHit != null) { if (visualHit is T) { result = (T)(object)visualHit; break; } visualHit = VisualTreeHelper.GetParent(visualHit); } } return result; } private void Button_Click(object sender, RoutedEventArgs e) { //値の変更が可能かテスト dataGrid.ItemsSource = new ObservableCollection<myData> { new myData { Title="あ", CurrentFol="/pic" }, new myData { Title="い", CurrentFol="/pic" }, new myData { Title="う", CurrentFol="/pic" }, new myData { Title="あ", CurrentFol="/pic" }, new myData { Title="い", CurrentFol="/pic" }, new myData { Title="う", CurrentFol="/pic" }, };
class myData { public string Title{ get; set; } public String CurrentFol { get; set; } public String Pic { get; set; } }
TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

クリックされたセルの修正(プログラム側で)⇒ObservableCollectionの修正方法

ObservableCollectionはアイテムの増減を通知しますが、中のアイテムの状態は関知しません。
ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs

INotifyCollectionChanged インターフェイス (System.Collections.Specialized) | Microsoft Docs

中のアイテムのプロパティの変更は、INotifyPropertyChangedを実装する必要があります。
INotifyPropertyChanged インターフェイス (System.ComponentModel) | Microsoft Docs

実例や詳細はいっぱい記事がありますので検索してください。

myData[row][col]="A"みたいに編集したりしたいです。

インデクサを作ればそのようなアクセスはできますがうれしいですかね?


CSVの編集ツールという感じなのでしょうか。
「存在しないフォルダやファイルは入力させたくない&手入力はだるい」のでダイアログで選択するような仕様と。

ソートやカラム移動を禁止しているので今の実装でも問題なさそうですが、DataGridCellにイベントを付けたほうがはるかに簡単だと思います。
そうすれば禁止にする必要もなくなります(Column.Headerswitchするのは微妙な気もしますが^^;

注)カレントフォルダの扱いが違っていると思います(完動コードとして作りにくいので^^;
注)新規行を追加するにはタイトルを先に入れる必要があります(先にカレントフォルダ等をダブルクリックしても無視する)
NuGet Gallery | Ookii.Dialogs.Wpf 3.1.0を使用

xml

1<Window 2 x:Class="Questions341357.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="1000" 6 Height="300" 7 HorizontalAlignment="Center"> 8 <DockPanel> 9 <StackPanel DockPanel.Dock="Bottom"> 10 <Button Click="ResetButton_Click" Content="リセット" /> 11 <Button Click="PrintButton_Click" Content="確認" /> 12 </StackPanel> 13 14 <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}"> 15 <DataGrid.CellStyle> 16 <Style TargetType="DataGridCell"> 17 <EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick" /> 18 </Style> 19 </DataGrid.CellStyle> 20 <DataGrid.Columns> 21 <DataGridTextColumn Binding="{Binding Title}" Header="タイトル" /> 22 <DataGridTextColumn 23 Width="*" 24 Binding="{Binding CurrentFolder}" 25 Header="カレントフォルダ" 26 IsReadOnly="True" /> 27 <DataGridTextColumn 28 Width="300" 29 Binding="{Binding PictureFileName}" 30 Header="画像" 31 IsReadOnly="True" /> 32 <DataGridTemplateColumn Width="32" IsReadOnly="True"> 33 <DataGridTemplateColumn.CellTemplate> 34 <DataTemplate> 35 <Image Source="{Binding PicturePath}" /> 36 </DataTemplate> 37 </DataGridTemplateColumn.CellTemplate> 38 </DataGridTemplateColumn> 39 </DataGrid.Columns> 40 </DataGrid> 41 </DockPanel> 42</Window>

cs

1using Microsoft.Win32; 2using Ookii.Dialogs.Wpf; 3using System; 4using System.Collections.ObjectModel; 5using System.ComponentModel; 6using System.Diagnostics; 7using System.IO; 8using System.Runtime.CompilerServices; 9using System.Windows; 10using System.Windows.Controls; 11using System.Windows.Input; 12 13namespace Questions341357 14{ 15 public partial class MainWindow : Window 16 { 17 public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>(); 18 19 public MainWindow() 20 { 21 InitializeComponent(); 22 DataContext = this; 23 Reset(); 24 } 25 26 private void Reset() 27 { 28 Items.Clear(); 29 Items.Add(new Item 30 { 31 //Title = "taroko", 32 CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u7/72728/", 33 PictureFileName = "FBvOAPUf_thumbnail_32x32.jpg", 34 }); 35 // インデクサを作れば↓のようなアクセスはできますがうれしいかどうか。。。 36 Items[0][0] = "taroko"; 37 38 Items.Add(new Item 39 { 40 Title = "TN8001", 41 CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/", 42 PictureFileName = "KnkDDC5A_thumbnail_32x32.jpg", 43 }); 44 } 45 46 private void ResetButton_Click(object sender, RoutedEventArgs e) => Reset(); 47 48 private void PrintButton_Click(object sender, RoutedEventArgs e) 49 { 50 foreach (var item in Items) 51 { 52 Debug.WriteLine($"Title:{item.Title}"); 53 Debug.WriteLine($"CurrentFolder:{item.CurrentFolder}"); 54 Debug.WriteLine($"PictureFileName:{item.PictureFileName}"); 55 Debug.WriteLine(""); 56 } 57 } 58 59 private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e) 60 { 61 if (!(sender is DataGridCell cell)) return; 62 if (!(cell.DataContext is Item item)) return; 63 64 var header = cell.Column.Header as string; 65 var exeFolder = AppDomain.CurrentDomain.BaseDirectory; 66 switch (header) 67 { 68 case "カレントフォルダ": 69 var folderDlg = new VistaFolderBrowserDialog { SelectedPath = exeFolder, }; 70 if (folderDlg.ShowDialog() == true) 71 { 72 item.CurrentFolder = folderDlg.SelectedPath; 73 } 74 break; 75 case "画像": 76 var fileDlg = new OpenFileDialog 77 { 78 InitialDirectory = exeFolder, 79 Filter = "画像ファイル(*.bmp,*.jpg,*.jpeg,*.png)|*.bmp;*.jpg;*.jpeg;*.png", 80 }; 81 if (fileDlg.ShowDialog() == true) 82 { 83 item.CurrentFolder = Path.GetDirectoryName(fileDlg.FileName); 84 item.PictureFileName = Path.GetFileName(fileDlg.FileName); 85 } 86 break; 87 default: return; 88 } 89 } 90 } 91 92 public class Item : Observable 93 { 94 // View側(ユーザー操作)からしか変更されないのであれば、PropertyChangedを呼ぶ必要はない 95 public string Title { get; set; } 96 //private string _Title; 97 //public string Title { get => _Title; set => Set(ref _Title, value); } 98 99 // ViewModel側(コード)でも変更がある場合は、PropertyChangedを呼びViewに変更を伝えなければならない 100 // 関連したプロパティ(PicturePath)があるので冗長だが、 101 // 通常の独立したプロパティなら↑のコメント化したTitleのような書き方 102 private string _CurrentFolder = ""; // Path.CombineでArgumentNullExceptionがでるので^^; 103 public string CurrentFolder 104 { 105 get => _CurrentFolder; 106 set 107 { 108 if (Set(ref _CurrentFolder, value)) 109 { 110 // PicturePathも変わるわけなので、同じくPropertyChangedを呼ぶ 111 OnPropertyChanged(nameof(PicturePath)); 112 } 113 } 114 } 115 116 private string _PictureFileName = ""; 117 public string PictureFileName 118 { 119 get => _PictureFileName; 120 set 121 { 122 if (Set(ref _PictureFileName, value)) 123 { 124 OnPropertyChanged(nameof(PicturePath)); 125 } 126 } 127 } 128 129 // PicturePathのPropertyChangedを呼べばViewが再取得するので、最新の値に常になる 130 public string PicturePath => Path.Combine(CurrentFolder, PictureFileName); 131 132 133 // インデクサ 134 public string this[int index] 135 { 136 get 137 { 138 switch (index) 139 { 140 case 0: return Title; 141 case 1: return CurrentFolder; 142 case 2: return PictureFileName; 143 default: throw new ArgumentOutOfRangeException(); 144 } 145 } 146 set 147 { 148 switch (index) 149 { 150 case 0: Title = value; break; 151 case 1: CurrentFolder = value; break; 152 case 2: PictureFileName = value; break; 153 default: throw new ArgumentOutOfRangeException(); 154 } 155 } 156 } 157 } 158 159 // INotifyPropertyChangedのよくある実装 ViewModelBase等の場合も 160 public class Observable : INotifyPropertyChanged 161 { 162 public event PropertyChangedEventHandler PropertyChanged; 163 protected bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 164 { 165 if (Equals(storage, value)) return false; 166 storage = value; 167 OnPropertyChanged(propertyName); 168 return true; 169 } 170 protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 171 } 172}

アプリ画像

投稿2021/05/31 10:55

編集2023/07/27 14:34
TN8001

総合スコア9396

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

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

退会済みユーザー

退会済みユーザー

2021/05/31 23:53

回答ありがとうございます。 HTMLを自動的に作るようなツールを目指しています。 HTMLから参照するため、画像までのパスをフルパスにしてしまうと 少々困ったことになるため、このようなことになってます。 カレントフォルダという名称は違うかもしれませんが、 現在のパスから、どのフォルダを経て画像フォルダにいくのか それを記載しようと思い、このような流れになっています。 data[i][j]=""のように修正できたら良いと書きましたが 単純に「ObservableCollection<myData>」の値を どのようにしたら修正出来るのか確認したかったのです。 例えば、カレントフォルダを選択しなおした場合 そのセルだけカレントフォルダの内容を書き換えるようなものです。 最終的にこのデータはCSVで書き出したりすることもでき 読み込みも出来るようにしたいと思っています。 例えばなのですが、 var TEST = dataGrid.ItemsSource; TEST[rowIndex].title = "タイトル変更後"; このような感じで編集できないでしょうか。
TN8001

2021/06/01 03:39

できますよ。見ているものは同じですから。 ただしキャストがいります。 var TEST = dataGrid.ItemsSource as ObservableCollection<myData>; MainWindowクラスにObservableCollectionのプロパティを用意すると、 DataContext = this; ItemsSource="{Binding Items}" は、 dataGrid.ItemsSource = Items; と同じことをしていることになります。 MVVMでは DataContext = new ViewModel(); と、別のクラスに分けるのですが、その前段階として DataContext = this; と、MainWindow自身を設定するのは割とおすすめです。 publicプロパティにしないとバインドできないのは注意です。
退会済みユーザー

退会済みユーザー

2021/06/01 04:14

ありがとうございます。 理解力が乏しく、中々上手くいきませんでした。 var TEST = dataGrid.ItemsSource as ObservableCollection<myData>; TEST[rowIndex].Title = "タイトル変更後"; dataGrid.ItemsSource= TEST; dataGrid.Items.Refresh();//更新 このようにしたら、無事表示できました。 色々と基礎が足りていないため、C#の本でも読んでみようと思います。 ありがとうございました。
TN8001

2021/06/01 04:46

> dataGrid.ItemsSource= TEST; この操作は不要です。 > dataGrid.Items.Refresh();//更新 件数が多くなくラグ等を感じないのであればまあいいとは思います^^ これがないと確かに反映されませんが、これは大変重い操作です。 1か所読み直せばいいはずですが、全部を読み直してしまいます。 どこが変わったのかをViewに教えてあげてそこだけ更新してもらうのが、INotifyPropertyChangedなのです。 回答では画像を表示したかったので、かなり過剰なコードになってしまったのは失敗だったかもしれません^^; INotifyPropertyChangedはWPFの基本なので、ぜひマスターしてください^^ 参考 [Windows 10 アプリケーションにデータ バインディングを実装する - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/modules/implement-data-binding-in-windows-10-app/
退会済みユーザー

退会済みユーザー

2021/06/01 08:23

丁寧にありがとうございます! 色々と勉強になりました。 じっくりとソースを読んで1行1行考えてみようと思います。 教えて頂いたサイト、そしてソースを参照させて頂き INotifyPropertyChangedについて勉強します! またわかないことがあれば相談させてください、 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問