Home > C# > 目的別資料 > 地域設定

添付プロパティ結合

WPFのための地域設定の手引き Localization Guidance for WPF

原文

WPF Localization Guidance 「Rick StrahlとMichele Leroux Bustamante(2009年6月)」の和訳です。
地域設定の実装の参考にしてください。

関連ファイル

添付プロパティ結合


Attached Property Binding

WPFは、他の強力な仕組みでリソースを要素にフッキングできます。:

添付プロパティ、添付プロパティは、サブクラスを構築することなく、既存の要素とコントロールを拡張できます。 むしろ添付プロパティは、既存の要素に添付することができます。 そして、添付プロパティが変更されると、コードを実行できます。 プロパティが、変更されたとき、変更された値は、変更が発生したUIElementのインスタンスに添えて渡されます。

地域設定のために、これは、いくつかの興味深い機会を提供します。 あなたが、使い慣れているのなら、WindowsフォームとWebフォームの両方は、コントロール・プロパティの地域設定のために、コントロール・グループを使用します。 例えば、blName.Content、lblName.Tooltipを、コントロール上の対応するプロパティに結合します。 そして、あなたは、ここに説明される添付プロパティの概念について、十分な理解をすでに持っているでしょう。

マークアップ拡張機能で、すべての単一の結合したプロパティは、私たちが、Content、Tooltip、Width、Marginなどと結び付けました。 結合構文でXAMLコードの中で明示的にマッピングする必要があります。添付プロパティを使用して、 あなたは、明示されたプロパティ結合を取り除き、代わりに、あなたが示す、要素のマーカー・プロパティを指定する、 あなたが、それを地域設定したい場所のモデルに移行することで、処理を分かりやすくすることができます。 識別子に基づいたリソースは、続いて、Resxファイルの中で一致し、すべて一致したプロパティは、結合します。

Translate拡張機能で定義される、Translateプロパティを使用すると、次のようになります。:


<Window 
  x:Name="wndLocalizationInfo" 
  x:Class="WpfLocalizationResx.LocalizationInfo" 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:res="http://www.west-wind.com/WpfControls" 
  xmlns:local="clr-namespace:WpfLocalizationResx.LocalResources" 
  Background="{DynamicResource WindowBackgroundBrush}" 
  SizeTocontent="WidthAndHeight" 
  Title="LocalizationInfo" 
  res:TranslationExtension.TranslateResourceSet= 
      "WpfLocalizationResx.LocalResources.LocalizationInfoRes" 
  res:TranslationExtension.TranslateResourceAssembly= 
      "WpfLocalizationResx" 
> 
    <Label x:Uid="lblAttachedValue" 
      x:Name="lblAttachedValue" 
      res:TranslationExtension.Translate="True" Grid.Row="3" Grid.Column="1" 
  >Hello World Attached Property Text (non res text)
   Hello Worldの添付プロパティテキスト(非RESテキスト)</Label> 
</Window> 
</Label>
</Window>
			

関係する3つの添付プロパティがあります。:

Ÿ TranslateResourceSet

リソース・セットを指定するドキュメント・レベル・プロパティは、このドキュメントの翻訳のために使用されます。

Ÿ TranslateAssembly

アセンブリの名前は、地域設定で使用されることになっています。省略される(そして、通常、あなたができる)場合、文書クラスのアセンブリが使用されます。

Ÿ Translate

単純なフラグは、どんな要素にも適用することができます。あなたは、変換する必要のあるプロパティを持っています。 あなたは、単純に、res:TranslationExtension.Translate="True" を要素の上で設定します。 そして、その次に、変換するリソースの設定で、キーの構文が、elementName.propertyName の場所に、適切なResxリソース・キーを与えます。 文字列の値だけ、この方法で指定することができます。

LocalizationInfo.resx:

リソース・キー  値 
lblAttachedValue.Tooltip  Hello World(Res) 
lblAttachedValue.Tooltip  Hello World Tooltip 

LocalizationInfo.de.resx:

リソース・キー  値 
lblAttachedValue.Content  Hallo Welt (Res) 
lblAttachedValue.Tooltip  Hallo Welt Tooltip 

ページが読み込まれるとき、TranslateプロパティはTrueに割り当てられます。 それは、起動するための、添付プロパティのユーザー定義したコードを発生させます。 ResourceManagerは、続いて、一番上のレベルのドキュメントがTranslateExtension.ResourceSet要素で与えられるように、適切なリソースの設定を見つけ出します。 そして、接頭辞として、要素の名前を持つすべてのプロパティを見つけ出します。すべての一致するリソース・キーは、Reflectionを使用して、適切な要素に割り当てられます。 Figure 22で示される、わずかに省略されたコードは、これがどのように達成されることができるかを具体的に説明します。 (完全なWpfControlsサンプルを参照するのは、こちら。)。

FIGURE 23:

Attached Propertyの実装は、すべての一致する要素のプロパティを割り当てます。


