Råmaterialet er beskrevet i modulen Noen datasett, og er en XML-fil som som ser slik ut:
<?xml version="1.0" encoding="UTF-8"?>
<wines>
<wine>
<type>sparkling</type>
<name>Gosset Grande Réserve Brut</name>
<catalog>32299</catalog>
<country>Frankrike</country>
<volume>75</volume>
<price>259.00</price>
<dice>5</dice>
<description>Syrefrisk og harmonisk smak med god fylde.</description>
</wine>
...
</wines>
Altså en enkel liste med <wine>-elementer uten noen spesiell ordning.
Vi kan lage utvalg fra denne fila på mange forskjellige måter.
Det er i hvert fall tre angrepsvinkler som peker seg ut:
- Vi kan etablere et DOM-tre i hukommelsen og skrive vår egen rutine som vandrer i treet og plukker ut det vi vil ha
- Vi kan etablere et DOM-tre i hukommelsen og skrive vår egen rutine som bruker et XPath uttrykk til å plukke ut det vi er ute etter
- Vi kan skrive en XSLT-fil som transformerer XML-fila ved å lage et utvalg.
Vi velger den siste løsningen. Fordelen er at vi kan lage en transformasjon som
lager HTML-format for oss direkte. Dette ka vi presentere i et browser vindu i applikasjonen.
Ulempen er at vi kanskje har litt mindre kontroll dersom vi skal lage kompliserte utvalgskriterier.
Transformasjonen
Siden vi skal la brukeren gjøre ulike typer utvalg, må vi gjøre XSLT-transformasjonen parameterstyrt.
Den transformasjonen vi bruker ser slik ut:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output
method="xml"
encoding="ISO-8859-1"
indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
media-type="text/xml"
omit-xml-declaration="yes"
version="1.0"/>
<!-- default values -->
<xsl:param name="theCountry" select="'Frankrike'"/>
<xsl:param name="theType" select="'red'"/>
<xsl:template match="/">
<html>
<head>
<title>Vinliste</title>
<link rel="STYLESHEET" href="vin.css"/>
</head>
<body>
<h1>Vinliste</h1>
<xsl:variable name="list"
select="//wine[country=$theCountry and type=$theType]"/>
<p>
<xsl:value-of select="count($list)"/> <xsl:call-template name="convert">
<xsl:with-param name="type">
<xsl:value-of select="$theType"/>
</xsl:with-param>
</xsl:call-template> viner fra
<xsl:value-of select="$theCountry"/>
</p>
<hr/>
<xsl:apply-templates select="$list">
<xsl:sort select="type" order="ascending"/>
<xsl:sort select="dice" order="descending"/>
<xsl:sort select="price/volume" order="descending"/>
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="//wine">
<div class="wine">
<div style="float:left">
<xsl:element name="img">
<xsl:attribute name="src"><xsl:value-of
select="type"/><xsl:value-of
select="dice"/>.gif</xsl:attribute>
<xsl:attribute name="alt">dice</xsl:attribute>
</xsl:element>
</div>
<div class="wine-name"><xsl:value-of select="name"/></div>
<div class="wine-country"><xsl:value-of select="country"/></div>
</div>
<div style="clear:left"/>
<div class="wine-desc"><xsl:value-of select="description"/></div>
<div class="wine-price">
NOK <xsl:value-of select="price"/> / <xsl:value-of select="volume"/>cl
</div>
</xsl:template>
<xsl:template name="convert">
<xsl:param name="type"/>
<xsl:if test="$type='red'"> røde</xsl:if>
<xsl:if test="$type='white'"> hvite</xsl:if>
<xsl:if test="$type='sparkling'"> musserende</xsl:if>
<xsl:if test="$type='rose'"> rose</xsl:if>
</xsl:template>
</xsl:stylesheet>
PDF
PDF-vesjonen lages ved hjelp av Prince XML [1] .
Vi kjører Prince vi en ekstern kommando, slik:
private void buttonPDF_Click(object sender, EventArgs e)
{
// make pdf from the vines selected
String sourcePath = webBrowser1.Url.AbsolutePath;
String stylePath = Path.Combine(Application.StartupPath,
"data\\report.css");
String resultPath = Path.Combine(Application.StartupPath,
"data\\report.pdf");
Process PP = new Process();
try
{
PP.StartInfo.UseShellExecute = true;
String args = "{0} -s {1}";
PP.StartInfo.FileName = PRINCE_PATH;
PP.StartInfo.Arguments =
String.Format(args, sourcePath, stylePath);
PP.StartInfo.CreateNoWindow = true;
PP.Start();
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ERROR_FILE_NOT_FOUND)
MessageBox.Show(ex.Message + ". Check the path.");
else if (ex.NativeErrorCode == ERROR_ACCESS_DENIED)
MessageBox.Show(ex.Message +
". You do not have permission to print this file.");
}
}
Koden i vinduet som drar det hele ser slik ut:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Xsl;
using System.IO;
using System.Xml;
using System.Diagnostics;
namespace winexml
{
public partial class Form1 : Form
{
const int ERROR_FILE_NOT_FOUND = 2;
const int ERROR_ACCESS_DENIED = 5;
const String PRINCE_PATH = @"C:\fixed\prince\Engine\bin\prince.exe";
String XmlSource=
Path.Combine(Application.StartupPath,"data\\viner.xml");
// alternativ:
//"http://www.ia.hiof.no/~borres/commondata/vin/viner.xml";
public Form1()
{
InitializeComponent();
// if prince is not found we remove the PDF-button
if (!File.Exists(PRINCE_PATH))
buttonPDF.Visible = false;
}
private void buttonShow_MouseClick(object sender, MouseEventArgs e)
{
// figure out what user has chosen
String country = (String)comboCountry.SelectedItem;
String winetype = (String)comboType.SelectedItem;
// modify the selected string to match values on xml-file
if(winetype.CompareTo("Musserende")==0) winetype="sparkling";
else if (winetype.CompareTo("Rose") == 0) winetype = "rose";
else if (winetype.CompareTo("Hvit") == 0) winetype = "white";
else winetype = "red";
// Access XML-file and perform transformation
try
{
// prepare the transformation
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(Path.Combine(Application.StartupPath,
"data\\tohtml_param.xslt"));
// set up an argumentlist with parameters
XsltArgumentList argList = new XsltArgumentList();
argList.AddParam("theCountry", "", country);
argList.AddParam("theType", "", winetype);
// Create a XmlWriter.
XmlWriter writer =
XmlWriter.Create(Path.Combine(Application.StartupPath,
"data\\output.html"));
// Transform the file.
xslt.Transform(XmlSource, argList, writer);
writer.Flush();
// give the result to the browser window
webBrowser1.Url =
new System.Uri(Path.Combine(Application.StartupPath,
"data\\output.html"));
writer.Close();
buttonPDF.Enabled = true;
}
catch (Exception ex)
{
// use the webBrowser for error messages
webBrowser1.DocumentText =
String.Format("<htm><body>Error:<br/>{0}</body></html>",ex.Message);
buttonPDF.Enabled = false;
}
}
private void buttonQuit_MouseClick(object sender, MouseEventArgs e)
{
this.Dispose(true);
}
private void Form1_Load(object sender, EventArgs e)
{
// We could do this better by reading the XML-file
// and identify all existing countries on startup
comboCountry.Items.Add("Frankrike");
comboCountry.Items.Add("Italia");
comboCountry.Items.Add("Portugal");
comboCountry.Items.Add("Spania");
comboCountry.Items.Add("Chile");
comboCountry.Items.Add("Australia");
comboCountry.Items.Add("Tyskland");
comboCountry.Items.Add("Ungarn");
comboCountry.Items.Add("Argentina");
comboCountry.Items.Add("Sør-Afrika");
comboCountry.Items.Add("USA");
comboCountry.Items.Add("New Zealand");
comboCountry.SelectedItem = "Frankrike";
comboType.Items.Add("Rød");
comboType.Items.Add("Hvit");
comboType.Items.Add("Rose");
comboType.Items.Add("Musserende");
comboType.SelectedItem = "Rød";
// start message in browser window
webBrowser1.Url =
new System.Uri(Path.Combine(Application.StartupPath,
"data\\startpage.html"));
}
#region pdf
private void buttonPDF_Click(object sender, EventArgs e)
{
// make pdf from the vines selected
String sourcePath = webBrowser1.Url.AbsolutePath;
String stylePath = Path.Combine(Application.StartupPath,
"data\\report.css");
String resultPath = Path.Combine(Application.StartupPath,
"data\\report.pdf");
Process PP = new Process();
try
{
PP.StartInfo.UseShellExecute = true;
String args = "{0} -s {1}";
PP.StartInfo.FileName = PRINCE_PATH;
PP.StartInfo.Arguments =
String.Format(args, sourcePath, stylePath);
PP.StartInfo.CreateNoWindow = true;
PP.Start();
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ERROR_FILE_NOT_FOUND)
MessageBox.Show(ex.Message + ". Check the path.");
else if (ex.NativeErrorCode == ERROR_ACCESS_DENIED)
MessageBox.Show(ex.Message +
". You do not have permission to print this file.");
}
}
#endregion pdf
}
}
Stilsett
Det er to stilsett involvert. Ett som brukes i vinduet:
body{
color: black;
font-family:"Verdana","Geneva","Arial","Times",sans-serif;
font-size:13px;
text-align:left;
margin:0px;
padding:20px;
}
h1{font-size:24px}
.ingress{font-size:12px}
.wine{margin-top:15px}
.wine-name{font-size:18px;padding-left:50px}
.wine-desc{}
.wine-country{padding-left:50px; font-weight:bold}
.wine-price{font-weight:normal}
.wine-author{font-weight:normal;font-size:10px;color:gray}
I tillegg brukes følgende i forbindelse med Prince transformasjonen:
@page{
size: 20.995cm 27.31cm;
margin: 2.0cm 1.25cm 2.0cm 1.5875cm;
@top-right{
content: "Viner";
font-size:18px;
}
@bottom-right {
vertical-align:top;
content: counter(page);
font-style: italic;
font-size:11px;
border-top-style:solid;
border-top-width:thin;
}
@bottom-left {
vertical-align:top;
content:"Demo";
font-style: italic;
font-size:11px;
border-top-style:solid;
border-top-width:thin;
}
}
Det er klart at denne løsningen kunne vært realisert på mange forskjellige måter.
- Vi kunne bruke databaser
- Vi kunne la alt materialet ligge på en server
- Vi kunne faktisk løst problemet direkte i MSIE ved hjelp av Javascript
og transformasjoner direkte i nettleseren. Du finner noe materiale om
denne muligheten på
Scripting mot MSXML