モノトーンの伝説日記

OBS Studio と Blackmagic Design が大好き。

UWP で ResoruceDictionary 内に x:Bind な DataTemplate を作りたい件。 [UWP topics! (1)]

 いわゆる分離設計ですかね。UWP において x:Bind はパフォーマンス向上に必要ですが、それを使いたい、となると DataTemplate として別途定義していくことになります。しかし、そうなると 1 ファイルにすし詰めになるのでは? という疑問がある人への解消方法です。

概要

  1. そもそも x:Bind とはなんなの?
  2. コード ビハインドってなんで動いてるか知ってる?
  3. 同じようにすればいけるよね?
  4. 完成 & まとめ

1. そもそも x:Bind とはなんなの?

 コンパイル時 Bindingです。

 言ってしまえばコレ。このコードは分離された (parted) なコード ビハインドに生成されます。

2. コード ビハインドってなんで動いてるか知ってる?

 ページにはコードビハインドが必須と思ってる方もいそうですが、実のところ、xaml テーマとか見てもらったらわかるんですけど、コードビハインドって概念がそもそも存在しないものもありますよね。

 じゃあ何トリガーに起動してるの? って疑問があると思うんです。単純です。 x:Class

<Page
    x:Class="XBindLecture.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XBindLecture">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    </Grid>
</Page>

 そう、一番上にある x:Class="XBindLecture.MainPage" がコード ビハインド生成の手がかりなわけです。

3. 同じようにすればいけるよね?

 ResoruceDictionary にもこの論が通用します。例えば空のプロジェクトに DetailView.xaml っていう ResourceDictionary なファイル作ってみますか。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XBindLecture">
    
</ResourceDictionary>

 ここに、まず x:Class をつけます。名前は XBindLecture.Views.DetailView でいいでしょう。

<ResourceDictionary
    x:Class="XBindLecture.Views.DetailView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XBindLecture">
    
</ResourceDictionary>

 次に DetailView.xaml.cs というコードビハインドを記述するファイルを生成します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XBindLecture.Views
{
    class DetailView
    {
    }
}

 これをそれっぽく (デフォルト テンプレートのように) します。

using Windows.UI.Xaml;

namespace XBindLecture.Views
{
    public sealed partial class DetailView : ResourceDictionary
    {
        public DetailView()
        {
            this.InitializeComponent();
        }
    }
}

 こんな感じで準備おkです。

 まあそうですね、簡略化するために以下のモデル考えますか。名前と年齢と性別ね。

public enum SexType : byte { Unknown = 0, Male, Female };
public sealed class Person
{
  public string Name { get; }
  public ushort Age { get; }
  public SexType Sex { get; }
}
public sealed class PersonViewModel
{
  public Person OriginalSource { get; }

  public string Name => this.OriginalSource.Name;
  public string Age => this.OriginalSource.Age.ToString();
  public string Sex => this.OriginalSource.Sex.ToString();
}

 このモデルに即してテキトーな UI 作ります。

 つくりました。

<ResourceDictionary
    x:Class="XBindLecture.Views.DetailView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:XBindLecture.ViewModels">

    <DataTemplate
        x:DataType="vm:PersonViewModel">
        <StackPanel>
            <TextBlock
                Text="{x:Bind Name}" />
            <TextBlock
                Text="{x:Bind Age}" />
            <TextBlock
                Text="{x:Bind Name}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

 これを表示する DetailPage を作りましょう。

 つくりました。

<Page
    x:Class="XBindLecture.Views.DetailPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:v="using:XBindLecture.Views"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <v:DetailView />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <ContentPresenter
        EntranceNavigationTransitionInfo.IsTargetElement="True"
        Content="{x:Bind Person}"
        ContentTemplate="{StaticResource ProfileViewTemplate}" />
</Page>

 ポイントは <v:DetailView /> とするところです。

 <ResourceDictionary Source="DetailView.xaml" /> でよさそうに見えますが、これでは xaml loader が xaml を読み込むだけで、インスタンスによる初期化が実行されません。つまりコードビハインド類が実行されないので、正しく binding ができないようになります。

 あとはメインページでも同じようなことをすればいいので繰り返しになるので省略します。

4. 完成 & まとめ

f:id:mntone:20151227101452p:plain

f:id:mntone:20151227101502p:plain

 ということで完成です。UWP には必須の技術ですね!

github.com

 プログラムもあげてます! こちらもぜひ!

参照したサイト

http://igrali.com/2015/06/14/how-to-use-compiled-bindings-xbind-from-a-resource-dictionary/

Thanks to igor ralic.