Visual Studio Tooling, Resharper, and the Developer Circle of Life

October 14, 2009 2 comments

Visual Studio is pretty good. 2008 came a long way and I really enjoyed jumping to it with some production apps with the beta 2 release. But I also had those same thoughts about VS2005, and to be honest, VS2003 was a god-send after VS2002. I guess the point is that when something is new, such as an IDE or a language, you start to learn all the little tricks, and they’re all great. Productivity is enhanced, you discover new ways of doing things, and you hit this little peak for the year where you really enjoy what you do.

Invariably that peak levels off as the new and interesting becomes the common place. You start looking forward to the NEXT release and the productivity gains it will bring. I call this the ‘Developer Circle of Life’. We are invariably bound to it and cannot escape. Whatever we have now, we want it faster, with new features, and some new technologies to boot.

Its those middle months that suck the most, the longing, wishing you were using V.Next on your current project, and that everything is so hard and slow. So what can you do until that beta 2 ‘go live’ license to improve your developer experience? Where can you get more productivity gains? Well there’s a suite of plugins and tools you can install that work integrated or in parallel with Visual Studio out there. One of the most common is Resharper, and I’d like to share some of its qualities with you.

Resharper

Ok so I used to be that guy that wanted everything vanilla. Default options all the way. It allowed me to move between client sites easily and be completely operational immediately. When I hit a computer with Resharper, I’d immediately know because VS would start closing my brackets and quotations for me automatically; that was the hallmark of R#, and I would immediately disable it. But in the last year I’ve been really focusing on doing WPF work (I’m even launching a new WPF blog – coming soon) and found the VS2008 XAML editor to be loathsome. I mean, it just seems to be notepad with syntax highlighting and a little bit of intellisense – that’s it. Others around me were using R# so I bit the bullet and installed the trial.

Ok I got used to the closing brackets/quotation thing quickly. Even VS will close XML elements and quoted attributes for you. I got over that issue pretty quick and to be honest, I could only find two other griefs with it (I’ll mention them below) – otherwise there wasn’t much else to complain about.

But the benefits? Wow.

There’s so many things that you just keep discovering all the time, but there is definitely a few standouts that I use all the time. Here’s 5 of my favourites:

1. XML Namespacing

In XAML editor, you get a lot of improvements around refactoring but the feature I enjoy the most is the auto-discovery of namespaces. Lets say you have a control that you created and you want to declare it in XAML. In plain old VS (hereto referred to as POVS) you first have to find your namespace with the intellisense in the Window element at the top of your editor, declare the namespace prefix, then come back down to where you want your control, and instantiate it. With R#, you can just declare <myControls:SpecialControl /> and press the R# magic key combo of ALT+ENTER and walla! It discovers where SpecialControl is within your application and inserts the namespace automatically, and even calls it ‘myControls’ for you. This is a very quick operation that makes it easy to get your XAML bashed out faster, but also for the absent minded like me, means I don’t have to remember the namespace for my controls!

2. Convert Property to Backing Fields

You’re probably familiar with code snippets in POVS. In VS2005 I used the ‘prop’ snippet all the time; this created a public property with a private field backing it. However when VS2008 came out, they replaced this snippet with a new one that created an ‘automatic property’ instead. Automatic properties are extremely short and concise already, so its beyond me why the VS team would not leave the original prop declaration as is. R# to the rescue! You can quickly create an automatic property, and then using ALT-ENTER convert it to a property with a backing field. Given I do so much WPF (and love the MVVM pattern), I tend to need properties with backing fields all the time (for my little friend INotifyPropertyChanged). I love this feature. Oh, and it converts back to an automatic property as well if you don’t like it.

3. Go To Implementation

Or CTRL-F12. I also use a lot of IOC which means interfaces hiding my implementations a lot of the time. Also its a services world, and WCF patterns see interfaces for contracts as well. POVS had the F12 ability – go to definition, which bounced your cursor to wherever the item you clicked on was declared. BUT if this was an interface, well you got dumped at the interface. Fear not! R# lets you go to the actual implementation of your interface instead. You can actually count the time this saves in a day in terms of minutes I reckon. Oh and if more than one class implements the interface, it gives you a list to choose from of course!

