Is it possible to get XamlWriter to save a StaticResource?

Viewed 407

My problem is shown in the following program, where the GeometryModel3D's Material is set from a StaticResource in XAML.

Is it possible to get the XamlWriter to save out the actual StaticResources instead of the resolved references (which it does now)? If so, what do I need to do?

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);
        }
    }
}
2 Answers

I had the same issue with Bindings before and I found the solution to serialize bindings in this link: Binding Serialization

And, based on this solution i found out how to "filter" the Style serialization.

Define what a Style returns when it gets to be serialized, 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);
        }
    }

Important thing to notice is the use of StaticResourceExtension class.

Then, just make a new registration:

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

And voilá, when a Style gets to be serialized, it will return an instance of StaticResourceExtension and not a Style instance, and so, avoiding writing the whole Style description into the exported xaml. An exported component's Style will look like this:

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