[win8] DefaultViewModel と DataContext は同時に使えない

昨日の続きで、気になるところをチェック。

もともと、WPF とか Silverlight では DataContext を使っていた…ような気がするので、じゃあ、Windows ストア アプリで登場した「DefaultViewModel」との違いは? とか、同時に使えるのか? ってところを試してみます。

ええ、結論から言えば「同時には使えません」。と言うか、おそらく DefaultViewModel のマップオブジェクトは内部動作として DataContext を使っているだろうから、同時には使えんのだろう、ってのが結論ですね。

■画面の準備

実験的に下記のような表を作っておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<Grid HorizontalAlignment="Left" Height="243" Margin="32,38,0,0" Grid.Row="1" VerticalAlignment="Top" Width="727">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition Width="261*"/>
        <ColumnDefinition Width="266*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" FontSize="24" Margin="10" TextWrapping="Wrap" Text="ID"/>
    <TextBlock Grid.Row="1" FontSize="24" Margin="10" TextWrapping="Wrap" Text="Name"/>
    <TextBlock Grid.Row="2" FontSize="24" Margin="10" TextWrapping="Wrap" Text="Address"/>
    <TextBlock Grid.Row="3" FontSize="24" Margin="10" TextWrapping="Wrap" Text="Telephone"/>
    <!-- Buiding by DataContext -->
    <TextBlock
        Text="{Binding ID}"
        Grid.Row="0" Grid.Column="1" FontSize="24" Margin="10" TextWrapping="Wrap"/>
    <TextBlock
        Text="{Binding Name}"
        Grid.Row="1" Grid.Column="1" FontSize="24" Margin="10" TextWrapping="Wrap" />
    <TextBlock
        Text="{Binding Address}"
        Grid.Row="2" Grid.Column="1" FontSize="24" Margin="10" TextWrapping="Wrap" />
    <TextBlock
        Text="{Binding Telephone}"
        Grid.Row="3" Grid.Column="1" FontSize="24" Margin="10" TextWrapping="Wrap"/>
 
    <!-- Buiding by DefaultViewModel -->
    <TextBlock
        Text="{Binding Item.ID}"
        Grid.Row="0" Grid.Column="2" FontSize="24" Margin="10" TextWrapping="Wrap"/>
    <TextBlock
        Text="{Binding Item.Name}"
        Grid.Row="1" Grid.Column="2" FontSize="24" Margin="10" TextWrapping="Wrap" />
    <TextBlock
        Text="{Binding Item.Address}"
        Grid.Row="2" Grid.Column="2" FontSize="24" Margin="10" TextWrapping="Wrap" />
    <TextBlock
        Text="{Binding Item.Telephone}"
        Grid.Row="3" Grid.Column="2" FontSize="24" Margin="10" TextWrapping="Wrap"/>
</Grid>

何をやっているかというと、左に DataContext でのバインディング、右に DefaultViewModel でのバインディングの表を作っているだけです。

■実験コード

  • DataContext だけで設定するパターン
  • DefaultViewModel をだけで使うパターン
  • 両方を使うパターン

の3つを用意しておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/// <summary>
/// DataContext に設定する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void clickDataContext(object sender, RoutedEventArgs e)
{
    var item = new DataModel()
    {
        ID = 10,
        Name = "masuda",
        Address = "Tokyo",
        Telephone = "03-0000-0000"
    };
    this.DataContext = item;
 
}
 
/// <summary>
/// DefaultViewModel に設定する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void clickDefaultViewModel(object sender, RoutedEventArgs e)
{
    var item = new DataModel()
    {
        ID = 10,
        Name = "tomoaki",
        Address = "Osaka",
        Telephone = "06-1111-0000"
    };
    this.DefaultViewModel["Item"] = item;
}
 
/// <summary>
/// 両方で設定する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void clickDual(object sender, RoutedEventArgs e)
{
    var item1 = new DataModel()
    {
        ID = 10,
        Name = "masuda",
        Address = "Tokyo",
        Telephone = "03-0000-0000"
    };
    var item2 = new DataModel()
    {
        ID = 10,
        Name = "tomoaki",
        Address = "Osaka",
        Telephone = "06-1111-0000"
    };
    this.DataContext = item1;
    this.DefaultViewModel["Item"] = item2;
 
}

さて、これで Dual ボタンをクリックするとどうなるのか?両方にうまく表示されるのか?というと、実は違って、DataContext のほうだけが表示されます。

なんか不思議ですが「DataContext」と「DefaultViewModel」のボタンを交互に押すと、「DataContext」のほうしか残りません。つーか、おそらく DefaultViewModel のほうは上書きされちゃったのでしょう。
なので、うっかり DataContext を使ったコードを流用すると、新しい DefaultViewModel を使ったものが動かなくなる、っていう「落とし穴」がありますね。まあ、どちらか片方の方式で統一するということで。

ちなみに、DataContext のほうは、オブジェクトを渡して内部のプロパティ名を Binding するので、複数のオブジェクトをバインディングすることができないという罠があります。コンテキストをひとつの DataModel としてまとめられればいいのですが、画面によっては複数のオブジェクトを割り付けたい場合は多々あります。
なので、DefaultViewModel のように、複数のオブジェクト(Item オブジェクトと SubItem オブジェクトとか)を渡せるのは、改善なんでしょう、やっぱり。

カテゴリー: C#, windows 8 パーマリンク