Live.js and Visual Studio Part 3 – Automated Testing
Posted by Andrew Swerlick in Uncategorized on January 31, 2013
This post is part of a series. Click for Part 1 or Part 2
In the last two posts I explored how Live.js can help you do client side testing, particularly for responsive layouts. Now we’ll be looking at another way that live.js can help out in your client side development.
But before we can do that, we have to take a brief foray into the world of javascript based unit testing. I’m not going to try to give a full treatise on the subject, but just a brief introduction so that we can see how live.js can help with this part of your development workflow too.
If you aren’t familiar with client side unit testing, don’t sweat it, it’s pretty straight forward. If you want a good overview check out smashing magazine’s intro or this great video on the qUnit framework. At a high level though it looks something like this.
1. Just like with your backend code, javascript testing starts with how you structure your code in the first place. Focus on small methods with minimal dependencies that return values that you can validate.
2. There are alot of javascript unit testing frameworks out there, but they all generally work the same way. Tests are functions passed into a method defined by the framework. Your to run your tests, you build a simple html page which has script references to the framework library, your test code and your application code. To run the tests, you load the page and the framework manipulates the html to report your results.
With this high level understanding, it’s pretty straight forward to see how live.js can help on this front. If you add live.js to that html page that runs your tests, then that page can refresh automatically and run your tests every time your test code or application code changes.
Note, that your automated testing page doesn’t have to be static html either. For example, in mvc we can set up a TestsController and Tests view that look a little like this.
Controller
public class TestsController : Controller
{
//
// GET: /Tests/
public ActionResult Index()
{
var testFiles = Directory.EnumerateFiles(Server.MapPath("~/Scripts/spec")).Where(f => f.EndsWith(".js"));
var sutFiles = testFiles.Select(s => s.Replace("_spec", ""));
ViewBag.SutFiles = sutFiles;
ViewBag.TestFiles = testFiles;
return View();
}
}
View
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="/Scripts/spec/lib/<your-testing-framework>.js"></script>
@foreach (var fullpath in ViewBag.SutFiles)
{
var fileName = Path.GetFileName(fullpath);
<script src="/Scripts/@fileName"></script>
}
@foreach (var fullpath in ViewBag.TestFiles)
{
var fileName = Path.GetFileName(fullpath);
<script src="/Scripts/spec/@fileName"></script>
}
<script>
onload = function () {
var runner = mocha.run();
};
</script>
</head>
<body>
</body>
</html>
The basic idea, is that we have a controller that builds up a list of files by looking in a specific folder where we put all of our tests. For all of the files it finds, it passes them along to the view, which then renders a set of script reference tags. The result is that our page dynamically adds all the assets it needs to test our javascript. Then live.js will do its thing and automatically refresh to run the tests any time there is a change.
Live.js and Visual Studio Part 2 – Test All The Devices
Posted by Andrew Swerlick in .NET on January 28, 2013
This post is part of a series. Click for Part 1 or Part 3
In my last post I introduced live.js and showed how you could setup a visual studio project to use it to speed up your front end development work. Today I’m going to walk you through how to configure your development environment so external mobile devices can connect to the machine and you can get this same live refresh on multiple screens and form factors simultaneously.
The idea is to create a setup where you can layout all the devices you’re currently testing, and see your changes on them live without lifting your fingers from the keyboard. Ultimately, this is fairly simple with live.js, because live.js all runs on the client side. So long as the devices your testing supports javascript, it will live refresh just like the browser on your development machine.
The complication is that unless your development machine is running with a full blown version of IIS, by default it won’t allow external connections. Fortunately, this is fairly easy to overcome. First, make sure you’re using IIS Express, instead of the Visual Studio’s Web Development Server (also called Cassini). If you’re on Visual Studio 2012, IIS Express is installed and the default, but if you’re on an older version you may have to download and install it. Note you’ll also need at least Visual Studio 2010 SP1 to make use of IIS Express.
Once you’ve got IIS Express installed, you can configure your project to use it. Go into the Properties section of your web Project and go into the “Web” sub-section. Look for the radio button labeled “Use Local IIS server.” This will ensure that visual studio uses the local IIS Express server you just installed.
However, by default IIS Express won’t allow external connections either. To resolve that, we have to edit the IIS applicationhost.config file, found at C:\Users\<you>\Documents\IISExpress\config. You can usually just do a file search for IISExpress to get there. In this file look for the node <sites>. It should look something like this.
<sites> <site name="WebSite1" id="1" serverAutoStart="true"> <application path="/"> <virtualDirectory path="/" physicalPath="%IIS_SITES_HOME%\WebSite1" /> </application> <bindings> <binding protocol="http" bindingInformation=":8080:localhost" /> </bindings> </site> <siteDefaults> <logFile logFormat="W3C" directory="%IIS_USER_HOME%\Logs" /> <traceFailedRequestsLogging directory="%IIS_USER_HOME%\TraceLogFiles" enabled="true" maxLogFileSizeKB="1024" /> </siteDefaults> <applicationDefaults applicationPool="Clr4IntegratedAppPool" /> <virtualDirectoryDefaults allowSubDirConfig="true" /> </sites>
Find the “site” node related to the site your working on and go down to the bindings node
<bindings> <binding protocol="http" bindingInformation=":8080:localhost" /> </bindings>
In the bindingInformation attribute remove the string “localhost”. Then save the file. Now IIS Express will try to listen for external connections on that same port.
Unfortunately, just because IIS Express wants to listen for external connections doesn’t mean the OS will let it. You have to tell Windows that its okay for IIS Express to listen at that url. For that there are two steps.
First, you need to add an http namespace reservation to the OS that tells Windows IIS Express can listen at the specified URL and port. The easiest way to do that is run the following command at an administrative command prompt.
netsh http add urlacl url=http://*:8080/ user=Everyone
This will allow any user to listen for connections that make a request for port 8080. You’ll need to change the port number to whatever port your application is running on. If you want to be more restrictive, you can change the * to your machine name, or change the user to the specific user that IIS express is running under. The command above though will allow you to browse to the device with just the IP address, which is generally more convenient.
Finally, the last thing you’ll need to do is go to the Windows Firewall and add an new inbound rule that permits inbound traffic to the port your application is running on.
Killer client side development with live.js and Visual Studio
Posted by Andrew Swerlick in .NET on January 27, 2013
This post is part of a series. Click for Part 2 or Part 3
Tuesday I got the opportunity to do a presentation at CINNUG. There were some requests to turn the contents into a blog post, and a figured that wouldn’t be a bad way to kick off blogging for the new year.
The talk was on how to use live.js to make debugging and developing client side code a breeze. If you aren’t familiar with live.js. It’s a simple javascript library that’s designed to automatically apply changes to your CSS/JS/HTML, without the need for a manual refresh. Basically, it polls the server for updates to the various assets constantly. With each poll it checks to see if anything has changed and then applies the changes, either through a force refresh, or through dynamic application.
The result is that you can see the affects of your changes live, without ever taking your hands off the keyboard.
So with the basic introduction, let’s take a look how we can take this for a test drive. For the purposes of this discussion we’ll be working with an MVC application, but everything will work just fine with pretty much any framework, WebForms included.
To start, spin up a new Empty MVC project. Then, go download live.js from http://livejs.com/ and add it to your Scripts folder. Next go into the Views-> Shared folder and edit your _Layout.cshtml page by adding a script reference like this,
<br><script type="mce-text/javascript" src="@Url.Content(" data-mce-src="@Url.Content("></script>
Now we can take live.js for a test drive. Start up your application and go to the homepage. Arrange your windows so you can see the browser and edit the application at the same time. Go into the Site.css file and edit something, like maybe the background color of the body. In just a moment you’ll see the application change just in front of you. To see how this works, boot up fiddler. You’ll see a constant stream of requests as live.js polls the server for updates. While this is great for debugging, we don’t want this kind of behavior happening in production. So how do we avoid that? Well it’s simple enough to use a basic HTMLHelper to only render the live.js reference if we’re running in debug mode. Here’s the helper code.
public static class IsDebugHelper
{
public static bool IsDebug(this HtmlHelper htmlHelper)
{
#if DEBUG
return true;
#else
return false;
#endif
}
}
Then we just call this helper in an if statement that conditionally renders our script reference, just like this.
@if (Html.IsDebug())
{<script type="mce-text/javascript" src="@Url.Content(" data-mce-src="@Url.Content("></script><br> }<br>
This gives us a basic livejs setup.For our mvc application. Aside from dynamically loading our css, livejs also does the same for html and javascript.
Next post I’ll show you how to use livejs to get this same sort of live refresh setup on external devices, like phones or tablets, to make it easier to test responsive designs.
TDD: A Case Study with Silverlight
Posted by Andrew Swerlick in Uncategorized on January 29, 2012
One of my goals for the new year was to follow TDD on a real project at work. I actually got my chance very early this year with a fairly basic Silverlight project. The project was short and simple, basically a fancy list of links and resources managed in sharepoint and exposed in a silverlight interface allowing a variety of queries and searches. It was large enough to be more than just a toy project, but small enough that I didn’t worry about doing much damage by trying out TDD for the first time.
I learned alot, and I think the work I did makes a good case study for someone interested in getting started with TDD. In my next few blog posts. I plan to walk readers through my development environment, the specifics of the techniques I followed, and the lessons I learned.
The Environment
As I said at the start, the project was written in Silverlight. For my testing I used the Silverlight Unit Test Framework, which allows for asynchronous testing, which is vitally important for any webservices based integration testing. On top of that I used a fantastic continuous test runner named Statlight. Statlight is a small console application that automatically runs your unit tests every time your test project’s .xap file changes. This means that running your tests is as easy as hitting Ctrl + Shift + B to build the project and Statlight does the rest. I quickly got in the habit of building after every code change so that I was getting instant feedback on what I was doing.
The Process
Since this was an experiment, I tried to stick as close to the rules of TDD as possible. This meant I never wrote a line of code until I had already written a test covering it, and that my tests were extremely granular. Even simple tasks like parsing a single line of XML returned from a webservice had a test devoted to it. I also tried not to overthink some of the details of my design, instead trying to put of design decisions until I had already written the test necessitating them.
The Result
Overall, my experience was hugely positive. I’m convinced that TDD definitely makes me more effective and productive and I want to leverage it wherever I can in the future. In general I found there were 3 major benefits to TDD, and I learned 3 lessons about how to do TDD better next time. Let’s start with the good
Flow – It was shocking how good it felt to be able to code without stopping. With TDD my brain stayed in code mode for hours at a time. Usually, I slip in and out of this mode out the day, especially when I’m manually testing code I’ve just written. With TDD, that never happened, and it made my concentration and focus 20x better. When I’m manually testing, there are all sorts of interruptions and opportunities for distraction. Waiting for the page I’m testing to load? I’ll just go browse google reader for a bit. Stepping through a tedious bit of code so I can examine the value of one variable? Let me just skim this email while I do that. With TDD though, my brain never gets an opportunity to slip away from the task at hand. Throughout the day I was laser focused on whatever I was doing.
Similarly, if I did have to step away for an interruption (meetings, lunch, help another dev, etc.) it was easy to get back into the flow and figure out where I was. Just hit Ctrl + Shift + B and see what test failed. Since each test was so small and covered such a small area, I didn’t have a ton of details about what I was doing slip away when I got distracted.
Design – I didn’t totally abandon upfront design, but I did do less design than I usually do. I mostly sketched out the layers at the boundaries of the application, the pieces that interacted with the user and the pieces that interacted with the data source, SharePoint, since both of those were external pieces that I couldn’t exercise complete control over. Once I had those layers designed though, I let TDD evolve the internal architecture of the application, which actually led to a couple of neat design decisions I don’t think I would have come up with otherwise. The coolest of these was how I handled loading up a given model for a given page. In our application the same view could be wired up to a variety of different models. The specific model depended on the url the user used. I ended up with two separate objects which handled this process, the Model Locator which parsed the incoming URL, and the Model Map, which tied each model to a path-like-string which represented how the data was maintained in the data store. The Model Locator would use the URL to extract the key elements to identify the right model, and then pass those into the Model Map, which would use those elements to find the right model by building the path representation for the model. The end result was a nice decoupling between the path structure the user used to browse to a model, and the way it was actually handled by the data layer. If I had been designing up front, I am almost positive I would have missed this approach, and put too much of the logic into the Model Locator itself, tightly coupling the data structure and the navigation structure. Instead, I put off making any decisions about how the Model Locator interacted with the data until the last minute, and by then it was clear that a new class would improve the design significantly.
Refactoring Ease of Mind – Not everything about this project was perfect. In fact, towards the middle there were some significant pain points because I had to be temporarily put on another higher priority project. To keep things moving another developer was assigned to the project. There wasn’t enough time invested in communication and as a result, he ended up taking a different approach in some key ares, and duplicating some work I’d already done. By the time I came back, his code was wired up to the UI, and it didn’t make sense to try and reincorporate the pieces of my code that were performing some of the same functions. Unfortunately, there were a number of pieces that handled things like search and model location that were still expecting the classes defined in my code. All of those had to be modified to work with his architecture instead.
This would have been a really scary refactoring to do in the timeline we had, except for the the automated tests I already had covering all of my code. With only a few minor tweaks, that test suite was modified to test my search services using his new classes, and we had extremely detailed information about where my code was now broken. After less than a day of work, we’d switched everything over without a hitch. And because of the tests, we had confidence that everything would work fine.
I won’t say much more in summary, because I think the benefits speak for themselves. Next post, I’ll talk about what I’d do differently next time, and how I plan to get better at TDD in the future.
Custom assertions with should.js
Posted by Andrew Swerlick in Uncategorized on January 19, 2012
Lately I’ve been playing with nodejs and vows, doing some TDD on a side project at home. I love the very readable syntax of should.js, which lets you frame your assertions as almost natural english. However, pretty early on I realized I wanted to add my own custom assertions to the should object to abstract away some of the messy details of my testing and keep the code readable. In the past I’ve used custom asserts with .NET for testing, and I find it allows you to quickly express domain specific concepts even inside your tests, for better readability and clarity.
One particular example was a test where I wanted to make sure the elements in a <ul> where the same as those in a javascript array. Rather than trying to parse out the list into another array and do a comparison in the test body, I wanted to have an assertion that was something like $(“#list”).children().should.consistOfTheElementsIn(array), where consistOfTheElementsIn will handle the parsing and comparison.
After a little bit a playing around, I worked out a pretty simple way to do this. Basically I create a new node module called customShould.js. customShould.js require’s should, and then exports the should object. Additionally, customShould adds a new method to the “Assertion” object created by should.js. Here’s the code
var should = require('should.js');
exports = module.exports = should;
should.Assertion.prototype.aHtmlListThatConsistOf =
function(list){
var compareArrays = function(first,second){
if (first.length != second.length) { return false; }
var a = first.sort(),
b = second.sort();
for (var i = 0; second[i]; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
var matches = this.match(/<li>.?*<//li>/gi);
for(matchIndex in matches){
matches[matchIndex] = matches.replace("<li>","").replace("</li>","");
}
this.assert(compareArray(matches, list), "lists do not match");
}
It’s all pretty straight forward. Then to use your custom asserts, you just require your customShould.js module instead of the normal should module.
Automatic Integration Testing With Joomla
Posted by Andrew Swerlick in Uncategorized on December 4, 2011
Lately, I’ve been deviating from my .NET ways to do a small website for my brother-in-law during my spare time. He works for a artistic iron works company and they were looking for a simple visual refresh to replace their 90′s era, MS FrontPage website.
I haven’t had much experience with Joomla, but I ended up choosing it because they have a dreamhost account and joomla is a one click install. I knew it was a big name in the CMS world, and even knew someone who makes his living off Joomla sites, so I figured it had to be pretty good. Frankly, after building out much of this site in it, I’m not impressed. The UI is clunky and not even intuitive for a techy like me. The documentation is sparse at the api level. And the extension development model seems to rely fair to heavily on static methods and singletons. But what irked me the most about Joomla is how difficult it was to get a solid automated integration test up and running. Hopefully what I document here will save someone else my pain later.
Before getting to the technical how-to though, a little bit of background on why I think this is important. In the last year I’ve become a huge proponent of automated testing. In general, when I start on a new project or feature now, the first thing I do is spin up my test project. This is especially true when I’m integrating with some sort of external framework, particularly when that framework lacks solid documentation. A good set of quickly executing automated integration tests are the fastest way to vet my assumptions about how a framework behaves with reality.
So that’s what I set out to create when I realized I would need to develop a joomla module. The goal of my module was simple. I was using the K2 joomla extension to let my users create photogalleries. I wanted a rollup module that would take the first photo from every gallery in the site, and render a slideshow out of those, with links back to each individual gallery. Following the guides I found on module development, I created a helper.php file to do the heavy lifting. Then I set out to create a test project to test that implementation.
The first sign that something was wrong, was that I couldn’t find anyone else who had tacked the same problem on google. There was a little bit about building custom applications on top of joomla, but nothing about testing. So I figured I’d just setup phpunit and hope for the best.
Right off the bat, the framework started fighting me. PHPUnit failed with no error message, just silently not running. I went back to the article on custom applications and that got me part way there, but I still had to struggle with a whole slew of missing dependency and undefined variable issues.
Eventually I got it to work with the following lines at the start of the of the file.
define('_JEXEC', 1);
define('JPATH_BASE', '/var/www/');
define('JPATH_PLATFORM', JPATH_BASE . '/libraries');
define('DS', DIRECTORY_SEPARATOR);
require_once JPATH_BASE . '/includes/defines.php';
require_once JPATH_BASE . '/includes/framework.php';
jimport( 'joomla.environment.request' );
jimport( 'joomla.application.helper');
jimport('joomla.application.application');
JFactory::getApplication('site');
$_SERVER['HTTP_HOST'] = "localhost";
require('');
const K2_JVERSION = 16;
Even this didn’t give me everything I needed. I kept getting infinite loop errors. Googling for that lead me to a link on github where somebody had fixed a similar error in Joomla. It turns out the actual error was in joomla’s exception throwing mechanism. Whenever Joomla tried to throw an error in the integration test, it got caught in an infinite loop and just reported the generic infinite loop exception.
Since this testing was on a dev machine, I decided the easiest fix would be to edit the joomla files themselves to print out the stack trace whenever and infinite loop detected. The file I edited was /libraries/joomla/error/error.php, replacing the generic error message on line 201 with the code to print a full backtrace
jexit(JText::_('JLIB_ERROR_INFINITE_LOOP')."\n".$exception->getMessage()."\n".$exception->getTraceAsString()."\n".$exception->getLine()."\n");
Only after all that could I successfully run an automated integration test against joomla.
I don’t want to criticize a platform I’ve done so little with, but the complete lack of documentation on basic automated testing doesn’t speak highly of the development enviroment Joomla has created. I hope this contribution helps someone else in my boat at least get started, and the joomla devs start thinking about how to bake this sort of testing process into the platform more directly.
Working with the SharePoint DataFormWebPart in Custom Application Pages: Part II
Posted by Andrew Swerlick in .NET on September 13, 2011
In the first post in this series, we setup a DataFormWebPart on our custom application page, using markup autogenerated by SharePoint. If everything went right, you should have a page with a DFW loading up and letting you do basic CRUD operations. Now for the harder stuff.
Trials and Tribulations
The first issue you’ll probably notice as soon as you save an item. Immediately after save, you’ll probably be redirected to the SharePoint List where the item is stored. Now maybe this isn’t an issue for you, but I suspect most of the time, you’d rather control where the user ends up.
While this seems like it should be easy, the challenge is that all this redirect logic is bundled with all the save logic up in a sharepoint WebControl, aptly labeled “SaveButton.” If you want to control redirect, you have to handle saving the item yourself. To do so, get a reference to the DataFormWebPart that’s on the page. Once you’ve got the reference, you’ll want to retrieve the ItemContext.ListItem property. This gives you access to the SharePoint List Item that stores whatever data the user has entered. Call ListItem.Update() and you’ve got the save handled. You can wrap that up inside the Click handler for a normal asp:Button and remove the SaveButton control from the form completely.
So now you’ve got a simple form which let’s you view and save your data. But what if you want something more complicated? What if you want to put custom server controls or access to page variables inside the xsl? Well then things start to get very tough. By default, the DFW will only allow custom server controls from Microsoft.SharePoint.WebControls. Adding any other controls will result in an “Unknown Server Tag.” Fortunately, another SharePoint blogger, Charlie Holland already dug into this issue and wrote a custom webpart to resolve it. His ExtendedDataFormWebPart allows you to specify a set of additional assemblies to allow controls from.
So that handles server controls, but what about page variables or inline script? Again, by default the DFW doesn’t allow any sort of inline code. However, we can take a similar approach to Charlie and get the ability to use PageVariables in our xsl, even if we can’t do full online inline code.
The best place to start looking for how to pull data into the XSL is the ParameterBindings List we looked at earlier for our QueryStrings. MSDN blogger Josh Gaffey has a good overview on these ParameterBindings. Basically each binding ends up corresponding to an XSL parameter that you can use inside of the XSL by using <xsl:value-of select=”{$param-name}”>. However, out of the box, you can only pull these parameter values from a limited number of locations (QueryStrings, CAMLVariables, Server Variables, Control Values, etc.) and Page Variables isn’t one of them.
Poking around in the properties of the dataform webpart, we can see there’s a property named ParameterValues. Looking at this property in debug mode, we see that it is a hashtable that holds all the values of parameter bindings. So what we need to do is inject our own values based on page variables, into the hashtable. Below is the code for a modified ExtendedDataFormWebPart class that incorporates both Charlie Holland’s additional assembly code, and our code to use page Variables.
<pre> [ToolboxItemAttribute(false)]
public class ExtendedDataFormWebPart : DataFormWebPart
{
public ExtendedDataFormWebPart()
: base()
{
}
[Browsable(false), WebPartStorage(Storage.None), PersistenceMode(PersistenceMode.InnerProperty)]
public string AssemblyReferences
{
get
{
List<AssemblyReference> response = new List<AssemblyReference>();
foreach (string reference in _assemblyReferences)
{
AssemblyReference ar = new AssemblyReference(reference);
response.Add(ar);
}
return response.ToString();
}
set
{
XDocument doc = XDocument.Parse("<root>" + value + "</root>");
var refs = from r in doc.Descendants("AssemblyReference")
select new AssemblyReference
{
Prefix = r.Attribute("Prefix").Value,
Namespace = r.Attribute("Namespace").Value,
Assembly = r.Attribute("Assembly").Value
};
_assemblyReferences = new string[refs.Count()];
int i = 0;
foreach (var ar in refs)
{
_assemblyReferences[i] = ar.ToString();
i++;
}
}
}
public override void DataBind()
{
BindPageVariablesToParameterBindings();
base.DataBind();
}
private void BindPageVariablesToParameterBindings()
{
var listOfParametersAndVariableNames = BuildListOfPageParameters();
SetParameterCollectionValues(listOfParametersAndVariableNames);
}
private void SetParameterCollectionValues(IEnumerable<KeyValuePair<string, string>> listOfParametersAndVariableNames)
{
foreach (var param in listOfParametersAndVariableNames)
{
object valueOfVariable;
FieldInfo field = Page.GetType().GetField(param.Value);
PropertyInfo prop = Page.GetType().GetProperty(param.Value);
if(field != null)
{
valueOfVariable = field.GetValue(Page);
}
else if(prop != null)
{
valueOfVariable = prop.GetValue(Page, null);
}
else
{
throw new InvalidOperationException(string.Format( "There is no member with the name {0} on {1}. Check your parameter binding to ensure the Location attribute is correct", param.Value, Page.ToString()));
}
valueOfVariable = (valueOfVariable != null) ? valueOfVariable.ToString() : "";
ParameterValues.Set(param.Key, valueOfVariable as string);
}
}
private IEnumerable<KeyValuePair<string, string>> BuildListOfPageParameters()
{
XDocument parametersXml = XDocument.Parse("<parameters>" + ParameterBindings +"</parameters>");
XName location = XName.Get("Location", "");
var parameters = parametersXml.Root.Elements().Where(e => e.Name.LocalName == "ParameterBinding" && e.Attribute(location).Value.Contains("PageVariable") );
return parameters.Select(p => BuildKeyValuePairFromParameter(p));
}
private KeyValuePair<string, string> BuildKeyValuePairFromParameter(XElement parameter)
{
string key = parameter.Attribute(XName.Get("Name", "")).Value;
string value = parameter.Attribute(XName.Get("Location", "")).Value.Replace("PageVariable(", "").Replace(")", "");
return new KeyValuePair<string, string>(key, value);
}
}
public sealed class AssemblyReference
{
public AssemblyReference()
{
}
public override string ToString()
{
return string.Format("<%@ Register TagPrefix=\"{0}\" Namespace=\"{1}\" Assembly=\"{2}\" %>", Prefix, Namespace, Assembly);
}
public AssemblyReference(string reference)
{
Match m = Regex.Match(reference, "TagPrefix=\"(\\S*)\" Namespace=\"(\\S*)\" Assembly=\"(.*)\"");
Prefix = m.Groups[1].Value;
Namespace = m.Groups[2].Value;
Assembly = m.Groups[3].Value;
}
public string Prefix;
public string Namespace;
public string Assembly;
}
What we’re doing here is overriding the DataBind method and calling a private method that uses reflection to insert our page variables into the ParameterValues collection. If we want to use a PageVariable, we just add ParameterBinding of the form <ParameterBinding Name=”<xsl param name>” Location=”PageVariable(<variable name>)”/>
The only gotcha is that our variable has to be either a public property or field, not private or protected, since reflection doesn’t seem to pick those up, even when we pass a BindingFlags.NonPublic into the GetFields and GetProperties methods.
Conclusion
SharePoint’s DataFormWebPart is a really neat concept, but the execution leaves a little to be desired when you’re heavily customizing things. Fortunately, most of those short comings can be overcome by extending the webpart with the code samples here and elsewhere. Be warned though, that XSL is a pain to work with, and very unforgiving when it comes to typos. Even with all the improvements offered by the ExtendedDataFormWebPart, it might be easier to build the data manipulation plumbing yourself.