Windows 10アプリケーションでコンパイルされたデータバインディング


Windows UAPの革新の1つは、コンパイルされるバインダーを作成できるようになったことです。 この革新により、アプリケーションのパフォーマンス(ダウンロード速度を含む)が大幅に向上します。 以前は、データバインディングはリフレクションに基づいていたため、低速でした。 さらに、コンパイルされたバインダーのコードをデバッグする方がはるかに便利になりました。

アプリケーションのコンパイル時に形成されるバインディングを作成するには、{Binding}の代わりに{x:Bind}を使用する必要があります。
バインドは、分離コードクラスと他のページコントロールの両方に対して実行できます。
例:

<Grid x:Name="grd" Tag=" "> <TextBlock FontSize="40" Text="{x:Bind grd.Tag}" /> </Grid> 

Tagを{x:Bind}の値として単に指定すると、PageオブジェクトのTag属性に対してバインディングが発生します。

コンパイルされたバインダーは厳密に型指定され(特定の型のオブジェクトにのみバインドできます)、コンパイル時にチェックされます。つまり、エラーがどこかで見つかった場合、コンパイルが中断されます。
WPFバインディングと同様に、OneTime、OneWay、TwoWayなどのさまざまなバインディングモードが可能です。 デフォルトのモードはOneTimeです。
次のグラフで、コンパイル済みバインダーと従来のバインダーのパフォーマンスの比較を確認できます。



バインドされたオブジェクトの変更に応じてアプリケーションウィンドウの制御を変更するために、開発者によく知られているインターフェイスINotifyPropertyChanged、IObservableVector、INotifyCollectionChangedがサポートされています。

xを使用する簡単な例を考えてみましょう:バインド
単純なEmployeeクラスを作成します。

 public class Employee { private string _name; private string _organization; private int? _age; public string Name { get { return _name; } set { _name = value;} } public string Organization { get { return _organization; } set { _organization = value; } } public int? Age { get { return _age; } set { _age = value; } } } 

ページのクラス(デフォルトではMainPage)に名前空間を追加します。

 using System.Collections.ObjectModel; 

ObservableCollectionを使用するために必要です。 INotifyCollectionChangedの実装が含まれているため、ObservableCollectionコレクションを使用します。つまり、コレクションに要素を追加または削除するたびに、コレクションデータがバインドされているコントロールが更新されます。
コレクションを宣言します。

 ObservableCollection<Employee> manager = new ObservableCollection<Employee>(); 

コレクションにいくつかのアイテムを追加します。 ページの初期化後にやろう。

  public MainPage() { this.InitializeComponent(); manager.Add(new Employee { Age = 45, Name = "", Organization = "   " }); manager.Add(new Employee { Age = 25, Name = "", Organization = " " }); manager.Add(new Employee { Age = 22, Name = "", Organization = "   " }); } 

これで、ページのXAMLコードで次の構成を使用できます。

 <ListView ItemsSource="{x:Bind manager}" Width="200" Height="250" BorderThickness="1" BorderBrush="Black"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:Employee"> <TextBlock Text="{x:Bind Name}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> 

ここでは、 local:Employeeの下で、ページヘッダーからの名前空間リンクが使用されます。

 <Page x:Class="CompiledBindingsDemo.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CompiledBindingsDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> 

その結果、以下が得られます。



{x:Bind}の値は次のとおりです: Converter、ConverterLanguage、ConverterParameter、FallbackValue、Mode、Path、TargetNullValue
{Binding}を使用したデータバインディングとは異なり、次の値は使用されません: Source、ElementName、RelativeSource、UpdateSourceTrigger
それらは不要になり、他の機能に置き換えられました。 RelativeSourceが要素の名前とその属性に置き換えられたとしましょう(最初の例を参照)。 x:BindのバインディングソースUpdateSourceTriggerを更新するすべての可能性のうち、デフォルトではPropertyChangedのみが残ります(バインディングオブジェクトのプロパティを変更した直後にバインディングソースを更新します)。 TextBox.Textのみがフォーカスを失った後にソースを更新します。

もう1つの優れた新機能はx:Phase属性です。これにより、アイテムのプログレッシブロードが可能になります。
私たちの例を考えてみましょう:

 <DataTemplate x:DataType="local:Employee"> <StackPanel Orientation="Vertical"> <TextBlock Text="{x:Bind Name}" x:Phase="1" /> <TextBlock Text="{x:Bind Organisation}" x:Phase="3" /> <TextBlock Text="{x:Bind Age}" x:Phase="2" /> </StackPanel> </DataTemplate> 

この場合、要素のロード/描画の順序は強制的に設定されます。 最初のものは名前を持つ最初のTextBlockをロードし、2番目のものは年齢を持つ3番目のもの、そして最後のものは組織の名前を持つテキストをロードします。 この場合、順序は特に重要ではありませんが、メディアメディアを使用する場合は特に重要です。 値x:Phase = "0"を指定した場合、この要素は起動順序に設定されていないことを意味します。

Bindings.Initialize()を使用してデータバインディングを初期化できますが、ページの読み込み中に強制初期化が発生するため、強制初期化は必要ありません。
Bindings.Update()を呼び出して、非同期データを更新できます。 同じメソッドは、最小のリソースを消費するOneTimeバインディングに使用できます。
データの変更を追跡せずにバインディングを一時停止するには、 Bindings.StopTracking()を呼び出します。 そして、追跡を続けるためにUpdate()が呼び出されます。

