65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 XML 字面量和嵌入式表达式创建 FlowDocuments

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (7投票s)

2011年12月13日

CPOL

4分钟阅读

viewsIcon

32252

downloadIcon

1362

如何使用 XML 字面量和嵌入式表达式创建 FlowDocument。

Screenshot_1.png

一个使用 XML 字面量创建 FlowDocument 的 UserControl

引言

在本文中,我将展示如何使用XML 字面量嵌入式表达式来定义一个 FlowDocument。 由于 XML 字面量是 VB.NET 独有的,因此本文可能不适合一些铁杆 C# 开发者。 希望在不久的将来,C# 也能够受益于 XML 字面量。 如果您的编程好奇心占了上风,请继续阅读。

FlowDocument

Flow Document 是一种具有丰富布局功能的文档。 它可以根据窗口大小、设备分辨率和其他变量动态布局其内容。 流文档主要适用于屏幕观看,并提供高级文档功能,如分页、列、搜索、查看模式和打印(搜索、查看模式和打印支持通过流文档容器提供)。

以下 XAML 代码创建一个流文档,由 FlowDocumentPageViewer 托管,显示一些文本

<FlowDocumentPageViewer>
  <FlowDocument>
    <Paragraph>CodeProject: Your Development Resource</Paragraph>
  </FlowDocument>
</FlowDocumentPageViewer>

XAML 代码的结果是

Screenshot_2.png

您可以在此处阅读更多关于流文档的信息。

XML 字面量

XML 字面量是指字面上定义在 Visual Basic 代码中的 XML 代码。 使用 XML 字面量,您可以直接在代码中定义 XML 文档和片段。 以下示例展示了 XML 文档和 XML 元素的创建

Dim cpDoc As XDocument = <?xml version="1.0"?>
                         <CodeProject>
                             <Member>
                                 <FirstName>Meshack</FirstName>
                                 <LastName>Musundi</LastName>
                             </Member>
                             <Member>
                                 <FirstName>Vee</FirstName>
                                 <LastName>Bee</LastName>
                             </Member>
                         </CodeProject>
                         
Dim cpElm As XElement = <CodeProject>
                            <Member>
                                <FirstName>Meshack</FirstName>
                                <LastName>Musundi</LastName>
                            </Member>
                            <Member>
                                <FirstName>Vee</FirstName>
                                <LastName>Bee</LastName>
                            </Member>
                        </CodeProject>

嵌入式表达式

嵌入式表达式与 XML 字面量协同工作,从而能够创建动态 XML 文档或 XML 片段。 通过嵌入式表达式,可以使用变量或 LINQ 查询中的值在运行时创建 XML 节点。 这是通过使用嵌入式表达式运算符 <%= %> 来完成的。

使用一些变量值创建 XML 文档非常简单,如下所示

Dim firstName As String = "Meshack"
Dim lastName As String = "Musundi"

Dim cpDoc As XDocument = <?xml version="1.0"?>
                         <CodeProject>
                             <Member>
                                 <FirstName><%= firstName %></FirstName>
                                 <LastName><%= lastName %></LastName>
                             </Member>
                         </CodeProject>

使用 LINQ 查询创建 XML 元素非常简单,如下所示

Dim names() As String = {"Bill", "Meshack", "Vee"}

Dim cpElm As XElement = <CodeProject>
                            <%= From n In names Select _
                                <Member>
                                    <Name><%= n %></Name>
                                </Member> %>
                        </CodeProject>

上面生成的 XML 元素的结构如下所示

<CodeProject>
  <Member>
    <Name>Bill</Name>
  </Member>
  <Member>
    <Name>Meshack</Name>
  </Member>
  <Member>
    <Name>Vee</Name>
  </Member>
</CodeProject>

XML 字面量 + 嵌入式表达式 = 动态 FlowDocument

如果要创建一个“动态”流文档,一种选择是以编程方式定义流文档。 另一种选择是更改已经定义在 XAML 中的流文档的部分内容。 使用后一种方法,您必须深入挖掘流文档的深层嵌套内容,才能找到要更改的内容。 不幸的是,这两种方法都很繁琐且非常不直观。

使用 XML 字面量和嵌入式表达式,创建动态流文档的过程可以更加轻松。 考虑一下,我们打算创建一个流文档,其最终结果如下

Screenshot_3.png

流文档表的数据源将是一个以编程方式定义的 DataTable,以避免创建数据库。 流文档中显示的图像将来自应用程序资源,Logo.png

Screenshot_4.png

为了创建 DataTable,一个名为 AlbumTable() 的方法将创建并返回所需的对象。

