Posting to Campfire using PowerShell (and curl)

January 16, 2012 Comments off

I have a few tasks that kick off nightly that I wanted to post status updates into our team’s Campfire room. Thankfully, 37signals Campfire has an amazing API.  With that knowledge, time to create a quick PowerShell function!

NOTE: I use curl for this. The Linux/Unix folks likely know curl, however, I’m sure the Windows folks have funny looks on their faces. You can grab the latest curl here for Windows (the Win32 or Win64 generic versions are fine).

The full code for this function is available via gist.

I pass two parameters: the room number (though this could be tweaked to be optional if you only have one room) and the message to post.

param (
 [string]$RoomNumber = (Read-Host "The room to post to (default: 123456) "),
 [string]$Message = (Read-Host "The message to send ")
)
$defaultRoom = "123456"
if ($RoomNumber -eq "") {
 $RoomNumber = $defaultRoom
}

There are two baked-in variables, the authentication token for your posting user (we created a ‘robot’ account that we use) and the YOURDOMAIN prefix for Campfire.

$authToken = "YOUR AUTH TOKEN"
$postUrl = "https://YOURDOMAIN.campfirenow.com/room/{0}/speak.json" -f $RoomNumber

The rest is simply using curl to HTTP POST a bit of JSON back up to the web service. If you’re not familiar with the JSON data format, give this a quick read. The best way I can sum up JSON is that it’s XML objects for the web with less wrist-cutting. :)

