カスタムバインディングタイプを作成して、ランタイムバインディングシステムを拡張できます。カスタムバインディングタイプを作成するには、クラスを作成し、CustomBinding クラスから継承します。
CustomBinding は IBinding インターフェースに似ており、1 つのインスタンスではなく複数のバインディングインスタンスを登録することができます。CustomBinding は拡張性のエントリーポイントで、バインディングを更新するための Update メソッドのみを提供します。ただし、以下のメソッドを実装して、バインディングが登録または登録解除された場合、および要素のデータソースコンテキストが変更されたときにコールバックを受け取ることができます。
バインディングタイプのデータソースとデータソースパスを定義するには、IDataSourceProvider インターフェースを実装します。バインディングシステムは、このインターフェースが提供する dataSource と dataSourcePath のプロパティを使用して、解決されたデータソースとデータソースパスを決定します。これらのプロパティは、階層から取得した値をオーバーライドするため “ローカル” と呼ばれます。ここで重要なのは、これらの “ローカル” プロパティを変更しても、要素自体やその子孫には影響しないということです。
デフォルトでは、バインディングシステムはフレームごとに CustomBinding インスタンスを更新します。
更新トリガーを定義するには、以下のメソッドを使用します。
MarkDirty:バインディングオブジェクトを dirty として設定し、次のサイクル中に更新されるようにします。updateTrigger:この enum プロパティを使用して、バインディングの更新方法を変更します。BindingResult:このメソッドを使用して、更新プロセスをカスタマイズします。BindingResult は、更新が成功したかどうかを示す構造体です。status と message が含まれています。
BindingResult には、status と message が含まれます。status として以下の値を使用できます。
BindingResult メソッドの Pending の結果を使用して、次のサイクルでバインディングオブジェクトを更新する必要があるかどうかを、バインディングシステムに通知できます。
このセクションでは、カスタムバインディングタイプを作成し、UI Builder、UXML、および C# でバインディングを設定する方法を例を挙げて説明します。
以下の例では、現在の時刻を表示するカスタムバインディングタイプを作成します。ラベルの text プロパティにバインドして、時計を作成できます。
using System;
using Unity.Properties;
using UnityEngine.UIElements;
[UxmlObject]
public partial class CurrentTimeBinding : CustomBinding
{
[UxmlAttribute]
public string timeFormat = "HH:mm:ss";
public CurrentTimeBinding()
{
updateTrigger = BindingUpdateTrigger.EveryUpdate;
}
protected override BindingResult Update(in BindingContext context)
{
var timeNow = DateTime.Now.ToString(timeFormat);
var element = context.targetElement;
if (ConverterGroups.TrySetValueGlobal(ref element, context.bindingId, timeNow, out var errorCode))
return new BindingResult(BindingStatus.Success);
// Error handling
var bindingTypename = TypeUtility.GetTypeDisplayName(typeof(CurrentTimeBinding));
var bindingId = $"{TypeUtility.GetTypeDisplayName(element.GetType())}.{context.bindingId}";
return errorCode switch
{
VisitReturnCode.InvalidPath => new BindingResult(BindingStatus.Failure, $"{bindingTypename}: Binding id `{bindingId}` is either invalid or contains a `null` value."),
VisitReturnCode.InvalidCast => new BindingResult(BindingStatus.Failure, $"{bindingTypename}: Invalid conversion from `string` for binding id `{bindingId}`"),
VisitReturnCode.AccessViolation => new BindingResult(BindingStatus.Failure, $"{bindingTypename}: Trying set value for binding id `{bindingId}`, but it is read-only."),
_ => throw new ArgumentOutOfRangeException()
};
}
}
カスタムバインディングタイプを作成すると、UI Builder の Add binding ウィンドウに表示されます。UI Builder でバインディングを設定するには、Add Binding ウィンドウで Type リストから CurrentTimeBinding を選択します。
このバインディングに相当する UXML は以下のとおりです。
<ui:Label text="Label">
<Bindings>
<CurrentTimeBinding property="text" />
</Bindings>
</ui:Label>
このバインディングに相当する C# は以下のとおりです。
var label = new Label();
label.SetBinding("text", new CurrentTimeBinding());
パフォーマンスを最適化するには、以下のヒントとベストプラクティスに従ってください。
BindingUpdateTrigger.OnSourceChanged を使用する: ソースで変更が検出されたときにのみ更新が必要なバインディングタイプの場合は、updateTrigger を BindingUpdateTrigger.OnSourceChanged に設定します。これにより、バインディングタイプが必要なときにのみ更新され、パフォーマンスが最適化されます。BindingUpdateTrigger.WhenDirty を使用する: バインディングタイプを手動で更新し、即時の同期を必要としない場合は、updateTrigger を BindingUpdateTrigger.WhenDirty に設定します。これにより、バインディングタイプが更新されたときに手動で制御できるようになり、柔軟な同期の制御が可能になります。Update コールバックの代わりに OnActivated、OnDeactivated、または OnDataSourceChanged のコールバックを使用してください。これらのコールバックは、特定のライフサイクルイベントでトリガーされるため、不要な更新が減り、効率が向上します。適切なコールバックを使用することで、バインディングタイプの動作を最適化し、必要なときに正確に更新を行うことができます。