Often times teams depend on scrum task cards on a board to track the progress of a project. When the tasks are in Team Foundation Server, Ade Miller describes a method in his blog whereby you could export the query results to excel and use the mail merge feature of Microsoft Word to print out address labels with VSTS work items. That’s a cool trick. But recently I came across the PDFSharp in CodePlex and thought about programmatically generating a PDF file which can be printed and cut into scrum task cards for a project. A bit of programming and I was able to quickly come up with a console application which will do just that!
Code for the console application is given below, when you create the console application project in VS2008 for this code, you will have to download and add reference to PDFSharp assembly. You will also have to add reference to Microsoft.TeamFoundation.Client.dll and Microsoft.TeamFoundation.WorkItemTracking.Client.dll.
The console application takes as input a .wiq file containing the query for the tasks and generates a PDF file. By modifying the code as appropriate you can even generate color coded scrum task cards. Have fun!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Common;
using PdfSharp;
using PdfSharp.Drawing;
using PdfSharp.Drawing.Layout;
using PdfSharp.Pdf;
namespace ScrumTaskCardGen
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Please specify the .wiq file name!");
return;
}
FileInfo fileInfo = new FileInfo(args[0]);
if (false == fileInfo.Exists)
{
Console.WriteLine("The specified file does not exist!");
return;
}
if (false == fileInfo.Extension.Equals(".wiq"))
{
Console.WriteLine("Please specify a .wiq file as input!");
return;
}
try
{
XmlReader reader = XmlReader.Create(fileInfo.FullName);
reader.ReadToFollowing("TeamFoundationServer");
string connection = reader.ReadString();
reader.ReadToFollowing("TeamProject");
string project = reader.ReadString();
reader.ReadToFollowing("Wiql");
string query = reader.ReadString();
GeneratePDF(connection, project, query, fileInfo);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static void GeneratePDF(string connection, string project, string dtsQuery, FileInfo wiqlFile)
{
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(connection);
WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
Hashtable context = new Hashtable();
context.Add("project", project);
WorkItemCollection result = store.Query(dtsQuery, context);
PdfDocument doc = new PdfDocument();
PdfPage page = doc.AddPage();
XGraphics grafix = XGraphics.FromPdfPage(page);
XTextFormatter textFormatter = new XTextFormatter(grafix);
textFormatter.Alignment = XParagraphAlignment.Center;
int x = 0,
y = 0;
XBrush blackBrush = XBrushes.Black;
XPen blackPen = XPens.Black;
XFont font = new XFont("Verdana", 10, XFontStyle.Bold);
for (int i = 0; i < result.Count; i++)
{
Point origin = new Point(x, y);
Size size = new Size(Convert.ToInt32(Math.Round(page.Width / 3)), Convert.ToInt32(Math.Round(page.Height / 4)));
Rectangle rect = new Rectangle(origin, size);
float layoutWidth = (float)0.0,
layoutHeight = (float)0.0;
layoutWidth = (float)page.Width.Value / 3;
layoutHeight = (float)page.Height.Value / 4;
RectangleF rectl = new RectangleF((float)x, (float)y+50, layoutWidth, 0);
textFormatter.LayoutRectangle = new XRect((double)x + 10, (double)y + 10, (double)layoutWidth, (double)layoutHeight);
grafix.DrawRectangle(blackPen, rect);
string title = result[i].Title;
if (title.Length > 30)
{
if (title.Length > 60)
{
title = title.Remove(57);
title = title + "...";
}
title = title.Insert(30, "\r\n");
}
textFormatter.DrawString(
"TaskId = " + result[i].Id + "\r\n\r\n" +
title + "\r\n\r\n" +
"~ " + result[i].Fields["Assigned To"].Value.ToString().Split(new char[] { ' ' })[0] + " ~",
font, blackBrush, rectl, XStringFormats.TopLeft);
x += Convert.ToInt32(Math.Round(page.Width / 3));
if (x >= Convert.ToInt32(Math.Round(page.Width)))
{
x = 0;
y += Convert.ToInt32(Math.Round(page.Height / 4));
if (y >= Convert.ToInt32(Math.Round(page.Height)))
{
y = 0;
page = doc.AddPage();
grafix = XGraphics.FromPdfPage(page);
textFormatter = new XTextFormatter(grafix);
textFormatter.Alignment = XParagraphAlignment.Center;
}
}
}
string[] inputFileNameParts = wiqlFile.Name.Split(new char[] { '.' });
string outputFileName = inputFileNameParts[0];
string outputFile = wiqlFile.Directory + "\\" + outputFileName + ".pdf";
doc.Save(outputFile);
doc.Close();
Console.WriteLine("Generated file " + outputFile);
}
}
}
Nice article. Here is another small tool doing the same: http://taskcardcreator.codeplex.com/
ReplyDelete