Continued: Using XML to implement universal web report printing (implementation)

xiaoxiao2021-03-05  19

Continued: Using XML to implement universal web report printing (implementation)

Summary:

This article combines the code in detail the implementation and expansion process of "Using XML Realization General Web Report" (hereinafter referred to as "" profit ""). This article is the sequel of "Library", and the code examples raised in the article are written in C # language.

table of Contents:

Introduction Software Principle Structure Design Code Implementation Scheme Expansion Summary

introduction:

After the publication of "Lis", a large number of readers sent E-mail to me, showing me that the program is very interested, and also ask how specific resolution and print details of the report format and request the source code of the program. The enthusiasm of the reader makes me not, although I will answer the source, but I feel sorry and regret, because of the relationship between time and energy, I can't make a very detailed detail for each letter. The answer, and the source code I wrote is also very messy. In fact, I spent two hours, I spent two hours (the original procedure was destroyed because the hard disk fault was destroyed), not only did not have any annotations and not perfect, including some The label has not been implemented yet.

In order to make up for the previous shortcomings, I spent some time to improve the structure of the program, rewrite all source code, realize all the labels, and start to explain the design and encoding process of the program, before reading this article It is highly recommended that you read the "Le" one by reading the concept. If there is a clear explanation in this article, this article will no longer introduce, here will only explain the "profit" text does not mention or introduce it. It is unclear that the readers let me know the most part of the part.

Software principle:

The principle of this software is actually very simple, that is, it is to facilitate parsing the defined XML format tag, interpret the parameter definition of the tag in the file, and finally restore this information to the printer output graphic format.

In order to express complex report styles, we need to define some tags, attach specific style information in these tags, and act similar to HTML tags, and our resolution program is equivalent to IE browser, the difference is IE The graph is output to the screen, and we are output to the printer, because the printer relative to the specialty of the display (such as paging), we cannot print directly with the web browser's label parsing function, need to be a need to meet the needs "Print Browser".

For most reports, I only define two format tags: text (TEXT) and tables, their specific attribute definitions and other settings of label definition, please refer to "Le" text, here again A structural diagram helps readers understand. As follows:

Structural design:

To describe all style markers, I first define a abstract base class Printelement, which has a virtual method DRAW, then corresponds to the table and text, derived two subclasses from Printelement, which is Table and Text, I also created a Parser The class is used to parse different styles tags and create a corresponding object, which has a static method CreateElement that creates a corresponding object based on different format tags. The structure is shown below:

The readers who have read "design patterns" must have already seen, which applies a very famous model in design mode: Abstract Factory. The advantage of using this mode is that the label objects and parsers are independent, reducing the system coupling degree, which is advantageous to increase other format labels in the future (hereinafter will give an instance) and convenient Replace different user interfaces (the client represents a Windows application or a web plugin). Code:

First, create a new project of "Windows Control Library", write Remoteprint at the project name, as shown below:

Then put the default UserControl1 class in the new project, and its constructor name and file name are changed to PrintControl. Set it back to white, add three press, and set their enable properties to false, the Anchor property is set to Bottom, Right, add a Label control to display program status, its Anchor property Set to Left. As shown below:

Then drag in three print objects from the control bar: PrintDocument, PagesetupDialog, PrintPreviewDialog, as shown below:

All Document properties of PageSetupDialog1 and PrintPreviewDialog1 are set to PrintDocument1.

Then add a new class of Printelement to the project, the code is as follows:

Using system;

USING SYSTEM.XML;

Using system.drawing;

Namespace Remoteprint

{

Public Class Printelement

{

Public Printelement ()

{

}

Public Virtual Bool Draw (Graphics G)

{

Return False;

}

}

}

There is only one virtual method DRAW in this class, pay attention to it specifies that a BOOL value needs to be returned. This value is used to indicate whether the label is printed in the page.

Then add a Table new class, the code is as follows:

Using system;

USING SYSTEM.XML;

Using system.drawing;

Namespace Remoteprint

