teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

見直しキャンペーン中

2023/07/27 14:34

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,247 +1,248 @@
1
- > クリックされたセルの修正(プログラム側で)⇒ObservableCollectionの修正方法
2
-
3
- `ObservableCollection`はアイテムの増減を通知しますが、中のアイテムの状態は関知しません。
4
- [ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.objectmodel.observablecollection-1)
5
-
6
- [INotifyCollectionChanged インターフェイス (System.Collections.Specialized) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.specialized.inotifycollectionchanged)
7
-
8
- 中のアイテムのプロパティの変更は、`INotifyPropertyChanged`を実装する必要があります。
9
- [INotifyPropertyChanged インターフェイス (System.ComponentModel) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.inotifypropertychanged)
10
-
11
- 実例や詳細はいっぱい記事がありますので検索してください。
12
-
13
- > myData[row][col]="A"みたいに編集したりしたいです。
14
-
15
- インデクサを作ればそのようなアクセスはできますがうれしいですかね?
16
-
17
- ---
18
-
19
- CSVの編集ツールという感じなのでしょうか。
20
- 「存在しないフォルダやファイルは入力させたくない&手入力はだるい」のでダイアログで選択するような仕様と。
21
- ソートやカラム移動を禁止しているので今の実装でも問題なさそうですが、`DataGridCell`にイベントを付けたほうがはるかに簡単だと思います。
22
- そうすれば禁止にす必要もなくなりま`Column.Header`で`switch`するの微妙な気もしますが^^;
23
-
24
-
25
- 注)カレントフォルダの扱いが違っていると思います(完動コードとして作りにくいので^^;
26
- 注)新規行を追加するにはタイトルを先に入れる必要があります(先にカレントフォルダ等をダブルクリックしも無視す
27
- [NuGet Gallery | Ookii.Dialogs.Wpf 3.1.0](https://www.nuget.org/packages/Ookii.Dialogs.Wpf/3.1.0)使用
28
- ```xaml
29
- <Window
30
- x:Class="Questions341357.MainWindow"
31
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
32
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
- Width="1000"
34
- Height="300"
35
- HorizontalAlignment="Center">
36
- <DockPanel>
37
- <StackPanel DockPanel.Dock="Bottom">
38
- <Button Click="ResetButton_Click" Content="リセット" />
39
- <Button Click="PrintButton_Click" Content="確認" />
40
- </StackPanel>
41
-
42
- <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}">
43
- <DataGrid.CellStyle>
44
- <Style TargetType="DataGridCell">
45
- <EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick" />
46
- </Style>
47
- </DataGrid.CellStyle>
48
- <DataGrid.Columns>
49
- <DataGridTextColumn Binding="{Binding Title}" Header="タイトル" />
50
- <DataGridTextColumn
51
- Width="*"
52
- Binding="{Binding CurrentFolder}"
53
- Header="カレントフォルダ"
54
- IsReadOnly="True" />
55
- <DataGridTextColumn
56
- Width="300"
57
- Binding="{Binding PictureFileName}"
58
- Header="画像"
59
- IsReadOnly="True" />
60
- <DataGridTemplateColumn Width="32" IsReadOnly="True">
61
- <DataGridTemplateColumn.CellTemplate>
62
- <DataTemplate>
63
- <Image Source="{Binding PicturePath}" />
64
- </DataTemplate>
65
- </DataGridTemplateColumn.CellTemplate>
66
- </DataGridTemplateColumn>
67
- </DataGrid.Columns>
68
- </DataGrid>
69
- </DockPanel>
70
- </Window>
71
- ```
72
-
73
- ```C#
74
- using Microsoft.Win32;
75
- using Ookii.Dialogs.Wpf;
76
- using System;
77
- using System.Collections.ObjectModel;
78
- using System.ComponentModel;
79
- using System.Diagnostics;
80
- using System.IO;
81
- using System.Runtime.CompilerServices;
82
- using System.Windows;
83
- using System.Windows.Controls;
84
- using System.Windows.Input;
85
-
86
- namespace Questions341357
87
- {
88
- public partial class MainWindow : Window
89
- {
90
- public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
91
-
92
- public MainWindow()
93
- {
94
- InitializeComponent();
95
- DataContext = this;
96
- Reset();
97
- }
98
-
99
- private void Reset()
100
- {
101
- Items.Clear();
102
- Items.Add(new Item
103
- {
104
- //Title = "taroko",
105
- CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u7/72728/",
106
- PictureFileName = "FBvOAPUf_thumbnail_32x32.jpg",
107
- });
108
- // インデクサを作れば↓のようなアクセスはできますがうれしいかどうか。。。
109
- Items[0][0] = "taroko";
110
-
111
- Items.Add(new Item
112
- {
113
- Title = "TN8001",
114
- CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/",
115
- PictureFileName = "KnkDDC5A_thumbnail_32x32.jpg",
116
- });
117
- }
118
-
119
- private void ResetButton_Click(object sender, RoutedEventArgs e) => Reset();
120
-
121
- private void PrintButton_Click(object sender, RoutedEventArgs e)
122
- {
123
- foreach (var item in Items)
124
- {
125
- Debug.WriteLine($"Title:{item.Title}");
126
- Debug.WriteLine($"CurrentFolder:{item.CurrentFolder}");
127
- Debug.WriteLine($"PictureFileName:{item.PictureFileName}");
128
- Debug.WriteLine("");
129
- }
130
- }
131
-
132
- private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e)
133
- {
134
- if (!(sender is DataGridCell cell)) return;
135
- if (!(cell.DataContext is Item item)) return;
136
-
137
- var header = cell.Column.Header as string;
138
- var exeFolder = AppDomain.CurrentDomain.BaseDirectory;
139
- switch (header)
140
- {
141
- case "カレントフォルダ":
142
- var folderDlg = new VistaFolderBrowserDialog { SelectedPath = exeFolder, };
143
- if (folderDlg.ShowDialog() == true)
144
- {
145
- item.CurrentFolder = folderDlg.SelectedPath;
146
- }
147
- break;
148
- case "画像":
149
- var fileDlg = new OpenFileDialog
150
- {
151
- InitialDirectory = exeFolder,
152
- Filter = "画像ファイル(*.bmp,*.jpg,*.jpeg,*.png)|*.bmp;*.jpg;*.jpeg;*.png",
153
- };
154
- if (fileDlg.ShowDialog() == true)
155
- {
156
- item.CurrentFolder = Path.GetDirectoryName(fileDlg.FileName);
157
- item.PictureFileName = Path.GetFileName(fileDlg.FileName);
158
- }
159
- break;
160
- default: return;
161
- }
162
- }
163
- }
164
-
165
- public class Item : Observable
166
- {
167
- // View側(ユーザー操作)からしか変更されないのであれば、PropertyChangedを呼ぶ必要はない
168
- public string Title { get; set; }
169
- //private string _Title;
170
- //public string Title { get => _Title; set => Set(ref _Title, value); }
171
-
172
- // ViewModel側(コード)でも変更がある場合は、PropertyChangedを呼びViewに変更を伝えなければならない
173
- // 関連したプロパティPicturePath)があるので冗長だが
174
- // 通常の独立したプロパティなら↑コメント化したTitleのような書き方
175
- private string _CurrentFolder = ""; // Path.CombineでArgumentNullExceptionがでるで^^;
176
- public string CurrentFolder
177
- {
178
- get => _CurrentFolder;
179
- set
180
- {
181
- if (Set(ref _CurrentFolder, value))
182
- {
183
- // PicturePathも変わるわけなので、同じくPropertyChangedを呼ぶ
184
- OnPropertyChanged(nameof(PicturePath));
185
- }
186
- }
187
- }
188
-
189
- private string _PictureFileName = "";
190
- public string PictureFileName
191
- {
192
- get => _PictureFileName;
193
- set
194
- {
195
- if (Set(ref _PictureFileName, value))
196
- {
197
- OnPropertyChanged(nameof(PicturePath));
198
- }
199
- }
200
- }
201
-
202
- // PicturePathのPropertyChangedを呼べばViewが再取得するので、最新の値に常になる
203
- public string PicturePath => Path.Combine(CurrentFolder, PictureFileName);
204
-
205
-
206
- // インデクサ
207
- public string this[int index]
208
- {
209
- get
210
- {
211
- switch (index)
212
- {
213
- case 0: return Title;
214
- case 1: return CurrentFolder;
215
- case 2: return PictureFileName;
216
- default: throw new ArgumentOutOfRangeException();
217
- }
218
- }
219
- set
220
- {
221
- switch (index)
222
- {
223
- case 0: Title = value; break;
224
- case 1: CurrentFolder = value; break;
225
- case 2: PictureFileName = value; break;
226
- default: throw new ArgumentOutOfRangeException();
227
- }
228
- }
229
- }
230
- }
231
-
232
- // INotifyPropertyChangedのよくある実装 ViewModelBase等の場合も
233
- public class Observable : INotifyPropertyChanged
234
- {
235
- public event PropertyChangedEventHandler PropertyChanged;
236
- protected bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
237
- {
238
- if (Equals(storage, value)) return false;
239
- storage = value;
240
- OnPropertyChanged(propertyName);
241
- return true;
242
- }
243
- protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
244
- }
245
- }
246
- ```
1
+ > クリックされたセルの修正(プログラム側で)⇒ObservableCollectionの修正方法
2
+
3
+ `ObservableCollection`はアイテムの増減を通知しますが、中のアイテムの状態は関知しません。
4
+ [ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.objectmodel.observablecollection-1)
5
+
6
+ [INotifyCollectionChanged インターフェイス (System.Collections.Specialized) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.specialized.inotifycollectionchanged)
7
+
8
+ 中のアイテムのプロパティの変更は、`INotifyPropertyChanged`を実装する必要があります。
9
+ [INotifyPropertyChanged インターフェイス (System.ComponentModel) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.inotifypropertychanged)
10
+
11
+ 実例や詳細はいっぱい記事がありますので検索してください。
12
+
13
+ > myData[row][col]="A"みたいに編集したりしたいです。
14
+
15
+ インデクサを作ればそのようなアクセスはできますがうれしいですかね?
16
+
17
+ ---
18
+
19
+ CSVの編集ツールという感じなのでしょうか。
20
+ 「存在しないフォルダやファイルは入力させたくない&手入力はだるい」のでダイアログで選択するような仕様と。
21
+
22
+ ソートやカラム移動を禁止していので今の実装で問題さそうでが、`DataGridCell`にイベントを付けたほうがるかに簡単だと思います
23
+ そうすれば禁止にする必要もなくなります(`Column.Header`で`switch`するのは微妙な気もしますが^^;
24
+
25
+
26
+ 注)カレントフォルダの扱いが違っと思います(完動コードとして作りにくいので^^;
27
+ 注)新規行追加するにはタイトルを先に入れる必要があります(先にカレントフォルダ等をダブルクリックしても無視する)
28
+ [NuGet Gallery | Ookii.Dialogs.Wpf 3.1.0](https://www.nuget.org/packages/Ookii.Dialogs.Wpf/3.1.0)を使用
29
+ ```xml
30
+ <Window
31
+ x:Class="Questions341357.MainWindow"
32
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
34
+ Width="1000"
35
+ Height="300"
36
+ HorizontalAlignment="Center">
37
+ <DockPanel>
38
+ <StackPanel DockPanel.Dock="Bottom">
39
+ <Button Click="ResetButton_Click" Content="リセット" />
40
+ <Button Click="PrintButton_Click" Content="確認" />
41
+ </StackPanel>
42
+
43
+ <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}">
44
+ <DataGrid.CellStyle>
45
+ <Style TargetType="DataGridCell">
46
+ <EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick" />
47
+ </Style>
48
+ </DataGrid.CellStyle>
49
+ <DataGrid.Columns>
50
+ <DataGridTextColumn Binding="{Binding Title}" Header="タイトル" />
51
+ <DataGridTextColumn
52
+ Width="*"
53
+ Binding="{Binding CurrentFolder}"
54
+ Header="カレントフォルダ"
55
+ IsReadOnly="True" />
56
+ <DataGridTextColumn
57
+ Width="300"
58
+ Binding="{Binding PictureFileName}"
59
+ Header="画像"
60
+ IsReadOnly="True" />
61
+ <DataGridTemplateColumn Width="32" IsReadOnly="True">
62
+ <DataGridTemplateColumn.CellTemplate>
63
+ <DataTemplate>
64
+ <Image Source="{Binding PicturePath}" />
65
+ </DataTemplate>
66
+ </DataGridTemplateColumn.CellTemplate>
67
+ </DataGridTemplateColumn>
68
+ </DataGrid.Columns>
69
+ </DataGrid>
70
+ </DockPanel>
71
+ </Window>
72
+ ```
73
+
74
+ ```cs
75
+ using Microsoft.Win32;
76
+ using Ookii.Dialogs.Wpf;
77
+ using System;
78
+ using System.Collections.ObjectModel;
79
+ using System.ComponentModel;
80
+ using System.Diagnostics;
81
+ using System.IO;
82
+ using System.Runtime.CompilerServices;
83
+ using System.Windows;
84
+ using System.Windows.Controls;
85
+ using System.Windows.Input;
86
+
87
+ namespace Questions341357
88
+ {
89
+ public partial class MainWindow : Window
90
+ {
91
+ public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
92
+
93
+ public MainWindow()
94
+ {
95
+ InitializeComponent();
96
+ DataContext = this;
97
+ Reset();
98
+ }
99
+
100
+ private void Reset()
101
+ {
102
+ Items.Clear();
103
+ Items.Add(new Item
104
+ {
105
+ //Title = "taroko",
106
+ CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u7/72728/",
107
+ PictureFileName = "FBvOAPUf_thumbnail_32x32.jpg",
108
+ });
109
+ // インデクサを作れば↓のようなアクセスはできますがうれしいかどうか。。。
110
+ Items[0][0] = "taroko";
111
+
112
+ Items.Add(new Item
113
+ {
114
+ Title = "TN8001",
115
+ CurrentFolder = "https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/",
116
+ PictureFileName = "KnkDDC5A_thumbnail_32x32.jpg",
117
+ });
118
+ }
119
+
120
+ private void ResetButton_Click(object sender, RoutedEventArgs e) => Reset();
121
+
122
+ private void PrintButton_Click(object sender, RoutedEventArgs e)
123
+ {
124
+ foreach (var item in Items)
125
+ {
126
+ Debug.WriteLine($"Title:{item.Title}");
127
+ Debug.WriteLine($"CurrentFolder:{item.CurrentFolder}");
128
+ Debug.WriteLine($"PictureFileName:{item.PictureFileName}");
129
+ Debug.WriteLine("");
130
+ }
131
+ }
132
+
133
+ private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e)
134
+ {
135
+ if (!(sender is DataGridCell cell)) return;
136
+ if (!(cell.DataContext is Item item)) return;
137
+
138
+ var header = cell.Column.Header as string;
139
+ var exeFolder = AppDomain.CurrentDomain.BaseDirectory;
140
+ switch (header)
141
+ {
142
+ case "カレントフォルダ":
143
+ var folderDlg = new VistaFolderBrowserDialog { SelectedPath = exeFolder, };
144
+ if (folderDlg.ShowDialog() == true)
145
+ {
146
+ item.CurrentFolder = folderDlg.SelectedPath;
147
+ }
148
+ break;
149
+ case "画像":
150
+ var fileDlg = new OpenFileDialog
151
+ {
152
+ InitialDirectory = exeFolder,
153
+ Filter = "画像ファイル(*.bmp,*.jpg,*.jpeg,*.png)|*.bmp;*.jpg;*.jpeg;*.png",
154
+ };
155
+ if (fileDlg.ShowDialog() == true)
156
+ {
157
+ item.CurrentFolder = Path.GetDirectoryName(fileDlg.FileName);
158
+ item.PictureFileName = Path.GetFileName(fileDlg.FileName);
159
+ }
160
+ break;
161
+ default: return;
162
+ }
163
+ }
164
+ }
165
+
166
+ public class Item : Observable
167
+ {
168
+ // View側(ユーザー操作)からしか変更されないのであれば、PropertyChangedを呼ぶ必要はない
169
+ public string Title { get; set; }
170
+ //private string _Title;
171
+ //public string Title { get => _Title; set => Set(ref _Title, value); }
172
+
173
+ // ViewModel側コードでも変更がある場合はPropertyChangedを呼びViewに変更を伝えなければならない
174
+ // 関連したプロパティ(PicturePath)があるで冗長だが、
175
+ // 通常独立したプロパティなら↑のコメント化したTitleのような書き方
176
+ private string _CurrentFolder = ""; // Path.CombineでArgumentNullExceptionがでるので^^;
177
+ public string CurrentFolder
178
+ {
179
+ get => _CurrentFolder;
180
+ set
181
+ {
182
+ if (Set(ref _CurrentFolder, value))
183
+ {
184
+ // PicturePathも変わるわけなので、同じくPropertyChangedを呼ぶ
185
+ OnPropertyChanged(nameof(PicturePath));
186
+ }
187
+ }
188
+ }
189
+
190
+ private string _PictureFileName = "";
191
+ public string PictureFileName
192
+ {
193
+ get => _PictureFileName;
194
+ set
195
+ {
196
+ if (Set(ref _PictureFileName, value))
197
+ {
198
+ OnPropertyChanged(nameof(PicturePath));
199
+ }
200
+ }
201
+ }
202
+
203
+ // PicturePathのPropertyChangedを呼べばViewが再取得するので、最新の値に常になる
204
+ public string PicturePath => Path.Combine(CurrentFolder, PictureFileName);
205
+
206
+
207
+ // インデクサ
208
+ public string this[int index]
209
+ {
210
+ get
211
+ {
212
+ switch (index)
213
+ {
214
+ case 0: return Title;
215
+ case 1: return CurrentFolder;
216
+ case 2: return PictureFileName;
217
+ default: throw new ArgumentOutOfRangeException();
218
+ }
219
+ }
220
+ set
221
+ {
222
+ switch (index)
223
+ {
224
+ case 0: Title = value; break;
225
+ case 1: CurrentFolder = value; break;
226
+ case 2: PictureFileName = value; break;
227
+ default: throw new ArgumentOutOfRangeException();
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ // INotifyPropertyChangedのよくある実装 ViewModelBase等の場合も
234
+ public class Observable : INotifyPropertyChanged
235
+ {
236
+ public event PropertyChangedEventHandler PropertyChanged;
237
+ protected bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
238
+ {
239
+ if (Equals(storage, value)) return false;
240
+ storage = value;
241
+ OnPropertyChanged(propertyName);
242
+ return true;
243
+ }
244
+ protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
245
+ }
246
+ }
247
+ ```
247
248
  ![アプリ画像](8fdc53d1c61112de73459c1f0839cd67.png)