public class TranslationExtension : DependencyObject 
{ 

  public static readonly DependencyProperty TranslateProperty = 
    DependencyProperty.RegisterAttached("Translate", 
      typeof(bool), 
      typeof(TranslationExtension), 
      new FrameworkPropertyMetadata(false, 
        FrameworkPropertyMetadataOptions.AffectsRender, 
        new PropertyChangedCallback(OnTranslateChanged)) );
  public static void SetTranslate(UIElement element, bool value) 
  { 
    element.SetValue(TranslateProperty, value); 
  } 
  public static bool GetTranslate(UIElement element) 
  { 
    return (bool)element.GetValue(TranslateProperty); 
  } 
  private static void OnTranslateChanged(DependencyObject d, 
        DependencyPropertyChangedEventArgs e) { 
    if ((bool) e.NewValue == true) 
      TranslateKeys(d as FrameworkElement); 
  } 

  static void TranslateKeys(UIElement element) 
  { 
    if (DesignerProperties.GetIsInDesignMode(element)) 
      return;

    // just display XAML doc values
    // 実際に、XAMLドキュメントの値を表示する
    // walk the element tree to find the top level document
    // 一番上のレベルのドキュメントを見つけ出すために、要素ツリーを少しずつ動かします。
    FrameworkElement root = WpfUtils.GetRootVisual(element as FrameworkElement);
    if (root == null) 
      return; // must be framework element to find root
    // フレームワーク要素は、ルートを見つけ出す必要があります。

    // Retrieve the ResourceSet and assembly from the top level element
    // 一番上のレベルの要素からResourceSetとアセンブリを取得します。
    string resourceset = root.GetValue(TranslateResourceSetProperty) 
        as string; 
    string resourceAssembly = 
        root.GetValue(TranslateResourceAssemblyProperty) as string; 

    ResourceManager manager = null; 
    manager = LocalizationSettings.GetResourceManager(resourceset,
                                   resourceAssembly);

    // Look at Neutral Culture to find all keys
    // すべてキーを見つけ出すために、Neutral Cultureを調べます。
    ResourceSet set = manager.GetResourceSet( 
                CultureInfo.InvariantCulture, 
                true, true); 
    IDictionaryEnumerator enumerator = set.GetEnumerator(); 

    while (enumerator.MoveNext()) 
    { 
      string key = enumerator.Key as string; 
      if (key.StartsWith(element.Uid + ".")) 
      { 
        string property = key.Split('.')[1] as string; 
        object value = manager.GetObject(key); // enumerator.Value;

        // Bind the value AFTER control has initialized or else the 
        // default will override what we bind here
        // 値を結合されたAFTERコントロールは、初期化、もしくは、既定の設定が、
        // 私たちがここで結合するもので上書きされることになります。

        root.Initialized += delegate 
        {
        try 
        { 
          PropertyInfo prop = element.GetType() 
                        .GetProperty(property, 
            BindingFlags.Public | 
            BindingFlags.Instance | 
            BindingFlags.FlattenHierarchy | 
            BindingFlags.IgnoreCase); 
          prop.SetValue(element, value, null); 
        } 
          catch(Exception ex) 
        { 
          Trace.WriteLine(…); 
        } 
      }; 
    } 
  } 
} 
}
			

コードは、クラスTranslateExtensionから構成されています。それは、TranslateResourcesSetとTranslateAssemblyと同じように、 TranslatePropertyのための添付プロパティの宣言が含まれています。それは、ここでは示されません。 依存関係プロパティのような、添付プロパティは、静的に宣言され、値が設定されるか、取得されると呼び出される一連のメソッドから構成されます。 静的RegisterAttachedメソッドは、添付プロパティを登録します。そして、他の要素に対して適用するために、XAMLパーサで利用できるように作成します。 Translateプロパティのための構文は、単純にres:TranslateExtension.Translate="True"です。 それに設定された時、登録されたOnTranslateChange変更イベントがフックされ、マークアップに発生します。

OnTranslateChangeは、要素のx:Uid値と一致する、すべてのリソースIDを見つけ出すための役割を果たすTranslateKeysを呼び出します。 あなたは、要素の上に、あるいは、全体のプロジェクトですべてのXAML要素のためのx:Uid属性を自動的に作成するために、 msbuild /t:updateuidを用いて、以前に説明したLocBamlの地域設定として、手動でx:Uid値を割り当てることができます。

TranlsateKeysは、ドキュメントのルート要素に割り当てられた、ResourceSetとアセンブリ添付プロパティに基づいて使用するために、ResourceSetを理解します。 このように処理することは、一度だけで、それぞれの要素の上ではなくドキュメントで、これらの値の設定を可能にします。 ページ・レベルのコンテキストを効果的に与えます。 内部で取得するResourceManageは、リソースに設定される名前でキャッシュに格納されます。 それで、この処理は、かなり効果的です。