4. CTRL-T

I’m sure this has a proper name, but I just call it “THE GREEN BUTTON” since it is such an enabler. Basically, need to know where something is? CTRL-T will find it. Its the enhanced search aspect of R# but its killer feature is that you can type in the letters of your class based on its pascal case definition, and it will be returned as a search result. I know that crappy description really does describe it correctly, so I’ll use an example. Lets say you have a class called ‘VolumeIntegrationProvider’ (which I do) – you can press CTRL-T and type “VIP” and it will find that class! So you see what I mean about the pascal case?

5. Initialise Field From Parameter

Another thing about IOC is that you inject a lot of classes into your constructors. Its then a manual task to create a private field within your class to store that parameter, and of course the code in the constructor to assign it:

private IVolumeIntegrationProvider _volumeIntegrationProvider;
public MyClass(IVolumeIntegrationProvider volumeIntegrationProvider)
{
    _volumeIntegrationProvider = volumeIntegrationProvider;
}

This really is time consuming, but with R#, you only need to declare the constructor parameter and ALT-ENTER takes care of the rest! This is another one of those features that saves you minutes a day (hmm, there seems to be a few of them!).

Dislikes

The first is that sometimes the R# intellisense/autocompletion is not fast enough for me. Somehow I just know that a class name is unique after the first 4 characters (perhaps I’ve just typed it 3 times before) and so I’ll bash out the keys really quickly. For example, imagine I wanted to type this:

if (_volumeIntegrationProvider is IVolumeIntegrationProvider) …

So I’d type the following characters: 
i f SPACE ( _ V o CTRL-SPACE SPACE i s SPACE I V o CTRL-SPACE ) ENTER

(CTRL-SPACE is auto-completion, so it should fill in the remainding characters for that class/interface)
Generally I’ll bash out those key commands so quick that Resharper can’t react in time, and the result is a complete mess on my screen. This takes time to mend. Subsequently I’ve become tuned to slowing down on auto-completion.

The only other dislike I have is that R# leaves your VS in a bit of a mess when you uninstall it, the worst part being that your intellisense and other features might actually be disabled. However this is pretty rare and really isn’t such a big deal.

All I can say is that you have to give R# a try. Install the trial and see how you go. I bet that after the trial expires you’ll be forking out the measley few USD it takes to get it going again. =)

Other Tools

As a user of TFS, I quite like the TFS Power Tools. This provides some must-have extensions to the command line configuration of TFS, as well as shell integration for your source control. I also usually install the VS2008 Power Tools which gives you some useful functions like being able to copy-paste your assembly references. I used to think RockScroll was cool, but lately I think it just steals too much screen real-estate and it doesn’t deal with collapsed code very well.

Its not very 3rd party but when it comes to dealing with the designer aspect of XAML (building UI that looks good) I actually prefer to use Expression Blend, and I’ve even taken the leap into Expression Design lately to build some of my assets and icons. I’m really enjoying those products but am also suffering some of those random crashes people have been complaining about. Even still, Blend 3 is awesome and sometimes I just sit in there and bash out ideas for fun.

Categories: Uncategorized

WPF ListViewItem – Stretch to take the full width

May 24, 2009 Leave a comment

I’m doing a bit of WPF at the moment for a company in Sydney and also working on a WPF project in my spare time. Tonight I came across this issue and thought I’d document the simple solution in case I run into it again in the future.

I have a ListView that takes up the full width of my window. I’ve edited the ListViewItem template to put a border around the items. The code looked like this:

<ListView BorderThickness="0">
                <ListView.ItemTemplate>
                    <DataTemplate>
                            <Border Margin="10" Padding="8" BorderBrush="Red" Background="GhostWhite" CornerRadius="12">
                                          ……. my content here ….
                            </Border>
                    </DataTemplate>
                </ListView.ItemTemplate>
</ListView>

The problem was that the border was only as big as the content inside it, even though the listview was the width of the window. What I wanted was to show an equal sized border for each item despite the size of the content inside.

The solution in the end was really quite simple: to add the HorizontalContentAlignment="Stretch" attribute to the ListView declaration. Easy!

Categories: WPF

Ada Lovelace Day 2009 – Catherine Eibner

