现在的位置: 首页 > 综合 > 正文

Repeat Table Headers Across Multiple Printed Pages

2011年12月04日 ⁄ 综合 ⁄ 共 13389字 ⁄ 字号 评论关闭

转:http://www.infopathdev.com/blogs/matt/archive/2005/11/29/Repeat-Table-Headers-Across-Multiple-Printed-Pages.aspx

InfoPath
is a great tool for collecting and organizing data, but it lacks when
it comes to print formatting. For example, when a dynamic table with a
large set of rows must be formatted to print across several pages of a
report, the column headers should appear, for better readability, at
the top of each page containing this table. Otherwise, you have to flip
back to a previous page each time you needed to reference the column
names. Furthermore, rows should not break across two pages (i.e. part
of the row on one page and part on the next).

In this task we
will use code to create table headers that repeat across multiple
printed pages. We will also explain how to expand your code to prevent
rows breaking across pages. We will accomplish this using a repeating
section which contains a page break and a repeating table. This
structure, along with our code, will dynamically create page breaks so
that table headers appear at the top of the page. To demonstrate this
technique, we need a data source with plenty of rows.


THE SECONDARY DATA SOURCE

Copy the following code into a text editor, and then save the file as Vehicles.xml . This file will be used as a secondary data source in our form.

<?xml version="1.0" encoding="UTF-8"?>
<Vehicles>
    <Vehicle year="1963" make="Chevrolet" model="Corvette" description="Features:
    -Fast
    -Stylish
    -Powerful   
    -Convertible"/>
    <Vehicle year="1963" make="Chevrolet" model="Nova" description="Features:
    -Fast
    -Stylish
    -Powerful
    -2 year warranty"/>
    <Vehicle year="1963" make="Porsche" model="356" description="Features:
    -Very fast
    -Stylish
    -Impressive body design   
    -3 year warranty"/>
    <Vehicle year="1963" make="Porsche" model="Carrera" description="Features:
    -Fast
    -Powerful
    -Plenty of room
    -3 year warranty
    -Free tires"/>
    <Vehicle year="1973" make="Ford" model="Bronco" description="Features:
    Towing power
    -High road clearance
    -Free tires
    -3 year warranty
    -Mud flaps"/>
    <Vehicle year="1973" make="Ford" model="Mustang" description="Features:
    -Fast
    -Stylish
    -Convertible
    -Great sound system" />
    <Vehicle year="1973" make="Ford" model="Pinto" description="Features:
    -Beautiful finish
    -3 year warranty
    -Extra mirrors"/>
    <Vehicle year="1973" make="Toyota" model="Celica" description="Features:
    -Reliable
    -Stylish
    -Good gas mileage"/>
    <Vehicle year="1973" make="Toyota" model="Land Cruiser" description="Features:
    -Tire upgrade options
    -Sunroof
    -Cruise Control
    - Off-road capabilities"/>
    <Vehicle year="1979" make="Jaguar" model="XJ6" description="Features:
    -Stylish
    -Powerful
    -Convertible
    -European style headlights
    -3 year warranty
    -Fun to drive"/>
    <Vehicle year="1979" make="Pontiac" model="Trans Am" description="Features:
    -Fast
    -Stylish
    -Reliable
    -2 year parts and labor included   
    -Financing available"/>
    <Vehicle year="1979" make="Volvo" model="240" description="Features:
    -Reliable
    -Convertible
    -European style headlights
    -3 year warranty"/>
    <Vehicle year="1979" make="Volvo" model="244DL" description="Features:
    -Stylish
    -Powerful   
    -Great sound system
    -Great gas mileage
    -3 year warranty
    -Fun to drive"/>
    <Vehicle year="1987" make="Dodge" model="Ram Charger" description="Features:
    -Fast
    -Stylish
    -Powerful   
    -High safety rating
    -3 year warranty
    -Fun to drive around very tight corners and up slippery slopes filled with ice"/>   
</Vehicles>

THE FORM

Now
that we have our secondary data source, let's create a new blank form
with two views: a default view for data entry, and a print view that
repeats the table header across pages. We create two views so that data
collection is easier, and so that InfoPath only has to format our print
view upon switching to that view as formatting might take some time.
We, therefore, want to run this code as infrequently as possible.

Add the secondary data source to a form:

  1. Design a new blank form.
  2. Choose Data Connections from the Tools menu, and then click Add.
  3. In the Data Connection Wizard, select Receive Data, and then click Next.
  4. Select XML Document, and then click Next.
  5. Click Browse, locate and select the Vehicles.xml file, click Open, and then click Next.
  6. Click Finish, click Yes, and then click Close.

Design the data entry view:

  1. Open the Data Source task pane.
  2. Choose the Vehicles from the Data Source drop down list.
  3. Drag-and-drop the year attribute into the view. This will create a repeating section with the year text box inside.
  4. Drag-and-drop the make , model and description attributes into the repeating section, and then resize the fields as shown in Figure 1.
  5. On the Display tab of the Text Box Properties dialog box, select Paragraph Breaks, Read-Only, and Wrap Text, and then click OK.


