有没有办法将多个XElement序列化到同一行?

| 我正在处理Silverlight 3中令人恐惧的
<Run/>
,并且必须以编程方式创建
<TextBlock>
及其内联: 为什么害怕?我想,因为它不起作用,所以您期望的那样。下面的图表A应该产生BARN(每个字符都带有奇特的颜色),但产生的却是:B A R N 附件A
<TextBlock FontFamily=\"Comic Sans MS\" FontSize=\"88\">
    <Run Foreground=\"#A200FF\">B</Run>
    <Run Foreground=\"#FF0000\">A</Run>
    <Run Foreground=\"#FFC000\">R</Run>
    <Run Foreground=\"#FFFF00\">N</Run>
</TextBlock>
但是,产生预期结果的是: 附件B
<TextBlock FontFamily=\"Comic Sans MS\" FontSize=\"88\">
    <Run Foreground=\"#A200FF\">B</Run><Run Foreground=\"#FF0000\">A</Run><Run Foreground=\"#FFC000\">R</Run><Run Foreground=\"#FFFF00\">N</Run>
</TextBlock>
愚蠢吧?无论如何,这在Whitespace Handling下记录了@XAML Silverlight 3和Silverlight 4之间的处理差异,其中指出:   Silverlight 3更对待空白   实际上,范围更广,包括   在某些情况下,考虑使用CLRF   重大。这有时导致   省略CRLF的文件格式XAML   为了避免不必要的空格   演示文稿,但不是   在编辑中易于阅读   环境。 Silverlight 4使用了   更直观的有效空白   与WPF相似的模型。这个   模型折叠文件格式   在大多数情况下,空白   某些CLR归因的例外   处理所有空白的容器   一样重要。这个空白模型   提供更好的编辑环境   引入空白的自由   可以改善XAML代码格式。   另外,Silverlight 4具有文本元素   可以更好地控制   空白演示文稿问题。 很好,但是我没有使用SL4,因为我正在以编程方式创建WP7应用。是的,我的XAML已生成。使用XML文字。然后发送到字符串。像这样:
Dim r1 As XElement = <Run Foreground=\"#A200FF\">B</Run>
Dim r2 As XElement = <Run Foreground=\"#FF0000\">A</Run>
Dim r3 As XElement = <Run Foreground=\"#FFC000\">R</Run>
Dim r4 As XElement = <Run Foreground=\"#FFFF00\">N</Run>
Dim tb = <TextBlock FontFamily=\"Comic Sans MS\" FontSize=\"88\">
             <%= r1 %><%= r2 %><%= r3 %><%= r4 %>
         </TextBlock>
Dim result = tb.ToString
毕竟,这是我的问题:我如何生成附件B而不是附件A。该文本块将成为XAML页面中更多元素的一部分,因此
.ToString
部分在此不完全准确位置-用户控制页面的所有XAML被踢出文件时发生。 编辑(2011年5月6日):有所进展和赏金 我已经取得了一些进步,如下所示,但是我在这里遇到了一个难题:如何完成不寻常的拆分和处理XML以输出字符串。举一个新的例子:
<Canvas>
  <Grid>
    <TextBlock>
      <Run Text=\"r\"/>
      <Run Text=\"u\"/>
      <Run Text=\"n\"/>
    </TextBlock>
    <TextBlock>
      <Run Text=\"far a\"/>
      <Run Text=\"way\"/>
      <Run Text=\" from me\"/>
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text=\"I\"/>
      <Run Text=\" \"/>
      <Run Text=\"want\"/>
      <LineBreak/>
    </TextBlock>
    <TextBlock>
      <LineBreak/>
      <Run Text=\"...thi\"/>
      <Run Text=\"s to\"/>
      <LineBreak/>
      <Run Text=\" work\"/>
    </TextBlock>
  </Grid>
</Canvas>
我希望输出字符串为:
<Canvas>
  <Grid>
    <TextBlock>
      <Run Text=\"r\"/><Run Text=\"u\"/><Run Text=\"n\"/>
    </TextBlock>
    <TextBlock>
      <Run Text=\"far a\"/><Run Text=\"way\"/><Run Text=\" from me\"/>
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text=\"I\"/><Run Text=\" \"/><Run Text=\"want\"/>
      <LineBreak/>
    </TextBlock>
    <TextBlock>
      <LineBreak/>
      <Run Text=\"...thi\"/><Run Text=\"s to\"/>
      <LineBreak/>
      <Run Text=\" work\"/>
    </TextBlock>
  </Grid>