$data = "`"{'message':{'body':'$message'}}`""

$command = "curl -i --user {0}:X -H 'Content-Type: application/json' --data {1} {2}" 
     -f $authToken, $data, $postUrl

$result = Invoke-Expression ($command)

if ($result[0].EndsWith("Created") -ne $true) {
	Write-Host "Error!" -foregroundcolor red
	$result
}
else {
	Write-Host "Success!" -foregroundcolor green
}
Running SendTo-CampFire

Running SendTo-Campfire with Feedback

Indeed, there be success!

Success!

Success!

It’s important to remember that PowerShell IS extremely powerful, but can become even more powerful coupled with other available tools–even the web itself!

Changing Default Printers by Network Subnet

January 13, 2012 Comments off

Windows 7 includes a pretty handy feature for mobile devices called location-aware printing. The feature itself is pretty cool and great if you’re moving between two distinct networks (home and work, for example). However, if you’re moving within the SAME network–and the SAME wireless SSID, it doesn’t register a difference. LAP doesn’t pay attention to your IP address, just the SSID you’re connected to.

In our organization, and most large corporations, wireless access points have the same name/credentials so that users can move seamlessly through the enterprise. How can we address location-based printing then?

One of my peers recently moved into a position where they are constantly between two buildings multiple times per day and frequently forgetting to reset their default printer.

Here’s how I helped her out using a bit of PowerShell.

For the full code, check out this gist.

Set-PrinterByLocation in action!

Set-PrinterByLocation in action!

To begin, we need to specify our IP subnets and the printers associated to them. As this gets bigger (say 4-5 sites), it’d be easier to toss this into a separate file as a key-value pair and import it.

$homeNet = "10.1.4.*", "OfficePrinter"
$remoteNet = "10.1.6.*", "W382_HP_Printer"

Next, let’s grab all of the IP addresses currently active on our computer. Since we could have both wireless and wired plugged in, this returns an array.

$ipAddress = @()
$ipAddress = gwmi win32_NetworkAdapterConfiguration |
	? { $_.IPEnabled -eq $true } |
	% { $_.IPAddress } |
	% { [IPAddress]$_ } |
	? { $_.AddressFamily -eq 'internetwork'  } |
	% { $_.IPAddressToString }

Write-Host -fore cyan "Your current network is $ipAddress."

Our last step is to switch (using the awesome -wildcard flag since we’re using wildcards ‘*’ in our subnets) based on the returned IPs. The Set-DefaultPrinter function is a tweaked version of this code from The Scripting Guy.

function Set-DefaultPrinter([string]$printerPath) {
	$printers = gwmi -class Win32_Printer -computer .
	Write-Host -fore cyan "Default Printer: $printerPath"
	$dp = $printers | ? { $_.deviceID -match $printerPath }
	$dp.SetDefaultPrinter() | Out-Null
}

switch -wildcard ($ipAddress) {
	$homeNet[0] { Set-DefaultPrinter $homeNet[1] }
	$remoteNet[0] { Set-DefaultPrinter $remoteNet[1] }
	default { Set-DefaultPrinter $homeNet[1] }

The full source code (and a constantly updated version available from gist).

$homeNet = "10.1.4.*", "OfficePrinter"
$remoteNet = "10.1.6.*", "W382_HP_Printer"

function Set-DefaultPrinter([string]$printerPath) {
	$printers = gwmi -class Win32_Printer -computer .
	Write-Host -fore cyan "Default Printer: $printerPath"
	$dp = $printers | ? { $_.deviceID -match $printerPath }
	$dp.SetDefaultPrinter() | Out-Null
}

$ipAddress = @()
$ipAddress = gwmi win32_NetworkAdapterConfiguration |
	? { $_.IPEnabled -eq $true } |
	% { $_.IPAddress } |
	% { [IPAddress]$_ } |
	? { $_.AddressFamily -eq 'internetwork'  } |
	% { $_.IPAddressToString }

Write-Host -fore cyan "Your current network is $ipAddress."

switch -wildcard ($ipAddress) {
	$homeNet[0] { Set-DefaultPrinter $homeNet[1] }
	$remoteNet[0] { Set-DefaultPrinter $remoteNet[1] }
	default { Set-DefaultPrinter $homeNet[1] }
}

Tip: Using Spark Conditionals to Toggle CSS and JavaScript

January 3, 2012 Comments off

The conditional attribute is a fantastic shortcut to toggle CSS, input boxes, and other elements on a page–and is something I don’t see used in very many examples. One of my favorites is applying classes to an element based on a output model property, such as a permission boolean.

Here’s an example.

In a recent project, a dashboard screen had several charts that toggled on and off based on the user’s preference. Rather than rebuild the screen each time, each class simply toggled an ‘enabled’ class based on a Model.{Property}.

<div class="charts">
    <div id="enteredbycount" class="loading enabled?{Model.ShowEnteredBy}"
		style="inlineChart">
	</div>
    <div id="schoolcount" class="loading enabled?{Model.ShowSchoolCount}"
		style="inlineChart">
	</div>
</div>

The Spark Conditional only renders the text preceding the conditional ?{} if the condition is true. In this example, if our Model.ShowSchoolCount returns false, enabled never renders and our chart (due to some styling), remains hidden and never posts back to the server to get the chart data (saving an unnecessary AJAX call).

By toggling a class, you can trigger a certain set of styles, events using JavaScript, or most anything else you can dream up.

Finding TODOs and Reporting in PowerShell

December 28, 2011 Comments off

After @andyedinborough shared a blog post on how to report TODOs in CruiseControl.NET, I figured there HAD to be a snazzy way to do this with TeamCity.

I already have TeamCity configured to dump out the results from our Machine.Specifications tests and PartCover code coverage (simple HTML and XML transforms) so another HTML file shouldn’t be difficult. When we’re done, we’ll have an HTML file, so we just need to setup a custom report to look for the file name. For more information on setting up custom reports in TeamCity, checkout this post (step 3 in particular).

Let’s Dig In!

 

The code! The current working version of the code is available via this gist.

The code is designed to be placed in a file of it’s own. Mine’s named Get-TODO.ps1

Note: If you want to include this in another file (such as your PowerShell profile), be sure to replace the param() into function Get-TODO( {all the params} ).

The the script parameters make a few assumptions based on my own coding standards; change as necessary:

  • My projects are named {SolutionName}.Web/.Specifications/.Core and are inside a directory called {SolutionName},
  • By default, it includes several file extensions: .spark, .cs, .coffee, .js, and .rb,
  • By default, it excludes several directories: fubu-content, packages, build, and release.

It also includes a couple of flags for Html output–we’ll get to that in a bit.

param(
	#this is appalling; there has to be a better way to get the raw name of "this" directory. 
	[string]$DirectoryMask = 
		(get-location),
	[array]$Include = 
		@("*.spark","*.cs","*.js","*.coffee","*.rb"),
	[array]$Exclude = 
		@("fubu-content","packages","build","release"),
	[switch]$Html,
	[string]$FileName = "todoList.html")

Fetching Our Files

We need to grab a collection of all of our solution files. This will include the file extensions from $Include and use the directories from $DirectoryMask (which the default is everything from the current location).

$originalFiles = 
	gci -Recurse -Include $Include -Path $DirectoryMask;

Unfortunately, the -Exclude flag is…well, unhappy, in PowerShell as it doesn’t seem to work with -Recurse (or if it does, there’s voodoo involved).

How do we address voodoo issues? Regular expressions. Smile Aww, yeah.

$withoutExcludes = $originalFiles | ? {
	if ($Exclude.count -gt 0) {
		#need moar regex
		[regex] $ex = 
			# 1: case insensitive and start of string
			# 2: join our array, using RegEx to escape special characters
			# the * allow wildcards so directory names filter out
			# and not just exact paths.
			# 3: end of string
			#  1                   2										3
			'(?i)^(.*'+(($Exclude|%{[regex]::escape($_)}) -join ".*|.*")+'.*)$'
		$_ -notmatch $ex
	}
	else { 
		$_ 
	}
}

Breath, it’s okay. That is mostly code comments to explain what each line is doing.  This snippet is dynamically creating a regular expression string based on our $Exclude parameter then comparing the current file in the iteration to the regular expression and returning the ones that do not match.  If $Exclude is empty, it simply returns all files.

Finding our TODOs

Select-String provides us with a quick way to find patterns inside of text files and provides us with helpful output such as the line of text, line number, and file name (select-string is somewhat like PowerShell’s grep command).

$todos = $withoutExcludes | 
	% { select-string $_ -pattern "TODO:" } |
	select-object LineNumber, FileName, Line;

Let’s take all of our remaining files, look at them as STRINGs and find the pattern "TODO:".  This returns an array of MatchInfo objects. Since these have a few ancillary properties we don’t want, let’s simply grab the LineNumber, FileName, and the Line (of the match) for our reporting.

Now that we have our list, let’s pretty it up a bit.  PowerShell’s select-object has the ability to reformat objects on the fly using expressions.  I could have combined this in the last command; however, it’s a bit clearer to fetch our results THEN format them.

$formattedOutput = $todos | select-object `
	@{Name='LineNumber'; Expression={$_.LineNumber}}, `
	@{Name='FileName'; Expression={$_.FileName}}, `
	@{Name='TODO'; Expression={$_.Line.Trim() -replace '// TODO: ',''}}
  1. LineNumber: We’re keeping this line the same.
  2. FileName: Again, input->output. Smile
  3. TODO: Here’s the big change. "Line" isn’t real descriptive, so let’s set Name=’TODO’ for this column. Second, let’s trim the whitespace off the line AND replace the string "// TODO: " with an empty string (remove it).  This cleans it up a bit.

