質問

TL/DR: What's the easiest method to change the namespace value using LINQ to XML, say from xmlns:gcs="clr-namespace:NsOne;assembly=AsmOne" to xmlns:gcs="clr-namespace:NsTwo;assembly=AsmTwo"?

Why? because:

I serialized Xaml using System.Windows.Markup.XamlWriter.Save(myControl). I want to visualize this GUI appearance somewhere else (deserializing using System.Windows.Markup.XamlReader.Parse(raw)), in another project.

I don't want to link to the original assembly!

I just need to change the namespace, so XamlReader.Parse(raw) won't throw an exception. I currently do it using regular-expressions and it works, but I don't like that method (e.g. if I have xmlns:gcs="clr-namespace:NsOne;assembly=AsmOne" inside a CDATA)

This is my serialized Xaml:

<FlowDocument PagePadding="5,0,5,0" AllowDrop="True"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:gcs="clr-namespace:NsOne;assembly=AsmOne"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <gcs:MyParagraph Margin="0,0,0,0">
        <gcs:MyParagraph.MetaData>
            <gcs:ParagraphMetaData UpdaterParagraphUniqueId="1" />
        </gcs:MyParagraph.MetaData>
        <Span>some text...</Span>
    </gcs:MyParagraph>
    <gcs:MyParagraph Margin="0,0,0,0" Background="#FFF0F0F0">
        <gcs:MyParagraph.MetaData>
            <gcs:ParagraphMetaData UpdaterParagraphUniqueId="2" />
        </gcs:MyParagraph.MetaData>
        <Span Foreground="#FF0000FF">some more text...</Span>
    </gcs:MyParagraph>
</FlowDocument>

(MyParagraph and ParagraphMetaData are custom types and they both exist at the source assembly and the destination assembly. MyParagraph inherits WPF's Paragraph)

役に立ちましたか?

解決

This is fairly easy to do.

var doc = XDocument.Parse(raw);
XNamespace fromNs = "clr-namespace:NsOne;assembly=AsmOne";
XNamespace toNs = "clr-namespace:NsTwo;assembly=AsmTwo";

// redefines "gcs", but doesn't change what namespace the elements are in
doc.Root.SetAttributeValue(XNamespace.Xmlns + "gcs", toNs);

// this actually changes the namespaces of the elements from the old to the new
foreach (var element in doc.Root.Descendants()
                                  .Where(x => x.Name.Namespace == fromNs))
    element.Name = toNs + element.Name.LocalName;

Both portions are needed to result in XAML that's both correct and easily-readable, because the namespace of the elements is stored separately from the xmlns declarations in an XDocument. If you only change what "gcs" means, then it will write xmlns statements to keep the elements in their old namespace. If you only change what namespace the elements are in, then it will include xmlns="clr-namespace:NsTwo;assembly=AsmTwo" statements as necessary, and ignore gcs (which will still reference the old NsOne).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top