March 24, 2009 Leave a comment

Ada Lovelace Day is an international day of blogging to draw attention to women excelling in technology.”

From: http://findingada.com/

I only heard about this today to be honest. Perhaps that makes me part of the problem, I’m not sure. I don’t want to go into the gory details of equality in the workplace. What I do want to say is that there is definitely a clear lack of female role models in the industry. That’s why this pledge is so important: there are a lot of women out there doing excellent things in the community and that’s why I’m writing this post: my pledge to talk about a woman in the industry that I admire: Catherine Eibner.

Catherine is one of the most community focused people I have ever met, male or female. A single mother who also started her own company, while kicking off the Sydney CRM user group, a podcaster and geek girl,  a regular attendee of Sydney user groups in general and frequent speaker herself, Australia wide.

Catherine was awarded the MVP status from Microsoft, only to have it recalled when she became a Microsoft full-time employee for the DPE group: just two more indicators of how much the community appreciates her involvement.

Whenever I am in Sydney and looking for the local community events, Catherine is always there to show me the way! Thankyou Catherine, and keep up your good work. The world’s a better place for having you.

You can find Catherine here:

I encourage all of you to post about a female in the tech industry you admire and why. If you don’t know one, at least spend the time to think about why that might be.

Categories: Uncategorized

A Fractal Generator in C# for .Net 3.5

February 27, 2009 1 comment

Fractals are interesting creatures. They are the visual representation of complex mathematical functions. Writing a fractal generator was something I always wanted to do, and recently found the time. My initial motivation was to discover if fractals were able to be generated using parallel processing, which they most certainly are, so I set off with a clear goal in mind: a multi-threaded fractal generator. This post is the result of that work.

I’ll start by linking to Snagy.Fractals.zip. This zip file contains 3 projects: a core fractal library, a console app, and a WPF app. The 2 apps use the fractal library to generate fractals in both single and multi-threaded scenarios.

Mandelbrot

One of the most common functions is the set of numbers defined by Benoit Mandelbrot in the Mandelbrot Set. This function relates to the concept of Complex Numbers and the plotting of co-ordinates on the Complex Plane. This particular branch of mathematics is complex and I’m not going to talk about it here (mostly because I still don’t know how it all works!). Suffice to say that the fractals generated from the Mandelbrot set are the most common and well documented. I selected the Mandelbrot algorithm for my fractal generator.

Existing Generators

Before I started this work I thought it more worth while to use an existing generator if one was available. There were some, but one of the key problems I discovered was that they were badly written and difficult to understand. Having said that, some might find the same problems with mine, and if that is the case, please let me know!

Another problem I discovered was that some of these generators were written by mathematicians. This meant that the language and terminology used presumed an understanding of the maths (which I only have part of) and that made the code difficult to read. So my goal was to lose the jargon and stick to programming terms.

Snagy.Fractals

After spending some time on the mathematics, I followed some online guides to programming the fractal, as well as munging in some concepts from some example applications I found. Snagy.Fractals namespace is the end result, and here is an overview of the important classes within.

FractalSettings – Contains just properties representing the required settings for a fractal
MandelbrotBase – An abstract class that provides helpers for settings, timing, and pixel creation
MandelbrotSingleThreaded – A derived class that creates fractals in a single thread
MandelbrotMultiThreaded – A derived class that creates fractals in multiple threads
Fractory – A factory class for generating the derived class based on settings
ColourMap – Static class that creates arrays of colours used in the fractal

How It Works

This won’t be a math lecture so we’ll just cover some simple concepts. First, fractals are generated on a pixel by pixel basis. This makes them excellent candidates for parallelism: 2 pixels can be generated at the same time because there is no relationship between pixels.

The work we do is broken up into ‘Strips’. Imagine we want a fractal that is 900×900 pixels in size. We can break this up into 200×900 strips, with the 5th strip only being 100×900 pixels. These strips allow us to break our work up into logical units. These strips are what get distributed amongst multiple threads.

Scale

