Search code examples
c#xelementprefix

Creating an XML element with a prefix using XElement


I have an application that makes use of a canvas that contains several WPF custom components. I'd like to export these components into a XAML file so that it can be fetched by another application but, in order to do that, I need to add prefix qualifiers to the exported components. For example, if I were to export a FrequencyButtonA component, I'd need to output something like

<PanelControls:FrequencyButtonA Frequency="113.123" Width="250"/>

I've tried the following, but I'm getting an exception due to the use of the ':' character:

return new XElement("PanelControls:" + "FrequencyButtonA");

Any ideas? I've found some other questions here in SO which seem to be similar to the problem I'm experiencing (e.g., this link), but not the exact same scenario.

Thanks in advance!

EDIT - More background information: This is an example of the complete output I'd need to produce:

<Border x:Name="CommsPanelBorder"
    Style="{DynamicResource BorderTemplate}"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:PanelControls="clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib"
    VerticalAlignment="Top">
        <PanelControls:FrequencyButtonB Frequency="113.123" Width="250"/>
        <PanelControls:FrequencyButtonA Frequency="102.3" Width="150"/>

I forgot to mention in my original post that the root node (Border) gets created inside a method. The method then loops through all the elements placed in the canvas and calls a method on said elements that returns an XElement which is later added to the root node. Thus, I need to make the XElements be able to create themselves. The code for the method is as follows:

XNamespace aw = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
        XNamespace ns = "PanelControls";
        var root = new XElement(aw + "Border",
            new XAttribute("Style", "{DynamicResource BorderTemplate}"),
            new XAttribute("Name", "CommsPanelBorder"),
            new XAttribute(XNamespace.Xmlns + "x", "http://schemas.microsoft.com/winfx/2006/xaml"),
            new XAttribute(XNamespace.Xmlns + "PanelControls", "clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib"),
            new XAttribute("VerticalAlignment", "Top")
        );

        IEnumerable<CommsPanelControl> commsPanelControls = editCanvas.Children.OfType<CommsPanelControl>();

        foreach (var commsPanelControl in commsPanelControls)
        {
            XElement xElement = commsPanelControl.AddXElement(root, ns);
            root.Add(xElement);
        }

EDIT 2. Added some code so Reinder can see my current approach:

XNamespace aw = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
        XNamespace ns = "PanelControls";
        var root = new XElement(aw + "Border",
            new XAttribute("Style", "{DynamicResource BorderTemplate}"),
            new XAttribute("Name", "CommsPanelBorder"),
            new XAttribute(XNamespace.Xmlns + "x", "http://schemas.microsoft.com/winfx/2006/xaml"),
            new XAttribute(XNamespace.Xmlns + "PanelControls", "clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib"),
            new XAttribute("VerticalAlignment", "Top")
        );

        XElement xElement = new XElement(ns + "FrequencyButtonA",
            new XAttribute("Frequency", "113.123"),
            new XAttribute("Width", "250"));
        root.Add(xElement);

EDIT 3. Just as a reference, here's a possible solution to my problem. Thanks Reiner for your input!

XNamespace aw = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
        XNamespace ns = "clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib";
        var root = new XElement(aw + "Border",
            new XAttribute("Style", "{DynamicResource BorderTemplate}"),
            new XAttribute("Name", "CommsPanelBorder"),
            new XAttribute(XNamespace.Xmlns + "x", "http://schemas.microsoft.com/winfx/2006/xaml"),
            new XAttribute(XNamespace.Xmlns + "PanelControls", "clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib"),
            new XAttribute("VerticalAlignment", "Top")
        );

        XElement xElement = new XElement(ns + "FrequencyButtonA",
            new XAttribute("Frequency", "113.123"),
            new XAttribute("Width", "250"));
        root.Add(xElement);

Solution

  • a prefix like PanelControls only makes sense if you declare the namespace that it relates to.

    You need to specify this in the node itself or for instance in the root.

    // create the root element, with the 'PanelControls' namespace
    XNamespace nSpace = "PanelControls";
    XElement element = new XElement("root",
              new XAttribute(XNamespace.Xmlns + "PanelControls", nSpace));
    
    element.Add(addXElement("FrequencyButtonA", nSpace ));
    
    ...
    
    private static XElement addXElement(string n, XNamespace ns)
    {
        return new XElement(ns + n,
            new XAttribute("Frequency", 113.123),
            new XAttribute("Width", 250));
    }
    

    the method 'addXElement()' will create the new XElement within the namespace ns, so you end up with this:

    <?xml version="1.0"?>
    <root xmlns:PanelControls="PanelControls">
        <PanelControls:FrequencyButtonA Width="250" Frequency="113.123"/>
    </root>