Figure 1. The completed data entry view.

Add a new view for printing:

  1. Open the Views task pane.
  2. Click Add A New View.
  3. Name the view Print View , and then click OK.

Design the print view:

  1. Open the Controls task pane.
  2. Insert a Repeating Section into the view.
  3. Click inside the Section to set the insertion point.
  4. Choose Page Break from the Insert menu.
  5. Press Enter to move to the next line.
  6. Click Repeating Table in the Controls task pane, type 4 for the number of columns, and then click OK.
  7. Type Year , Make , Model and Description for the column header labels
  8. Right-Click the Year label, and then choose Table Properties.
  9. On the Column tab of the Repeating Table Properties dialog box, set the column widths of columns A-D to 56px , 75px , 91px and 400px respectively, as shown in Figure 2.


Figure 2. The completed print view.

As
you inserted controls into the view, InfoPath automatically generated
schema nodes for these controls. Let's rename these generated nodes so
that they are easier to identify.

Rename the generated schema nodes:

  1. Open the Data Source task pane.
  2. Choose Main from the Data Source drop down list.
  3. Double-click each node in the data source tree and rename as follows (refer to Figure 3):
    • group1 to Document
    • group2 to Page
    • group3 to Table
    • group4 to Row
    • field1 to Year
    • field2 to Make
    • field3 to Model
    • field4 to Description


Figure 3. The revised schema.

Now
that we have finished designing our form layout, let's add
functionality by adding code. The code will generate our print list
when the user switches to the print view.

Add the OnSwitchView event handler code:

  1. Choose Programming | On Switch Views Event from the Tools menu.
  2. In the Microsoft Script Editor, replace the contents of the OnAfterChange event handler with the following code:

if(XDocument.View.Name == "Print View")
    GeneratePrintList();

This
specifies to call a custom function each time the user switches to the
print view. Now let's write the code to generate the repeating table
header pages.

  1. Paste the following code directly below the OnSwitchView() function:

function GeneratePrintList()
{
    // Number of lines we want on a page.
    var MAX_ROWS_ON_PAGE = 10;

    // Number of lines we have placed on current page.
    var iCurrentPageRowCount = 0;

    // Index of the current page we are adding to.
    var iCurrentPageIndex = 1;

    // Set up access to secondary data source.
    var domVehicles = XDocument.GetDOM("Vehicles");   

    // Source nodes.
    var oVehicles = domVehicles.selectNodes("/Vehicles/Vehicle");

    // Now we will get a node list for each of our groups and clear them
    // of all children. We will be generating the list from scratch each
    // time we switch to the Print View so that we can deal with changes
    // that have occurred since the last generation of the list.

    // ALL ROWS
    // Get the node list.
    var oRows = XDocument.DOM.selectNodes("/my:myFields/my:Document/my:Page/my:Table/my:Row");

    // Save a sample node from this list for later - we will have to clone
    // it to add our rows back in.
    var oSampleRow = oRows.nextNode();

    // Remove all the rows from all the repeating tables.
    oRows.removeAll();

    // ALL TABLES
    var oTables = XDocument.DOM.selectNodes("/my:myFields/my:Document/my:Page/my:Table");   
    var oSampleTable = oTables.nextNode();   
    oTables.removeAll();

    // ALL PAGES
    var oPages = XDocument.DOM.selectNodes("/my:myFields/my:Document/my:Page");   
    var oSamplePage = oPages.nextNode();   
    oPages.removeAll();   

    // THE DOCUMENT
    var oDocument = XDocument.DOM.selectSingleNode("/my:myFields/my:Document");   

    // We have only 1 document node, but we just emptied it of all
    // contents, so we must add back in at least one page and one
    // table node because we know we will be adding at least one row
    // into this table.

    // Add the first page.
    oDocument.appendChild(oSamplePage.cloneNode(false));

    // Add the first table.
    XDocument.DOM.selectSingleNode("/my:myFields/my:Document/my:Page").appendChild(oSampleTable.cloneNode(false));

    // Loop through all source nodes and add them
    // to the table we are generating.
    while(oSrc = oVehicles.nextNode())
    {   
        // We are about to put a new row on the page.
        iCurrentPageRowCount++;

        // If the row we about to put on the page will not fit, create a new page.
        if(iCurrentPageRowCount > MAX_ROWS_ON_PAGE)
        {
            // Reset our row count (we are about to add a row to the next page,
            // so that page's row count will be 1).
            iCurrentPageRowCount = 1;

            // We are creating a new page and working with it, so increment the index.
            iCurrentPageIndex += 1;

            // Add the new page.
            var oNewPage = oDocument.appendChild(oSamplePage.cloneNode(false));

            // Add a new table to the page that we just created.
            oNewPage.appendChild(oSampleTable.cloneNode(false));           
        }

        // Create a new row by cloning our sample row.
        // This node must be cloned using "deep" mode to maintain structure of the
        // DOM below it. However, we are about to overwrite all of the nodes anyway.
        var oNewRow = oSampleRow.cloneNode(true);

        // Set the values in the new row from the source row

        // that we are reading from on this iteration.

        oNewRow.selectSingleNode("my:Year").text = oSrc.selectSingleNode("@year").nodeValue;

        oNewRow.selectSingleNode("my:Make").text = oSrc.selectSingleNode("@make").nodeValue;

        oNewRow.selectSingleNode("my:Model").text = oSrc.selectSingleNode("@model").nodeValue;

        oNewRow.selectSingleNode("my:Description").text = oSrc.selectSingleNode("@description").nodeValue;

        // Add the row to the table that is on the current page.
       
XDocument.DOM.selectSingleNode("/my:myFields/my:Document/my:Page[" +
iCurrentPageIndex + "]/my:Table").appendChild(oNewRow);

    } // End while looping through all source nodes.
} // End function GeneratePrintList().