さらに、イベントバインディングを使用できるようになりました。 デフォルトでは、クリックイベントは次のように宣言されます。

 <Button Click="PokeEmployee">Poke Employee</Button> 

このイベントバインディングを使用して、クラス内でイベントを宣言できます。

 <Button Click="{x:Bind SomeDataClass.Poke}">Poke Employee</Button> 

Pokeという単語に関する英語-ロシア語辞書の意味の1つは、「データ要素レコード」です。 したがって、明らかに、イベントバインディングの主な目的は、データの必須の変更です。
サンプルを変更します。

  <DataTemplate x:DataType="local:Employee"> <StackPanel Orientation="Vertical"> <TextBlock Text="{x:Bind Name}" x:Phase="1" /> <TextBlock Text="{x:Bind Organization}" x:Phase="3" /> <TextBlock Text="{x:Bind Age,Mode=OneWay}" x:Phase="2" /> <Button Click="{x:Bind Poke}" Content=" "/> </StackPanel> </DataTemplate> 

そして、Employeeクラスに以下を追加します。

  public void Poke() { this.Age = this.Age+1; } 

しかし、残念なことに、デバッグ時にAge値が予想どおりに増加することは注目に値しますが、インターフェースは更新されません。 これは、ObservableCollectionコレクションがバインディングを更新するのは、アイテムが追加または削除された場合のみだからです。 更新が発生し、データが変更された場合、INotifyPropertyChangedインターフェイスを実装する必要があります。 これは以前とまったく同じ方法で行われます。 名前空間を追加することにより:

 using System.ComponentModel; 

インターフェイス宣言:

  public class Employee : INotifyPropertyChanged 

そして、プロパティ変更イベントを実装することにより:

  public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } 

これで、Ageを設定した後、セッターでこのイベントをトリガーできます。

  public int? Age { get { return _age; } set { _age = value; RaisePropertyChanged("Age"); } } 

すべてのクラスコード:
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ComponentModel; namespace CompiledBindingsDemo { public class Employee : INotifyPropertyChanged { private string _name; private string _organization; private int? _age; public string Name { get { return _name; } set { _name = value;} } public string Organization { get { return _organization; } set { _organization = value; } } public int? Age { get { return _age; } set { _age = value; RaisePropertyChanged("Age"); } } public void Poke() { this.Age = this.Age+1; } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } } 


接続されたイベントは、 ボイドPoke()パラメーターなしでトリガーできます。
およびvoid Pokeパラメーター(オブジェクト送信者、RoutedEventArgs e)
または、基本イベントタイプvoid Poke(オブジェクト送信者、オブジェクトe)のパラメーターを使用
過負荷はサポートされていません。
もちろん、メソッドの名前はPokeである必要はありません。
イベント{x:Bind}は、ICommandやEventToCommandなどのMVVMイベントを置き換えることができますが、CanExecuteなどのパラメーターはサポートされていません。

コンパイル済みバインディングは、古き良きコンバータをサポートします。 つまり、IValueConverterインターフェイスを実装するクラスを作成し、そのクラスで変換を行うことができます。

クラスは次のようになります。
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.UI.Xaml.Data; namespace CompiledBindingsDemo { class ConverterExample : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value == null) return string.Empty; // -    value string newtext = value.ToString(); newtext = newtext.ToUpper(); return newtext; } public object ConvertBack(object value, Type targetType, object parameter, string language) { //   throw new NotImplementedException(); } } } 


アプリケーションにそのようなクラスがある場合、ページのXAMLリソースに追加できます。

  <Page.Resources> <local:ConverterExample x:Name="ThatsMyConverter"/> </Page.Resources> 

これで、データバインディングにコンバーターを使用できます。

 <TextBlock Text="{x:Bind Name,Converter={StaticResource ThatsMyConverter}}" /> 

そして、名前に含まれるテキストの大文字と小文字は、表示されると、コンバーターによって(大文字の)大文字に変更されます。

コンパイルされたバインダーは、すべての状況に適しているわけではありません。 一部では、{x:Bind}の代わりに{Binding}で従来のデータバインディングを使用した方が良い場合があります
{Binding}は、JSONまたはその他のオブジェクトの型指定されていないディクショナリで機能します。 {x:Bind}は、特定のデータ型に関する情報がないと機能しません。
アヒルのタイピング(何かがアヒルのように動き、カックがアヒルのように動く場合、それをアヒルのように扱います)-異なるオブジェクトの同じプロパティ名は{Binding}でうまく機能しますが、{x:Bind}では機能しません。 たとえば、Text = "{Binding Age}"はPersonクラスとWineクラスの両方で機能します。 x:Bindを使用するには、基本クラスまたはインターフェイスを作成する必要があります。
プログラムによるリンクは{Binding}でのみ可能です。 {x:Bind}を使用する場合、実行時にバインディングを追加または削除する方法はありません。
{x:Bind}はセッターのスタイルでは使用できませんが、DataTemplateで使用できます(上記で説明)。

githubで見つけることができる例

Source: https://habr.com/ru/post/J265993/


All Articles