XamlWriterとバインディング

おやすみHabraコミュニティ。

私はあなたの会社への招待状を受け取り、すぐに誰かに役立つと思われる何かを書くことにしました...厳密に判断しないでください。



私は、1つのオープンソースプロジェクトの開発者の1人です。その主な部分の1つは、WPFオブジェクトモデルの一部としてベクトルグラフィックをXAML形式で保存する必要があるグラフィカルエディターです。 開発中に問題が発生しました。 コード(または読み込まれたXAMLファイル)から作成されたバインディングは、標準のXamlWriterでシリアル化しようとすると、XAMLに保存されません。 判明したように、これはMSDNで説明されている標準のXamlWriterの動作です。 私はネットで解決策を見つけようとしましたが、 CodeProjectに関する記事は1つしか見つかりませんでした 。 残念ながら、判明したように、このソリューションはいくつかの理由で複雑なXAMLドキュメントには適していません。 既に独自のシリアライザーを作成するオプションの検討を開始しました.TemplateBinding拡張機能が標準的な方法で完全に保存されていることがわかり、すべてが失われたわけではないと考えさせられ、MS Reference Source Codeとデバッガーを装備して、問題の調査を開始しました。 それが私に起こったことです。





デバッグプロセス中に、XamlWriterがDependencyPropertyが何らかの種類のバインドに関連付けられていることを検出すると、このバインドをMarkupExtensionタイプに変換する特定の種類のバインド(ExpressionConverter)に登録されているコンバーターを見つけようとします。 そのようなコンバーターが見つかった場合、その助けを借りて、バインディングがMarkupExtensionに送られ、その後シリアル化されます。



したがって、解決策は次のようになります。 まず、ExpressionConverterから継承した次のクラスを定義します。これにより、BindingからMarkupExtensionへの変換が提供されます。

class BindingConvertor : ExpressionConverter

{

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)

{

if (destinationType== typeof (MarkupExtension))

return true ;

else return false ;

}

public override object ConvertTo(ITypeDescriptorContext context,

System.Globalization.CultureInfo culture, object value , Type destinationType)

{

if (destinationType == typeof (MarkupExtension))

{

BindingExpression bindingExpression = value as BindingExpression;

if (bindingExpression == null )

throw new Exception();

return bindingExpression.ParentBinding;

}



return base .ConvertTo(context, culture, value , destinationType);

}

}




* This source code was highlighted with Source Code Highlighter .








この後、このコンバーターを登録する必要があります。 これを行うには、静的ヘルパーメソッドを定義し、アプリケーションの初期化時に呼び出します。 このように:



static class EditorHelper

{

public static void Register <T, TC>()

{

Attribute [] attr = new Attribute [1];

TypeConverterAttribute vConv = new TypeConverterAttribute( typeof (TC));

attr[0] = vConv;

TypeDescriptor.AddAttributes( typeof (T), attr);

}







}

....

EditorHelper.Register <BindingExpression,BindingConvertor>();




* This source code was highlighted with Source Code Highlighter .








それがすべてのようですが、後で判明したように、いいえ。 バインディングはシリアル化されましたが、Sourceプロパティは気付かれずに空白になりました;出力Xamlファイルでは単純にスキップされたため、このようなシリアル化の使用は非常に限られていました。 デバッグを再び装備し、シリアル化システムが、いくつかの属性と任意のがらくたの組み合わせに基づいて、プロパティがシリアル化の対象であることを決定することを発見しました。 変更されたかどうか、デフォルト値があるかどうか、コードのどこかにあるcなMicrosoftが、実際の値を返す代わりに「return false」を挿入したようなもので、プロパティをシリアル化する必要はありません。 最初は非常に怒っていたので、これで終わりだとすでに決心していましたが、それを考えてから次の解決策を見つけました。 コードによると、プロパティにDefaultValue(null)属性がある場合、シリアル化する必要があることは明らかでした。 バインドのために型記述子を再定義するだけで、SourceプロパティのPropertyInfoが次のようにDefaultValue属性の内容に置き換えられることがわかります。



class BindingTypeDescriptionProvider : TypeDescriptionProvider

{

private static TypeDescriptionProvider defaultTypeProvider =

TypeDescriptor.GetProvider( typeof (System.Windows.Data.Binding));



public BindingTypeDescriptionProvider()

: base (defaultTypeProvider)

{

}



public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,

object instance)

{

ICustomTypeDescriptor defaultDescriptor =

base .GetTypeDescriptor(objectType, instance);



return instance == null ? defaultDescriptor :

new BindingCustomTypeDescriptor(defaultDescriptor);

}

}



class BindingCustomTypeDescriptor : CustomTypeDescriptor

{

public BindingCustomTypeDescriptor(ICustomTypeDescriptor parent)

: base (parent)

{



}



public override PropertyDescriptorCollection GetProperties()

{



return GetProperties( new Attribute []{});

}



public override PropertyDescriptorCollection GetProperties( Attribute [] attributes)

{

PropertyDescriptorCollection pdc = new PropertyDescriptorCollection( base .GetProperties().Cast<PropertyDescriptor>().ToArray());



string [] props = { "Source" , "ValidationRules" };



foreach (PropertyDescriptor pd in props.Select(x => pdc.Find(x, false )))

{

PropertyDescriptor pd2;

pd2 = TypeDescriptor.CreateProperty( typeof (System.Windows.Data.Binding), pd, new Attribute [] { new System.ComponentModel.DefaultValueAttribute( null ), new System.ComponentModel.DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content) });





pdc.Add(pd2);



pdc.Remove(pd);

}



return pdc;

}



}




* This source code was highlighted with Source Code Highlighter .








さて、アプリケーションを初期化するときにこのタイプ記述子を登録します。

TypeDescriptor.AddProvider(新しいBindingTypeDescriptionProvider()、typeof(System.Windows.Data.Binding));



簡単に言うと、ここで行ったことは、Binding型に対して、SourceおよびValidationRulesプロパティをPropertyDescriptosに置き換える新しい型記述子を返すTypeDecriptorProiderを作成しました。 これはすべて、GetProperties()メソッドで行われます。



XAMLでWPFオブジェクトを保存する必要があるのは、XAMLでオブジェクトを保存するための標準手順に従うことだけです。



StringBuilder outstr= new StringBuilder ();

XmlWriterSettings settings = new XmlWriterSettings();

settings.Indent = true ;

settings.OmitXmlDeclaration = true ;

XamlDesignerSerializationManager dsm = new XamlDesignerSerializationManager(XmlWriter.Create(outstr,settings));

//this string need for turning on expression saving mode

dsm.XamlWriterMode = XamlWriterMode.Expression;

XamlWriter.Save(YourWWPFObj, dsm);




* This source code was highlighted with Source Code Highlighter .








できた

PS。 この記事は、 CodeProjectからの私の記事に追加された無料の翻訳です



UPD。 ありがとう、WPFブログに移動しました。



All Articles