一旦、ResourceManagerが利用できると、すべての既定のリソースを取得するために、使用されます。 (カルチャとしてCultureInfo.Invariantを利用する。) それは、利用できるすべてのリソース・キーが含まれています。 コードは、要素として同じ名前で始まる何かを、見つけ出すために、それらの全てをループします。 私たちは、区切り文字の点を加えて、x:Uidプロパティに基づいて動作しています。(すなわち「lblName」)。 一致が見つかった場合、プロパティ名は、完全なリソース・キーから抽出されます。 そして、リフレクションは、プロパティにリソース値を適用するために使用されます。 Reflection値の割当ては、要素のInitializedイベントにフックする、匿名型デリゲートの内側に発生することに注意します。 これは、プロパティに地域設定された値を確実に割り当てます。XAMLは、その既定の値を割り当てたあと、ドキュメントで定義されます。

この添付プロパティの方法の利点は、あなたは、長い結合構文で結合することを望む、個々のプロパティのすべてをマッピングする必要がないことです。 あなたが、一つだけ設定している、Translate添付プロパティを使用して、要素の切り取り、貼付け可能なプロパティを示します。 それは、どんな一致する特性も見つけ出し、それらに適用することによって、変換されます。また、Translateプロパティは、デザイナーで設定することができます。 それで、あなたはXAMLに移動する必要はありません。また、あなたが、あなたが、通常、行う、あなたの既定のテキストを指定することができることに注意します。 あなたが、地域設定していない場合、既定の値は、いつものように適用されます。そして、それは、あなたが、デザイナーで見ると思われるものです。 実行時に、地域設定されたテキストは、初期化が完了したあと、あらゆる既定の静的なテキストに地域設定された値で上書きされます。

添付プロパティ割当ての長所は以下のとおりです。:

より簡単な要素マッピング

それぞれの要素に対して単一の汎用プロパティを設定します。 そして、プロパティにマッピングするための要素キーを割り当てるために、命名規約を使用します。 プロパティは、XAMLで簡単に入力し、簡単に切り取り、張り付けることができます。 あるいは、あなたは、Visual StudioやBlendデザイナーで値を設定することができます。

控え目な設計時の値

あなたは、通常通り、既定のプロパティ値と内容を入力し続けることができます。

ユーザー定義した結合構文がありません。

標準プロパティ値/内容は、デザイナーで表示され、地域設定されたテキストだけ実行時に表示されます。

開発が完了した後に追加するのが簡単です。

あなたは、単純に、それぞれの要素に一つの添付プロパティの値を追加します。 それは、開発が完了した後、XAMLで切り取り、貼付けすることによって、この機能を追加することが簡単です。 簡単に、地域設定可能な何かを作成するために、要素にres:TranslateExtension.Translate="True"属性を追加します。 そして、Resxファイルに、適切なResxキーを追加します。

添付プロパティ割当ての短所は、次のとおりです。:

型コンバータのサポートが存在しません。

この仕組みは、実行時に、動的に要素プロパティ名を推測するため、基盤となる依存関係プロパティとそれらの型コンバータにアクセスがありません。 あなたは、地域設定のために、文字列や単純な数値の作業にのみ使用することができます。 この動作は、x:Static結合で説明されるのと同じです。

わずかに効率的ではありません。

添付プロパティは、コンテクストなしで、ジェネリック的に呼び出されます。 あなたは、値と要素を手に入れまが、依存関係プロパティに結び付いた情報がありません。 これは、添付プロパティが呼び出されるたびに、コンテクストは、ページ、リソースの設定とアセンブリのルート要素を再確立する必要があることを表しています。 加えて、それぞれのアクセスのために、全体のResourceSetは、要素を見つけ出すために列挙する必要があります。 それは、要素のx:Uidと一致しています。最後に、リフレクションは、値を割り当てるために使用されます。 きわめて複雑なドキュメントとたくさんの地域設定可能な値のため、わずかな処理能力は、おそらく、全てこの繰返しにあたるでしょう。

含まれる要素のみの作業

このコンポーネントが、TranslateResourceSetとTranslateAssemblyを取得するため、ルート要素に依存する、このコンポーネントは、 要素コンテナ階層構造の精密検査が完了しているルート要素を見つけ出すことができる必要があります。 しかしながら、ContextMenuのような、いくつかのUI要素は、ドキュメントの論理ツリーの一部ではなく、 そして、ルート要素を見つけ出すことができません。これらのコンポーネントとそれらの子供たちのために、 あなたは、明示的に、『parentlessな』要素の上に、あるいは、マークアップ拡張機能を使用して、 TranslateResourceSetとTranslateAssemblyを追加する必要があります。

このエントリーをはてなブックマークに追加

Home PC C# Illustration

Copyright (C) 2011 Horio Kazuhiko(kukekko) All Rights Reserved.
kukekko@gmail.com
ご連絡の際は、お問い合わせページのURLの明記をお願いします。
「掲載内容は私自身の見解であり、所属する組織を代表するものではありません。」