Try it:

  1. Switch back to the InfoPath Designer.
  2. Open the Views task pane, and then select View 1.
  3. Preview the form.
  4. Choose Print View from the Views menu.
  5. Choose Print Preview from the File menu.

While
editing, the tables will appear to be right next to each other
vertically, but when looking at the print preview, each page has a
table with the column headers and ten data rows (as specified in our
code). You will also notice that at the bottom of the first table, the
row for "Jaguar XJ6" breaks in the middle, carries over to the next
page, and is immediately followed by page break. This creates a huge
gap in the printed pages.

The first page is blank because the
page break appears before the very first page node. This can easily be
remedied by placing the page break inside a section, and then adding
conditional formatting to hide the section for the first page node.

PREVENTING ROWS FROM BREAKING ACROSS PAGES

To
prevent rows from breaking across pages you need to take into
consideration more than the just the number of rows per page; you need
to know the number of lines of text per page. This process requires
quite a bit of tweaking to determine the most precise method of
counting lines per row and the number of lines that you can fit on a
page. This will depend on the kind of data your rows contain and how it
is formatted.

To begin this process you will need to change the iCurrentPageRowCount variable to iCurrentPageLineCount ,
change the if-statement that controls whether to start a new page, and
implement a method to count the number of lines of text in each row. We
do this by tracking the number of lines that fit on a page and the
number of characters per line before wrapping occurs.

To
determine the number of lines per page, view the form in print preview
mode and count the number of lines (not rows) that fit on the first
page. In this example, we know that the description field will always
have more lines than any of the other fields, so we count each line in
each description field on the first page to determine 48 lines per
page. We will use 47 as our value, underestimating on the side of
caution.

Determining the number of characters that will fit on
each line of the description field, before wrapping to a new line, is
more of an estimate. A line that wraps to a new line must be counted as
two lines. In this example, our description field is 400px wide, which
allows approximately 58 characters before wrapping to the next line. To
determine this number we look at the one wrapping line from all of the
descriptions (the last line of the last description). We then count the
number of characters on the line before it breaks, which is 58. Because
the wrapping behavior depends on the length of the word at the end of
the line, this number is only an estimate. Therefore, to be safe, we
will use 55.

function generatePrintList()
{
    // Number of lines we want on a page.
    var MAX_LINES_ON_PAGE = 47;

    // Number of lines we have placed on current page.
    var iCurrentPageLineCount = 0;

. . .

    // Loop through all source nodes and add them
    // to the table we are generating.
    while(oSrc = oVehicles.nextNode())
    {   
        // We are about to put a new row on the page.
        var iLinesInRow = CountLinesInRow(oSrc);
        iCurrentPageLineCount += iLinesInRow;

        // If the row we about to put on the page
        // will not fit, create a new page.
        if(iCurrentPageLineCount > MAX_LINES_ON_PAGE)
        {
            // Update the line count of the page we are about to add.
            iCurrentPageLineCount = iLinesInRow;

            // We are creating a new page and working with it,
            // so increment the index.
            iCurrentPageIndex += 1;

. . .

Now, let's implement the CountLinesInRow() function to accurately count the number of lines of text in each row added to the page. Paste the following code below the GeneratePrintList() function:

function CountLinesInRow(oRow)
{
    // Select the field that is always vertically bigger than the other
    // fields in the row.
    var oDescription = oRow.selectSingleNode("@description ");
    var sText = oDescription.text;

    // 'Description' is formatted so that line breaks are allowed
    // and auto-wrapping occurs approximately every 55 characters   
    var iCharsPerLine = 55;

    // Lines counted so far
    var iNumLines = 0;

    // Split the field at the line breaks
    var arrLines = sText.split("\n");

    // We know that each element created from this split is at
    // least one line (hence Math.ceil()) and every 45 characters
    // is an additional line.

    for(i = 0; i < arrLines.length; i++)
    {
        iNumLines += Math.ceil(arrLines[i].length / iCharsPerLine);
    }

    return iNumLines;
}


抱歉!评论已关闭.