xaml指向アプリケーションの開発における重要なポイントの1つは、バインディングの使用です。
バインディングは
仲介者 (仲介者)であり、関連するオブジェクト間でどのプロパティ値が同期されるかを助けます。
明らかですが、重要なニュアンスに注意する価値があります:
バインディングは何らかの方法でオブジェクトの相互作用を指しますが、ガベージコレクションを阻止しません!Bindingクラスからの継承は許可されていますが、セキュリティ上の理由から、作業のメインロジックに関連付けられている
ProvideValueメソッドのオーバーライドは許可されていません。 これは、どういうわけか、開発者が
コンバーターのパターンを使用するように促します。これは、バインディングのテーマと密接に関連しています。
バインディングは非常に強力なツールですが、場合によっては、その宣言が冗長であり、たとえばローカライズなどの通常の使用では不便であることがわかります。 この記事では、コードをよりクリーンで美しいものにする、シンプルでエレガントな方法を分析します。
xamlでバインディングを宣言すること
は 、2つの方法で有効です。
<TextBlock> <TextBlock.Text> <Binding ...> </TextBlock.Text> </TextBlock>
<TextBlock Text="{Binding ...}"/>
明らかに、最初の方法はあまり簡潔ではありませんが、2番目の方法は
マークアップ拡張機能の使用に基づいて最も頻繁に使用されます。
WPFプラットフォームでは、
カスタムマークアップ拡張機能を作成できます。 たとえば、ローカライズに便利です。
<TextBlock Text="{Localizing AppTitle}"/>
最も単純なケースでは、
MarkupExtensionクラスを継承し、キーによって目的の値を取得する
ProvideValueメソッドを実装する必要があります。
しかし、そのような実装は、プログラム実行中の言語のホットスワップをサポートしていません。 この改善を行うには、まず、ローカライズされたインターフェイス要素へのリンクを保存する必要があります。次に、あまり明らかではないが、何らかの方法でアプリケーションのLocalizingクラスのインスタンスへのリンクを作成し、ガベージコレクションから保護する必要があります。言語変更イベントからサブスクリプションとサブスクリプションを正しく実装する必要があります。
これらのことを誤って行うことで、ビューが作成され、アプリケーション中に動的に消えた場合にメモリリークが発生することが保証されます。多くの場合、これは事実です。 つまり、一見複雑ではないように思われる機能を追加すると、
イベント への
弱いリンクと
弱いサブスクリプションの重要なトピックに対処する必要があり
ます 。 また、コードはそれほど単純ではありません。
さらに、
Windows Phone 、
Windows Store 、および
Xamarin.Formsのxaml
プラットフォームでは、カスタムマークアップ拡張機能を作成する方法がありません。
これにより、バインディングをマークアップ拡張機能として使用するという考え方が浮かび上がります ...
茂みに打ち勝つのはやめましょう、必要なものは次のとおりです。
public abstract class BindingExtension : Binding, IValueConverter { protected BindingExtension() { Source = Converter = this; } protected BindingExtension(object source)
バインディングがそれ自体のコンバーターであることは注目に値します。 その結果、
MarkupExtensionクラスから継承する場合と非常によく似た動作が得られますが、さらに、
ガベージコレクションを制御するための標準メカニズムを使用すること
も可能です。現在、ローカライズのロジックはどこにも簡単ではありません。
public partial class Localizing : Base.BindingExtension { public static readonly Manager ActiveManager = new Manager(); public Localizing() { Source = ActiveManager; Path = new PropertyPath("Source"); } public Localizing(string key) { Key = key; Source = ActiveManager; Path = new PropertyPath("Source"); } public string Key { get; set; } public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var key = Key; var resourceManager = value as ResourceManager; var localizedValue = resourceManager == null || string.IsNullOrEmpty(key) ? ":" + key + ":" : (resourceManager.GetString(key) ?? ":" + key + ":"); return localizedValue; } }
public partial class Localizing { public class Manager : INotifyPropertyChanged { private ResourceManager _source; public ResourceManager Source { get { return _source; } set { _source = value; PropertyChanged(this, new PropertyChangedEventArgs("Source")); } } public string Get(string key, string stringFormat = null) { if (_source == null || string.IsNullOrWhiteSpace(key)) return key; var localizedValue = _source.GetString(key) ?? ":" + key + ":"; return string.IsNullOrEmpty(stringFormat) ? localizedValue : string.Format(stringFormat, localizedValue); } public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { }; } }
文字の大文字と小文字を変更する機能を簡単に追加できます。
public partial class Localizing : Base.BindingExtension { public enum Cases { Default, Lower, Upper } public static readonly Manager ActiveManager = new Manager(); public Localizing() { Source = ActiveManager; Path = new PropertyPath("Source"); } public Localizing(string key) { Key = key; Source = ActiveManager; Path = new PropertyPath("Source"); } public string Key { get; set; } public Cases Case { get; set; } public override string ToString() { return Convert(ActiveManager.Source, null, Key, Thread.CurrentThread.CurrentCulture) as string ?? string.Empty; } public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var key = Key; var resourceManager = value as ResourceManager; var localizedValue = resourceManager == null || string.IsNullOrEmpty(key) ? ":" + key + ":" : (resourceManager.GetString(key) ?? ":" + key + ":"); switch (Case) { case Cases.Lower: return localizedValue.ToLower(); case Cases.Upper: return localizedValue.ToUpper(); default: return localizedValue; } } }
xamlでは、レコードは便利で美しいように見えますが、異なるプラットフォーム上のマークアップパーサーにはいくつかの制限があります。
<TextBlock Text="{Localizing AppTitle, Case=Upper}"/> <TextBlock Text="{Localizing Key=AppDescription}"/> <TextBlock Text="{m:Localizing Key=AppTitle, Case=Upper}"/> <TextBlock Text="{m:Localizing Key=AppDescription}"/> <TextBlock> <TextBlock.Text> <m:Localizing Key=AppDescription> </TextBlock.Text> </TextBlock>
WPFで必須の
m:プレフィックスを取り除くには、マークアップ拡張機能を別のアセンブリに配置し、
Properties / AssemblyInfo.csで次のディレクティブを指定する必要があります。
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Aero.Markup")] [assembly: XmlnsPrefix("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "m")]
Windows Phoneまたは
ストアでプレフィックス名を調整するには:
[assembly: XmlnsDefinition("clr-namespace:Aero.Markup;assembly=Aero.Phone", "Aero.Markup")] [assembly: XmlnsPrefix("clr-namespace:Aero.Markup;assembly=Aero.Phone", "m")]
WPF バインディング拡張機能を使用しても、通常のマークアップ拡張機能が除外されるわけではありませんが、場合によってはより安全で簡単なオプションです。
また、これはすべてローカライズだけに限定されず、他の多くの目的に適しています...実証済みのアプローチは、
少し前に説明した
Aeroフレームワークライブラリで集中的に使用されます。
また、これらのメカニズムがすべて動作しているのを確認できるサンプルプロジェクトも付属しています。 ご清聴ありがとうございました!