MSDN > フォーラム ホーム > Windows Presentation Foundation (WPF) > XamlWriter, MarkupObject How to ?
質問する質問する
 

回答済みXamlWriter, MarkupObject How to ?

  • 2006年9月12日 13:39gumtoo ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    I'm trying to serialize a tree of object in xaml in some designer app.

    some node of this tree refers to the same reference that i added to a resource dictionnary.

    XamlWriter won't serialize the reference to the dictionnary resources as a StaticResource + ResourceKey

    but i understand reading this post :

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=286265&SiteID=1

    that this should be possible using MarkupObject and modifying the ouput for some node.

     let say :

     

                Transform3DGroup TG1 = new Transform3DGroup();

                Transform3DGroup TG2 = new Transform3DGroup();

     

                TranslateTransform3D ts2 = new TranslateTransform3D();   

     

                TG1.Children.Add(new TranslateTransform3D());

                TG1.Children.Add(ts2);

     

                TG2.Children.Add(new TranslateTransform3D());

                TG2.Children.Add(ts2);

     

     here's basically the output i'm looking for :

        <Transform3DGroup x:Name="TG1">

          <TranslateTransform3D/>

          <StaticResource ResourceKey="ts2"/>

        </Transform3DGroup>

     

        <Transform3DGroup x:Name="TG2">

          <TranslateTransform3D/>

          <StaticResource ResourceKey="ts2"/>

        </Transform3DGroup>

    Could someone provide me some information on how to achieve this ?

     

    Thanks

     

回答

  • 2006年9月12日 15:38Rob Relyeaモデレータユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     回答済み

    Here is a quick sample I worked on recently...

    I built my own XamlWriter using MarkupObject and MarkupProperties.
    Please let me know of any enhancements you have problems making.

    Thanks, Rob

    Update 1: enhanced this code to handle IDictionary cases (like Page.Resources - ResourceDictionary)
    Update 2: enhanced this code to generate xmlns in the heading & cached the ContentProperty per Type (helps speed even more)...also looks for a null value and uses {x:Null}.

    Reminder: this sample is not complete...add features you may need...

    using System;

    using System.IO;

    using System.Xml;

    using System.Windows.Markup.Primitives;

    using System.Text;

    using System.Windows.Markup;

    using System.Collections.Generic;

    using System.Collections;

    using System.Diagnostics;

    namespace Microsoft.Samples.XamlWriter

    {

    // Summary:

    // Provides a single static Overload:SXamlWriter.Save method

    // that can be used for limited Extensible Application

    // Markup Language (XAML) serialization of provided runtime objects. This class

    // cannot be inherited, and only has static methods.

    public static class XamlWriter

    {

    // Summary:

    // Returns a Extensible Application Markup Language (XAML) string that serializes

    // the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // Returns:

    // Extensible Application Markup Language (XAML) string that can be written

    // to a stream or file. The logical tree of all elements that fall under the

    // provided obj element will be serialized.

    //

    // Exceptions:

    // System.Security.SecurityException:

    // the application is not running in full trust.

    //

    // System.ArgumentNullException:

    // obj is null.

    public static string Save(object obj)

    {

    StringBuilder sb = new StringBuilder();

    WriteObject(obj, sb, true);

    return sb.ToString();

    }

    //WriteObject - 3 params (used primarily when isRoot is true or by the 2 param version)

    private static void WriteObject(object obj, StringBuilder sb, bool isRoot)

    {

    WriteObjectWithKey(null, obj, sb, isRoot);

    }

    //WriteObject - 2 param version

    private static void WriteObject(object obj, StringBuilder sb)

    {

    WriteObjectWithKey(null, obj, sb, false);

    }

    //WriteObject - 3 param version

    private static void WriteObjectWithKey(object key, object obj, StringBuilder sb)

    {

    WriteObjectWithKey(key, obj, sb, false);

    }

    private static Dictionary<Type, string> contentProperties = new Dictionary<Type, string>();

    //WriteObject - 4 params (used primarily when isRoot is true or by the 3 param version)

    private static void WriteObjectWithKey(object key, object obj, StringBuilder sb, bool isRoot)

    {

    List<MarkupProperty> propertyElements = new List<MarkupProperty>();

    //If the value is a string

    string s = obj as string;

    if (s != null)

    {

    //TODO: in a dictionary, this should be serialized as a <s:String />

    sb.Append(s);

    return;

    }

    MarkupProperty contentProperty = null;

    string contentPropertyName = null;

    MarkupObject markupObj = MarkupWriter.GetMarkupObjectFor(obj);

    Type objectType = obj.GetType();

    sb.Append("<" + markupObj.ObjectType.Name);

    if (isRoot)

    {

    sb.Append(" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");

    }

    if (key != null)

    {

    string keyString = key.ToString();

    if (keyString.Length > 0)

    sb.Append(" x:Key=\"" + keyString + "\"");

    else

    //TODO: key may not be a string, what about x:Type...

    throw new NotImplementedException("Sample XamlWriter cannot yet handle keys that aren't strings");

    }

    //Look for CPA info in our cache that keeps contentProperty names per Type

    //If it doesn't have an entry, go get the info and store it.

    if (!contentProperties.ContainsKey(objectType))

    {

    string lookedUpContentProperty = string.Empty;

    foreach (Attribute attr in markupObj.Attributes)

    {

    ContentPropertyAttribute cpa = attr as ContentPropertyAttribute;

    if (cpa != null)

    lookedUpContentProperty = cpa.Name;

    }

    contentProperties.Add(objectType, lookedUpContentProperty);

    }

    contentPropertyName = contentProperties[objectType];

     

    string contentString = string.Empty;

    foreach (MarkupProperty markupProperty in markupObj.Properties)

    {

    if (markupProperty.Name != contentPropertyName)

    {

    if (markupProperty.IsValueAsString)

    contentString = markupProperty.Value as string;

    else if (!markupProperty.IsComposite)

    sb.Append(" " + markupProperty.Name + "=\"" + markupProperty.Value + "\"");

    else if (markupProperty.Value.GetType() == typeof(NullExtension))

    sb.Append(" " + markupProperty.Name + "=\"{x:Null}\"");

    else

    {

    propertyElements.Add(markupProperty);

    }

    }

    else

    contentProperty = markupProperty;

    }

     

    if (contentProperty != null || propertyElements.Count > 0 || contentString != string.Empty)

    {

    sb.Append(">");

    foreach (MarkupProperty markupProp in propertyElements)

    {

    string propElementName = markupObj.ObjectType.Name + "." + markupProp.Name;

    sb.Append("<" + propElementName + ">");

    WriteChildren(sb, markupProp);

    sb.Append("</" + propElementName + ">");

    }

    if (contentString != string.Empty)

    sb.Append(contentString);

    else if (contentProperty != null)

    WriteChildren(sb, contentProperty);

    sb.Append("</" + markupObj.ObjectType.Name + ">");

    }

    else

    {

    sb.Append("/>");

    }

    }

    private static void WriteChildren(StringBuilder sb, MarkupProperty markupProp)

    {

    if (!markupProp.IsComposite)

    {

    XamlWriter.WriteObject(markupProp.Value, sb);

    }

    else

    {

    IList collection = markupProp.Value as IList;

    IDictionary dictionary = markupProp.Value as IDictionary;

    if (collection != null)

    {

    foreach (object o in collection)

    XamlWriter.WriteObject(o, sb);

    }

    else if (dictionary != null)

    {

    foreach (object key in dictionary.Keys)

    {

    XamlWriter.WriteObjectWithKey(key, dictionary[key], sb);

    }

    }

    else

    XamlWriter.WriteObject(markupProp.Value, sb);

    }

    }

     

     

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information into a provided

    // stream to serialize the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // stream:

    // Destination stream for the serialized XAML information.

    //

    // Exceptions:

    // System.Security.SecurityException:

    // the application is not running in full trust.

    //

    // System.ArgumentNullException:

    // obj is null -or- stream is null.

    public static void Save(object obj, Stream stream) { }

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information as the source

    // for a provided text writer object. The output of the text writer can then

    // be used to serialize the provided object.

    //

    // Parameters:

    // writer:

    // TextWriter instance to use to write the serialized XAML information.

    //

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // Exceptions:

    // System.ArgumentNullException:

    // obj is null -or- writer is null.

    //

    // System.Security.SecurityException:

    // the application is not running in full trust.

    public static void Save(object obj, TextWriter writer) { }

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information as the source

    // for a provided XML writer object. The output of the XML writer can then be

    // used to serialize the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // xmlWriter:

    // Writer to use to write the serialized XAML information.

    //

    // Exceptions:

    // System.ArgumentNullException:

    // obj is null -or- manager is null.

    //

    // System.Security.SecurityException:

    // the application is not running in full trust.

    public static void Save(object obj, XmlWriter xmlWriter) { }

    }

    }