Think about a 900×900 fractal image again. If we just run the standard co-ordinates through the functions (x = 1 to 900, y = 1 to 900) then the Mandelbrot fractal is quite small (a couple pixels). This doesn’t let us see anything interesting! So we need to simulate “zoom”. To do this we take a scale modifier and multiple the numbers against that. The best top level zoom starts at scale 0.01. This means we are supplying the algorithm with the numbers x = 0.01 to 9.00 and y = 0.01 to 9.00. But the most amazing art occurs much deeper still!

Offsets

Because Mandelbrot is based on quadratic equations (eg. y = ax^2 + bx + c ) then everything happens around the ‘Origin’. This means we need to introduce negative X and Y values, with the centre of our image being co-ordinate 0:0. To achieve this we need to offset calculations based on how big our image is (Height/2, Width/2) and also based on our zoom level. This moves the fractal to the centre of our screen (in this case, the centre of our bitmap that we are creating).

Centre

When we start zooming in, we don’t actually want to look at the exact centre of the image. If you look at pictures of Mandelbrot, you’ll see the very centre is empty! Nothing exciting happening there, so we actually need to zoom in at specific x/y locations. These locations have to take into account the offsets mentioned above, and of course the scale factor.

Iterations

The colour of any pixel is decided by the number of iterations of the Mandelbrot function that can occur. What we mean by this is that the function : f(n) can be called on itself recursively “i” number of times while satisfying the core equation (see complex quadratic equations for details). So if the deepest we can go at a particular point is f(f(f(f(f(n))))) then this means we reached 5 iterations.

Forget the math though; the MandelbrotBase class will calculate the iteration for you based on the x/y co-ordinate. However this x/y co-ordinate is actually relative to the centre mentioned above, so is in fact quite a small number pair, rather than the actual X/Y of the bitmap we are calculating.

To Infinity – And Beyond!

If you zoom in on an ‘edge’ of a fractal, you will see that you can keep zooming.. forever and ever. Well this isn’t technically true: the app is hardcoded to 1000 iterations max, which means that there are defined edges between the fractal and space. But theoretically.. .there is no such edge!

Snagy.Fractals.Console

All the fractal work is done in the class library, so its really very easy to consume and use in your own application. The console application is a perfect example of this. All the code in this app is around getting command line parameters and turning them into a FractalSettings instance, and then saving the resulting fractal image. Use this application as a guide to using the fractal classes.

Snagy.Fractals.WPF

This is a simple harness that will show you the fractal based on your settings on the left. Enter a scale and centre point, then click create.

I did attempt to put some navigation into it, but its very flaky. There is this tendency for UI events to “queue” while the fractal is being generated. As a result the experience is rather poor, but the framework is there if someone wants to work on it and make it work better.

Summary

This is open to anyone to use. Since its unusual to embed a fractal generator in a commercial app, I’m not too concerned about what you use it for. But if you do republish an app with my code, I’d appreciate a mention and a link through. =)

Thanks

I have to thank Buddhike for his help around the threading. Bud helped me get the locking sorted along with the right threading classes. So thanks Bud! I’d also like to thank Paul Stovell for making me ditch my original Win Forms harness and use WPF instead, which resulted in more work to convert bitmaps, recreate my flashy UX, and fight against dispatchers.

The WPF navigation (pan and zoom) was based on this article on the topic.

Technorati Tags: ,,,

Categories: Uncategorized

TFS Shared Resources in Mesh

January 30, 2009 Leave a comment

I was lucky enough to get an early invite to Live Mesh (I was working at Microsoft at the time). Back then it seemed more like a concept than an implementation there were that many flaws. I tried it for a while, but fell back to folder share and other technologies. Mesh got uninstalled.

To be honest, I hadn’t looked at Mesh since that time. Until today.

It all started as the result of a TFS engagement I had just finished consulting on down at the Gold Coast. I decided to build a TFS Resource Pack for all the common files you typically need on a TFS engagement. For example, VSTS SP1, TFS 08 SP1, SQL Server 2005 SP1, TFS sync tool, TFS migration tool, Branching Guidance document, TF Power Tools, Team System Web Access SP1, the list goes on!

Usually I just download these tools on site at the client but I figured it would be easier to have a portable HDD with all these items (I don’t own such a HDD but was willing to buy one). I ran the list of items in my pack past some internal Readify guys and one of the responses was: ‘Why not mesh?’