Reporting It Out

At this point, we could simply return $formattedOutput | ft -a and have a handy table that looks like this:

consoleoutput

Good for scripts, not so good for reporting and presenting to the boss/team (well, not my boss or team…). I added a -Html and -FileName flag to our parameters at the beginning just for this. I’ve pre-slugged the filename to todoList.html so I can set TeamCity to pickup the report automagically.  But how do we build the report?

PowerShell contains a fantastic built-in cmdlet called ConvertTo-Html that you can pipe pretty much any tabular content into and it turns it into a table.

 

if ($Html) {
	$head = @'
	<style> 
	body{	font-family:Segoe UI; background-color:white;} 
	table{	border-width: 1px;border-style: solid;
			border-color: black;border-collapse: collapse;width:100%;} 
	th{		font-family:Segoe Print;font-size:1.0em; border-width: 1px;
			padding: 2px;border-style: solid;border-color:black;
			background-color:lightblue;} 
	td{		border-width: 1px;padding: 2px;border-style: solid;
			border-color: black;background-color:white;} 
	</style> 
'@
	$solutionName = 
		(Get-Location).ToString().Split("\")[(Get-Location).ToString().Split("\").Count-1];

	$header = "<h1>"+$solutionName +" TODO List</h1><p>Generated on "+ [DateTime]::Now +".</p>"
	$title = $solutionName +" TODO List"
	$formattedOutput | 
              ConvertTo-HTML -head $head -body $header -title $title |
              Out-File $FileName
}
else {
	$formattedOutput
}

Our html <head> tag needs some style. Simple CSS to address that problem.  I even added a few fonts in there for kicks and placed them in $head.

As I mentioned before, my solutions are usually the name of the project–which is what I want as the $title and $header. The $solutionName is ugly right now–if anyone out there has a BETTER way to get the STRING name of the current directory (not the Path), I’m all ears.

We take our $formattedOutput, $header, $title and pass them into ConvertTo-Html. It looks a bit strange that we’re setting our ‘body’ as the header; however, whatever is piped into the cmdlet appends to what is passed into -body. It then parses our content out to the $FileName specified.

htmloutput

Using Cassette with Spark View Engine

July 21, 2011 Comments off

Knapsack… *cough*… I mean Cassette is a fantastic javascript/css/coffeescript resource manager from Andrew Davey. It did, however, take a bit to figure out why it wouldn’t work with Spark View Engine. Thankfully, blogs exist to remind me of this at a later date. :)