{

Public Class Table: Printelement

{

Private XMLnode Table;

Public static int count = 0, PC = 1;

Public Table (XMLNode Table)

{

Table = Table;

}

Public Override Bool Draw (Graphics G)

{

// Table coordinates

INT TABLEX = INT.PARS (Table.Attributes ["X"]. innerText);

Int Tabley = int.parse (Table.Attributes ["y"]. innerText);

INT x = TABLEX, Y = Tabley;

DrawTopline (G, Table); // Painting Top Line

Pen Pen = New Pen (color.fromname (Table.Attributes ["BorderColor"]. InnerText),

FLOAT.PARS (Table.Attributes ["Border"]. InnerText);

INT trheight = 0;

// Metead Foreach (XMLNode Table ["TableHead"]. ChildNodes)

{

Trheight = int.parse (Tr.attributes ["Height"]. InnerText);

DrawTR (X, Y, Tr, Pen, G);

Y = trheight;

}

// entry

For (int I = 0; i

{

XMLNode Tr = Table ["Tablebody"]. ChildNodes [count];

Trheight = int.parse (Tr.attributes ["Height"]. InnerText);

DrawTR (X, Y, Tr, Pen, G);

Y = trheight;

COUNT ;

IF (count == Table ["TableBody"]. ChildNodes.count)

Break;

}

x = TABLEX;

// bottom

Foreach (XMLNode Tr in Table ["Tablefoot"]. ChildNodes)

{

Trheight = int.parse (Tr.attributes ["Height"]. InnerText);

DrawTR (X, Y, Tr, Pen, G);

Y = trheight;

}

INT CurrentPage = PC;

PC ;

Bool Haspage = false;

IF (count

{

HASPAGE = true; // Need to continue printing

}

Else

{

Count = 0;

PC = 1;

HASPAGE = false; // Table print

}

Return Haspage;

}

Private Void DrawTopline (Graphics G, XMLNode Table)

{

Pen Pen = New Pen (color.fromname (Table.Attributes ["BorderColor"]. InnerText),

FLOAT.PARS (Table.Attributes ["Border"]. InnerText);

INT width = 0;

Foreach (XMLnode Td in Table.firstchild.Firstchild)

{

Width = int.parse (td.attributes ["width"]. innerText);

}

INT x = int.parse (Table.Attributes ["x"]. innertext);

INT Y = int.Parse (Table.Attributes ["Y"]. innertext);

g.drawline (Pen, X, Y, X Width, Y);

}

// Painting

Private Void Drawtr (int X, int y, xmlnode tr, pen pen, graphics g)

{

INT Height = int.Parse (Tr.attributes ["Height"]. InnerText);

Int width;

g.drawline (Pen, X, Y, X, Y Height); // Draw the left end of the line Foreach (XMLNode TD in TR)

{

Width = int.parse (td.attributes ["width"]. innertext);

DrawTD (X, Y, Width, Height, TD, G);

g.drawline (Pen, X Width, Y, X Width, Y Height); // Right Line

g.drawline (Pen, X, Y Height, X Width, Y Height); // Bottom

X = width;

}

}

// Selecting a single element

Private Void Drawtd (int X, int y, int width, int tent, xmlnode td, graphics g)

{

Brush brush = new solidbrush (color.fromname (td.attributes ["bgcolor"]. InnerText);

G.FillRectangle (Brush, X, Y, Width, Height);

FontStyle Style = fontstyle.regular;

// Set the font style

IF (td.attributes ["b"]. InnerText == "True")

STYLE | = fontstyle.bold;

IF (TD.Attributes ["I"]. InnerText == "True")

STYLE | = fontstyle.ital;

IF (TD.Attributes ["u"]. InnerText == "True")

Style | = fontstyle.underline;

Font font = new font (td.attributes ["fontname"]. InnerText,

FLOAT.PARSE (TD.Attributes ["FontSize"]. Innertext), Style);

Brush = new solidbrush (color.fromname (td.attributes ["fontcolor"]. innerText);

StringFormat sf = new stringFormat ();

// Set alignment

Switch (td.attributes ["align"]. inNerText)

{

Case "center":

Sf.Alignment = stringalignment.center;

Break;

Case "Right":

sf.alignment = stringalignment.near;

Break;

DEFAULT:

sf.alignment = stringalignment.far;

Break;

}

Sf.LineAlignment = stringalignment.center;

Rectanglef Rect = New Rectanglef ((float) x, (float) y,

(float) width, (float) Height;

g.drawstring (TD.INNNERTEXT, FONT, BrUSH, RECT, SF);

}

}

}

The Table class will be independently parsed by the Table tag, all of which are completed internally in the class, so that we are directly handed over to the Table class when we are in parsing the top label, do not need to care Implement details. Add another Text class, the code is as follows:

Using system;

USING SYSTEM.XML;

Using system.drawing;

Namespace Remoteprint

{

Public class text: Printelement

{

Private XMLNode Text = NULL;

Public text (XMLNode Text)

{

TEXT = TEXT;

}

Public Override Bool Draw (Graphics G)

{

Font font = new font (text.attributes ["fontname"]. InnerText,

INT.PARS (Text.attributes ["fontsize"]. innertext));

Brush brush = new solidbrush (color.fromname (text.attributes

["fontcolor"]. innerText);

g.drawstring (Text.innerText, Font, Brush, Float.Parse

(Text.attributes ["x"]. Innertext),

Float.parse (Text.attributes ["Y"]. innerText);

Return False;

}

}

}

Like the Table class, the Text class completes parsing and printing of the Text tag, but because of the simplicity of Text, it has a lot less code. Both them inherit from Printelement, which is heavy as the implementation of the DRAW method.

Finally, we also need a parser to parse the top layer label and generate the corresponding object, and its role in this mode is a "factory class", which is responsible for producing the "product" required by the user. code show as below:

Using system;

USING SYSTEM.XML;

Namespace Remoteprint

{

Public Class Parser

{

Public Parser ()

{

}

Public Static Printelement CreateElement (XMLNode Element)

{

Printelement Printelement = NULL;

Switch (Element.name)

{

Case "text":

Printelement = new text (element);

Break;

Case "Table":

Printelement = New Table (Element);

Break;

DEFAULT:

Printelement = new printelement ();

Break;

}

Return printelement;

}

}

}

Ok, the core analysis and the specific print method of the label have been completed, and now we go back to PrintControl to write some code to test our results.

First, you need to reference two namespaces to be used:

USING SYSTEM.XML;

Using system.drawing.printing;

Then, before printing, you need to set the pages of the printer according to the PageSetting tag in the XML file, so we write a method to set the printer. Add a private method in the PrintControl class: Private Void Settingprinter (XMLNode PS)

{

// Print direction (vertical / horizontal)

This.PrintDocument1.defaultpagesettings.landscape = bool.parse (PS ["LANDScape"]. innerText);

/ / Set paper type

String Papername = PS ["paperkind"]. innerText;

Bool fitpaper = false;

/ / Get all paper types supported by the printer

Foreach (PaperSize Size In this.printDocument1.printersettings.papersizes)

{

if (Papername == Size.Papername) // See if the printer has the type of paper we need

{

This.PrintDocument1.defaultpagesettings.papersize = size;

FITPAPER = TRUE;

}

}

IF (! fitpaper)

{

// If there is no standard type we need, use a custom size

this.printDocument1.defaultpagesettings.papersize =

New Papersize ("Custom", INT.PARSE (PS ["PaperWidth"]. InnerText),

INT.PARSE (PS ["PaperHeight"]. InnerText));

}

}

Next, we add an XMLDocument object and a static variable calculation page number:

Private xmldocument doc = new xmldocument ();

Public Static Int Pages = 1;

Then load XML report data for the object in the LOAD event of the control, the code is as follows:

Private void PrintControl_load (Object Sender, System.Eventargs E)

{

Try

{

// Load report XML data

THIS.Label1.text = "Loading report data, please wait ...";

Doc.Load ("http://localhost/report.xml");

This.Label1.Text = "The report data is loaded!";

This.button1.enabled = this.button2.enabled = this.button3.enabled = true;

}

Catch (Exception EX)

{

This.Label1.Text = "Errors:" ex.Message;

}

}

Please note that we only put a local test data file (written for this file ", in fact, you can change the static or dynamic XML files anywhere on the load network, such as the above Doc.Load ("http://localhost/report.xml") can be rewritten:

Doc.Load ("http://www.nywhere.com/report.xml");

Doc.Load ("http://www.anyywhere.com/report.asp"); doc.load ("http://www.anyywhere.com/report.jsp?date=xxx");

Wait, as long as the loaded data is possible to meet the XML data documentation we specified.

Then add a printed event to the control of the control:

Public PrintControl ()

{

InitializationComponent ();

This.PrintDocument1.printPage = new printpageeventhandler (this.pd_printpage);

}

The code of the entrustment method is as follows:

Private Void Pd_PrintPage (Object Sender, PrintPageEventArgs EV)

{

Graphics g = ev.graphics;

Bool Hasmorepages = false;

Printelement Printelement = NULL;

Foreach (XMLNode Node in Doc "[" REPORTTABLE "]. ChildNodes)

{

Printelement = PARSER.CREATEEEEMENT (Node); // Call the parser to generate the corresponding object

Try

{

Hasmorepages = Printelement.draw (g); // Do you need paging

}

Catch (Exception EX)

{

THIS.Label1.Text = ex.Message;

}

}

// Output page number in the middle of the page

Font font = new font ("black body", 12.0f);

Brush brush = new solidbrush (color.black);

g.drawstring ("No. Pages.toToString () " Page ",

Font, Brush, Ev.Marginbounds.width / 2 ev. Marginbounds.Left - 30,

Ev.pagebounds.height - 60);

IF (HASMOREPAGES)

{

Pages ;

}

ev.hasmorepages = HASMOREPAGES;

}

The Click event code of three heads is as follows:

//page settings

Private void Button1_Click (Object Sender, System.Eventargs E)

{

This.pagesetupdialog1.showdialog ();

THIS.PRINTDocument1.defaultpagesetting = this.pagesetupdialog1.pagesettings;

}

//Printing preview

Private void button2_click (Object Sender, System.Eventargs E)

{

Try

{

This.PrintPreviewDialog1.showdialog ();

}

Catch (Exception EX)

{

THIS.Label1.Text = ex.Message;

}

}

//print

Private void button3_click (Object Sender, System.Eventargs E)

{

Try

{

This.PrintDocument1.print ();

}

Catch (Exception EX)

{

THIS.Label1.Text = ex.Message;

}

Ok, our print control is all done here, choose to generate a release version, then copy the generated printControl.dll file to the IIS in the project directory, then create a RemotePrint.htm HTML Format file, plus: , in order to more image And beautiful, you can also make the data you need to print into a web page. If you need to get the XML is a dynamic data source, you can use the dynamic script of ASP to generate the web page, if you need to get the XML is a static Text, you can use XSLT to convert an XML file into a web form directly.

Open the browser, type: http://localhost/remoteprint.htm, if you have already made an XML report data file like me, you can see the effect shown below.

Please note: All the data in the illustration sample is the author's casual virtual. The table data and print data in the web page are not from the same data source, nor deliberately goes right, just to demonstrate the effect, so the webpage shows the report and print There are some new reports in the preview. In practical applications, web display data can be identical to printout data.

Program expansion:

A part of the readers asked how to print some special forms of charts. The "Lee" article has been mentioned that the program can be very convenient to define the label you need, in theory, the special chart of any style can be printed . Therefore, this article intends to detail the specific process of adding your own defined label expansion printing format.

First assume that our customers are basically satisfied after watching the printed effect, but there is still a little short, if you need to print some charts? For example, a line diagram, a K-line diagram, a pie chart, a histogram, and the like. With our existing tags, we must first expand our label library to make it more expressive. Here, I will only plan to let our print control learn to draw simple line charts, I hope the reader can give an anti-three to create other various printing effects.

The most basic line diagram is composed of a line connected by the X coordinate axis, Y coordinate axis, and a series of points, so I defined the following tags:

1. Linechart: Like the Table, the Text Tag, for the pattern root label.

Property: None

2. Coordinate: coordinates.

Property: None

3. XCoordinate: X-axis coordinate line

Attributes:

# x: starting point X coordinate value

# y: The starting point Y coordinate value

# Length: Length value

# stroke: thick

# color: Color

# arrow: Is there an arrow?

4. Ycoordinate: Y-axis coordinate line

Attribute: with XCoordinate.

5. Scale: Turning

Tag content: text displayed on the scale

Attributes:

# Length: Distance Point Length Value

# hT: Tariff Highness

# width: Turning Width

# color: Color

# fontsize: Font size 6. Chart: chart root

Property: None

7. Lines: line segment

Attribute value:

# stroke: thick

# color: Color

8. Point: Point

Attribute value:

# x: x coordinate value

# y: y coordinate value

# RADIUS: radius

# color: Color

The structure is shown below:

Below is an XML line diagram example of the label just defined:

100

200

300

400

500

600

700

100

200

300

Complete the definition of the label, the next step is to modify our program, let him "read" these labels.

First, we will add a new class of LineChart to the project, just like the Table, Text class, which is also inherited from the Printelement class, which also overloads the Draw virtual method. code show as below:

Using system;

USING SYSTEM.XML;

Using system.drawing;

Using system.drawing.drawing2d;

Namespace Remoteprint

{

Public Class Linechart: Printelement

{

Private XMLNode Chart;

Public Linechart (XMLNode Chart)

{

Chart = chart;

}

Public Override Bool Draw (Graphics G)

{

Drawcoordinate (G, Chart ["Coordinate"]); // Painting Sitting Shaft

Drawchart (g, chart ["chart"]);

Return False;

}

Private Void Drawcoordinate (Graphics G, XMLNode Coo)

{

Drawxcoor (g, coo ["xcoordinate"]); // draw X coordinate

DrawyCoor (G, COO ["Ycoordinate"]); // painting Y coordinate

}

Private Void Drawxcoor (Graphics G, XMLNode XCOO)

{

INT x = int.parse (xcoo.attributes ["x"]. innertext);

INT Y = int.Parse (Xcoo.attributes ["y"]. innerText); int layth = int.parse (xcoo.attributes ["length"]. innerText);

Bool arrow = BOOL.PARSE (Xcoo.attributes ["arrow"]. innerText);

Int stroke = int.parse (xcoo.attributes ["stroke"]. innerText);

Color color = color.fromname (Xcoo.attributes ["color"]. InNerText);

Pen Pen = New Pen (Color, (Float) stroke;

IF (arrow) // Is there an arrow?

{

AdjustableArrowcap arrow = new AdjustableArrowcap

(float) (stroke * 1.5 1.5),

(FLOAT) (stroke * 1.5 2), true);

Pen.customedcap = arrow;

}

g.drawline (Pen, X, Y, X Length, Y); // Painting Coordinate

// Draw

Foreach (xmlnode scale in xcoo.childnodes)

{

INT LEN = INT.PARS (Scale.attributes ["Length"]. innerText);

INT Height = int.parse (Scale.attributes ["Height"]. innerText);

Int width = int.parse (Scale.attributes ["width"]. innertext);

INT FONTSIZE = INT.PARSE (Scale.attributes ["Fontsize"]. InnerText);

Color CLR = color.fromname (scale.attributes ["color"]. Innertext);

String name = scale.innertext;

Pen P = New Pen (CLR, (FLOAT) WIDTH);

G. Drawline (P, X LEN, Y, X LEN, Y - HEIGHT);

Font Font = New Font ("Arial", (Float) Fontsize;

g.drawstring

Name, Font, New Solidbrush (CLR),

(FLOAT) (X LEN - 10), (FLOAT) (Y 10));

}

}

Private Void Drawycoor (Graphics G, XMLNode Ycoo)

{

INT x = int.parse (Ycoo.attributes ["x"]. innertext);

Int y = int.parse (Ycoo.attributes ["y"]. innertext);

INT length = int.parse (Ycoo.attributes ["Length"]. InnerText);

Bool arrow = bool.parse (Ycoo.attributes ["arrow"]. innertext);

INT stroke = int.parse (Ycoke "]. InnerText); Color Color = Color.FromName (Ycoo.attributes [" color "]. innertext);

Pen Pen = New Pen (Color, (Float) stroke;

IF (arrow) // Is there an arrow?

{

AdjustableArrowcap arrow = new AdjustableArrowcap

(float) (stroke * 1.5 2),

(FLOAT) (stroke * 1.5 3),

True);

Pen.customedcap = arrow;

}

g.drawline (Pen, X, Y, X, Y Length); // Painting

// Draw

Foreach (Xmlnode Scale in Ycoo.childNodes)

{

INT LEN = INT.PARS (Scale.attributes ["Length"]. innerText);

INT Height = int.parse (Scale.attributes ["Height"]. innerText);

Int width = int.parse (Scale.attributes ["width"]. innertext);

INT FONTSIZE = INT.PARSE (Scale.attributes ["Fontsize"]. InnerText);

Color CLR = color.fromname (scale.attributes ["color"]. Innertext);

String name = scale.innertext;

Pen P = New Pen (CLR, (FLOAT) WIDTH);

g.drawline (P, X, Y LEN, X HEIGHT, Y LEN);

Font Font = New Font ("Arial", (Float) Fontsize;

StringFormat sf = new stringFormat ();

sf.alignment = stringalignment.far;

Rectanglef Rect = New Rectanglef

(float) (x - 100),

(FLOAT) (Y LEN - 25),

90F,

50F);

Sf.LineAlignment = stringalignment.center;

G. DrawString (Name, Font, New Solidbrush (CLR), RECT, SF);

}

}

Private void Drawchart (Graphics G, XMLNode Chart)

{

Foreach (XMLnode Lines in Chart.childNodes)

{

Drawlines (G, Lines);

}

}

Private Void Drawlines (Graphics G, XMLNode Lines)

{

Int stroke = int.parse (lines.attributes ["stroke"]. innerText);

Point [] Points = new point [lines.childnodes.count];

Color LineColor = color.fromname (Lines.attributes ["color"]. Innertext);

For (int i = 0; i

{

XMLNode node = lines.childnodes [i];

Points [i] = new point

INT.PARSE (Node.attributes ["X"]. Innertext),

INT.PARSE (Node.attributes ["Y"]. Innertext));

Int radius = int.parse (Node.attributes ["RADIUS"]. InnerText);

Color PointColor = color.fromname (node.attributes ["color"]. Innertext);

IF (Radius! = 0) // Draw points

{

G.fillellipse (New Solidbrush (PointColor),

Points [i] .x - Radius,

Points [i] .y - radius,

RADIUS * 2,

RADIUS * 2);

}

}

Pen Pen = New Pen (LineColor);

g.drawlines (Pen, Points); / / Picture

}

}

}

Then, add a small CASE for the CreateElement method of the Parser class, the code is as follows:

Switch (Element.name)

{

Case "text":

Printelement = new text (element);

Break;

Case "Table":

Printelement = New Table (Element);

Break;

Case "linechart": // Added LineChart

Printelement = new linechart (element);

Break;

DEFAULT:

Printelement = new printelement ();

Break;

}

Replace the Table tag in the original XML file and its child tags to the next paragraph, then compile the program, the effect is as follows:

Now, our print control can print the drawline map, because we use the Abstract Factory design mode, divide the resolution of the prints and formats of the report so that this program has a very convenient expansion capability, if you need a new new The formal chart, then you need to define the label, write a parsing class, and add a CASE to this class in PASER to get it, the code inside the PrintControl does not need to be rewritten.

to sum up:

The above is how to make a detailed introduction of print controls, basically answers most of the problems in the reader letter, and there are several questions that have been asked here to answer the answer here:

Q: Whether this plan must be needed to have .NET FRAMEWORK?

A: Yes, this is also a defect in this program. But I can be sure, in the near future, Microsoft will definitely install .NET Framework in the form of an upgrade or patch to most of our Windows or even Linux operating systems. At that time, there would be no current regrets now.

Q: I use the form of WinForm applications, then is there a problem with a deployment? For example, I add a new chart format, so all print clients need to upgrade to a new version?

A: Yes, but theoretically use .NET Remoting design to avoid this problem: TEXT, CHART, etc.) and factory class (PASER) are placed on the server side to provide remote calling methods through Remoting, and only put print control (printControl) on the client, then when we add charts, you can do not need Any upgrade for the client. Q: Open the web control will not run, only display a white box, what should I do?

A: This is because you have installed .NET Framework SP1 or SP2, the default security policy is not allowed to run, then you need to make the following modifications: Open Microsoft .NET Framework Wizards, in "program", you can also Find it in the Administration Tool, click "Adjust .NET Security", as shown below:

Set the security level of the Internet area to "Full Trust", as shown below:

It is more introduced throughout the program. If you have any questions in use, please email nluyan@msn.com, nluyan@163.net, or to Microsoft's Chinese DOTNET news group to discuss. Thank you!

Author: Lu Yan

2002-11-8

转载请注明原文地址:https://www.9cbs.com/read-32713.html

New Post(0)
CopyRight © 2020 All Rights Reserved
Processed: 0.049, SQL: 9