Why not indeed! There’s 5Gb worth of space in the cloud that I can use so why not put all these resources in one place where I can always get them? Maybe not the 1Gb ISO’s for service packs, but certainly the rest can go there.

So as of today, there is a folder in mesh with all this information, and I plan on continuing to add to it over time.

Want access? All you need is a Live Mesh account and send me your email: I’ll invite you to the folder so you can get the files as well (read access only if I don’t know you). And then just email me if you think I should add any particular file or application.

Technorati Tags: ,,

Categories: Uncategorized

The Right Administrator Permissions in TFS, WSS, and RS

January 24, 2009 49 comments

I tend to forget the right permissions to set for TFS 2008 and its various components. And not all the various permissions are readily searchable on the net. So this quick blog entry is just a reminder for myself.

Create Projects in TFS

To create a project in TFS you need the relevant TFS permission ‘Create new projects’ which is defined at the server level. By default, the ‘[SERVER] Team Foundation Administrators’ group has this permission, but being a member of this group is just not enough. When you create a new team project, TFS also creates a new site in Windows Sharepoint Services (WSS), and a new folder in Reporting Services (RS). So it only makes sense that the user should have permissions to be able to perform both those operations as well.

Reporting Services is pretty easy. With your admin account that installed TFS (TFSSetup perhaps?) go to your Reporting Services web folder, typically it is something like this: http://tfsrtm08/Reports. You will be able to see the sub folders for each of your projects, but we need to set permissions at this root level. Click on the ‘Properties’ tab and click ‘New Role Assignment’. Specify your user account (or AD group) and tick all the boxes. Finally click OK and you are done. RS permissions are inherited so the user will get access to all sub folders, now and in the future.

WSS is a bit more interesting. Adding a user to the root site and giving them ‘Full Control’ in fact does nothing. It is not like RS where the permissions are inherited. Full Control on the root site means ONLY the root site, not sub sites, and not the ability to create new sub sites. What you need is the ‘Site Collection Administrators’ permission for the root site instead. Find it by going ‘Site Actions –> Site Settings –> Site Collection Administrators’. Add your user here and they will be able to create sub sites. However it is worth noting that this does not entitle your user to full control over all sites that are created: it will only give them full access to the ones that THEY create. If someone else creates one, this user will NOT have access. We’ll deal with this below.

Full Admin Privileges

One of the things I find is that TFS management (including permissions) falls to the hands of the infrastructure team. And infrastructure guys like to manage permissions through Active Directory. They like to have a ‘TFS Admins’ group in AD and for anyone to have full access, they want to be able to just add them to that group and not have to think about it any further. It makes sense somewhat because this is how a lot of their user maintenance occurs in their day-to-day jobs.

As mentioned earlier, just because you have Site Collection Administrator privileges does not mean you have full control over all sub sites. If you create a new site, you will be given admin access to that site. But if someone else creates one, then you will NOT have access. Site Collection Admin only lets you create sites, nothing more. So normally you have to add users to a site after it is created. But sometimes there is a lead developer or sys admin who should have full access over all WSS sites, regardless of who created them. This permission is a little harder to find.

To fix this, you need to access WSS Central Administration, which is typically something like http://tfsrtm08:17012/. You will need to do this with your TFSSetup account (or equivalent) since it will be the only account out of the box that has WSS Central Admin privileges. Go to: ‘Application Management –> Policy for Web Application’ and click ‘Add Users’. On the first screen, ensure Zones is set to ‘All Zones’ and click Next. Then under ‘Choose Users’ add your user account to the box and click the little ‘tick’ icon below to ‘check names’. Check the box for ‘Full Control’ and click ‘Finish’.

This gives your user full access over the whole application, which means all future sub sites that may get created.

Summary

I’m not really sure what best practice is, but from my experience it is often best that only the TFSSetup account has master access to everything. Creating a new team project is not something that should happen every day. It represents something big, a new era of development. Its something that will have a lifecycle, iterations, management and buy-in from stakeholders inside and outside your development team. It makes sense that there should be one person responsible for the project and relevant RS and WSS site. This should be the person who gives out additional privileges for developers and managers to those additional components.

