ASP.NET MVC PDF Generation View

by Woodrowg 5. February 2010 10:25

The feeling had not got any better, and now sitting there in the post mortem the feeling I had had ever since I committing the crime was bubbling to the surface. Why had I done it? At the time I could have given you a thousand good reasons, but now with hindsight, I knew they were all lies. Laying on the table was the body of evidence against me, and as we began to dissect it I knew that I alone had killed MVC.

OK enough of the melodramatics; In my last project I created PDF rendering logic in my controller, in hindsight it was ugly horrid to manage and really against the idea of MVC. So let us just keep View and Controller logic separate in the future, OK. Here is my solution feel free to make a better one. I never said the solution was the best, in fact I won’t even go so far as to say that it is good. But it’s better than what I did for my last project and better than anything I found in Google under “asp.net mvc pdf view”.

Let’s start with the controller. Nothing special here just select a list of items from the datacontext and pass them to a view, as it should be. 

public ActionResult Index(String filetype)        {

Foo.Models.FooDataContext dc = new Foo.Models.FooDataContext();     
List<Bar> results = (from b in dc.Bar select b).ToList();  
switch (filetype)
            {               
    case "PDF":
                   
       return View("PDF", results);
               
   
case "TXT":
                   
       return View("TXT", results);
                 
    default:
                   
       return View();          
           
}
}

I then went into the view directory and created a Webform called PDF.aspx, yes a web form not a MVC view page, why? you ask, well that way i get a code behind page that i can abuse and I am to lazy to create them myself.  

Firstly delete the .aspx.design.cs file, it’s gonna break anyway.

Remove all the junk from the page except the header line and change it thus.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="PDF.aspx.cs" Inherits="Foo.Views.Dictionary.PDF<IEnumerable<Foo.Models.Bar>>" %> 

With me so far, good.Now change the line

public partial class PDF : System.Web.UI.Page
to
public partial class PDF<TModel> : System.Web.Mvc.ViewPage<IEnumerable<Models.Bar>>

excellent smithers.

Remove the
protected void Page_Load(object sender, EventArgs e) who needs that anyway.

And add 
public override void  RenderView(ViewContext viewContext){}

In theory this should now run and do nothing, maybe return a blank page but apart from that nada.

Add this code to the RenderView method.

viewContext.HttpContext.Response.ContentType = "application/pdf";
viewContext.HttpContext.Response.AddHeader("Content-Disposition", "attachment;Filename=Test.Pdf");           
MemoryStream m = PDF_Doc();
           
viewContext.HttpContext.Response.OutputStream.Write(m.GetBuffer(), 0, m.GetBuffer().Length);
           
viewContext.HttpContext.Response.OutputStream.Flush();
           
viewContext.HttpContext.Response.OutputStream.Close();
           
m.Close();

and create a method now that does your PDF generation. Just make sure you keep it in this .cs file. That way all your view logic is contained in real view and there is nothing floating around.This was mine

private MemoryStream PDF_Doc()        {            
MemoryStream m = new MemoryStream();
           
Document document = new Document(PageSize.A4, 20, 20, 20, 20);
           
Font[] fonts = new Font[2];
           
fonts[0] = FontFactory.GetFont("HELVETICA", 8.0f);
           
fonts[1] = FontFactory.GetFont("HELVETICA", 10.0f);
           
try
            {                
PdfWriter writer = PdfWriter.GetInstance(document, m);
               
writer.SetPdfVersion(PdfWriter.PDF_VERSION_1_7);
               
writer.CloseStream = false;
               
Phrase footPhraseImg = new Phrase("Page: ", fonts[0]);
               
HeaderFooter footer = new HeaderFooter(footPhraseImg, true);
               
footer.Border = Rectangle.NO_BORDER;
               
document.Footer = footer;
               
document.AddHeader("Header", "BAR");
               
document.Open();
               
HTMLWorker worker = new HTMLWorker(document);
                
foreach (Models.Bar bar in Model)
               
{
                   
document.Add(new Paragraph(bar.Text));
               
}
                          
}
           
catch (DocumentException ex)
            {               
Console.Error.WriteLine(ex.StackTrace);
               
Console.Error.WriteLine(ex.Message);
            }           
document.Close();
           
return m;
        }

As you can see i can now do the same sort of thing for the TXT view, or an excel view, you name it.

In conclusion there is nothing special about a PDF that makes it a controller, so why do people create controllers with PDF generation logic in them. Doing it in the controller will likely work fine for a small application but as soon as it gets to working in a team or with more than one output view ( I mean pdf, word, excel, txt ) the controller will become a mess and bang MVC is dead. I seem to not be the only one who commits this crime, in fact I have yet to find an example on the net that does not pollute the controller with horrid view generation code.

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Adventures with Code

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Gary, the last of the unbloged is finaly giving up and will try as often as posible to add anything interesting he finds to this site. especially stuff like ASP.net MVC and things to make people smile