Private Function AlbumTable() As DataTable
    Dim table As New DataTable

    table.Columns.Add("No.", GetType(Integer))
    table.Columns.Add("Album", GetType(String))
    table.Columns.Add("Artist", GetType(String))

    table.Rows.Add(1, "Let England Shake", "PJ Harvey")
    table.Rows.Add(2, "Bon Iver", "Bon Iver")
    table.Rows.Add(3, "21", "Adele")
    table.Rows.Add(4, "whokill", "tUnE-yArDs")
    table.Rows.Add(5, "Watch the Throne", "Jay-Z and Kanye West")
    table.Rows.Add(6, "The Whole Love", "Wilco")
    table.Rows.Add(7, "The English Riviera", "Metronomy")
    table.Rows.Add(8, "Skying", "The Horrors")
    table.Rows.Add(9, "House of Balloons", "The Weeknd")
    table.Rows.Add(10, "Helplessness Blues", "Fleet Foxes")

    Return (table)
End Function

然后,我们可以创建一个 XML 元素,使用带有所需嵌入式表达式的 XML 字面量,然后再将其转换为 FlowDocument,该文档用于设置 FlowDocumentPageViewerDocument 属性。

Private Sub ShowReport(ByVal albumTable As DataTable)

    Dim doc = <FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  FontFamily="Myriad Pro" FontSize="14" ColumnWidth="594">
                  <!--Logo-->
                  <BlockUIContainer><Image Source="Logo.png" Width="380" Height="78"/></BlockUIContainer>

                  <Paragraph Margin="10" FontWeight="SemiBold">
                      Top Albums of 2011 Report
                  </Paragraph>

                  <Table Margin="10" BorderThickness="1" BorderBrush="Gainsboro">
                      <Table.Columns>
                          <TableColumn Width="*"/>
                          <TableColumn Width="5*"/>
                          <TableColumn Width="5*"/>
                      </Table.Columns>

                      <TableRowGroup>
                          <TableRow Background="Crimson" Foreground="White" FontWeight="SemiBold">
                              <TableCell><Paragraph Margin="4">No.</Paragraph></TableCell>
                              <TableCell><Paragraph Margin="4">Album</Paragraph></TableCell>
                              <TableCell><Paragraph Margin="4">Artist</Paragraph></TableCell>
                          </TableRow>

                          <!--Data rows-->
                          <%= From alb In albumTable.Rows Select _
                              <TableRow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                                  <TableCell BorderThickness="1" BorderBrush="Gainsboro">
                                      <Paragraph Margin="4"><%= alb(0) %></Paragraph>
                                  </TableCell>
                                  <TableCell BorderThickness="1" BorderBrush="Gainsboro">
                                      <Paragraph Foreground="MidnightBlue" Margin="4"><%= alb(1) %></Paragraph>
                                  </TableCell>
                                  <TableCell BorderThickness="1" BorderBrush="Gainsboro">
                                      <Paragraph Foreground="Gray" Margin="4"><%= alb(2) %></Paragraph>
                                  </TableCell>
                              </TableRow> %>
                      </TableRowGroup>
                  </Table>
              </FlowDocument>

    Dim docString As String = doc.ToString()

    Dim flowDoc As FlowDocument = XamlReader.Load(New XmlTextReader(New StringReader(docString)))
    FlowDocPgVwr.Document = flowDoc

End Sub
    
Private Sub MainWindow_Loaded(ByVal sender As Object, _
                              ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    ShowReport(AlbumTable)
End Sub

我注意到这种方法的一件事是,您必须在 LINQ 查询的 Select 语句之后的第一个元素中指定 XML 命名空间。 如果您不指定命名空间,将自动指定一个命名空间,但其值为 null。 这将导致在尝试将 docString 转换为 XAML 时出现 XamlParseException

另一件需要注意/记住的事情是,XAML 是区分大小写的。 如果您忘记适当地大写元素名称,例如,<Tablerow> 而不是 <TableRow>,则会生成 XamlParseException。 这种方法也不是 WYSIWYG 的,因此您必须运行应用程序才能查看最终结果到底是什么。 尽管如此,如果您对如何创建流文档有很好的了解,您仍然可以获得所需的结果。

数据绑定

从先前显示的示例中,值得注意的是,这种方法创建了拥有类似数据绑定流文档的可能性。 您可以将流文档容器嵌套在 UserControl 中,并定义一个合适数据类型的依赖属性。 这是本文开头显示的应用程序屏幕截图中使用的方法。

Public Property Table As DataTable
    Get
        Return GetValue(TableProperty)
    End Get

    Set(ByVal value As DataTable)
        SetValue(TableProperty, value)
    End Set
End Property

Public Shared ReadOnly TableProperty As DependencyProperty = _
                       DependencyProperty.Register("Table", _
                       GetType(DataTable), GetType(ReportViewer), _
                       New FrameworkPropertyMetadata(New PropertyChangedCallback( _
                                                              AddressOf TableChanged)))

Private Shared Sub TableChanged(ByVal sender As Object, _
        ByVal e As DependencyPropertyChangedEventArgs)
    Dim newTable As DataTable = CType(e.NewValue, DataTable)
    CType(sender, ReportViewer).ShowReport(newTable)
End Sub

如果这种方法看起来不合适,您可以选择遵循此处描述的方法。

结论

就是这样。 我希望您从本文中学到了一些有用的东西。

历史

  • 2011 年 12 月 13 日:首次发布。
© . All rights reserved.