Still, the customer is always right. =)

Technorati Tags: ,,,,

Categories: Uncategorized

CreateFoldersItem Custom Task for MSBuild

January 20, 2009 7 comments

Last year I did a fair amount of MSBuild stuff, and really enjoyed it. And when I recently wrote an MSBuild task to clean up TFS source bindings in Visual Studio solutions and projects, it rekindled my interest and reminded me how much I like MSBuild.

That’s why today I decided to write another task to add to my library. I wanted to be able to get an item group containing folders, in particular the last name token each directory. For example, for the path “C:\Program Files\BitLocker\en-US\” I wanted to get the “en-US”.

I did a quick look to see if there was a way to do it with the existing ‘CreateItem’ task, or if anyone else had tried to do this and had a solution. But to be honest I didn’t look TOO hard because I really wanted to tackle this problem myself!

Enter: Snagy.Tasks.CreateFoldersItem

I’m actually writing this post while I work on the task. Think of it as a step by step account, and I’ll be writing in a present tense, so forgive me if it sounds daft. But if you’ve never created a custom task for MSBuild before, I’ll cover off all the basics.

First, I created a class library project in C# (actually I already had one from the last task I wrote, so I am just adding to that). Up front you’ll want to add some assemblies to your references list:

Microsoft.Build.Utilities
Microsoft.Build.Framework

Next, add a new class, call it ‘CreateFoldersItem’. At the top, add using statements for the 2 above namespaces, and add a using statement for System.IO while you are at it. Now, make your class inherit from ‘Task’ which is in the Microsoft.Build.Utilities namespace. You can also choose to implement ITask instead, but then you need to provide a little more information in your class. Its easier just to inherit from Task instead. One final thing you need to do to make it build is override the ‘Execute’ method. Do this now but don’t provide any implementation yet other than to ‘return true’. Build your task to make sure its all hunky dory.

Next we want to provide attributes on our task that people can use to set information to be used by the task. I was thinking of a syntax something like this:

<CreateFoldersItem Include=”C:\Projects;C:\Program Files”                    
                              Exclude=”C:\Projects\MSBuild\;C:\Projects\Lisp”
                              Recursive=”true”>
     <Output ItemName=”AllFolders” TaskParameter=”Include” />
</CreateFoldersItem>

This would take semi-colon delimited folders as input and provide a single ItemGroup of all sub-folders. If Recursive is false, only look 1 level deep, otherwise hit the bottom. The Exclude is a little tricky but I think just a semi-colon delimited list of folder names will suffice. Also I’ve deliberately used a semi-colon delimited list for Include and Exclude so that we can use ItemGroups as inputs in both those cases. That way you could use this task to create an include list and an exclude list, and then create a third list which is a delta of the two.

Ok so back to our class. We need to define 3 properties here. For my task, only the ‘Include’ attribute is mandatory. Exclude will be empty by default, and Recursive will be false by default. We can easily create the properties with C#3.0 syntax:

[Required] [Output] public ITaskItem[] Include { get; set; }
public bool Recursive { get; set; }
public ITaskItem[] Exclude { get; set; }

The ‘Required’ attribute means the Task cannot be called without mention of that property. And the ‘Output’ attribute means that the property can be used in the ‘TaskParameter’ attribute of the <Output> element. The ITaskItem interface stores individual items for passing around. It is best practise to use an array of these for accepting and returning ItemGroups.

Now we need to focus on our core functionality. We need to iterate through each Include item and add each sub-folder to a list. Since each include item might be a child or parent of another include item, we need to think about duplication. Until now I hadn’t thought if I want my returned ItemGroup to include duplicates. This could easily be configurable for the user by adding a ‘bool Distinct’ property but I’ll just assume that we don’t want duplicates to make it easy.

One of the things I want to be able to do is get some metadata out of my returned folders, specifically the last location in the folder path token (the ‘en-us’ in the example earlier). I’ll call this the folder ‘Name’ (since this is the convention used by the DirecoryInfo class in System.IO). This lets us get just the folder name using metadata like this:

<Message Text=”Folder name = %(AllFolders.Name)” />

Let’s start by creating a simple method that takes the string path of a folder and returns an ITaskItem which includes the above described metadata:

