<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
  <channel>
    <title><![CDATA[[SecurityRatty] tag: pluralsight]]></title>
    <link>http://securityratty.com/tag/pluralsight</link>
    <description></description>
    <pubDate>Fri, 07 Mar 2008 02:49:00 +0000</pubDate>
    <generator>iRatty Engine</generator>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <item>
      <title><![CDATA[Pluralsight On-Demand is now live!]]></title>
      <link>http://securityratty.com/article/3ea5514857ccf3eabb6c0b8aaad649bb</link>
      <guid>http://securityratty.com/article/3ea5514857ccf3eabb6c0b8aaad649bb</guid>
      <description><![CDATA[I've been rather dark over the last couple of months as I helped to finish up Pluralsight's online training offering, Pluralsight On-Demand . I'm psyched that we finally shipped
Be sure to check it...]]></description>
      <content:encoded><![CDATA[<p>I&#39;ve been rather dark over the last couple of months as I helped to finish up <a href="http://www.pluralsight.com" target="_blank">Pluralsight&#39;s</a> online training offering, <a href="http://www.pluralsight.com/main/Default.aspx" target="_blank">Pluralsight On-Demand</a>. I&#39;m psyched that we finally shipped!</p> <p>Be sure to check it out soon (you can preview bits of each course right now for free), as we&#39;re offering a limited-time early adopter discount that&#39;s good for the life of your subscription. Our online courses are told by the authors themselves, with names that you&#39;ll recognize, as many are MSDN Magazine contributing editors and book authors on their topics.</p> <p>Courses we now offer online include:</p> <blockquote> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=wcf-fundamentals">WCF Fundamentals</a> by <a href="http://www.pluralsight.com/main/instructor.aspx?name=aaron-skonnard">Aaron Skonnard</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=wf-fundamentals" target="_blank">Windows Workflow Fundamentals</a>, by <a href="http://www.pluralsight.com/main/instructor.aspx?name=matt-milner" target="_blank">Matt Milner</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=wpf-fundamentals">WPF Fundamentals</a>, by <a href="http://www.pluralsight.com/main/instructor.aspx?name=ian-griffiths">Ian Griffiths</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=silverlight-fundamentals">Silverlight Fundamentals</a>, by <a href="http://www.pluralsight.com/main/instructor.aspx?name=ian-griffiths">Ian Griffiths</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=aspdotnet-fundamentals" target="_blank">ASP.NET 3.5 Fundamentals</a>, by <a href="http://www.pluralsight.com/main/instructor.aspx?name=fritz-onion" target="_blank">Fritz Onion</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=aspdotnet-ajax-fundamentals" target="_blank">ASP.NET Ajax Fundamentals</a>, by <a href="http://www.pluralsight.com/main/instructor.aspx?name=fritz-onion" target="_blank">Fritz Onion</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=linq-fundamentals">LINQ Fundamentals</a> by <a href="http://www.pluralsight.com/main/instructor.aspx?name=scott-allen">Scott Allen</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=abts-fundamentals">BizTalk Fundamentals</a> by <a href="http://www.pluralsight.com/main/instructor.aspx?name=matt-milner" target="_blank">Matt Milner</a></p> <p><a href="http://www.pluralsight.com/main/olt/Course.aspx?n=btsr2-fundamentals">BizTalk Server 2006 R2 Fundamentals</a> by <a href="http://www.pluralsight.com/main/instructor.aspx?name=jon-flanders">Jon Flanders</a></p></blockquote> <p>We&#39;ll be expanding this library of content in the months to come, as we continue to grow this online resource. I plan on adding modules on the <a href="https://connect.microsoft.com/site/sitehome.aspx?SiteID=642&amp;wa=wsignin1.0" target="_blank">Geneva</a> family of identity products (Geneva Server, Geneva Framework, Geneva CardSpace) announced at PDC this week.</p> <p>I&#39;ve learned a lot of interesting tidbits as I helped to develop the back end infrastructure for Pluralsight On-Demand, and now that I&#39;m not so crammed for time, I&#39;ll be sharing those insights here on this blog.</p> <p>Congrats to all who helped bring this incredible resource to the public!</p> <p>Aaron <a href="http://www.pluralsight.com/community/blogs/aaron/archive/2008/10/29/announcing-pluralsight-on-demand.aspx">has more details</a> if you want to know about pricing, customer feedback, and so on.</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=54152" width="1" height="1">]]></content:encoded>
      <pubDate>Wed, 29 Oct 2008 16:49:34 +0000</pubDate>
      <category domain="http://securityratty.com/tag/fundamentals">fundamentals</category>
      <category domain="http://securityratty.com/tag/silverlight fundamentals">silverlight fundamentals</category>
      <category domain="http://securityratty.com/tag/windows workflow fundamentals">windows workflow fundamentals</category>
      <category domain="http://securityratty.com/tag/wcf fundamentals">wcf fundamentals</category>
      <category domain="http://securityratty.com/tag/net">net</category>
      <category domain="http://securityratty.com/tag/net ajax fundamentals">net ajax fundamentals</category>
      <category domain="http://securityratty.com/tag/pluralsight">pluralsight</category>
      <category domain="http://securityratty.com/tag/pluralsight on-demand">pluralsight on-demand</category>
      <category domain="http://securityratty.com/tag/courses">courses</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/10/29/pluralsight-on-demand-is-now-live.aspx">Pluralsight On-Demand is now live!</source>
    </item>
    <item>
      <title><![CDATA[Zermatt in Community Server]]></title>
      <link>http://securityratty.com/article/e775efcf6afa32aabd54630993695eaa</link>
      <guid>http://securityratty.com/article/e775efcf6afa32aabd54630993695eaa</guid>
      <description><![CDATA[I'm about to embark on a mission to get Zermatt integrated into pluralsight.com as our single-sign-on solution, and a big part of that is getting our Community Server installation wired into that. I'm...]]></description>
      <content:encoded><![CDATA[<p>I&#39;m about to embark on a mission to get Zermatt integrated into pluralsight.com as our single-sign-on solution, and a big part of that is getting our Community Server installation wired into that. I&#39;m curious if anyone else has seen any work being done in this area, or if I&#39;ll be the first?</p> <p>I plan to blog about my progress (and share it) if there&#39;s not already a built-in solution out there.</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=53780" width="1" height="1">]]></content:encoded>
      <pubDate>Mon, 06 Oct 2008 18:07:17 +0000</pubDate>
      <category domain="http://securityratty.com/tag/built-in solution">built-in solution</category>
      <category domain="http://securityratty.com/tag/single-sign-on solution">single-sign-on solution</category>
      <category domain="http://securityratty.com/tag/zermatt">zermatt</category>
      <category domain="http://securityratty.com/tag/embark">embark</category>
      <category domain="http://securityratty.com/tag/progress">progress</category>
      <category domain="http://securityratty.com/tag/mission">mission</category>
      <category domain="http://securityratty.com/tag/pluralsight">pluralsight</category>
      <category domain="http://securityratty.com/tag/blog">blog</category>
      <category domain="http://securityratty.com/tag/curious">curious</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/10/06/zermatt-in-community-server.aspx">Zermatt in Community Server</source>
    </item>
    <item>
      <title><![CDATA[Welcome, Kevin Jones!]]></title>
      <link>http://securityratty.com/article/3068772a5294d78567f80090016361ac</link>
      <guid>http://securityratty.com/article/3068772a5294d78567f80090016361ac</guid>
      <description><![CDATA[We've worked with Kevin in our past lives at a previous training company, and we're glad to say that he's joined us as a Pluralsight instructor . Located in the United Kingdom, Kevin will be helping...]]></description>
      <content:encoded><![CDATA[<p><img src="http://www.pluralsight.com/main/images/instructors/large/kevin_jones.jpg" border="0" alt="" /></p> <p>We&#39;ve worked with <a href="http://www.pluralsight.com/community/blogs/kevinj/default.aspx" target="_blank">Kevin</a> in our past lives at a previous training company, and we&#39;re glad to say that he&#39;s joined us as a <a href="http://www.pluralsight.com/main/Instructors.aspx" target="_blank">Pluralsight instructor</a>. Located in the United Kingdom, Kevin will be helping out in Europe as well as helping to round out our course curriculum, but more on that later!</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=52959" width="1" height="1">]]></content:encoded>
      <pubDate>Thu, 11 Sep 2008 13:13:51 +0000</pubDate>
      <category domain="http://securityratty.com/tag/kevin">kevin</category>
      <category domain="http://securityratty.com/tag/past lives">past lives</category>
      <category domain="http://securityratty.com/tag/pluralsight instructor">pluralsight instructor</category>
      <category domain="http://securityratty.com/tag/glad">glad</category>
      <category domain="http://securityratty.com/tag/round">round</category>
      <category domain="http://securityratty.com/tag/kingdom">kingdom</category>
      <category domain="http://securityratty.com/tag/previous">previous</category>
      <category domain="http://securityratty.com/tag/curriculum">curriculum</category>
      <category domain="http://securityratty.com/tag/europe">europe</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/09/11/welcome-kevin-jones.aspx">Welcome, Kevin Jones!</source>
    </item>
    <item>
      <title><![CDATA[Serializable XmlDocument]]></title>
      <link>http://securityratty.com/article/94c84cd2ea7a6ea71c9712991d27722d</link>
      <guid>http://securityratty.com/article/94c84cd2ea7a6ea71c9712991d27722d</guid>
      <description><![CDATA[It's surprising that XmlDocument isn't marked [Serializable], because it's very natural to serialize one into a stream. I wanted to put an object into ASP.NET ViewState the other day, and quickly ran...]]></description>
      <content:encoded><![CDATA[<p>It&#39;s surprising that XmlDocument isn&#39;t marked [Serializable], because it&#39;s very natural to serialize one into a stream. I wanted to put an object into ASP.NET ViewState the other day, and quickly ran into this roadblock, because part of the object included an XmlDocument, which is not serializable. A quick search revealed that most people deal with this problem by storing a string instead. Indeed, that was where I started, but I quickly realized that there are multiple places in my code where I want to do this sort of thing, and I don&#39;t want to have to mess with it in each data structure that contains an XmlDocument.</p>
<p>So I put together a simple class that holds an XmlDocument and implements ISerializable and called it SerializableXmlDocument. I&#39;m sharing the source code here in the hopes that</p>
<blockquote>
<p>a) somebody will find it useful, and</p>
<p>b) somebody smarter than I am will point out how I screwed it up and help me make it better.</p>
</blockquote>
<p>SerializableXmlDocument includes implicit conversion operators to make it easy to convert to/from an XmlDocument. It holds the actual document in a property called Value. This &quot;isomorph&quot; pattern is one that I picked up from <a href="http://www.pluralsight.com/community/blogs/craig/default.aspx" target="_blank">Craig</a>.</p>
<p>While writing this code, I also wrote a helpful extension method for getting a byte array out of a MemoryStream that is exactly the length of the data written to the stream so far (CopyUpToSeekPointer). So don&#39;t go looking in the docs for MemoryStream for this method :) This is obviously not the most efficient way to consume bytes written to a MemoryStream since it copies the data into a new byte array, but it&#39;s very convenient in many scenarios.</p>
<p>Here is SerializableXmlDocument.cs:</p>
<pre class="csharpcode"><span class="kwrd">using</span> System;<br /><span class="kwrd">using</span> System.Runtime.Serialization;<br /><span class="kwrd">using</span> System.Xml;<br /><span class="kwrd">using</span> System.IO;<br /><br /><span class="kwrd">namespace</span> Pluralsight.Samples<br />{<br />    [Serializable]<br />    <span class="kwrd">public</span> <span class="kwrd">class</span> SerializableXmlDocument : ISerializable<br />    {<br />        <span class="kwrd">public</span> SerializableXmlDocument() { }<br />        <span class="kwrd">public</span> SerializableXmlDocument(XmlDocument <span class="kwrd">value</span>)<br />        {<br />            <span class="kwrd">this</span>.Value = <span class="kwrd">value</span>;<br />        }<br /><br />        <span class="kwrd">public</span> XmlDocument Value { get; set; }<br /><br />        <span class="preproc">#region</span> ISerializable implementation<br />        <span class="kwrd">public</span> SerializableXmlDocument(SerializationInfo info,<br />                                       StreamingContext context)<br />        {<br />            <span class="kwrd">byte</span>[] serializedData = (<span class="kwrd">byte</span>[])info.GetValue(<span class="str">&quot;doc&quot;</span>,<br />                <span class="kwrd">typeof</span>(<span class="kwrd">byte</span>[]));<br />            <span class="kwrd">if</span> (<span class="kwrd">null</span> != serializedData)<br />                <span class="kwrd">this</span>.Value = Deserialize(serializedData);<br />        }<br /><br />        <span class="kwrd">public</span> <span class="kwrd">void</span> GetObjectData(SerializationInfo info,<br />                                  StreamingContext context)<br />        {<br />            <span class="kwrd">byte</span>[] serializedData = <span class="kwrd">null</span>;<br />            <span class="kwrd">if</span> (<span class="kwrd">null</span> != Value)<br />                serializedData = Serialize(Value);<br />            info.AddValue(<span class="str">&quot;doc&quot;</span>, serializedData);<br />        }<br />        <span class="preproc">#endregion</span><br /><br />        <span class="preproc">#region</span> <span class="kwrd">implicit</span> conversion to/from XmlDocument<br />        <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">implicit</span> <span class="kwrd">operator</span> SerializableXmlDocument(<br />            XmlDocument doc)<br />        {<br />            <span class="kwrd">return</span> <span class="kwrd">new</span> SerializableXmlDocument(doc);<br />        }<br />        <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">implicit</span> <span class="kwrd">operator</span> XmlDocument(<br />            SerializableXmlDocument sdoc)<br />        {<br />            <span class="kwrd">return</span> sdoc.Value;<br />        }<br />        <span class="preproc">#endregion</span><br /><br />        <span class="preproc">#region</span> Xml serialization helper methods<br />        <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">byte</span>[] Serialize(XmlDocument doc)<br />        {<br />            MemoryStream stream = <span class="kwrd">new</span> MemoryStream();<br />            doc.Save(stream);<br />            <span class="kwrd">return</span> stream.CopyUpToSeekPointer();<br />        }<br />        <span class="kwrd">private</span> <span class="kwrd">static</span> XmlDocument Deserialize(<span class="kwrd">byte</span>[] serializedData)<br />        {<br />            XmlDocument doc = <span class="kwrd">new</span> XmlDocument();<br />            doc.Load(<span class="kwrd">new</span> MemoryStream(serializedData, <span class="kwrd">false</span>));<br />            <span class="kwrd">return</span> doc;<br />        }<br />        <span class="preproc">#endregion</span><br />    }<br />}</pre>
<p>...and here&#39;s the CopyUpToSeekPointer extension method for MemoryStream:</p>
<pre class="csharpcode"><span class="kwrd">using</span> System;<br /><span class="kwrd">using</span> System.IO;<br /><br /><span class="kwrd">namespace</span> Pluralsight.Samples<br />{<br />    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> MemoryStreamExtensionMethods<br />    {<br />        <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">byte</span>[] CopyUpToSeekPointer(<br />            <span class="kwrd">this</span> MemoryStream stream)<br />        {<br />            <span class="rem">// copy only the part of the buffer</span><br />            <span class="rem">// that contains the serialized document</span><br />            <span class="kwrd">long</span> length = stream.Position;<br />            <span class="kwrd">byte</span>[] buffer = stream.GetBuffer();<br />            <span class="kwrd">byte</span>[] result = <span class="kwrd">new</span> <span class="kwrd">byte</span>[length];<br />            <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i &lt; length; ++i)<br />                result[i] = buffer[i];<br />            <span class="kwrd">return</span> result;<br />        }<br />    }<br />}</pre>
<p>...and here&#39;s a sample object that uses SerializableXmlDocument:</p>
<pre class="csharpcode"><span class="kwrd">using</span> System;<br /><br /><span class="kwrd">namespace</span> Pluralsight.Samples<br />{<br />    [Serializable]<br />    <span class="kwrd">public</span> <span class="kwrd">class</span> Item<br />    {<br />        <span class="kwrd">public</span> <span class="kwrd">string</span> Name { get; set; }<br />        <span class="kwrd">public</span> SerializableXmlDocument Data { get; set; }<br /><br />        <span class="kwrd">public</span> <span class="kwrd">void</span> Print()<br />        {<br />            Console.WriteLine(<span class="str">&quot;Name: {0}&quot;</span>, Name);<br />            Console.WriteLine(Data.Value.OuterXml);<br />        }<br />    }<br />}</pre>
<p>...and here&#39;s a sample program that creates an instance of Item, serializes it, then deserializes it, printing diagnostics along the way to show that it&#39;s working properly.</p>
<pre class="csharpcode"><span class="kwrd">using</span> System;<br /><span class="kwrd">using</span> System.Xml;<br /><span class="kwrd">using</span> System.Runtime.Serialization.Formatters.Binary;<br /><span class="kwrd">using</span> System.IO;<br /><span class="kwrd">using</span> Pluralsight.Samples;<br /><br /><span class="kwrd">class</span> DemoProgram<br />{<br />    <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)<br />    {<br />        XmlDocument doc = <span class="kwrd">new</span> XmlDocument();<br />        doc.LoadXml(<span class="str">&quot;&lt;root&gt;&lt;child&gt;text&lt;/child&gt;&lt;/root&gt;&quot;</span>);<br /><br />        Item item = <span class="kwrd">new</span> Item<br />        {<br />            Name = <span class="str">&quot;Testing 123&quot;</span>,<br />            Data = doc,<br />        };<br /><br />        <span class="rem">// print object before serialization</span><br />        item.Print();<br /><br />        BinaryFormatter formatter = <span class="kwrd">new</span> BinaryFormatter();<br />        MemoryStream stream = <span class="kwrd">new</span> MemoryStream();<br />        formatter.Serialize(stream, item);<br /><br />        <span class="kwrd">byte</span>[] serializedItem = stream.CopyUpToSeekPointer();<br /><br />        Console.WriteLine(<span class="str">&quot;Serialized data (base64): {0}&quot;</span>,<br />            Convert.ToBase64String(serializedItem));<br /><br />        item = (Item)formatter.Deserialize(<br />            <span class="kwrd">new</span> MemoryStream(serializedItem, <span class="kwrd">false</span>));<br /><br />        <span class="rem">// print object after deserialization</span><br />        item.Print();<br />    }<br />}</pre>
<p>Here&#39;s the output of the previous sample program:</p>
<p><a href="http://www.pluralsight.com/community/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/keith/sample_2D00_output_5F00_2.jpg"><img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" alt="sample-output" src="http://www.pluralsight.com/community/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/keith/sample_2D00_output_5F00_thumb.jpg" width="422" border="0" height="214" /></a>&nbsp;</p>
<p>Flame away!</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=52538" width="1" height="1">]]></content:encoded>
      <pubDate>Mon, 18 Aug 2008 22:58:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/public class item">public class item</category>
      <category domain="http://securityratty.com/tag/public">public</category>
      <category domain="http://securityratty.com/tag/public void getobjectdata">public void getobjectdata</category>
      <category domain="http://securityratty.com/tag/public static byte">public static byte</category>
      <category domain="http://securityratty.com/tag/xmldocument">xmldocument</category>
      <category domain="http://securityratty.com/tag/return doc">return doc</category>
      <category domain="http://securityratty.com/tag/return">return</category>
      <category domain="http://securityratty.com/tag/static byte">static byte</category>
      <category domain="http://securityratty.com/tag/public class">public class</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/08/18/serializable-xmldocument.aspx">Serializable XmlDocument</source>
    </item>
    <item>
      <title><![CDATA[Where to get Password Minder]]></title>
      <link>http://securityratty.com/article/6fd519c118d9365baa63f9bddb0cba63</link>
      <guid>http://securityratty.com/article/6fd519c118d9365baa63f9bddb0cba63</guid>
      <description><![CDATA[We recently updated our website and some links have broken as a result. Here's the place you should go to get the latest version of Password Minder
http://mercury.pluralsight.com/tools.aspx
Sorry for...]]></description>
      <content:encoded><![CDATA[<p>We recently updated our website and some links have broken as a result. Here&#39;s the place you should go to get the latest version of Password Minder:</p>
<p><a href="http://mercury.pluralsight.com/tools.aspx">http://mercury.pluralsight.com/tools.aspx</a></p>
<p>Sorry for any inconvenience!</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=52369" width="1" height="1">]]></content:encoded>
      <pubDate>Tue, 05 Aug 2008 13:00:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/password minder">password minder</category>
      <category domain="http://securityratty.com/tag/website">website</category>
      <category domain="http://securityratty.com/tag/recently">recently</category>
      <category domain="http://securityratty.com/tag/version">version</category>
      <category domain="http://securityratty.com/tag/pluralsight">pluralsight</category>
      <category domain="http://securityratty.com/tag/inconvenience">inconvenience</category>
      <category domain="http://securityratty.com/tag/aspx">aspx</category>
      <category domain="http://securityratty.com/tag/mercury">mercury</category>
      <category domain="http://securityratty.com/tag/links">links</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/08/05/where-to-get-password-minder.aspx">Where to get Password Minder</source>
    </item>
    <item>
      <title><![CDATA[Controlling column width in a GridView]]></title>
      <link>http://securityratty.com/article/ae0aa0fbe27711f6caa8fb16924a9208</link>
      <guid>http://securityratty.com/article/ae0aa0fbe27711f6caa8fb16924a9208</guid>
      <description><![CDATA[I've been building some internal pages for our sales team here at Pluralsight , and many of those pages make use of the ASP.NET GridView control to display rectangular data. It's generally a really...]]></description>
      <content:encoded><![CDATA[<p>I&#39;ve been building some internal pages for our sales team here at <a href="http://www.pluralsight.com/main/" target="_blank">Pluralsight</a>, and many of those pages make use of the ASP.NET GridView control to display rectangular data. It&#39;s generally a really easy to use control, but I&#39;ve always struggled with getting column widths to look right.</p>  <p>My goal is to fix the width of each column at design time, and any field that contains text that may be longer than my fixed width should wrap around, taking up more vertical space in the table. If you are trying to accomplish this goal, you might find these tips helpful.</p>  <blockquote>   <p>1) Set up a CssClass for the GridView itself and include the <a href="http://www.w3.org/TR/REC-CSS2/tables.html#fixed-table-layout" target="_blank">table-layout:fixed</a> style. This tells the browser that you&#39;re going to specify the width of each cell. You may also want to include the overall width of the grid here as I mention in (3).</p>    <p>2) The first row of the table sets the width for each cell, and that&#39;s usually the HEADER row, not the item row, so use either HeaderStyle-CssClass or HeaderStyle-Width to set the width of the cell. I wasted a lot of time trying to set the width using the ItemStyle.</p>    <p>3) Make certain the table itself is wide enough to hold all of the cells. I added up all of my cell widths and used that to set the width via the CssClass attribute on the GridView.</p> </blockquote>  <p>Using these guidelines, I&#39;m having much better luck controlling the layout of my GridView controls. I hope this simple advise helps someone else!</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=52286" width="1" height="1">]]></content:encoded>
      <pubDate>Wed, 30 Jul 2008 13:23:27 +0000</pubDate>
      <category domain="http://securityratty.com/tag/width">width</category>
      <category domain="http://securityratty.com/tag/gridview">gridview</category>
      <category domain="http://securityratty.com/tag/fixed width">fixed width</category>
      <category domain="http://securityratty.com/tag/gridview controls">gridview controls</category>
      <category domain="http://securityratty.com/tag/net gridview control">net gridview control</category>
      <category domain="http://securityratty.com/tag/column">column</category>
      <category domain="http://securityratty.com/tag/control">control</category>
      <category domain="http://securityratty.com/tag/table">table</category>
      <category domain="http://securityratty.com/tag/row">row</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/07/30/controlling-column-width-in-a-gridview.aspx">Controlling column width in a GridView</source>
    </item>
    <item>
      <title><![CDATA[Welcome, David Starr]]></title>
      <link>http://securityratty.com/article/e0f82b4678ac634c32e08d0c1752c7f1</link>
      <guid>http://securityratty.com/article/e0f82b4678ac634c32e08d0c1752c7f1</guid>
      <description><![CDATA[One of the main reasons that Fritz , Aaron , and I wanted to create this company was to provide a home for people who love to teach. We didn't want to build an empire, and we weren't out to get rich....]]></description>
      <content:encoded><![CDATA[<p>One of the main reasons that <a href="http://www.pluralsight.com/community/blogs/fritz/default.aspx">Fritz</a>, <a href="http://www.pluralsight.com/community/blogs/aaron/default.aspx">Aaron</a>, and I wanted to create this company was to provide a home for people who love to teach. We didn&#39;t want to build an empire, and we weren&#39;t out to get rich. We just wanted a place where we could comfortably practice what we love to do: giving software developers a boost - watching that light go on over their heads when a new concept becomes clear. We love to teach, and it&#39;s always exciting to find talented individuals who share that passion.</p>
<p><a href="http://www.pluralsight.com/community/members/david_2D00_starr/default.aspx">David Starr</a> is one of those people. I&#39;ve seen him in action, and not only does he have a lot of very practical experience from the trenches, but he also has a clear ability to convey his knowledge and experience to his students. It&#39;s clearly important to David to connect with his students, and he does so in a way that lets them know that it&#39;s all about them - he&#39;s not there just to show off his mad skillz (which he has in abundance!) So I&#39;m very excited to welcome David on board as our newest Pluralsight instructor. I apologize for not writing this sooner - we&#39;ve all been really busy getting the new website out the door.</p>
<p>Here&#39;s what David <a href="http://www.pluralsight.com/community/blogs/starr/archive/2008/07/02/a_2D00_note_2D00_from_2D00_the_2D00_new_2D00_guy.aspx">had to say</a>.</p>
<p>Welcome!</p>
<p>&nbsp;</p><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=51665" width="1" height="1">]]></content:encoded>
      <pubDate>Wed, 09 Jul 2008 09:01:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/david starr">david starr</category>
      <category domain="http://securityratty.com/tag/david">david</category>
      <category domain="http://securityratty.com/tag/experience">experience</category>
      <category domain="http://securityratty.com/tag/practical experience">practical experience</category>
      <category domain="http://securityratty.com/tag/love">love</category>
      <category domain="http://securityratty.com/tag/people">people</category>
      <category domain="http://securityratty.com/tag/software developers">software developers</category>
      <category domain="http://securityratty.com/tag/pluralsight instructor">pluralsight instructor</category>
      <category domain="http://securityratty.com/tag/comfortably practice">comfortably practice</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/07/09/welcome-david-starr.aspx">Welcome, David Starr</source>
    </item>
    <item>
      <title><![CDATA[Enabling hierarchical nant builds]]></title>
      <link>http://securityratty.com/article/6d991aa98c59ab5248eca3f43819fd48</link>
      <guid>http://securityratty.com/article/6d991aa98c59ab5248eca3f43819fd48</guid>
      <description><![CDATA[In a recent post , I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of...]]></description>
      <content:encoded><![CDATA[<p>In a <a href="http://www.pluralsight.com/blogs/keith/archive/2008/01/18/49967.aspx" target="_blank">recent post</a>, I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of that, I wanted to ensure that I could run the build from anywhere in the source tree. We use a typical hierarchical build where each project has a build script that knows how to compile, test, deploy, etc. based on the specified target. Then at the top of the tree, there's a build script that runs all the other ones. That root build script is what gets run automatically by <a href="http://ccnet.thoughtworks.com/" target="_blank">Cruise Control</a>.</p>
<p>My root script defines a bunch of properties, like where the output directories for the overall build live, where the tools live, and so on. And that works fine when I run the build from the root. The properties get defined, all the child scripts are run with &lt;nant/&gt; tasks, and they see those properties. But if I want to drill down into the tree and run one of the build scripts lower down, suddenly there's problems because it depends on properties that are only defined in the root script. I really like being able to run builds from anywhere in the tree for perf - if I'm trying to fix a particular project, I don't necessarily want to wait for unit tests on the entire tree to run in order to see if mine passed.</p>
<p>Craig made a great suggestion. Put the properties into a separate script (we named it properties.nant) and &lt;include/&gt; that script. Then to enable hierarchical builds, we'd create a properties.nant file for each folder in the tree, which would &lt;include/&gt; its parent. That way I could define properties anywhere in the tree, and they would be "inherited" by anything below it.</p>
<p>I took that idea one step further, because I didn't want to maintain a bunch of property scripts with nothing in them but an &lt;include/&gt; for the parent. I wrote an &lt;includefromparent/&gt; nant task that walks up the directory tree looking for the target file. So now I can do this:</p>
<p><tt>&lt;includefromparent buildfile="properties.nant"/&gt;</tt></p>
<p>This worked great! But now I ran into a problem. Many of my properties are defined like so in the root properties.nant file:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${project::get-base-directory()}\artifacts\libraries"/&gt;</tt></p>
<p>Do you see the issue? If I run the build with the root script, everything works fine, because it's the root nant project I'm building, and get-base-directory() refers to the root of the project, where the artifacts folder lives. But if I run from lower in the tree, it's a different project, and get-base-directory() refers to a subfolder, where the artifacts folder definitely should NOT be.</p>
<p>I needed a way to find the root of the project tree. So I build a second really simple nant task:</p>
<p><tt>&lt;findmarkeddir markerfile="filetolookfor.txt" property="root"/&gt;</tt></p>
<p>This task simply looks up the directory hierarchy until it finds the specified marker file, then puts the name of that directory (the "marked" directory) into a designated property (here I've called it root). With that in place, I rewrote my property definitions in terms of the base directory:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${root}\artifacts\libraries"/&gt;</tt></p>
<p>Voilà! I can now run builds from any of my build scripts. They inherit properties hierarchically like you'd expect, and the system is quite easy to maintain. If you'd like to use these tasks, I've included the code for them below (not much code, really). And if you've never written a nant task yourself before, here's <a href="http://blogs.geekdojo.net/rcase/archive/2005/01/06/5971.aspx" target="_blank">the article</a> I used to figure out how it's done (it's super easy). <a href="http://nant.sourceforge.net/release/latest/help/fundamentals/tasks.html#taskloader" target="_blank">Here's</a> what you should read to learn about the various options for deploying your custom task assembly so nant recognizes it.</p>
<p>Enjoy!</p>
<p>Here is <b>FindMarkedDirTask.cs</b></p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using System.IO;

namespace PluralsightNantTasks {

[TaskName("findmarkeddir")]
public class FindMarkedDirTask : Task {

  [TaskAttribute("markerfile", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string MarkerFileName { get; set; }

  [TaskAttribute("property", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string PropertyName { get; set; }

  protected override void ExecuteTask() {
    string searchDir = this.Project.BaseDirectory;
    do {
      if (MarkerFileExistsIn(searchDir)) {
        this.Project.Properties[PropertyName] = searchDir;
        return;
      }
      searchDir = ParentOf(searchDir);
    } while (!IsRootDirectory(searchDir));
  }

  private bool IsRootDirectory(string path) {
    return Path.GetPathRoot(path) == Path.GetFullPath(path);
  }

  private string ParentOf(string directory) {
    return Path.GetFullPath(Path.Combine(directory, ".."));
  }

  private bool MarkerFileExistsIn(string directory) {
    return File.Exists(Path.Combine(directory, MarkerFileName));
  }
}
}
</pre>
<p>And here is <b>IncludeFromParentTask.cs</b> (note I derive from the built-in include task):</p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using System.IO;
using System.Globalization;

namespace PluralsightNantTasks {

[TaskName("includefromparent")]
public class IncludeFromParentTask : IncludeTask {

  protected override void Initialize() {
    string fileName = BuildFileName;
    if (fileName.Contains("/") || fileName.Contains(@"\\"))
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "buildfile attribute must only be a filename"));

    string relativePathToFoundFile = SearchParentDirectory(
      Project.BaseDirectory, fileName, 0);
      
    if (null == relativePathToFoundFile)
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "Couldn't find a file named {0}" +
        " in a parent directory of {1}",
        fileName, Project.BaseDirectory));

    // have to use a relative path here
    // because <include /> task uses
    // Path.Combine(projectDir, BuildFileName)
    // to get the full path
    BuildFileName = relativePathToFoundFile;

    base.Initialize();
  }

  private string SearchParentDirectory(string directory,
                        string fileName, int searchDepth) {
    ++searchDepth;
  
    // see if we've traversed all the way to the root
    string currentPath = Path.GetFullPath(directory);
    if (currentPath == Path.GetPathRoot(currentPath))
      return null;

    // recurse until we find the file
    string parentDir = Path.GetFullPath(
      Path.Combine(currentPath, ".."));
    string path = Path.Combine(parentDir, fileName);
    if (File.Exists(path)) {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i &lt; searchDepth; ++i)
        sb.Append(@"..\");
      return Path.Combine(sb.ToString(), fileName);
    }
    else return SearchParentDirectory(parentDir,
                          fileName, searchDepth);
  }
}
}
</pre><div style="clear:both;"></div><img src="http://pluralsight.com/community/aggbug.aspx?PostID=50388" width="1" height="1">]]></content:encoded>
      <pubDate>Fri, 07 Mar 2008 09:49:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/nant">nant</category>
      <category domain="http://securityratty.com/tag/root script defines">root script defines</category>
      <category domain="http://securityratty.com/tag/script">script</category>
      <category domain="http://securityratty.com/tag/root">root</category>
      <category domain="http://securityratty.com/tag/task">task</category>
      <category domain="http://securityratty.com/tag/simple nant task">simple nant task</category>
      <category domain="http://securityratty.com/tag/root nant project">root nant project</category>
      <category domain="http://securityratty.com/tag/return path">return path</category>
      <category domain="http://securityratty.com/tag/return">return</category>
      <source url="http://pluralsight.com/community/blogs/keith/archive/2008/03/07/50388.aspx">Enabling hierarchical nant builds</source>
    </item>
    <item>
      <title><![CDATA[Enabling hierarchical nant builds]]></title>
      <link>http://securityratty.com/article/83d94a82ba041a457afd0b8abf809cf5</link>
      <guid>http://securityratty.com/article/83d94a82ba041a457afd0b8abf809cf5</guid>
      <description><![CDATA[In a recent post , I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of...]]></description>
      <content:encoded><![CDATA[<p>In a <a href="http://www.pluralsight.com/blogs/keith/archive/2008/01/18/49967.aspx" target="_blank">recent post</a>, I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of that, I wanted to ensure that I could run the build from anywhere in the source tree. We use a typical hierarchical build where each project has a build script that knows how to compile, test, deploy, etc. based on the specified target. Then at the top of the tree, there's a build script that runs all the other ones. That root build script is what gets run automatically by <a href="http://ccnet.thoughtworks.com/" target="_blank">Cruise Control</a>.</p>
<p>My root script defines a bunch of properties, like where the output directories for the overall build live, where the tools live, and so on. And that works fine when I run the build from the root. The properties get defined, all the child scripts are run with &lt;nant/&gt; tasks, and they see those properties. But if I want to drill down into the tree and run one of the build scripts lower down, suddenly there's problems because it depends on properties that are only defined in the root script. I really like being able to run builds from anywhere in the tree for perf - if I'm trying to fix a particular project, I don't necessarily want to wait for unit tests on the entire tree to run in order to see if mine passed.</p>
<p>Craig made a great suggestion. Put the properties into a separate script (we named it properties.nant) and &lt;include/&gt; that script. Then to enable hierarchical builds, we'd create a properties.nant file for each folder in the tree, which would &lt;include/&gt; its parent. That way I could define properties anywhere in the tree, and they would be "inherited" by anything below it.</p>
<p>I took that idea one step further, because I didn't want to maintain a bunch of property scripts with nothing in them but an &lt;include/&gt; for the parent. I wrote an &lt;includefromparent/&gt; nant task that walks up the directory tree looking for the target file. So now I can do this:</p>
<p><tt>&lt;includefromparent buildfile="properties.nant"/&gt;</tt></p>
<p>This worked great! But now I ran into a problem. Many of my properties are defined like so in the root properties.nant file:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${project::get-base-directory()}\artifacts\libraries"/&gt;</tt></p>
<p>Do you see the issue? If I run the build with the root script, everything works fine, because it's the root nant project I'm building, and get-base-directory() refers to the root of the project, where the artifacts folder lives. But if I run from lower in the tree, it's a different project, and get-base-directory() refers to a subfolder, where the artifacts folder definitely should NOT be.</p>
<p>I needed a way to find the root of the project tree. So I build a second really simple nant task:</p>
<p><tt>&lt;findmarkeddir markerfile="filetolookfor.txt" property="root"/&gt;</tt></p>
<p>This task simply looks up the directory hierarchy until it finds the specified marker file, then puts the name of that directory (the "marked" directory) into a designated property (here I've called it root). With that in place, I rewrote my property definitions in terms of the base directory:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${root}\artifacts\libraries"/&gt;</tt></p>
<p>Voilà! I can now run builds from any of my build scripts. They inherit properties hierarchically like you'd expect, and the system is quite easy to maintain. If you'd like to use these tasks, I've included the code for them below (not much code, really). And if you've never written a nant task yourself before, here's <a href="http://blogs.geekdojo.net/rcase/archive/2005/01/06/5971.aspx" target="_blank">the article</a> I used to figure out how it's done (it's super easy). <a href="http://nant.sourceforge.net/release/latest/help/fundamentals/tasks.html#taskloader" target="_blank">Here's</a> what you should read to learn about the various options for deploying your custom task assembly so nant recognizes it.</p>
<p>Enjoy!</p>
<p>Here is <b>FindMarkedDirTask.cs</b></p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using System.IO;

namespace PluralsightNantTasks {

[TaskName("findmarkeddir")]
public class FindMarkedDirTask : Task {

  [TaskAttribute("markerfile", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string MarkerFileName { get; set; }

  [TaskAttribute("property", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string PropertyName { get; set; }

  protected override void ExecuteTask() {
    string searchDir = this.Project.BaseDirectory;
    do {
      if (MarkerFileExistsIn(searchDir)) {
        this.Project.Properties[PropertyName] = searchDir;
        return;
      }
      searchDir = ParentOf(searchDir);
    } while (!IsRootDirectory(searchDir));
  }

  private bool IsRootDirectory(string path) {
    return Path.GetPathRoot(path) == Path.GetFullPath(path);
  }

  private string ParentOf(string directory) {
    return Path.GetFullPath(Path.Combine(directory, ".."));
  }

  private bool MarkerFileExistsIn(string directory) {
    return File.Exists(Path.Combine(directory, MarkerFileName));
  }
}
}
</pre>
<p>And here is <b>IncludeFromParentTask.cs</b> (note I derive from the built-in include task):</p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using System.IO;
using System.Globalization;

namespace PluralsightNantTasks {

[TaskName("includefromparent")]
public class IncludeFromParentTask : IncludeTask {

  protected override void Initialize() {
    string fileName = BuildFileName;
    if (fileName.Contains("/") || fileName.Contains(@"\\"))
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "buildfile attribute must only be a filename"));

    string relativePathToFoundFile = SearchParentDirectory(
      Project.BaseDirectory, fileName, 0);
      
    if (null == relativePathToFoundFile)
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "Couldn't find a file named {0}" +
        " in a parent directory of {1}",
        fileName, Project.BaseDirectory));

    // have to use a relative path here
    // because <include /> task uses
    // Path.Combine(projectDir, BuildFileName)
    // to get the full path
    BuildFileName = relativePathToFoundFile;

    base.Initialize();
  }

  private string SearchParentDirectory(string directory,
                        string fileName, int searchDepth) {
    ++searchDepth;
  
    // see if we've traversed all the way to the root
    string currentPath = Path.GetFullPath(directory);
    if (currentPath == Path.GetPathRoot(currentPath))
      return null;

    // recurse until we find the file
    string parentDir = Path.GetFullPath(
      Path.Combine(currentPath, ".."));
    string path = Path.Combine(parentDir, fileName);
    if (File.Exists(path)) {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i &lt; searchDepth; ++i)
        sb.Append(@"..\");
      return Path.Combine(sb.ToString(), fileName);
    }
    else return SearchParentDirectory(parentDir,
                          fileName, searchDepth);
  }
}
}
</pre><div style="clear:both;"></div><img src="http://www.pluralsight.com/community/aggbug.aspx?PostID=50388" width="1" height="1">]]></content:encoded>
      <pubDate>Fri, 07 Mar 2008 09:49:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/nant">nant</category>
      <category domain="http://securityratty.com/tag/root script defines">root script defines</category>
      <category domain="http://securityratty.com/tag/script">script</category>
      <category domain="http://securityratty.com/tag/root">root</category>
      <category domain="http://securityratty.com/tag/task">task</category>
      <category domain="http://securityratty.com/tag/simple nant task">simple nant task</category>
      <category domain="http://securityratty.com/tag/root nant project">root nant project</category>
      <category domain="http://securityratty.com/tag/return path">return path</category>
      <category domain="http://securityratty.com/tag/return">return</category>
      <source url="http://www.pluralsight.com/community/blogs/keith/archive/2008/03/07/50388.aspx">Enabling hierarchical nant builds</source>
    </item>
    <item>
      <title><![CDATA[Enabling hierarchical nant builds]]></title>
      <link>http://securityratty.com/article/960ca4f58e5dc775718cb2749c340681</link>
      <guid>http://securityratty.com/article/960ca4f58e5dc775718cb2749c340681</guid>
      <description><![CDATA[In a recent post , I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of...]]></description>
      <content:encoded><![CDATA[<p>In a <a href="http://www.pluralsight.com/blogs/keith/archive/2008/01/18/49967.aspx" target="_blank">recent post</a>, I talked about my experience enabling continuous integration for the internal builds here at Pluralsight. I recently worked with Craig to restructure our nant build. As part of that, I wanted to ensure that I could run the build from anywhere in the source tree. We use a typical hierarchical build where each project has a build script that knows how to compile, test, deploy, etc. based on the specified target. Then at the top of the tree, there's a build script that runs all the other ones. That root build script is what gets run automatically by <a href="http://ccnet.thoughtworks.com/" target="_blank">Cruise Control</a>.</p>
<p>My root script defines a bunch of properties, like where the output directories for the overall build live, where the tools live, and so on. And that works fine when I run the build from the root. The properties get defined, all the child scripts are run with &lt;nant/&gt; tasks, and they see those properties. But if I want to drill down into the tree and run one of the build scripts lower down, suddenly there's problems because it depends on properties that are only defined in the root script. I really like being able to run builds from anywhere in the tree for perf - if I'm trying to fix a particular project, I don't necessarily want to wait for unit tests on the entire tree to run in order to see if mine passed.</p>
<p>Craig made a great suggestion. Put the properties into a separate script (we named it properties.nant) and &lt;include/&gt; that script. Then to enable hierarchical builds, we'd create a properties.nant file for each folder in the tree, which would &lt;include/&gt; its parent. That way I could define properties anywhere in the tree, and they would be "inherited" by anything below it.</p>
<p>I took that idea one step further, because I didn't want to maintain a bunch of property scripts with nothing in them but an &lt;include/&gt; for the parent. I wrote an &lt;includefromparent/&gt; nant task that walks up the directory tree looking for the target file. So now I can do this:</p>
<p><tt>&lt;includefromparent buildfile="properties.nant"/&gt;</tt></p>
<p>This worked great! But now I ran into a problem. Many of my properties are defined like so in the root properties.nant file:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${project::get-base-directory()}\artifacts\libraries"/&gt;</tt></p>
<p>Do you see the issue? If I run the build with the root script, everything works fine, because it's the root nant project I'm building, and get-base-directory() refers to the root of the project, where the artifacts folder lives. But if I run from lower in the tree, it's a different project, and get-base-directory() refers to a subfolder, where the artifacts folder definitely should NOT be.</p>
<p>I needed a way to find the root of the project tree. So I build a second really simple nant task:</p>
<p><tt>&lt;findmarkeddir markerfile="filetolookfor.txt" property="root"/&gt;</tt></p>
<p>This task simply looks up the directory hierarchy until it finds the specified marker file, then puts the name of that directory (the "marked" directory) into a designated property (here I've called it root). With that in place, I rewrote my property definitions in terms of the base directory:</p>
<p><tt>&lt;property name="libraryOutputDir" value="${root}\artifacts\libraries"/&gt;</tt></p>
<p>Voilà! I can now run builds from any of my build scripts. They inherit properties hierarchically like you'd expect, and the system is quite easy to maintain. If you'd like to use these tasks, I've included the code for them below (not much code, really). And if you've never written a nant task yourself before, here's <a href="http://blogs.geekdojo.net/rcase/archive/2005/01/06/5971.aspx" target="_blank">the article</a> I used to figure out how it's done (it's super easy). <a href="http://nant.sourceforge.net/release/latest/help/fundamentals/tasks.html#taskloader" target="_blank">Here's</a> what you should read to learn about the various options for deploying your custom task assembly so nant recognizes it.</p>
<p>Enjoy!</p>
<p>Here is <b>FindMarkedDirTask.cs</b></p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using System.IO;

namespace PluralsightNantTasks {

[TaskName("findmarkeddir")]
public class FindMarkedDirTask : Task {

  [TaskAttribute("markerfile", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string MarkerFileName { get; set; }

  [TaskAttribute("property", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public string PropertyName { get; set; }

  protected override void ExecuteTask() {
    string searchDir = this.Project.BaseDirectory;
    do {
      if (MarkerFileExistsIn(searchDir)) {
        this.Project.Properties[PropertyName] = searchDir;
        return;
      }
      searchDir = ParentOf(searchDir);
    } while (!IsRootDirectory(searchDir));
  }

  private bool IsRootDirectory(string path) {
    return Path.GetPathRoot(path) == Path.GetFullPath(path);
  }

  private string ParentOf(string directory) {
    return Path.GetFullPath(Path.Combine(directory, ".."));
  }

  private bool MarkerFileExistsIn(string directory) {
    return File.Exists(Path.Combine(directory, MarkerFileName));
  }
}
}
</pre>
<p>And here is <b>IncludeFromParentTask.cs</b> (note I derive from the built-in include task):</p><pre>using System;
using System.Collections.Generic;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using System.IO;
using System.Globalization;

namespace PluralsightNantTasks {

[TaskName("includefromparent")]
public class IncludeFromParentTask : IncludeTask {

  protected override void Initialize() {
    string fileName = BuildFileName;
    if (fileName.Contains("/") || fileName.Contains(@"\\"))
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "buildfile attribute must only be a filename"));

    string relativePathToFoundFile = SearchParentDirectory(
      Project.BaseDirectory, fileName, 0);
      
    if (null == relativePathToFoundFile)
      throw new BuildException(string.Format(
        CultureInfo.CurrentCulture,
        "Couldn't find a file named {0}" +
        " in a parent directory of {1}",
        fileName, Project.BaseDirectory));

    // have to use a relative path here
    // because <include /> task uses
    // Path.Combine(projectDir, BuildFileName)
    // to get the full path
    BuildFileName = relativePathToFoundFile;

    base.Initialize();
  }

  private string SearchParentDirectory(string directory,
                        string fileName, int searchDepth) {
    ++searchDepth;
  
    // see if we've traversed all the way to the root
    string currentPath = Path.GetFullPath(directory);
    if (currentPath == Path.GetPathRoot(currentPath))
      return null;

    // recurse until we find the file
    string parentDir = Path.GetFullPath(
      Path.Combine(currentPath, ".."));
    string path = Path.Combine(parentDir, fileName);
    if (File.Exists(path)) {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i &lt; searchDepth; ++i)
        sb.Append(@"..\");
      return Path.Combine(sb.ToString(), fileName);
    }
    else return SearchParentDirectory(parentDir,
                          fileName, searchDepth);
  }
}
}
</pre><img src ="http://pluralsight.com/blogs/keith/aggbug/50388.aspx" width = "1" height = "1" />]]></content:encoded>
      <pubDate>Fri, 07 Mar 2008 02:49:00 +0000</pubDate>
      <category domain="http://securityratty.com/tag/nant">nant</category>
      <category domain="http://securityratty.com/tag/root script defines">root script defines</category>
      <category domain="http://securityratty.com/tag/script">script</category>
      <category domain="http://securityratty.com/tag/root">root</category>
      <category domain="http://securityratty.com/tag/task">task</category>
      <category domain="http://securityratty.com/tag/simple nant task">simple nant task</category>
      <category domain="http://securityratty.com/tag/root nant project">root nant project</category>
      <category domain="http://securityratty.com/tag/return path">return path</category>
      <category domain="http://securityratty.com/tag/return">return</category>
      <source url="http://pluralsight.com/blogs/keith/archive/2008/03/07/50388.aspx">Enabling hierarchical nant builds</source>
    </item>
  </channel>
</rss>
