2

这是一个XamlWriter.Save非常慢的已知问题(只是谷歌“xamlwriter 慢”)。我编写的应用程序生成 XPS 文档,并且它使用的元素范围有限。因此,我想XamlWriter用更有效的替换。是否可以替换使用的默认XamlWriterXpsDocumentWriter


研究

下面是 XpsDocumentWriter 如何序列化为 Xaml。

这就是我写信的方式XpsDocument

var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
var vc = writer.CreateVisualsCollator();
vc.BeginBatchWrite();
foreach (var pfe in pageFrameworkElements) {
    vc.Write(pfe);
}
vc.EndBatchWrite();

所以我想做的是改变 VisualsCollat​​or 在序列化为 Xaml 时使用的序列化程序。

VisualsCollator实际上是一个VisualsToXpsDocument

// Generated by .NET Reflector from C:\Windows\Microsoft.Net\assembly\GAC_64\System.Printing\v4.0_4.0.0.0__31bf3856ad364e35\System.Printing.dll
namespace System.Windows.Xps
{
    /* Removed Code */

    public class VisualsToXpsDocument : SerializerWriterCollator
    {
        /* Removed Code */

        [SecurityCritical]
        private PackageSerializationManager _manager;

        /* Removed Code */

        [return: MarshalAs(UnmanagedType.U1)]
        [SecuritySafeCritical]
        private unsafe bool WriteVisual([MarshalAs(UnmanagedType.U1)] bool asyncMode, PrintTicket printTicket, PrintTicketLevel printTicketLevel, Visual visual)
        {
            /* Removed Code */

            if (!asyncMode)
            {
                try
                {
                    try
                    {
                        this._manager.SaveAsXaml(visual); /* This line is called to serialize to Xaml */
                        return flag;
                    }
                    catch (PrintingCanceledException exception)
                    {
                        this.parentWriter.OnWritingCanceled(this, exception);
                    }
                    return flag;
                }
                finally
                {
                    flag = true;
                }
            }
            XpsWriterException.ThrowException("XPSWriter.BatchSync");
            return flag;
        }
    }
}

PackageSerializationManager _manager实际上是一个XpsSerializationManager

// Generated by .NET Reflector from C:\Windows\Microsoft.Net\assembly\GAC_MSIL\ReachFramework\v4.0_4.0.0.0__31bf3856ad364e35\ReachFramework.dll
namespace System.Windows.Xps.Serialization
{
    /* Removed Code */

    public class XpsSerializationManager : PackageSerializationManager
    {
        /* Removed Code */

        private ReachHierarchySimulator _simulator;

        /* Removed Code */

        internal override ReachSerializer GetSerializer(object serializedObject)
        {
            ReachSerializer serializer = null;
            serializer = base.GetSerializer(serializedObject);
            if (serializer == null)
            {
                serializer = base.SerializersCacheManager.GetSerializer(serializedObject);
            }
            return serializer;
        }

        /* Removed Code */

        public override void SaveAsXaml(object serializedObject)
        {
            /* Removed Code */

            ReachSerializer serializer = this.GetSerializer(serializedObject);
            if (serializer == null)
            {
                throw new XpsSerializationException(System.Windows.Xps.SR.Get("ReachSerialization_NoSerializer"));
            }
            serializer.SerializeObject(serializedObject);

            /* Removed Code */
        }

        /* Removed Code */
    }
}

这是它继承的 PackageSerializationManager:

// Generated by .NET Reflector from C:\Windows\Microsoft.Net\assembly\GAC_MSIL\ReachFramework\v4.0_4.0.0.0__31bf3856ad364e35\ReachFramework.dll
namespace System.Windows.Xps.Serialization
{
    /* Removed Code */

    public abstract class PackageSerializationManager : IDisposable
    {
        /* Removed Code */

        private System.Windows.Xps.Serialization.SerializersCacheManager _serializersCacheManager;

        /* Removed Code */

        internal virtual ReachSerializer GetSerializer(object serializedObject)
        {
            return this._serializersCacheManager.GetSerializer(serializedObject);
        }

        /* Removed Code */

        public abstract void SaveAsXaml(object serializedObject);

        /* Removed Code */

        internal System.Windows.Xps.Serialization.SerializersCacheManager CacheManager
        {
            get
            {
                return this._serializersCacheManager;
            }
        }

        /* Removed Code */

        internal System.Windows.Xps.Serialization.SerializersCacheManager SerializersCacheManager
        {
            get
            {
                return this._serializersCacheManager;
            }
        }
    }
}

到目前为止我最接近的非解决方案

我正在考虑继承XpsSerializationManager并覆盖该SaveAsXaml方法,但我不确定如何实际覆盖它,因为它不返回值或提供流或任何要写入的对象。即使我这样做了,我也必须使用反射来更改类PackageSerializationManager _manager中的字段VisualsToXpsDocument,所以它有点 hacky。

到目前为止,我还没有解决方案。


未完成的潜在 XamlWriter

如果可以替换序列化程序,那么这XamlWriter是我在此处找到的自定义示例:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Markup;
using System.Windows.Markup.Primitives;
using System.Xml;

// 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) {
        var 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) {
        var propertyElements = new List<MarkupProperty>();
        //If the value is a string
        var 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;
        var markupObj = MarkupWriter.GetMarkupObjectFor(obj);
        var 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) {
            var 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)) {
            var lookedUpContentProperty = string.Empty;
            foreach (Attribute attr in markupObj.Attributes) {
                var cpa = attr as ContentPropertyAttribute;
                if (cpa != null)
                    lookedUpContentProperty = cpa.Name;
            }
            contentProperties.Add(objectType, lookedUpContentProperty);
        }
        contentPropertyName = contentProperties[objectType];

        var contentString = string.Empty;
        foreach (var 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 (var markupProp in propertyElements) {
                var 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) {
            WriteObject(markupProp.Value, sb);
        }
        else {
            var collection = markupProp.Value as IList;
            var dictionary = markupProp.Value as IDictionary;
            if (collection != null) {
                foreach (var o in collection)
                    WriteObject(o, sb);
            }
            else if (dictionary != null) {
                foreach (var key in dictionary.Keys) {
                    WriteObjectWithKey(key, dictionary[key], sb);
                }
            }
            else
                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) {
        var writer = new StreamWriter(stream);
        stream.Seek(0, SeekOrigin.Begin); //this line may not be needed.
        writer.Write(Save(obj));
        writer.Flush();
    }

    //
    // 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) {}
}
4

0 回答 0