private TaskItem CreateTaskItemFromFolder(string folder)
{
    DirectoryInfo di = new DirectoryInfo(folder);
    Hashtable metadata = new Hashtable();
    metadata.Add("Name", di.Name);
    return new TaskItem(folder, metadata);
}

So what’s happening here? Well we use the DirectoryInfo class to provide us the last token in the directory path, via the ‘Name’ property (ie. di.Name). Metadata needs to be added via a non-generic implementation of IDictionary, in this case I used a System.Collections.Hashtable. The best implementation of an ITaskItem is the TaskItem class (surprised?) and its constructor lets us provide the folder path and the metadata as parameters.

Now we can use this method to create a TaskItem for each folder that we find. We need a method that will accept a base folder as a parameter, and return a List of TaskItems based on all child folders (with consideration to the ‘Recursive’ option). Here’s a method that does just that:

        public IList<TaskItem> GetChildFolders(string folder, bool recursive)
        {
            SearchOption searchOption = (recursive ?
                                         SearchOption.AllDirectories :
                                         SearchOption.TopDirectoryOnly);
            string[] subfolders = Directory.GetDirectories(folder, "*", searchOption);           
            IEnumerable<TaskItem> children =
                  subfolders.Select(s => CreateTaskItemFromFolder(s));
            return children.ToList();
        }

We utilise the existing ‘GetDirectories’ static method of the System.IO.Directory class. This method has an overload that accepts the ‘SearchOption’ enum which pretty much means recursive or not. Note that when specifying SearchOption.AllDirectories, the search will recursively search your whole tree from that point, even following folder shortcuts. This means if you have a shortcut to a higher level folder, you could potentially get stuck in an infinite loop. So be careful: if this task is called with Recursive=true, then ensure its not called on a folder that nests to itself via shortcuts.

Finally with a little bit of lambda magic, we create a task item from each folder returned by GetDirectories. That’s all the hard work done, we just need to bring it together in our Execute method and overwrite the ‘Include’ folder with the new results. Here’s what the first bit of code in our Execute method looks like:

IList<TaskItem> results = new List<TaskItem>();

          foreach (ITaskItem item in Include)
          {               
                IList<TaskItem> children = GetChildFolders(item.ItemSpec, Recursive);
                results = results.Union(children).ToList();
          }

Essentially the above code just creates one big list of task items by unioning the result of each folder call. After the loop completes we have all the included subfolders in one list. We now need to remove any exclude folders from that list. The next bit of code looks like this:

            foreach (ITaskItem item in Exclude)
            {
                 results = results.Where(x => x.ItemSpec.ToLower() != item.ItemSpec.ToLower()).ToList();
            }

This code is not as difficult as it looks. Once again we use a lambda to specify a condition on which we want to filter results. That condition is based on matching the folder strings from the exclude list against those in the current results list. The output is a new list without the matching items, and we just overwrite Results with this new list.

The last piece of the puzzle is to push our output back into the Include list of task items:

var r = results.Cast<ITaskItem>();
Include = r.ToArray();

Simply put, we need to cast our list of TaskItem to a list of ITaskItem and then push it out as an array.

And that’s it! A couple things to mention though. In the final version of my code there are lots of checks around whether folders exist. Its also worth mentioning that instead of using .Select and .Where methods, you can use standard LINQ syntax instead. I just found the lambda syntax more concise and easier to read.

So what’s the final result? Well you can do funky things like this:

<CreateFoldersItem Include="C:\Projects\Secret"
                              Recursive="true"
                              Exclude="C:\Projects\Secret\NotReallySecret">
             <Output ItemName="SecretFolders" TaskParameter="Include" />
</CreateFoldersItem>
<CreateFoldersItem Include="C:\Projects"
                              Recursive="true"
                              Exclude="@(SecretFolders)">
             <Output ItemName="AllFolders" TaskParameter="Include" />
</CreateFoldersItem>

In the above example, we get an item group for all folders under C:\Projects except for all the secret projects. Note that AllFolders will include C:\Projects\Secret\NotReallySecret\ because it was excluded from the first list!

I hope you find this task useful. You can download the DLL and example usage here.

Categories: Uncategorized