回答編集履歴

3

一部補足。(WinFormsのノリで~のところ)

2016/08/25 16:11

投稿

flied_onion
flied_onion

スコア2604

test CHANGED
@@ -500,7 +500,7 @@
500
500
 
501
501
 
502
502
 
503
- 簡単な例では、MainWindow.xamlにもうひとつボタンを配置して、クリックイベントを作ります(ここではWinFormsのノリで作っていいです。配置してダブルクリックするとか、イベントプロパティから作るとか)
503
+ 簡単な例では、MainWindow.xamlにもうひとつボタンを配置して、クリックイベントを作ります(ここでは動きを知りたいだけなのでWinFormsのノリで作っていいです。配置してダブルクリックするとか、イベントプロパティから作るとか)
504
504
 
505
505
  で、以下の様なコードを書いてみます。
506
506
 

2

「MyButtonにMyButtonをBindingして…の謎」以降

2016/08/25 16:11

投稿

flied_onion
flied_onion

スコア2604

test CHANGED
@@ -202,6 +202,336 @@
202
202
 
203
203
  名前は`OrigButton`とします。
204
204
 
205
-
205
+ Buttonと名付けたものの、デフォルトではUserControlを継承してますのでButtonになってません。
206
-
206
+
207
- …ちょっとここでで投稿ます。またとで続き書き
207
+ ずはButtonを継承てButtonにしてしょう
208
+
209
+
210
+
211
+ Xamlとコードビハインド両方の修正が必要です。
212
+
213
+
214
+
215
+ OrigButton.xaml
216
+
217
+ ```xaml
218
+
219
+ <Button x:Class="tt45592.OrigButton"
220
+
221
+ ```
222
+
223
+ (タグをUserControlからButtonに。タグを閉じる方は自動的にかわっている(`</Button>`になってる)はずなので割愛)
224
+
225
+ OrigButton.xaml.cs
226
+
227
+ ```c#
228
+
229
+ public partial class OrigButton : Button {
230
+
231
+ ```
232
+
233
+ 同様にUserControlからButtonに。
234
+
235
+ (こっちは継承情報消してもXamlにあれば良かったと思うけど、一応書き換える方向で)
236
+
237
+
238
+
239
+ テキストブロックを置くためにXamlを編集します。今回はGridのまま。
240
+
241
+ OrigButton.xaml
242
+
243
+ ```xaml
244
+
245
+ <Grid>
246
+
247
+ <TextBlock Text="{Binding SourceText}"></TextBlock>
248
+
249
+ </Grid>
250
+
251
+ ```
252
+
253
+ Bindingが現れました。じゃぁC#側でDataContextを追加...**しません**。
254
+
255
+
256
+
257
+ そのままMainWindow.xamlにこのコントロールを追加してあげます。
258
+
259
+ ```xaml
260
+
261
+ <StackPanel>
262
+
263
+ <TextBox Text="{Binding SourceText, Mode=TwoWay}"></TextBox>
264
+
265
+ <TextBox Text="{Binding SourceText, Mode=OneWay}"></TextBox>
266
+
267
+ <TextBox Text="{Binding SourceText, Mode=TwoWay}"></TextBox>
268
+
269
+ <local:OrigButton></local:OrigButton>
270
+
271
+ </StackPanel>
272
+
273
+ ```
274
+
275
+ 質問にあった `DataContext=`もありません。
276
+
277
+ やたら細いボタンが追加されたら、実行します。
278
+
279
+
280
+
281
+ ※ 突然 `local:`というのが出てきました。これはMainWindowのxamlの方で、`xmlns:local="clr-namespace:tt45592"`と定義されているものです。このプロジェクトの名前空間の別名と思っておいてください。
282
+
283
+ ※ 無理やりC#風に書くなら `using local = tt45592`(名前空間の別名)からの、`local.OrigButton`といった感じです。
284
+
285
+
286
+
287
+ 実行してみると、OrigButtonのDataContextに対して何もしていないのに`Hello, Binding`と表示されました。
288
+
289
+
290
+
291
+ ![Hello, Binding on the OrigButton](cb61ab730f92166f00cea3c77fac4892.png)
292
+
293
+
294
+
295
+ 自分のDataContextになくとも、親のコンテナのDataContextにあるのでそれが利用されています。
296
+
297
+
298
+
299
+ ※ ここから少し駆け足気味になります。
300
+
301
+
302
+
303
+ ところで、現実問題、どれもこれも同じプロパティをバインドすることは、まぁありませんね。
304
+
305
+ ボタンにはボタンのためのプロパティが欲しくなりました。
306
+
307
+ MyClassに新しいプロパティを追加してあげましょう。
308
+
309
+
310
+
311
+ MyClass.cs
312
+
313
+ ```c#
314
+
315
+ public class MyClass {
316
+
317
+ public string SourceText { get; set; }
318
+
319
+ public string ButtonText { get; set; }
320
+
321
+ ```
322
+
323
+
324
+
325
+ このままじゃButtonTextは空なので、MainWindowのmyClassを作っているところでこれの値もセットします。
326
+
327
+ MainWindow.xaml.cs
328
+
329
+ ```c#
330
+
331
+ // MainWindowコンストラクタの中
332
+
333
+ var myClass = new MyClass();
334
+
335
+ myClass.SourceText = "Hello, Binding";
336
+
337
+ myClass.ButtonText = "OrigButton!"; // ここ
338
+
339
+ DataContext = myClass;
340
+
341
+ ```
342
+
343
+
344
+
345
+ バインド対象も変えないといけませんね
346
+
347
+ OrigButton.xaml
348
+
349
+ ```xaml
350
+
351
+ <Grid>
352
+
353
+ <TextBlock Text="{Binding ButtonText}"></TextBlock>
354
+
355
+ </Grid>
356
+
357
+ ```
358
+
359
+ 実行して変わったのが確認できたと思います。
360
+
361
+
362
+
363
+ MyClassのプロパティ、このまま増えるとどれがボタン用でどれが他の用途かわかりませんね。
364
+
365
+ OrigButton用にクラスを作ってあげて、それ(のインスタンス)をMyClassに持たせてあげましょう。
366
+
367
+
368
+
369
+ MyClass.cs
370
+
371
+ ```c#
372
+
373
+ public class MyClass {
374
+
375
+ public string SourceText { get; set; }
376
+
377
+ public MyClass() {
378
+
379
+ MyButton = new MyButton();
380
+
381
+ }
382
+
383
+ public MyButton MyButton { get; } // あえてですがこの名前かぶりに注意。あとgetterのみ。
384
+
385
+ }
386
+
387
+
388
+
389
+ public class MyButton {
390
+
391
+ public string ButtonText { get; set; }
392
+
393
+ }
394
+
395
+ ```
396
+
397
+
398
+
399
+
400
+
401
+ MainWindow.xaml.csのコンストラクタも修正しないといけません。
402
+
403
+ ```c#
404
+
405
+ // MainWindowコンストラクタの中
406
+
407
+ myClass.SourceText = "Hello, Binding";
408
+
409
+ myClass.MyButton.ButtonText = "OrigButton!"; // これ
410
+
411
+ DataContext = myClass;
412
+
413
+ ```
414
+
415
+
416
+
417
+ Bindingはどうなるでしょうか。
418
+
419
+ このように指定します。上のコンストラクタでも同じようにアクセスしてますよね。
420
+
421
+ OrigButton.xaml
422
+
423
+ ```xaml
424
+
425
+ <Grid>
426
+
427
+ <TextBlock Text="{Binding MyButton.ButtonText}"></TextBlock>
428
+
429
+ </Grid>
430
+
431
+ ```
432
+
433
+
434
+
435
+ 実行して、変化がない(しかし構成を変えることができた)ことを確認します。
436
+
437
+
438
+
439
+ ところで、OrigButtonにしてみれば、「MyButton.ButtonText」よりも「ButtonText」の方が良かったりします。
440
+
441
+ なぜなら、DataContextの構成が MyButtonプロパティ(クラスじゃないんです)を持っていることまで知らないといけないためです。
442
+
443
+ 欲しいのはButtonTextを持っているなにかだけが使える方がシンプルなわけで、MyButtonというのを間に挟んでいるのは(DataContextに指定した)クラスの都合に過ぎず、OrigButtonは知らなくても良いわけです。
444
+
445
+ (知ってなきゃいけない実装にしたいときはそうすればいいですが、今回は欲しいのはButtonTextだけです)
446
+
447
+
448
+
449
+ で、それに対する対策の一つとして、MainWindowの方で、OrigButtonのDataContextを設定してあげるという手があります。
450
+
451
+ それがこれです。
452
+
453
+ MainWindow.xaml
454
+
455
+ ```xaml
456
+
457
+ <!-- 省略 -->
458
+
459
+ <local:OrigButton DataContext="{Binding MyButton}"></local:OrigButton>
460
+
461
+ </StackPanel>
462
+
463
+ ```
464
+
465
+
466
+
467
+ これで、OrigButtonのDataContextには(MainWindowのDataContextのおさがりではなく)ここで指定したMyButton(MainWindowのDataContextのMyButtonプロパティ)がバインドされます。OrigButtonも併せて修正してみましょう。
468
+
469
+
470
+
471
+ OrigButton.xaml
472
+
473
+ ```xaml
474
+
475
+ <Grid>
476
+
477
+ <TextBlock Text="{Binding ButtonText}"></TextBlock>
478
+
479
+ </Grid>
480
+
481
+ ```
482
+
483
+
484
+
485
+ そして、質問のxamlの様になりました。
486
+
487
+ 後半大分駆け足ですが、これが基本です。
488
+
489
+ なお、これだけだと、ただのクラスをBindしているにすぎません(ViewModelと呼ぶには役割がはっきりしていない)。Modelの役割、ViewModelの役割を他資料で参照してみてください。
490
+
491
+
492
+
493
+ ---
494
+
495
+
496
+
497
+ 最後にINotifyPropertyChangedについて簡単に。
498
+
499
+ TwoWayバインディングしたTextBoxはテキストを書き換えたら、プロパティも書き換わるし、それを参照している他のTextBoxも書き換わりました。では変更の通知がなぜ必要なのかという疑問がでるかもしれません。
500
+
501
+
502
+
503
+ 簡単な例では、MainWindow.xamlにもうひとつボタンを配置して、クリックイベントを作ります(ここではWinFormsのノリで作っていいです。配置してダブルクリックするとか、イベントプロパティから作るとか)
504
+
505
+ で、以下の様なコードを書いてみます。
506
+
507
+ ```c#
508
+
509
+ private void Button_Click(object sender, RoutedEventArgs e) {
510
+
511
+ MessageBox.Show(((MyClass)DataContext).SourceText);
512
+
513
+ ((MyClass)DataContext).MyButton.ButtonText = "Greeting!!";
514
+
515
+ }
516
+
517
+ ```
518
+
519
+ 1行目はSourceTextも変わったことを確認できるようにしているだけです。
520
+
521
+ 2行目で、ButtonTextを書き換えています。しかし、OrigButtonのテキストは変わりません。
522
+
523
+
524
+
525
+ こういう場合もあります。そしてこのような場合、OrigButtonのBindingの方で、「何がおきたら元ネタ(Source)が変わったとするか」設定してあげます。そこで「プロパティが変わったと報告が来たらSourceは更新されたトリガとする」という設定をしておいてあげて、
526
+
527
+ DataContext(この場合は、ButtonTextを持つMyButtonクラス)には、INotifiProertyChangedの実装を通じて、「(MyButton) <xxプロパティ変わったよ!」と通知する機能を追加してあげます。
528
+
529
+ これが組み合わさって、OrigButtonはプロパティが変わったときSourceの更新を行う事ができるようになります。
530
+
531
+
532
+
533
+
534
+
535
+ 以上。長くなりましたが、他の資料を読む上での助けになれば。幸いです。
536
+
537
+ わかりにくかったらごめんなさい。

1

記述ちょっと修正

2016/08/25 16:02

投稿

flied_onion
flied_onion

スコア2604

test CHANGED
@@ -40,9 +40,9 @@
40
40
 
41
41
  です。
42
42
 
43
- バインディングはデータソース(DataContext)であるクラスの情報をコントロール(とは限らないが)に提供する事です。
43
+ バインディングはデータソース(DataContext)であるクラス(インスタンス)の情報をコントロールに提供する事です。
44
-
44
+
45
- DataGridViewのDataSourceにDataTableをセットすると、DataGridViewにその表が表示されます。
45
+ WinFormsでたとえるなら、DataGridViewのDataSourceにDataTableをセットすると、DataGridViewにその表が表示されます。
46
46
 
47
47
  OneWayとTwoWayは、この例では DataTableを変更するとDataGridViewのセル表示も変わるが、セルをいじってもDataTableの内容が変わらないような状態がOneWay。どっちをいじっても双方とも変わるのがTwoWayです。
48
48