すべての返信

  • 2006年9月12日 15:38Rob Relyeaモデレータユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     回答済み

    Here is a quick sample I worked on recently...

    I built my own XamlWriter using MarkupObject and MarkupProperties.
    Please let me know of any enhancements you have problems making.

    Thanks, Rob

    Update 1: enhanced this code to handle IDictionary cases (like Page.Resources - ResourceDictionary)
    Update 2: enhanced this code to generate xmlns in the heading & cached the ContentProperty per Type (helps speed even more)...also looks for a null value and uses {x:Null}.

    Reminder: this sample is not complete...add features you may need...

    using System;

    using System.IO;

    using System.Xml;

    using System.Windows.Markup.Primitives;

    using System.Text;

    using System.Windows.Markup;

    using System.Collections.Generic;

    using System.Collections;

    using System.Diagnostics;

    namespace Microsoft.Samples.XamlWriter

    {

    // Summary:

    // Provides a single static Overload:SXamlWriter.Save method

    // that can be used for limited Extensible Application

    // Markup Language (XAML) serialization of provided runtime objects. This class

    // cannot be inherited, and only has static methods.

    public static class XamlWriter

    {

    // Summary:

    // Returns a Extensible Application Markup Language (XAML) string that serializes

    // the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // Returns:

    // Extensible Application Markup Language (XAML) string that can be written

    // to a stream or file. The logical tree of all elements that fall under the

    // provided obj element will be serialized.

    //

    // Exceptions:

    // System.Security.SecurityException:

    // the application is not running in full trust.

    //

    // System.ArgumentNullException:

    // obj is null.

    public static string Save(object obj)

    {

    StringBuilder sb = new StringBuilder();

    WriteObject(obj, sb, true);

    return sb.ToString();

    }

    //WriteObject - 3 params (used primarily when isRoot is true or by the 2 param version)

    private static void WriteObject(object obj, StringBuilder sb, bool isRoot)

    {

    WriteObjectWithKey(null, obj, sb, isRoot);

    }

    //WriteObject - 2 param version

    private static void WriteObject(object obj, StringBuilder sb)

    {

    WriteObjectWithKey(null, obj, sb, false);

    }

    //WriteObject - 3 param version

    private static void WriteObjectWithKey(object key, object obj, StringBuilder sb)

    {

    WriteObjectWithKey(key, obj, sb, false);

    }

    private static Dictionary<Type, string> contentProperties = new Dictionary<Type, string>();

    //WriteObject - 4 params (used primarily when isRoot is true or by the 3 param version)

    private static void WriteObjectWithKey(object key, object obj, StringBuilder sb, bool isRoot)

    {

    List<MarkupProperty> propertyElements = new List<MarkupProperty>();

    //If the value is a string

    string s = obj as string;

    if (s != null)

    {

    //TODO: in a dictionary, this should be serialized as a <s:String />

    sb.Append(s);

    return;

    }

    MarkupProperty contentProperty = null;

    string contentPropertyName = null;

    MarkupObject markupObj = MarkupWriter.GetMarkupObjectFor(obj);

    Type objectType = obj.GetType();

    sb.Append("<" + markupObj.ObjectType.Name);

    if (isRoot)

    {

    sb.Append(" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");

    }

    if (key != null)

    {

    string keyString = key.ToString();

    if (keyString.Length > 0)

    sb.Append(" x:Key=\"" + keyString + "\"");

    else

    //TODO: key may not be a string, what about x:Type...

    throw new NotImplementedException("Sample XamlWriter cannot yet handle keys that aren't strings");

    }

    //Look for CPA info in our cache that keeps contentProperty names per Type

    //If it doesn't have an entry, go get the info and store it.

    if (!contentProperties.ContainsKey(objectType))

    {

    string lookedUpContentProperty = string.Empty;

    foreach (Attribute attr in markupObj.Attributes)

    {

    ContentPropertyAttribute cpa = attr as ContentPropertyAttribute;

    if (cpa != null)

    lookedUpContentProperty = cpa.Name;

    }

    contentProperties.Add(objectType, lookedUpContentProperty);

    }

    contentPropertyName = contentProperties[objectType];

     

    string contentString = string.Empty;

    foreach (MarkupProperty markupProperty in markupObj.Properties)

    {

    if (markupProperty.Name != contentPropertyName)

    {

    if (markupProperty.IsValueAsString)

    contentString = markupProperty.Value as string;

    else if (!markupProperty.IsComposite)

    sb.Append(" " + markupProperty.Name + "=\"" + markupProperty.Value + "\"");

    else if (markupProperty.Value.GetType() == typeof(NullExtension))

    sb.Append(" " + markupProperty.Name + "=\"{x:Null}\"");

    else

    {

    propertyElements.Add(markupProperty);

    }

    }

    else

    contentProperty = markupProperty;

    }

     

    if (contentProperty != null || propertyElements.Count > 0 || contentString != string.Empty)

    {

    sb.Append(">");

    foreach (MarkupProperty markupProp in propertyElements)

    {

    string propElementName = markupObj.ObjectType.Name + "." + markupProp.Name;

    sb.Append("<" + propElementName + ">");

    WriteChildren(sb, markupProp);

    sb.Append("</" + propElementName + ">");

    }

    if (contentString != string.Empty)

    sb.Append(contentString);

    else if (contentProperty != null)

    WriteChildren(sb, contentProperty);

    sb.Append("</" + markupObj.ObjectType.Name + ">");

    }

    else

    {

    sb.Append("/>");

    }

    }

    private static void WriteChildren(StringBuilder sb, MarkupProperty markupProp)

    {

    if (!markupProp.IsComposite)

    {

    XamlWriter.WriteObject(markupProp.Value, sb);

    }

    else

    {

    IList collection = markupProp.Value as IList;

    IDictionary dictionary = markupProp.Value as IDictionary;

    if (collection != null)

    {

    foreach (object o in collection)

    XamlWriter.WriteObject(o, sb);

    }

    else if (dictionary != null)

    {

    foreach (object key in dictionary.Keys)

    {

    XamlWriter.WriteObjectWithKey(key, dictionary[key], sb);

    }

    }

    else

    XamlWriter.WriteObject(markupProp.Value, sb);

    }

    }

     

     

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information into a provided

    // stream to serialize the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // stream:

    // Destination stream for the serialized XAML information.

    //

    // Exceptions:

    // System.Security.SecurityException:

    // the application is not running in full trust.

    //

    // System.ArgumentNullException:

    // obj is null -or- stream is null.

    public static void Save(object obj, Stream stream) { }

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information as the source

    // for a provided text writer object. The output of the text writer can then

    // be used to serialize the provided object.

    //

    // Parameters:

    // writer:

    // TextWriter instance to use to write the serialized XAML information.

    //

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // Exceptions:

    // System.ArgumentNullException:

    // obj is null -or- writer is null.

    //

    // System.Security.SecurityException:

    // the application is not running in full trust.

    public static void Save(object obj, TextWriter writer) { }

    //

    // Summary:

    // Saves Extensible Application Markup Language (XAML) information as the source

    // for a provided XML writer object. The output of the XML writer can then be

    // used to serialize the provided object.

    //

    // Parameters:

    // obj:

    // The element to be serialized. Typically, this is the root element of a page

    // or application.

    //

    // xmlWriter:

    // Writer to use to write the serialized XAML information.

    //

    // Exceptions:

    // System.ArgumentNullException:

    // obj is null -or- manager is null.

    //

    // System.Security.SecurityException:

    // the application is not running in full trust.

    public static void Save(object obj, XmlWriter xmlWriter) { }

    }

    }

  • 2006年9月12日 15:52gumtoo ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    Thank you very much Rob !

    I did not have the time to dig into it but after days of msdn reading on Markup.Primitives & others,  this look like the answer to the rubik cube.

     

     

  • 2006年9月14日 13:08gumtoo ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    working great so far, by adding a check to a lookup hashTable to see if the object (node) that

    is going to be serialized needs to be replaced by a <StaticResource/> node.

    serializing a ResourceDictionary will end up with a

    "An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll"

    since ResourceDictionary have a property ( or attached prop )  'ResourceDictionary.Entries'

    which is a ResourceDictionary itself.

     

    i understand this, since this is a basic learning sample.

    i just added a line that check if the node is a ResourceDictionary and if so replace the output by the

    the full XamlWriter.Save output for that node ( which handle that just fine).

    Funny, dirty, but i'm only prototyping, experimenting right now.

     

    on a side note 'System.StackOverflowException' can append with the full XamlWriter too.

     

    Thanks again Rob, this sample is really helpfull.

     

     

  • 2006年9月14日 14:34Rob Relyeaモデレータユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    shortly after i posted the example, i realized it hadn't handled IDictionary (RD, and other) cases.  I updated the sample to handle it...(see the post above.)

    Let me know what other problems you run into here...

  • 2006年9月15日 13:10Fritzenhammer ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    Thanks Rob - looks like this could be a lot faster from my initial tests.

    However, there are some problems with the markup this generates.  At some point, I will need to load the generated markup with XamlReader, and it's currently choking on a few things.  Consider the following Xaml (sorry about the formatting - I tried):

    Generated by existing XamlWriter:

    <?xml version="1.0" encoding="utf-8"?>

    <GeometryDrawing Brush="{x:Null}" Geometry="M0,0L100,100 200,200A100,100,0,0,1,300,300" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <GeometryDrawing.Pen>

    <Pen Thickness="1" Brush="#FFFF0000" />

    </GeometryDrawing.Pen>

    </GeometryDrawing>

    Generated by new, lighter XamlWriter:

    <?xml version="1.0" encoding="utf-8"?>

    <GeometryDrawing Geometry="M0,0L100,100 200,200A100,100,0,0,1,300,300">
    <GeometryDrawing.Brush>
    <NullExtension />
    </GeometryDrawing.Brush>
    <GeometryDrawing.Pen>
    <Pen Thickness="1" Brush="#FFFF0000" />
    </GeometryDrawing.Pen>
    <GeometryDrawing/>

    XamlReader initially chokes on the missing xmlns attributes, and after that, the NullExtension Element.  I can modify the code to place the xmlns attributes on the root element, but I'm not so sure what to do about NullExtension, since it seems likely that this will happen in other instances as well.

    Ideas?

    Thanks,

    Fred

  • 2006年9月15日 16:48Rob Relyeaモデレータユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    I've added a check for a null value, added a small cache for storing the contentproperty for a type, and added the real xmlns files.
    The cache may make scenarios that repeat the same element several time faster.

    Thanks for the bug finding in the sample.

    Note: the <NullExtension /> failed because it should have put it in the x: namespace.
    Regardless, it is more succinct to just use {x:Null} in attribute syntax.

    -Rob

  • 2006年9月15日 18:29Fritzenhammer ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    Thanks Rob - Everything seems to be working now.

    In my scenario, this is yielding somewhere between 50 and 59% improvements in terms of speed - twice as fast as before!

    Still not knocking on XamlReader's door, but a nice gain nonetheless.  Thanks a lot for looking into this, and double thanks for the code - I now have something to work with.

    Thanks again,

    F.

     

  • 2006年9月26日 15:50gumtoo ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    Hello Rob,

    So far everything run's fine in my atempt to extend your sample to add the possibility for some elements in the tree
    i'm xamlizing to be replaced by a <StaticResource ResourceKey="..."> elements.

    right now, the serialized object is a custom class extending FrameworkElement so i can put my Resources in the  .Resources property.

    i get to the point but in my output . the .<local:theNode.Resources>  is at the bottom of the xaml output instead of the top as it usually sit.

    when i try to read the xaml. an error is throwned that it can't find the specified resourceKey in the resources.
    my guess is that the resources are not parsed yet.

    i tryed to add a [DependsOnAttribute("Resources")] on my property but i guess that if i don't do anything in the writer to handle this , it won't do anything , right ?


    Thanks.

      
    <local:theNode xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:local=".....">
    ....
              <Transform3DGroup>
                <StaticResource ResourceKey="0422eb15-70c6-42c6-91e9-72d506e98c68" />
              </Transform3DGroup>
     ....        
      <local:theNode.Resources>
        <TranslateTransform3D x:Key="0422eb15-70c6-42c6-91e9-72d506e98c68" />
      </local:theNode.Resources>
    </local:theNode>

     

    Edit: yes, that's the problem. by manually moving Resources up in the xaml it works fine.

    i might be able to find a way to move it up at write time but any advice would be great. : ]

     

  • 2006年11月10日 13:16DotNetWise ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    Guys,

    Could you please, post a finaly full source code for this?

    Thanks!

  • 2007年6月16日 22:24BryanLivingston ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

     

    The serializer was producing the following invalid xaml:

     

    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Path Fill="{x:Null}" Stroke="#FF6C3131" Canvas.Left="-59.5344272949037" Canvas.Top="159.146446614676">
    <Path.Data>
    <PathGeometry>
    <PathFigureCollection>M143.534427294904,0.353553385324918L143.534427294904,0.353553385324918 143.534427294904,0.353553385324918 142.534427294904,1.35355338532492 142.534427294904,1.35355338532492 141.534427294904,1.35355338532492 292.534427294904,307.353553385324 295.534427294904,309.353553385324 296.534427294904,309.353553385324 297.534427294904,310.353553385324 298.534427294904,310.353553385324</PathFigureCollection>
    </PathGeometry>
    </Path.Data>
    </Path>
    </Canvas>

     

    I fixed it by adding  || !markupProperty.IsContent) to the following section of

    WriteObjectWithKey.

     

    string contentString = string.Empty;

    foreach (MarkupProperty markupProperty in markupObj.Properties)

    {

    if (markupProperty.Name != contentPropertyName || !markupProperty.IsContent)

    {

    if (markupProperty.IsValueAsString)

    contentString = markupProperty.Value as string;

    else if (!markupProperty.IsComposite)

    sb.Append(" " + markupProperty.Name + "=\"" + markupProperty.Value + "\"");

    else if (markupProperty.Value.GetType() == typeof(NullExtension))

    sb.Append(" " + markupProperty.Name + "=\"{x:Null}\"");

    else

    {

    propertyElements.Add(markupProperty);

    }

    }

    else

    contentProperty = markupProperty;

    }

     

     

    The IsContent flag on MarkupProperty makes me wonder if the serializer shouldn't be using the ContentPropertyAttribute at all.

     

    Thanks again for the great serializer.  It's saved me a huge amout of time and I recently customized it accept "adoptedChildren" on the root element so I can ues it for copy/paste without moving selected entites around.

     

    Bryan Livingston

  • 2007年10月19日 8:10Jaap Taal ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

    I’m trying to use the Sample XamlWriter, however I get an exception in some situation. After some debugging I found out that the MarkupProperty throws an exception.

    I’ve isolated the problem. Funny thing is that the original XamlWriter functions without any error.

     

    Setter setter = new Setter();

    setter.Property = TextBox.VerticalScrollBarVisibilityProperty;

    setter.Value = ScrollBarVisibility.Auto;

    MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(setter);

    foreach (MarkupProperty markupProperty in markupObject.Properties)

    {

          Console.WriteLine("Value", markupProperty.Value);

    }

    Console.WriteLine(System.Windows.Markup.XamlWriter.Save(setter));

     

    Output:

    NullReferenceException

    Object reference not set to an instance of an object.

       at System.Windows.Markup.Primitives.ElementProperty.CheckForMarkupExtension(Type propertyType, Object value, IValueSerializerContext context, Boolean convertEnums)

       at System.Windows.Markup.Primitives.ElementProperty.get_Value()

       at …

     

    (Somehow the Sample XamlWriter throws this exception testing the IsComposite property, my code only throws exception when using the Value property)

     

    I guess it has something to do with that the value is an enum value, the output of the original xamlwriter is:

    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Setter.Value>

    <xTongue Tiedtatic Member="ScrollBarVisibility.Auto" />

    </Setter.Value>

    </Setter>

     

    Please help me understand this error, I’m really stuck on this. How should I use the markupobject/markupproperty classes to know that I need a xTongue Tiedtatic markupextension?

     

  • 2007年12月20日 20:26BryanLivingston ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     

     

    I've found a bug in this serializer, and am not sure it can be fixed.

     

    It happens with certain international languages, for example dutch netherlands.  The Writer will produce xaml that looks like this:

     

    <Path xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Stroke="#FF000000" StrokeThickness="1" StrokeStartLineCap="Flat" StrokeEndLineCap="Flat" StrokeDashCap="Square" StrokeLineJoin="Miter" StrokeDashOffset="0" StrokeDashArray="">
    <Path.Data>
    <GeometryGroup FillRule="Nonzero">
    <GeometryGroup.Children>
    <PathGeometry FillRule="Nonzero" Figures="M21,9839992523193;27,216667175293L47,1360015869141;30,3846664428711C50,4000015258789;32,1766738891602 52,1279983520508;34...

     

    The code in the figures is incorrect, it uses comma for a decimal point and the semicolon's should be spaces.

     

    Since the value for figures is being created by the system method MarkupWriter.GetMarkupObjectFor(obj), there isn't away to control it or pass it a invariant culture.

     

    Is this MarkupWriter class broken or is there a way to fix this?

     

    Thanks.

     

    Bryan

  • 2008年8月22日 2:40peanut_zwei ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     
    Hi, i tried to use the XamlWriter in the sample in my application.

    However, in my application, i will have lots and lots of self defined elements which required explicit namespaces to be appended as prefix of each elements. In that case, the XamlWriter doesn't work anymore since it is generating content all without the user defined namespaces.

    Could anything be done to cater for the mentioned issue?

    Thank you.

    peanut_zwei
  • 2008年8月29日 16:21Virender Sandhu ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     
    I am working on XamlWriter to support explicit namespaces. It shall be ready in 2-3 days. Once it is in working condition, I'll post it here.
  • 2008年9月4日 7:43peanut_zwei ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     
    Thanks Sandhu for the effort. I will be waiting for your code to try it out within my application.
    Thanks a lot. :)

    peanut_zwei
  • 2008年9月4日 23:57Virender Sandhu ユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダル
     回答の候補
    Hello,

    I couldn't spare time to cleanup the code. So the code works but may need performance tuneup depending on your usages. The most of code is copied from Rob's sample code; I have added custom namespace support.

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Xml;

    using System.Windows.Markup.Primitives;

    using System.IO;

    using System.Windows.Markup;

    using System.Collections;

    namespace Ikon.Samples.Wpf

    {

    public class XamlWriter

    {

    Dictionary<string, NamespaceMap> m_nsMap = new Dictionary<string, NamespaceMap>();

    private Dictionary<Type, string> m_contentPropertiesCache = new Dictionary<Type, string>();

    private NamespaceCache m_namespaces = new NamespaceCache();

    public string Save(object obj)

    {

    ResolveXmlNamespaces(obj);

    StringBuilder sb = new StringBuilder();

    StringWriter writer = new StringWriter(sb);

    XmlTextWriter tw = new XmlTextWriter(writer);

    tw.Namespaces = false;

    WriteObject(null, obj, tw, true);

    tw.Flush();

    tw.Close();

    return sb.ToString();

    }

    private void WriteObject(object key, object obj, XmlTextWriter writer, bool isRoot)

    {

    List<MarkupProperty> propertyElements = new List<MarkupProperty>();

    MarkupProperty contentProperty = null;

    string contentPropertyName = null;

    MarkupObject markupObj = MarkupWriter.GetMarkupObjectFor(obj);

    Type objectType = markupObj.ObjectType;

    m_namespaces.GetXmlNamespace(objectType);

    string ns = m_namespaces.GetXmlNamespace(objectType);

    string prefix = m_namespaces.GetPrefixForNamespace(ns);

    if (string.IsNullOrEmpty(prefix))

    writer.WriteStartElement(markupObj.ObjectType.Name);

    else

    writer.WriteStartElement(prefix + ":" + markupObj.ObjectType.Name);

    if (isRoot)

    {

    foreach (NamespaceMap map in m_nsMap.Values)

    {

    if (string.IsNullOrEmpty(map.Prefix))

    writer.WriteAttributeString("xmlns", map.XmlNamespace);

    else

    writer.WriteAttributeString("xmlns:" + map.Prefix, map.XmlNamespace);

    }

    if (!m_nsMap.ContainsKey(NamespaceCache.XamlNamespace))

    {

    writer.WriteAttributeString("xmlns:x", NamespaceCache.XamlNamespace);

    }

    writer.WriteAttributeString("xml:space", "preserve");

    }

    //writer.

    if (key != null)

    {

    string keyString = key.ToString();

    if (keyString.Length > 0)

    writer.WriteAttributeString("x:Key", keyString);

    else

    //TODO: key may not be a string, what about x:Type...

    throw new NotImplementedException("Sample XamlWriter cannot yet handle keys that aren't strings");

    }

    //Look for CPA info in our cache that keeps contentProperty names per Type

    //If it doesn't have an entry, go get the info and store it.

    if (!m_contentPropertiesCache.ContainsKey(objectType))

    {

    string lookedUpContentProperty = string.Empty;

    foreach (Attribute attr in markupObj.Attributes)

    {

    ContentPropertyAttribute cpa = attr as ContentPropertyAttribute;

    if (cpa != null)

    {

    lookedUpContentProperty = cpa.Name;

    //Once content property is found, come out of the loop.

    break;

    }

    }

    m_contentPropertiesCache.Add(objectType, lookedUpContentProperty);

    }

    contentPropertyName = m_contentPropertiesCache[objectType];

    string contentString = string.Empty;

    foreach (MarkupProperty markupProperty in markupObj.Properties)

    {

    if (markupProperty.Name != contentPropertyName)

    {

    if (markupProperty.IsValueAsString)

    {

    contentString = markupProperty.Value as string;

    }

    else if (!markupProperty.IsComposite)

    {

    string temp = markupProperty.Value == null ? string.Empty : markupProperty.Value.ToString();

    if (markupProperty.IsAttached)

    {

    string ns1 = m_namespaces.GetXmlNamespace(markupProperty.DependencyProperty.OwnerType);

    string prefix1 = m_namespaces.GetPrefixForNamespace(ns1);

    if (string.IsNullOrEmpty(prefix1))

    {

    writer.WriteAttributeString(markupProperty.Name, temp);

    }

    else

    {

    writer.WriteAttributeString(prefix1 + ":" + markupProperty.Name, temp);

    }

    }

    else

    {

    if (markupProperty.Name == "Name" && this.m_namespaces.GetAssemblyNameFromType(markupProperty.DependencyProperty.OwnerType).Equals("PresentationFramework"))

    {

    writer.WriteAttributeString("x:" + markupProperty.Name, temp);

    }

    else

    {

    writer.WriteAttributeString(markupProperty.Name, temp);

    }

    }

    }

    else if (markupProperty.Value.GetType() == typeof(NullExtension))

    {

    writer.WriteAttributeString(markupProperty.Name, "{x:Null}");

    }

    else

    {

    propertyElements.Add(markupProperty);

    }

    }

    else

    {

    contentProperty = markupProperty;

    }

    }

    if (contentProperty != null || propertyElements.Count > 0 || contentString != string.Empty)

    {

    foreach (MarkupProperty markupProp in propertyElements)

    {

    string ns2 = m_namespaces.GetXmlNamespace(markupObj.ObjectType);

    string prefix2 = m_namespaces.GetPrefixForNamespace(ns2);

    string propElementName = markupObj.ObjectType.Name + "." + markupProp.Name;

    if (string.IsNullOrEmpty(prefix2))

    {

    writer.WriteStartElement(propElementName);

    }

    else

    {

    writer.WriteStartElement(prefix2 + ":" + propElementName);

    }

    WriteChildren(writer, markupProp);

    writer.WriteEndElement();

    }

    if (contentString != string.Empty)

    {

    writer.WriteValue(contentString);

    }

    else if (contentProperty != null)

    {

    if (contentProperty.Value is string)

    {

    writer.WriteValue(contentProperty.StringValue);

    }

    else

    {

    WriteChildren(writer, contentProperty);

    }

    }

    }

    writer.WriteEndElement();

    }

    private void WriteChildren(XmlTextWriter writer, MarkupProperty markupProp)

    {

    if (!markupProp.IsComposite)

    {

    WriteObject(null, markupProp.Value, writer, false);

    }

    else

    {

    IList collection = markupProp.Value as IList;

    IDictionary dictionary = markupProp.Value as IDictionary;

    if (collection != null)

    {

    foreach (object o in collection)

    {

    WriteObject(null, o, writer, false);

    }

    }

    else if (dictionary != null)

    {

    foreach (object key in dictionary.Keys)

    {

    WriteObject(key, dictionary[key], writer, false);

    }

    }

    else

    {

    WriteObject(null, markupProp.Value, writer, false);

    }

    }

    }

    private void ResolveXmlNamespaces(object obj)

    {

    List<MarkupProperty> propertyElements = new List<MarkupProperty>();

    MarkupProperty contentProperty = null;

    string contentPropertyName = null;

    MarkupObject markupObj = MarkupWriter.GetMarkupObjectFor(obj);

    Type objectType = markupObj.ObjectType;

    m_namespaces.GetXmlNamespace(objectType);

    string ns = m_namespaces.GetXmlNamespace(objectType);

    string prefix = m_namespaces.GetPrefixForNamespace(ns);

    m_nsMap[ns] = new NamespaceMap(prefix, ns);

    //Look for CPA info in our cache that keeps contentProperty names per Type

    //If it doesn't have an entry, go get the info and store it.

    if (!m_contentPropertiesCache.ContainsKey(objectType))

    {

    string lookedUpContentProperty = string.Empty;

    foreach (Attribute attr in markupObj.Attributes)

    {

    ContentPropertyAttribute cpa = attr as ContentPropertyAttribute;

    if (cpa != null)

    {

    lookedUpContentProperty = cpa.Name;

    //Once content property is found, come out of the loop.

    break;

    }

    }

    m_contentPropertiesCache.Add(objectType, lookedUpContentProperty);

    }

    contentPropertyName = m_contentPropertiesCache[objectType];

    string contentString = string.Empty;

    foreach (MarkupProperty markupProperty in markupObj.Properties)

    {

    if (markupProperty.Name != contentPropertyName)

    {

    if (markupProperty.IsValueAsString)

    {

    contentString = markupProperty.Value as string;

    }

    else if (!markupProperty.IsComposite)

    {

    //Bug Fix DX-0120123

    string ns1 = m_namespaces.GetXmlNamespace(markupProperty.DependencyProperty.OwnerType);

    string prefix1 = m_namespaces.GetPrefixForNamespace(ns1);

    if (!string.IsNullOrEmpty(prefix1))

    {

    m_nsMap[ns1] = new NamespaceMap(prefix1, ns1);

    }

    }

    else if (markupProperty.Value.GetType() == typeof(NullExtension))

    {

    }

    else

    {

    propertyElements.Add(markupProperty);

    }

    }

    else

    {

    contentProperty = markupProperty;

    }

    }

    if (contentProperty != null || propertyElements.Count > 0 || contentString != string.Empty)

    {

    foreach (MarkupProperty markupProp in propertyElements)

    {

    string ns2 = m_namespaces.GetXmlNamespace(markupObj.ObjectType);

    string prefix2 = m_namespaces.GetPrefixForNamespace(ns2);

    m_nsMap[ns2] = new NamespaceMap(prefix2, ns2);

    ResolveChildXmlNamespaces(markupProp);

    }

    if (contentProperty != null)

    {

    if (!(contentProperty.Value is String))

    {

    ResolveChildXmlNamespaces(contentProperty);

    }

    }

    }

    }

    private void ResolveChildXmlNamespaces(MarkupProperty markupProp)

    {

    if (!markupProp.IsComposite)

    {

    ResolveXmlNamespaces(markupProp);

    }

    else

    {

    IList collection = markupProp.Value as IList;

    IDictionary dictionary = markupProp.Value as IDictionary;

    if (collection != null)

    {

    foreach (object o in collection)

    {

    ResolveXmlNamespaces(o);

    }

    }

    else if (dictionary != null)

    {

    foreach (object key in dictionary.Keys)

    {

    ResolveXmlNamespaces(dictionary[key]);

    }

    }

    else

    {

    ResolveXmlNamespaces(markupProp.Value);

    }

    }

    }

    }

    internal class NamespaceCache

    {

    private Dictionary<string, string> m_namespaceKeyCache = new Dictionary<string, string>();

    private Dictionary<string, Dictionary<string, string>> m_assemblyNamespaces = new Dictionary<string, Dictionary<string, string>>();

    public static readonly string DefaultNamespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";

    public static readonly string XmlNamespace = "http://www.w3.org/XML/1998/namespace";

    public static readonly string XamlNamespace = "http://schemas.microsoft.com/winfx/2006/xaml";

    //private

    public string GetPrefixForNamespace(string ns)

    {

    string ret = string.Empty;

    if (ns != DefaultNamespace && m_namespaceKeyCache.ContainsKey(ns))

    {

    ret = m_namespaceKeyCache[ns];

    }

    return ret;

    }

    public string GetXmlNamespace(Type type)

    {

    string uri = EnsureNamespaceMaps(type);

    if (!m_namespaceKeyCache.ContainsKey(uri))

    {

    string prefix = "ns" + (m_namespaceKeyCache.Count + 1).ToString();

    m_namespaceKeyCache[uri] = prefix;

    }

    //System.Diagnostics.Debug.WriteLine("Returned URI: " + uri);

    //string assemblyName = this.GetAssemblyNameFromType(type);

    //if (assemblyName.Equals("PresentationFramework"))

    //if(uri.Equals(NamespaceCache.DefaultNamespace)

    //{

    // uri = DefaultNamespace;

    //}

    return uri;

    }

    private string EnsureNamespaceMaps(Type type)

    {

    string assemblyName = GetAssemblyNameFromType(type);

    //System.Diagnostics.Debug.WriteLine("Assembly Name: " + assemblyName);

    if (!m_assemblyNamespaces.ContainsKey(type.Assembly.FullName))

    {

    foreach (XmlnsPrefixAttribute attribute in type.Assembly.GetCustomAttributes(typeof(XmlnsPrefixAttribute), true))

    {

    m_namespaceKeyCache[attribute.XmlNamespace] = attribute.Prefix;

    }

    Dictionary<string, string> temp = new Dictionary<string, string>();

    m_assemblyNamespaces.Add(type.Assembly.FullName, temp);

    foreach (XmlnsDefinitionAttribute attribute2 in type.Assembly.GetCustomAttributes(typeof(XmlnsDefinitionAttribute), true))

    {

    if (attribute2.AssemblyName == null)

    {

    if (!attribute2.XmlNamespace.Contains("2007"))

    {

    temp[attribute2.ClrNamespace] = attribute2.XmlNamespace;

    }

    }

    else

    {

    //System.Diagnostics.Debug.WriteLine("Assembly Name: - " + attribute2.AssemblyName);

    throw new ApplicationException("XmlnsDefinitions - Not handled yet.");

    }

    //System.Diagnostics.Debug.WriteLine("ClrNS - " + attribute2.ClrNamespace);

    //System.Diagnostics.Debug.WriteLine("XMLNS - " + attribute2.XmlNamespace);

    }

    }

    Dictionary<string, string> namespaces = m_assemblyNamespaces[type.Assembly.FullName];

    if (!namespaces.ContainsKey(type.Namespace))

    {

    string uri = string.Format("clr-namespace:{0};assembly={1}", type.Namespace, assemblyName);

    namespaces.Add(type.Namespace, uri);

    }

    return namespaces[type.Namespace];

    }

    public string GetAssemblyNameFromType(Type type)

    {

    string ret = string.Empty;

    string[] names = type.Assembly.FullName.Split(',');

    if (names.Length > 0)

    {

    ret = names[0];

    }

    return ret;

    }

    }

    internal class NamespaceMap

    {

    public string Prefix;

    public string XmlNamespace;

    public NamespaceMap(string prefix, string xmlNamespace)

    {

    Prefix = prefix;

    XmlNamespace = xmlNamespace;

    }

    }

    }