Namely—because I’ve never tried to use anything that returned void before. Helpers tend to always return either Html or a value.

I finally stumbled on a section in the Spark View Engine documentation for inline expressions.

Sometimes, when push comes to shove, you have a situation where you’re not writing output and there isn’t a markup construct for what you want to do. As a last resort you can produce code directly in-place in the generated class.

Well then, that sounds like what I want.

So our void methods, the Html.ReferenceScript and Html.ReferenceStylesheet should be written as:

#{Html.ReferenceScript("scripts/app/home.index.js");}
#{Html.ReferenceStylesheet("styles/app");}

Note the # (pound sign) and the semi-colon at the end of the statement block.

Our rendering scripts; however, use standard Spark output syntax:

${Html.RenderScripts()}
${Html.RenderStylesheetLinks()}

Now my Spark view contains the hashed Urls–in order–as it should.

 <!DOCTYPE html>
 <html>
 <head>
   <link href="/styles/app/site.css?f8f8e3a3aec6d4e07008efb57d1233562d2c4b70" type="text/css" rel="stylesheet" />
 </head>
 <body>
 <h2>Index</h2>
   <script src="/scripts/libs/jquery-1.6.2.js?eeee9d4604e71f2e01b818fc1439f7b5baf1be7a" type="text/javascript"></script>
   <script src="/scripts/app/application.js?91c81d13cf1762045ede31783560e6a46efc33d3" type="text/javascript"></script>
   <script src="/scripts/app/home.index.js?b0a66f7ba204e2dcf5261ab75934baba9cb94e51" type="text/javascript"></script>
 </body> 

Excellent.

Review: Asus Transformer TF-101

tl;dr: Awesome!

I wanted to give my review after at least a week to settle in and integrate into my daily ‘flow’.

Why the Asus Transformer?

I’ve looked at more tablets than I can remember, teetering between the Motorola Xoom, Apple iPad2, Notion Ink Adam, and the Transformer.  All of these seemed like great devices and had their own pros; however, the Transformer sold me on a few of its flashy innovations:

  • Asus seems dedicated to constant updates. They’ve already pushed out Android Honeycomb 3.1 OTA and it updated like a champ.
  • The docking station/keyboard is brilliant and really bridges the gap between netbook and tablet and adds incredible battery life.
  • The mix of hardware performance for an incredible price point (499$US for the 32GB unit and 149$US for the keyboard/docking station at Amazon.com).
  • Flash support. Yes, I’m looking at you iPad2.

Photo of the Transformer... and swanky placemat backdrop.

 

How has the Transformer changed things?

I’ve noticed it’s changed things in the oddest of ways. I haven’t opened my personal laptop since I got the Transformer (it looks big and powerful, but it’s just old and … well, old).

Around the house, I’ve found myself using the Transformer in place of my EVO. I use it in the kitchen to look up recipes, check for coupons, make note of my groceries in Mighty Shopper, and listen to music. Other times, like for this post, for simple word processing, web browsing, and email. More functionality and less squinting.

Work Computer Size ComparisonAt work, I’ve used Polaris Office for note taking, presentations (via the HDMI out), web browsing and research as I wander around the office with folks, and email. The remote desktop client (2X Client) allows me to remote into servers, my office desktop, and client machines for diagnostic work.

With Android 3.1, the web browser engine finally supports NTLM/Windows authentication so I can log into our corporate web sites (I hope this is coming to the Gingerbread builds soon).

I still have my phone with me through all this.. the Transformer hasn’t replaced the portability and features of my phone, but augmented what I can do without an actual desktop computer.

 

The Hardware

You can read the unit’s specifications, so I won’t drive into them too much. There are a few things to mention.

The bulk

Overall, the unit (and keyboard doc) are quite light compared to a normal 14" laptop.  The tablet itself is very comfortable to carry around (long days here at work, so I’ve been pacing around while researching on the web).

The keyboard dock

