views:

1233

answers:

3

A web page displays the following:-

  • Bob
  • Fred
  • John

However when the user (that would be me) prints the web page I want multiple repeats of this output with slight variations to be printed on separate pages :-

  • Bob
  • Fred
  • John

>page break here<

  • Bob
  • Fred
  • John

>page break here<

  • Bob
  • Fred
  • John

In reality the page content is larger (although well within an standard A4 page worth) and the number of output pages is about 20. I'd prefer an elegant cross browser solution but ultimately a solution that works on IE7 is acceptable.

I'm using ASP.NET-MVC with JQuery (although a straight Javascript solution is fine).

Edit: Page-Break-XXX css style is going be part of the answer but I'm particular interested in a means to dynamically create the print version HTML from the screen version. Note I don't want to navigate to 'printer friendly' page and then print that. I just want to hit the browsers print button and magic happens.

+2  A: 

I'm not sure you can do it all via CSS, but here's some to get you started.

To get a page break in CSS when printing use one of:

These take values of:

auto   // Default. Insert a page break before/after the element if necessary
always // Insert a page break before/after the element
avoid  // Avoid inserting a page break before/after the element
left   // Insert page breaks before/after the element until it reaches a blank left page
right  // Insert page breaks before/after the element until it reaches a blank right page

If you're prepared to have some sort of "Print" page that lists the information as you want, with those styles on the repeated elements, you should be good to go.

Zhaph - Ben Duguid
Zhaph, thanks for the input but I don't want to navigate to a print page. I want a single page that has one appearance when on screen and another in print.
AnthonyWJones
page-break-xxxx definitely forms part of the answer but its generating the 'not quite a duplicate' copies of the screen html that I'm interested in.
AnthonyWJones
Which is where it gets tricky. Obviously, with a media="print" you can override the screen styles for a printer, but I'm not sure if you can repeat elements of the page, but with differences each time.There are ways to generate content in css, e.g. http://www.w3schools.com/Css/pr_gen_content.asp
Zhaph - Ben Duguid
Also, a bit of JS on the print page to auto fire the print dialog would reduce mouse clicks, but I guess it's mostly the repetition of the view that you're against?
Zhaph - Ben Duguid
The browser print button works quite nicely and in IE doesn't bother you with a dialog to dismiss.
AnthonyWJones
+1  A: 

You will need to do it all via JavaScript, create a print link that when clicked calls window.print().

However before that is called create the output that you want and place it into a seperate container (div, span, any html element that supports the tags you are going to place within it). Hide the container using your main stylesheet and create a seperate stylesheet for print media. Within that new stylesheet show the container element and hide the old elements and anything else that you do not wish to print.

If you want to clean up the page afterwards then set an interval using SetInterval to clear up the unwanted container. I wouldn't do it immediately after calling window.print() because I imagine the printer would not pick up on the HTML changes, however a quick test will tell you otherwise.

If there is a way of listening for a print of your page using javascript then you could hook up that as well.

Edit: As you are using jQuery I would clone the original list by using

$('#listID').clone().appendTo('#hiddencontainer')

Clone documentation

John_
+6  A: 

Ok, so taking into account the comments etc to my post something like this:

For ease of display here, I'm using on page styles, but you can easily use off page references.

Declaring the following styles:

<!-- media="print" means these styles will only be used by printing 
  devices -->
<style type="text/css" media="print">
    .printable{ 
      page-break-after: always;
    }
    .no_print{
      display: none;
    }
</style>
<!-- media="screen" means these styles will only be used by screen 
  devices (e.g. monitors) -->
<style type="text/css" media="screen">
    .printable{
      display: none;
    }
</style>
<!-- media types can be combined with commas to affect multiple devices -->
<style type="text/css" media="screen,print">
    h1{
      font-family: Arial, Helvetica, sans-serif;
      font-size: large;
      font-weight: bold;
    }
</style>

Then, you're going to need to do some heavy lifting in your view to get HTML looking something like this:

<!-- Intial display for screen -->
<div class="no_print">
    <!-- Loop through users here using preferred looping/display method -->
    <ul>
        <li>Bob</li>
        <li>Fred</li>
        <li>John</li>
    </ul>
</div>
<!-- Now loop through each user, highlighting as you go, but for printer* -->
<div class="printable">
    <ul>
        <li><b>Bob</b></li>
        <li>Fred</li>
        <li>John</li>
    </ul>
</div>
<div class="printable">
    <ul>
        <li>Bob</li>
        <li><b>Fred</b></li>
        <li>John</li>
    </ul>
</div>
<div class="printable">
    <ul>
        <li>Bob</li>
        <li>Fred</li>
        <li><b>John</b></li>
    </ul>
</div>

Now for an "appropriate display method".

I'm just getting started with MVC, so you've probably got a better method of doing this, but for now I'd probably use a RenderPartial method like this:

<%
  /* 
  Using RenderPartial to render a usercontrol,
  we're passing in the Model here as the Model for the control, 
  depends on where you've stored your objects really, then we
  create a new anonymous type containing the properties we want
  to set.
  */

  // Display the main list
  Html.RenderPartial("~/Views/Shared/Controls/UserList.ascx",
      ViewData.Model, 
      new {HighlightUser = null, IsPrintable = false});

  // Loop through highlighting each user
  foreach (var user in ViewData.Model)
  {
    Html.RenderPartial("~/Views/Shared/Controls/UserList.ascx",
      ViewData.Model, 
      new {HighlightUser = user, IsPrintable = true});
  }
%>

Your user control can then handle the bulk of the display, setting the class of the containing divs (or whatever element you use) as appropriate, and bolding up the user based on the one passed in - as I said, there's possibly a better way of doing the partial stuff.

Obviously, you probably want completely different rendering of the users for display and print - I guess you're probably adding quite a bit of jQuery goodness hiding and displaying stuff, etc, that you are actually going to display on the printed version, so you probably want two differnt user controls, and can then get away with not passing in the IsPrintable property.

Also, as it stands, this will print a blank page at the end, due to the "page-break-after" style on all divs - you'd possibly want to note that you're on the last div, and have a different style on that, unless you have information that you'd like on that last page.

Zhaph - Ben Duguid
After I posted this, I wasn't sure if I should have just edited my previous answer, http://stackoverflow.com/questions/361802/is-it-ok-to-post-multiple-answers-to-a-question didn't really clarify.If people object, I'd be happy to combine them and delete this one.
Zhaph - Ben Duguid
I've removed my +1 from your other answer so you can delete that and keep this. I've decided for other reasons that I would like to keep the logic deciding who gets a copy in the controller. Hence this answer is pretty close to what I need. Thanks. ;)
AnthonyWJones
You can try this:$('.printable:last-child').css('page-break-after','avoid');dunno if the property will be page-break-after.
Gabriel Guimarães