Including generated files as input for a project

Saturday, 2 April 2011

Sometimes you may have a project in your solution who’s sole purpose is to generate files that need to be referenced by a second project. If the files are fixed, then it’s just a matter of adding a link to them in the second project, but what about when you don’t know at build time how many files will be generated?

It turns out that MSBuild can handle this situation. For example, given the following solution:

image

Note that we’ve added a reference for the Generate project to the IncludeProjectOutputs project. We’ve also set ‘Copy Local’ to false as the only reason for the reference is to enforce building the projects in the right order (we don’t actually refer to the Generate assembly within the IncludeProjectOutputs code).

So that the executable output from the generator project is run, add the following to the generator project’s project file:

<Target Name="AfterBuild">
  <Message Text="After build execute $(TargetPath)" Importance="high" />
  <Exec Command="$(TargetPath)" />
</Target>

In the project that should be including the generated files, add the following:

<Target Name="IncludeGenerated" BeforeTargets="ResolveAssemblyReferences">
    <ItemGroup>
        <Content Include="$(ProjectDir)..\Generate\*.txt" >
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
    </ItemGroup>
</Target>

In this instance, we’re including all .txt files from the ..\Generate folder. Because we define the <Content /> item, they’re added as content (equivalent to the ‘Content’ build action). I haven’t tested it, but conceivably you could also add to <Compile/> or <References/> too.

The ability to add ItemGroup elements within a Target element was added in .NET 3.5. The advantage of this is that the filenames matching the pattern of the Include attribute are evaluated in the execution phase. This contrasts with normal ItemGroup elements, which are assigned values during the evaluation phase of the build. See MSBuild Items for more details.

Building this project gives the following result:

Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.431]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 2/04/2011 8:49:43 AM.
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" on node 1 (default targets).
ValidateSolutionConfiguration:
  Building solution configuration "Debug|Mixed Platforms".
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" (1) is building "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (2) on node 1 (default targets).
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (2) is building "C:\dev\Sandbox\IncludeProjectOutputs\Generate\Generate.csproj" (3) on node 1 (default targets).
ResolveAssemblyReferences:
  A TargetFramework profile exclusion list will be generated.
GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files.
CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
_CopyOutOfDateSourceItemsToOutputDirectory:
Skipping target "_CopyOutOfDateSourceItemsToOutputDirectory" because all output files are up-to-date with respect to the input files.
CopyFilesToOutputDirectory:
  Generate -> C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
AfterBuild:
  After build execute C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
  C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
  Generated file
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\Generate\Generate.csproj" (default targets).
ResolveAssemblyReferences:
  A TargetFramework profile exclusion list will be generated.
GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files.
CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
_CopyOutOfDateSourceItemsToOutputDirectory:
  Copying file from "C:\dev\Sandbox\IncludeProjectOutputs\Generate\stuff.txt" to "bin\Debug\stuff.txt".
CopyFilesToOutputDirectory:
  IncludeProjectOutputs -> C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\bin\Debug\IncludeProjectOutputs.exe
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (default targets).
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" (default targets).

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.20

Kids' Camp Out 1983 to KCO 2011

Friday, 25 March 2011

KCO (formerly KUCA Camp Out, formerly Kid's Camp Out) is an annual overnight camping event that has been running in South Australia since 1978. It's also probably the largest event that the Uniting Church runs in Australia, as they've often had more than 2,700 kids, parents and helpers.

Groups of kids aged 7-12 years (with parents & leaders) come from all over the state. As well as camping, there's games, crafts, rides, activities, musicals, dramas, bands, and(sometimes) fireworks, in a safe, well organised setting.

My own involvement dates back to at least 1983, as you can see from this old, silverfish-eaten certificate I came across just the other day:

P1240606

A few years later when I was too old to attend as a kid, I went instead as a teenage helper. Directing traffic (sometimes in the rain), setting up, packing down, all kinds of stuff.

A bit later still, I co-led the teenage helper group for a few years until 2000. This also meant being on the organising committee for the whole event. Believe me, there's a lot of planning that goes into making sure KCO runs smoothly! One of my highlights was that I got to have my own walkie-talkie (my call-sign was 'KUCA Big Dave' if you're interested).

Probably the only year I missed during that period was 1998, which KCO kindly let me have off as it coincided with my wedding day!

While I have many fond memories, I'm quite excited because tomorrow morning I'm getting on a bus with my two oldest children (along with over 100 other kids and parent leaders from our church) and heading off to KCO 2011.

Some things will have changed in the 10 years since I last went (for one thing it's now Nuriootpa instead of West Beach – I'll leave my boardies at home this time), but I know it's going to be a fantastic weekend!

Passed 70-583

Friday, 18 February 2011

I just discovered that I've passed yet another beta exam!

So when I get around to taking the 70-513 exam, I would have then fulfilled the requirements for the certification:

Time to brush up on my WCF!

Aussie Toilets WP7 App

Tuesday, 15 February 2011