Image of the the keyboard dock.I was a bit worried that the keyboard dock would be uncomfortable to use; however, that’s not the case.  The keys are well spaced apart (once you get used to the ‘special’ keys at the top–I keep accidently locking the unit when I hit backspace) and the monitor can tilt back far enough for easy reading.

It’s important to note that the keyboard dock’s additional battery life is due to the dock charging the main unit while it’s docked.  This is advantageous.  Wander around and run the battery down a bit on the tablet, then dock and let the keyboard charge things up, detach, and you have a fully charged tablet again.

The POWER!

It is quite disappointing that the Transformer has a unique USB cable; however, even more so that it’s only about a meter long.  This hassle is aggravated by the fact you cannot charge the unit via USB while it’s on, so if you need a charge AND need to use it, you’re trapped a meter from your electrical connection or extension cord.

Thankfully, with the 14-16 hours of battery life, charging isn’t a central focus of the day (unlike my EVO which is dead by the time I get to work).

The video output

Less of a big deal and more of a ‘oh’ moment when digging for cables.  My EVO and a few other video devices all use micro HDMI so I had to pickup a 1.3a mini HDMI cable for the Transformer.

The screen

I love the screen. It’s a bit hard to see in high light (it’s not a Kindle for outdoor reading); however, the fingerprints are a bit out of control. I plan on getting a screen protector which, I hope, will help.

The camera

The camera is only a 5MP (compared to the 8MP in my EVO) and takes 4:3 photos rather than wide screen. That aside, it’s quite a good camera for general ‘here I am!’ sorts of photos–like lounging on the hammock in the back yard on a peaceful evening.

Example Transformer camera image--my back yard.

The lack of right click

I understand… I really do. The Android device doesn’t have a context for ‘right-click’; however, I dream of the day when it does so that the remote desktop clients have right-click.

Keeping it safe

I picked up a Belkin 10" Netbook Sleeve when ordering and highly recommend it as a carry case for the Transformer.  The unit, whether you’re just carrying the tablet or the tablet and keyboard dock, fit very well into the sleeve and the zips/material feel and look good.

 

The Software

Out of the box, the Transformer walks you through a simple configuration–much like my EVO (or any Android phone). Setting up email, syncing contacts, and such worked just fine. I was impressed that the unit also synced my Google bookmarks. That’s an added bonus.

The basics are included:

  • Email (Enterprise, Gmail, etc.) – The layout and flow of both mail applications is the same and works VERY well on landscape mode.
  • Calendaring – nice, clean "Outlook-esque" layout.
  • Contacts – standard Android contacts client.
  • Polaris Office – bundled Office client for DOC, XLS, and PPT. Works quite well (and what I used for this post). It can also read/write to Google Docs, Dropbox, and a few other cloud services. At this time, I’m getting 400 errors trying to save BACK to Google Docs; however, from the forums, it appears that it is a Google API issue.
  • MyReader – Simple eBook/pdf reader akin to Aikido.
  • MyNet – network-based HD streaming client. Detects my PlayOn devices and streams perfectly.
  • MyCloud – a subscription-based, unlimited storage system for music and files. Transformer includes a free 1 year subscription. I haven’t dug into this much as I have LiveSync and DropBox.

In addition to the boxed software, there are a few applications that I’d recommend (and seem to work quite well with the Transformer and Honeycomb):

  • Dolphin Browser HD – Bit cleaner interface for web browsing and plugin modules (3.1 addresses some rendering issues though).

    .

  • Amazon Kindle Reader – Great for reading Kindle books on the run (and without your Kindle). The update for Android 3.1 works even better!
  • Pandora – Great and solid music client.
  • Grooveshark – Formatting is a bit off, but works fantastic to query and stream your favorite tunes.
  • Google Reader – Great tablet support for full-screen reading.
  • imo Chat beta – Best IM client I’ve found for any type of Android device.
  • Plume – best Twitter client that takes advantage of the notifications and wide screen layout.
  • DropBox – the Android DropBox client is pretty stellar.
  • PrintBot - a free (and for cheap) application to print to network devices over WIFI. Works great and, hopefully, future versions will allow for multiple printers.
    NOTE: Similar to the Gingerbread break, Netflix is defunct on Honeycomb as well. I had hopes (dreams) that 3.1 would fix it, but alas it doesn’t seem so. Netflix is adamant that a new version is in the works. I hope so. It was great to have mobile movies on my cell phone and the Transformer seems ideal

