是否可以让 XamlWriter 保存 StaticResource?

Is it possible to get XamlWriter to save a StaticResource?

我的问题显示在以下程序中,其中 GeometryModel3D 的 Material 是从 XAML 中的 StaticResource 设置的。

是否可以让 XamlWriter 保存实际的 StaticResources 而不是解析的引用(它现在这样做)?如果可以,我需要做什么?

using System;
using System.IO;
using System.Text;
using System.Windows.Controls;
using System.Windows.Markup;

namespace MaterialTest
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            string xaml = "";
            xaml += "<Viewport3D xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>";
            xaml += "  <Viewport3D.Resources>";
            xaml += "    <DiffuseMaterial x:Key='Steel' />";
            xaml += "  </Viewport3D.Resources>";
            xaml += "  <ModelVisual3D>";
            xaml += "    <ModelVisual3D.Content>";
            xaml += "      <GeometryModel3D Material='{StaticResource Steel}'>";
            xaml += "        <GeometryModel3D.Geometry>";
            xaml += "          <MeshGeometry3D Positions='-0.5,-0.5,0 0.5,-0.5,0 0.5,0.5,0 -0.5,0.5,0' TriangleIndices='0 1 2 0 2 3' />";
            xaml += "        </GeometryModel3D.Geometry>";
            xaml += "      </GeometryModel3D>";
            xaml += "    </ModelVisual3D.Content>";
            xaml += "  </ModelVisual3D>";
            xaml += "</Viewport3D>";

            MemoryStream buffer = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
            Viewport3D viewport = XamlReader.Load(buffer) as Viewport3D;

            string xaml_out = XamlWriter.Save(viewport);
        }
    }
}

Is it possible to get the XamlWriter to save out the actual StaticResources instead of the resolved references (which it does now)?

不,恐怕不是。这是 MSDN 上记录的 XamlWriter.Save 的已知限制:https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/serialization-limitations-of-xamlwriter-save

Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output.

我之前遇到过与绑定相同的问题,我在这个 link 中找到了序列化绑定的解决方案: Binding Serialization

并且,基于这个解决方案,我发现了如何“过滤”样式序列化。

定义序列化时的样式 return,StyleConverterClass:

class StyleConverter : System.Windows.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))
            {
                Style style = value as Style;
                if (style == null)
                    throw new Exception();

                foreach (var dictionarys in ((MetroWindow)Application.Current.MainWindow).Resources.MergedDictionaries)
                {                    
                    foreach (DictionaryEntry entry in dictionarys)
                    {
                        if (entry.Value is Style && (entry.Value as Style) == style)
                        {
                            return new StaticResourceExtension(entry.Key);
                        }
                    }
                }
                
                return style;
            }

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

需要注意的重要一点是 StaticResourceExtension class.

的使用

然后,重新注册:

EditorHelper.Register<Style, ExpressionConverter.StyleConverter>();

瞧,当 Style 被序列化时,它将 return StaticResourceExtension 的实例而不是 Style 实例,因此,避免将整个 Style 描述写入导出的 xaml .导出组件的样式将如下所示:

<cuc:ComboBoxWrapper IsDropDownOpen="False" Style="{av:StaticResource
ResourceKey=MahApps.Styles.ComboBox.Virtualized}" av:Grid.Column="0"
av:Grid.Row="1" />