Icon for Aussie Toilets appMy second app 'Aussie Toilets' has now been published on the Windows Phone 7 marketplace. It's an app for locating the nearest public toilet, and it's free!

It uses data provided by the Australian Government's National Toilet Map website. It's a useful app to have on your phone, especially if you're out and about and need to find the nearest 'public conveniences'.


'Aussie Toilets' map screenThe app displays a map of your current location (or defaults to Australia if it isn't sure, or you've declined permission for the app to access your phone's GPS).

Once you've zoomed in to a particular location, it will begin loading the database. This may take a few seconds, as the database is quite large.

It then displays up to 5 nearest public toilets.

You can pan around the map, and use the standard 'pinch to zoom' to zoom in closer or further out.

Screengrab-hoursScreengrab-facilitiesScreengrab-addressTapping on a name will take you to a new page with more details about that facility. The data is displaying using the 'pivot' control, so you can swipe left or right to pan to each section.

Specific facilities include male/female toilets, baby change and showers. Other facilities will be added in future versions (e.g. accessible toilets etc).

The opening hours are also displayed. Some locations might be open 24 hours, others (eg. in a shopping centre) only during certain times.

These details vary for each facility and are only as accurate as the data supplied to the database by the organisations responsible for each toilet (e.g. Councils, State government agencies, transport providers, shopping centres, service stations and food outlets). The app's database in the initial release is current as of January 2011.

To install Aussie Toilets on your Windows Phone 7 device, go to http://social.zune.net/redirect?type=phoneApp&id=dcf35aa5-3829-e011-854c-00237de2db9e (Opens in Zune)

Future plans

Footnote

I took quite a while to settle on the name 'Aussie Toilets'. I'd had a few other good suggestions – things like 'Dunny Directions', 'Loo Locator' or 'Aussie Loos'. In the end I decided I'd stick with a nice obvious self-explanatory (if slightly more pedestrian) name.

Sharing code between two WP7 projects

Thursday, 10 February 2011

So you want to publish a free (aka 'lite') as well as paid versions of your Windows Phone 7 app? Ideally you can do this with a minimum of duplication between the two projects. This is how I've approached the problem.

Create second project

Assuming you've got a solution with a working WP7 project (I'll refer to this as 'primary'). Add a new WP7 project to your existing solution (referred to as 'secondary'). In the new secondary project, add a reference to the primary project.

Move resources into resource dictionary

If you have any resources defined in your App.xaml file, you should move them to a separate xaml file and use a ResourceDictionary to reference them.
Here's a cutdown version of my Styles.xaml. This lives in the primary project and has a build action of 'Page'.
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:unsupported="clr-namespace:Microsoft.Phone.Controls.Unsupported">
    <Style x:Key="PerformanceProgressBar" TargetType="ProgressBar">
       ...
    </Style>
</ResourceDictionary>
App.xaml for primary
I've been using Caliburn Micro framework so the bootstrapper property has to be moved inside the nested ResourceDictionary.
<Application x:Class="Primary.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Primary">
    <Application.Resources>
        <ResourceDictionary >
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:AppBootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>
                <ResourceDictionary Source="Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>

    </Application.Resources>
</Application>
App.xaml for secondary
In this file, we need to fully specify the path to Styles.xaml
<Application x:Class="Secondary.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Secondary">
    <Application.Resources>
        <ResourceDictionary >
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:AppBootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>
                <ResourceDictionary Source="/Primary;component/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
In my case, the secondary project has it's own AppBootstrapper class that inherits from the primary project's similarly named AppBootstrapper.
There's more info on using ResourceDictionaries with WP7 in this StackOverflow question.

Make page navigation Uris fully qualified

If you have any NavigationService calls to navigate to other pages in the primary project, they will need to be fully qualified so that they continue to work when called from the secondary project.
_navigationService.Navigate( new Uri( "/Primary;component/SettingsPage.xaml", UriKind.RelativeOrAbsolute ) );

Fully qualify the value of the NavigationPage attribute in the WMAppManifest.xml file

Assuming you want the same page to load at the start in the secondary project, you'll need to change the reference in the NavigationPage attribute in the WMAppManifest.xml file.
<Tasks>
  <DefaultTask Name="_default" NavigationPage="Primary;component/MainPage.xaml" />
</Tasks>
Bear in mind that if you edit the application properties through the Visual Studio project properties interface, this will revert the NavigationPage value back to just "MainPage.xaml", so you'll need to update it again.

Add links to existing files for all 'Content' files and application bar images

imageAny files in your primary project that have a build action of 'Content' will need to be added to the secondary project. Add them as existing items and choose the 'Add as link' option so you just reference the existing file. Update the build action of all of these files to be 'Content'. Common examples of these files would include ApplicationIcon.png and Background.png.
Likewise, if you have any application bar icons, you'll probably need to do the same.

Conclusion

Following these steps produces a nice lean secondary project like this:
image
It's hard to see from the icons (as Mercurial has overlaid green ticks on top), but the only unique files to the project are App.xaml, AppBootstrapper.cs and the contents of the Properties folder. All the others are linked to ones in the Primary project.