Upgrading to Android Honeycomb 3.1

honeycomb_3-1-580x309On 10 June 2011, Asus released Android Honeycomb 3.1 for the Transformer. The official version is v8.4.4.5 (the SKUs are separate by locale). 

There are two options:

I’m impatient, so I opted for the manual method.  The process was quite simple:

  1. Download the 8.4.4.5 zip file from the internet.
  2. On a microSD card create an \asus\update directory and copy the ZIP file into that directory.
  3. Insert the microSD card into the Transformer. You should see a notification that an update is available.
  4. The device will reboot a few times and then be good to go.

There are a few highlights to 3.1 (detailed by the release notes) that I really dig:

  1. The browser (and any browsers relying on it’s webkit) now supports NTLM/Windows Authentication. AWESOME for work!
  2. (Most) widgets are now resizable. I’ve come across a few that won’t resize.
  3. Speed! 3.1 generally feels faster.  Multitasking across a dozen apps with notifications flying and music playing doesn’t seem to bother it.

Summary

With the tablet upgraded to 3.1, the applications that I like, and features that keep impressing me, I think the Transformer will continue to be a great fit. The keyboard matches my dire need to rapidly input information (take notes, respond to emails) and the portability matches my recent need to roam free of wires and surf the web. 

The Transformer is a fantastic bridge between the smallest of laptops and the largest of cell phones and, unfortunately, is what I’d hoped the Google Chromebook would have been.  Don’t get me wrong, I like my Chromebook–it was free and I like free–but the Android-based Transformer can actually DO the things I need to do in my day-to-day life.

Dynamically Adding Sub Reports to an ActiveReport

April 5, 2011 Comments off

I’m currently working on a project where I needed to iterate through a group of users and plug in a little sub report that contains some demographic information and a Code38 barcode.

One sub report is easy–add the control to the page, set the .Report property and away we go; however, adding multiple sub reports dynamically and getting the spacing right proved to be a bit challenging.

Warning: It “works on my machine”.

To add controls to your report dynamically, you must use the _ReportStart event of your report.

To address the spacing issue, let’s start out by specifying our ‘base’ top and the height of our sub report.

const float height = 0.605f;
var currentTop = 7.250f;

In my case, I want my sub reports to start at about 7.25″ and be ~0.605″ in height.

The actual creation of the sub report placeholder is fairly standard–new it up and assign a few properties. I’ll get into the looping a bit later.

var subReport = new SubReport
{
    CloseBorder = false,
    Height = height,
    Left = 0F,
    Width = 7.5F,
    Top = currentTop,
    Name = person.DisplayName + "_SubReport",
    ReportName = person.DisplayName + "_SubReport",
};

Notice how I’ve set the Top property to be our currentTop variable. Keep that in mind.

The next step is to new up our actual sub report object.  My sub report has two properties on it, each for a data item.  I could pass it along as an object, but it seems a bit overkill for two string properties. After the report object is assigned to our new sub report container, add the container to our details section.

var spReport = new _PersonAssignment()
{
    Name = person.DisplayName,
    EmployeeId = person.EmployeeId
};

subReport.Report = spReport;
this.Sections["detail"].Controls.Add(subReport);

Because we’ve assigned it a left, width, and top, our sub report will be added where expected.

The final piece is incrementing the ‘top’ to accommodate for the height of the last sub report.

currentTop += height;

Easy.  Now the next sub report will start at 7.250″ + 0.605″ or 7.855″.  Keep in mind that the 0.605″ includes a bit of whitespace, if you need additional whitespace, pad the height number.

The full _ReportStart event looks like:

const float height = 0.605f;
var currentTop = 7.250f;
foreach (var person in Model.People)
{
    var subReport = new SubReport
    {
        CloseBorder = false,
        Height = height,
        Left = 0F,
        Width = 7.5F,
        Top = currentTop,
        Name = person.DisplayName + "_SubReport",
        ReportName = person.DisplayName + "_SubReport",
      };

    var spReport = new _PersonAssignment()
    {
        Name = person.DisplayName,
        EmployeeId = person.EmployeeId
    };

    subReport.Report = spReport;
    this.Sections["detail"].Controls.Add(subReport);
    currentTop += height;
}

Bingo.

Example of Dynamic Sub Reports

Categories: .net 3.5, c# Tags: , ,
Follow

Get every new post delivered to your Inbox.