</Canvas>
根据埃里克·怀特(Eric White)的帖子,我一直在看
XMLWriter
和ѭ9which,这似乎是比赛的一个不错的开端(尚不包括潜在的
<LineBreak/>
,这也让我很沮丧)。像这样:
Sub Main()
    Dim myXML As XElement = <Canvas>
                                <Grid>
                                    <TextBlock>
                                        <Run Text=\"r\"/>
                                        <Run Text=\"u\"/>
                                        <Run Text=\"n\"/>
                                    </TextBlock>
                                    <TextBlock>
                                        <Run Text=\"far a\"/>
                                        <Run Text=\"way\"/>
                                        <Run Text=\" from me\"/>
                                    </TextBlock>
                                </Grid>
                            </Canvas>
    Console.Write(ToXMLString(myXML))
    Console.ReadLine()
End Sub
Public Function ToXMLString(xml As XElement) As String
    Dim tb As XElement = xml.Elements.<TextBlock>.FirstOrDefault
    Dim xmlWriterSettings As New XmlWriterSettings
    XmlWriterSettings.NewLineHandling = NewLineHandling.None
    XmlWriterSettings.OmitXmlDeclaration = True
    Dim sb As New StringBuilder
    Using xmlwriter As XmlWriter = xmlwriter.Create(sb, XmlWriterSettings)
        tb.WriteTo(xmlwriter)
    End Using
    Return sb.ToString
End Function
但是,在弄清楚如何解析它以产生上面期望的输出时,我还有一个更大的问题。     
已邀请:
解决此问题的关键是编写一个遍历XML树的递归函数,将各种元素和属性写入专门创建的XmlWriter对象。有一个\'outer \'XmlWriter对象,它写缩进的XML,还有一个''inner \'XmlWriter对象,它写成非缩进的XML。 递归函数最初使用\'outer \'XmlWriter,编写缩进的XML,直到看到TextBlock元素为止。当遇到TextBlock元素时,它将创建\'inner \'XmlWriter对象,并将TextBlock元素的子元素写入该对象。它还会将空白写入\'inner \'XmlWriter。 当\'inner \'XmlWriter对象完成写入TextBlock元素时,使用WriteRaw方法将编写者编写的文本写入\'outer \'XmlWriter。 这种方法的优点是没有XML的后处理。对XML进行后处理是非常困难的,并且要确定您已经正确处理了所有情况,包括CData节点中的任意文本等。所有XML仅使用XmlWriter类编写,从而确保将始终写入有效的XML。 。唯一的例外是使用WriteRaw方法写入的特制空白,它实现了所需的缩进行为。 一个关键点是\ inner \ XmlWriter对象的一致性级别设置为ConformanceLevel.Fragment,因为\ inner \ XmlWriter需要编写没有根元素的XML。 为了实现所需的Run元素格式(即,相邻的Run元素之间没有无关紧要的空白),代码使用GroupAdjacent扩展方法。前一段时间,我写了一篇关于VB的GroupAdjacent扩展方法的博客文章。 使用指定的示例XML运行代码时,其输出:
<Canvas>
  <Grid>
    <TextBlock>
      <Run Text=\"r\" /><Run Text=\"u\" /><Run Text=\"n\" />
    </TextBlock>
    <TextBlock>
      <Run Text=\"far a\" /><Run Text=\"way\" /><Run Text=\" from me\" />
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text=\"I\" /><Run Text=\" \" /><Run Text=\"want\" />
      <LineBreak />
    </TextBlock>
    <TextBlock>
      <LineBreak />
      <Run Text=\"...thi\" /><Run Text=\"s to\" />
      <LineBreak />
      <Run Text=\" work\" />
    </TextBlock>
  </Grid>
</Canvas>
以下是VB.NET示例程序的完整列表。另外,我写了一篇博客文章,《使用LINQ to XML的XML自定义格式》,展示了等效的C#代码。 `
Imports System.Text
Imports System.Xml

