Next, we add the table rows
Because Table does not support binding to a collection of Items
(unlike for example other WPF UI controls such as ListBox or
ItemsControl) - also known as list binding, we manually add table
rows in code.
To do so, we load the XAML design file at runtime. We then use
XML classes to duplicate rows and bind them to different data
items. To load XAML design files at runtime, we must set their type
as "Content" in the Visual Studio file properties. It may also help
to copy them into the binary location.
MemoryStream ModifyXamlUsingTextProvider(string mDataProvider, string mXamlFile, string mTableXamlLocation, string mTableName)
{
TextDataProvider dataProvider = new TextDataProvider(mDataProvider);
XmlDocument xamlDoc = new XmlDocument();
FileStream xamlFile = new FileStream(mXamlFile, FileMode.Open);
xamlDoc.Load(xamlFile);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xamlDoc.NameTable);
nsmgr.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
XmlNode itemsTable = xamlDoc.DocumentElement.SelectSingleNode(mTableXamlLocation + "[@Name='" + mTableName + "']", nsmgr);
for (int i = 1; i < dataProvider.Count; i++) {
XmlNode rowGroup = itemsTable.LastChild;
XmlNode newRowGroup = rowGroup.Clone();
string bindingText = newRowGroup.Attributes["DataContext"].Value;
bindingText = bindingText.Remove(bindingText.LastIndexOf('[')) + "[" + i + "] }";
newRowGroup.Attributes["DataContext"].Value = bindingText;
newRowGroup.Attributes["Background"].Value = (i % 2) == 0 ? "White" : "LightGray";
itemsTable.InsertAfter(newRowGroup, rowGroup);
}
MemoryStream memStream = new MemoryStream();
xamlDoc.Save(memStream);
xamlFile.Close();
return memStream;
}
Private Function ModifyXamlUsingTextProvider(mDataProvider As String, mXamlFile As String, mTableXamlLocation As String, mTableName As String) As MemoryStream
Dim dataProvider As New TextDataProvider(mDataProvider)
Dim xamlDoc As New XmlDocument()
Dim xamlFile As New FileStream(mXamlFile, FileMode.Open)
xamlDoc.Load(xamlFile)
Dim nsmgr As New XmlNamespaceManager(xamlDoc.NameTable)
nsmgr.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
Dim itemsTable As XmlNode = xamlDoc.DocumentElement.SelectSingleNode(mTableXamlLocation + "[@Name='" + mTableName + "']", nsmgr)
Dim i As Integer = 1
While i < dataProvider.Count
Dim rowGroup As XmlNode = itemsTable.LastChild
Dim newRowGroup As XmlNode = rowGroup.Clone()
Dim bindingText As String = newRowGroup.Attributes("DataContext").Value
bindingText = bindingText.Remove(bindingText.LastIndexOf("["C)) + "[" + i + "] }"
newRowGroup.Attributes("DataContext").Value = bindingText
newRowGroup.Attributes("Background").Value = If((i Mod 2) = 0, "White", "LightGray")
itemsTable.InsertAfter(newRowGroup, rowGroup)
System.Math.Max(System.Threading.Interlocked.Increment(i),i - 1)
End While
Dim memStream As New MemoryStream()
xamlDoc.Save(memStream)
xamlFile.Close()
Return memStream
End Function
The data provider is loaded directly from code so we know how
many data items (text file lines) are available. The XAML design
document is loaded as a XmlDocument. We retrieve the XML node that
corresponds to our table via the Xpath query passed to
SelectSingleNode() in the document element. For this, we need to
load the WPF namespace else the query will fail.
Once we have our items table, we get hold of the row group. The
row group is the last child of the table. We duplicate the row
group by using the XmlNode.Clone() method and then change the data
binding to point to the next row. We also alternate the row
background colors. After all items have been added, we save the XML
document into a memory stream.
Stream stm = ModifyXamlUsingTextProvider(null, null, null, null);
var page = XamlReader.Load(stm) as System.Windows.Controls.Page;
FlowDocumentPageViewer docViewer = LogicalTreeHelper.FindLogicalNode(page, "DocViewer") as FlowDocumentPageViewer;
page.Content = null;
stm.Close();
Dim stm As Stream = ModifyXamlUsingTextProvider(Nothing, Nothing, Nothing, Nothing)
Dim page = TryCast(XamlReader.Load(stm), System.Windows.Controls.Page)
Dim docViewer As FlowDocumentPageViewer = TryCast(LogicalTreeHelper.FindLogicalNode(page, "DocViewer"), FlowDocumentPageViewer)
page.Content = Nothing
stm.Close()
We use XamlReader.Load() to load the root object specified in the
XAML design file (stored in the memory stream). This is not the
Table, or the FlowDocument (so as to then convert it directly to
XPS and PDF as shown below); it is a Page. This is because it is
convenient to view the Table in the Visual Studio XAML designer so
we chose to have a Page as the root element. This page contains a
flow document viewer and this allows the designer to show the table
as we type XML tags. We then simply discard the page and keep the
document viewer if we want to show the document in a window or we
just keep the document if we only want to convert it to XPS. The
page content must be set to null; otherwise, the child elements
(the document viewer and document) cannot be attached to a
different container (the window or the XPS document).
|
|
|