Public Class GroupOfAdjacent(Of TElement, TKey)
    Implements IEnumerable(Of TElement)

    Private _key As TKey
    Private _groupList As List(Of TElement)

    Public Property GroupList() As List(Of TElement)
        Get
            Return _groupList
        End Get
        Set(ByVal value As List(Of TElement))
            _groupList = value
        End Set
    End Property

    Public ReadOnly Property Key() As TKey
        Get
            Return _key
        End Get
    End Property

    Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TElement) _
            Implements System.Collections.Generic.IEnumerable(Of TElement).GetEnumerator
        Return _groupList.GetEnumerator
    End Function

    Public Function GetEnumerator1() As System.Collections.IEnumerator _
            Implements System.Collections.IEnumerable.GetEnumerator
        Return _groupList.GetEnumerator
    End Function

    Public Sub New(ByVal key As TKey)
        _key = key
        _groupList = New List(Of TElement)
    End Sub
End Class

Module Module1
    <System.Runtime.CompilerServices.Extension()> _
    Public Function GroupAdjacent(Of TElement, TKey)(ByVal source As IEnumerable(Of TElement), _
                ByVal keySelector As Func(Of TElement, TKey)) As List(Of GroupOfAdjacent(Of TElement, TKey))
        Dim lastKey As TKey = Nothing
        Dim currentGroup As GroupOfAdjacent(Of TElement, TKey) = Nothing
        Dim allGroups As List(Of GroupOfAdjacent(Of TElement, TKey)) = New List(Of GroupOfAdjacent(Of TElement, TKey))()
        For Each item In source
            Dim thisKey As TKey = keySelector(item)
            If lastKey IsNot Nothing And Not thisKey.Equals(lastKey) Then
                allGroups.Add(currentGroup)
            End If
            If Not thisKey.Equals(lastKey) Then
                currentGroup = New GroupOfAdjacent(Of TElement, TKey)(keySelector(item))
            End If
            currentGroup.GroupList.Add(item)
            lastKey = thisKey
        Next
        If lastKey IsNot Nothing Then
            allGroups.Add(currentGroup)
        End If
        Return allGroups
    End Function

    Public Sub WriteStartElement(ByVal writer As XmlWriter, ByVal e As XElement)
        Dim ns As XNamespace = e.Name.Namespace
        writer.WriteStartElement(e.GetPrefixOfNamespace(ns), _
            e.Name.LocalName, ns.NamespaceName)
        For Each a In e.Attributes
            ns = a.Name.Namespace
            Dim localName As String = a.Name.LocalName
            Dim namespaceName As String = ns.NamespaceName
            writer.WriteAttributeString( _
                e.GetPrefixOfNamespace(ns), _
                localName, _
                IIf(namespaceName.Length = 0 And localName = \"xmlns\", _
                    XNamespace.Xmlns.NamespaceName, namespaceName),
                a.Value)
        Next
    End Sub

    Public Sub WriteElement(ByVal writer As XmlWriter, ByVal e As XElement)
        If (e.Name = \"TextBlock\") Then
            WriteStartElement(writer, e)
            writer.WriteRaw(Environment.NewLine)

            \' Create an XML writer that outputs no insignificant white space so that we can
            \' write to it and explicitly control white space.
            Dim settings As XmlWriterSettings = New XmlWriterSettings()
            settings.Indent = False
            settings.OmitXmlDeclaration = True
            settings.ConformanceLevel = ConformanceLevel.Fragment
            Dim sb As StringBuilder = New StringBuilder()
            Using newXmlWriter As XmlWriter = XmlWriter.Create(sb, settings)
                \' Group adjacent runs so that they can be output with no whitespace between them
                Dim groupedRuns = e.Nodes().GroupAdjacent( _
                    Function(n) As Boolean?
                        If TypeOf n Is XElement Then
                            Dim element As XElement = n
                            If element.Name = \"Run\" Then
                                Return True
                            End If
                            Return False
                        End If
                        Return False
                    End Function)
                For Each g In groupedRuns
                    If g.Key = True Then
                        \' Write white space so that the line of Run elements is properly indented.
                        newXmlWriter.WriteRaw(\"\".PadRight((e.Ancestors().Count() + 1) * 2))
                        For Each run In g
                            run.WriteTo(newXmlWriter)
                        Next
                        newXmlWriter.WriteRaw(Environment.NewLine)
                    Else
                        For Each g2 In g
                            \' Write some white space so that each child element is properly indented.
                            newXmlWriter.WriteRaw(\"\".PadRight((e.Ancestors().Count() + 1) * 2))
                            g2.WriteTo(newXmlWriter)
                            newXmlWriter.WriteRaw(Environment.NewLine)
                        Next
                    End If
                Next
            End Using
            writer.WriteRaw(sb.ToString())
            writer.WriteRaw(\"\".PadRight(e.Ancestors().Count() * 2))
            writer.WriteEndElement()
        Else
            WriteStartElement(writer, e)
            For Each n In e.Nodes
                If TypeOf n Is XElement Then
                    Dim element = n
                    WriteElement(writer, element)
                    Continue For
                End If
                n.WriteTo(writer)
            Next
            writer.WriteEndElement()
        End If
    End Sub

    Function ToStringWithCustomWhiteSpace(ByVal element As XElement) As String
        \' Create XmlWriter that indents.
        Dim settings As XmlWriterSettings = New XmlWriterSettings()
        settings.Indent = True
        settings.OmitXmlDeclaration = True
        Dim sb As StringBuilder = New StringBuilder()
        Using xmlWriter As XmlWriter = xmlWriter.Create(sb, settings)
            WriteElement(xmlWriter, element)
        End Using
        Return sb.ToString()
    End Function

    Sub Main()
        Dim myXML As XElement = _
            <Canvas>
                <Grid>
                    <TextBlock>
                        <Run Text=\'r\'/>
                        <Run Text=\'u\'/>
                        <Run Text=\'n\'/>
                    </TextBlock>
                    <TextBlock>
                        <Run Text=\'far a\'/>
                        <Run Text=\'way\'/>
                        <Run Text=\' from me\'/>
                    </TextBlock>
                </Grid>
                <Grid>
                    <TextBlock>
                        <Run Text=\'I\'/>
                        <Run Text=\' \'/>
                        <Run Text=\'want\'/>
                        <LineBreak/>
                    </TextBlock>
                    <TextBlock>
                        <LineBreak/>
                        <Run Text=\'...thi\'/>
                        <Run Text=\'s to\'/>
                        <LineBreak/>
                        <Run Text=\' work\'/>
                    </TextBlock>
                </Grid>
            </Canvas>
        Console.Write(ToStringWithCustomWhiteSpace(myXML))
        Console.ReadLine()
    End Sub

End Module
`     
这是您可以尝试的另一种方法。我所做的测试非常有效。 这利用了LINQ to XML和正则表达式。这个想法是使用特制的注释注释掉所有
Run
元素,并获得字符串表示形式。然后扫描以查找连续的
Run
元素并将它们“合并”在一起。然后,完成此操作后,“取消注释”所有
Run
元素。
<Extension()>
Public Function ToXamlString(ByVal element As XElement) As String
    Dim proxy As New XElement(element)
    Dim rng As New Random
    Dim seed1 = rng.Next
    Dim seed2 = rng.Next
    Dim query = proxy...<Run>
    For Each run In query.ToList
        run.ReplaceWith(New XComment(String.Concat(seed1, run, seed2)))
    Next
    Dim asStr = proxy.ToString
    asStr = Regex.Replace(asStr, String.Concat(seed2, \"-->\\s+<!--\", seed1), String.Empty, RegexOptions.Multiline)
    Return Regex.Replace(asStr, String.Concat(\"<!--\", seed1, \"(<Run[\\s\\S]+?>)\", seed2, \"-->\"), \"$1\", RegexOptions.Multiline)
End Function
并输出:
<Canvas>
  <Grid>
    <TextBlock>
      <Run Text=\"r\" /><Run Text=\"u\" /><Run Text=\"n\" />
    </TextBlock>
    <TextBlock>
      <Run Text=\"far a\" /><Run Text=\"way\" /><Run Text=\" from me\" />
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text=\"I\" /><Run Text=\" \" /><Run Text=\"want\" />
      <LineBreak />
    </TextBlock>
    <TextBlock>
      <LineBreak />
      <Run Text=\"...thi\" /><Run Text=\"s to\" />
      <LineBreak />
      <Run Text=\" work\" />
    </TextBlock>
  </Grid>
</Canvas>
    
您可以使用
ToString()
重载,该重载允许您指定
SaveOptions
,以便禁用格式化。但是我认为您不能为单个
XElements
设置保存选项。您将必须像以前一样手动写出字符串。这只是部分实现,尽管它只将TextBlocks全部写在一行上。那会是可接受的格式吗?
Public Function ToXamlString(element As XElement, indentLevel As Int32) As String

    Dim sb As New StringBuilder()
    Dim indent As New String(\" \"c, indentLevel * 2)

    If element.Name = \"TextBlock\" Then
        sb.Append(indent).AppendLine(element.ToString(SaveOptions.DisableFormatting))
    Else
        sb.Append(indent).AppendLine(\"<\" & element.Name.ToString & \">\")

        For Each child In element.Elements
            sb.Append(ToXamlString(child, indentLevel + 1))
        Next

        sb.Append(indent).AppendLine(\"</\" & element.Name.ToString & \">\")
    End If

    Return sb.ToString

End Function

\'\'# call it
Console.WriteLine(ToXamlString(element, 0))
并输出:
<Canvas>
  <Grid>
    <TextBlock><Run Text=\"r\" /><Run Text=\"u\" /><Run Text=\"n\" /></TextBlock>
    <TextBlock><Run Text=\"far a\" /><Run Text=\"way\" /><Run Text=\" from me\" /></TextBlock>
  </Grid>
  <Grid>
    <TextBlock><Run Text=\"I\" /><Run Text=\" \" /><Run Text=\"want\" /><LineBreak /></TextBlock>
    <TextBlock><LineBreak /><Run Text=\"...thi\" /><Run Text=\"s to\" /><LineBreak /><Run Text=\" work\" /></TextBlock>
  </Grid>
</Canvas>
    
我不确定我是否掌握了VB.NET xml语法。但是,最终您将自动生成Xaml,从根本上讲,它是通过XamlParser推入的字符串。由于您是使用代码生成Xaml的,因此您无需在任何时候手动编辑结果。 因此,为什么不将最后一个字符串中的所有CR和LF字符删除,然后再将其发送到XamlParser?     
我不知道我是否正确回答了您的问题,但以下是我认为您正在寻找的示例: C#
Canvas _testCanvas = new Canvas();
            string _testString = \"<Canvas x:Name=\'testCanvas\' xmlns=\'http://schemas.microsoft.com/winfx/2006/xaml/presentation\' xmlns:x=\'http://schemas.microsoft.com/winfx/2006/xaml\'><Grid><StackPanel Orientation=\'Vertical\'><TextBlock><Run Text=\'r\'/><Run Text=\'u\'/><Run Text=\'n\'/></TextBlock><TextBlock><Run Text=\'far a\'/><Run Text=\'way\'/><Run Text=\' from me\'/></TextBlock></StackPanel></Grid></Canvas>\";

            _testCanvas = (Canvas)XamlReader.Load(_testString);

            ContentPanel.Children.Add(_testCanvas);
VB.NET(我使用了developerfusion转换器,所以请多多包涵):
Dim _testCanvas As New Canvas()
Dim _testString As String = \"<Canvas x:Name=\'testCanvas\' xmlns=\'http://schemas.microsoft.com/winfx/2006/xaml/presentation\' xmlns:x=\'http://schemas.microsoft.com/winfx/2006/xaml\'><Grid><StackPanel Orientation=\'Vertical\'><TextBlock><Run Text=\'r\'/><Run Text=\'u\'/><Run Text=\'n\'/></TextBlock><TextBlock><Run Text=\'far a\'/><Run Text=\'way\'/><Run Text=\' from me\'/></TextBlock></StackPanel></Grid></Canvas>\"

_testCanvas = DirectCast(XamlReader.Load(_testString), Canvas)

ContentPanel.Children.Add(_testCanvas)
    

要回复问题请先登录注册