Silverlight and WPF – Sharing Library Projects

One thing that I got asked a couple of times down at REMIX UK was the question of;

How come I can’t reference a regular .NET library from a Silverlight project?

Which really comes down to the CLR that’s in Silverlight ( the “CoreCLR” ) not being the same CLR as you see on the desktop or the server.

What do you do about this? If you’ve got some code that you want to build into a library that will compile against both the full .NET Framework and the Silverlight .NET Framework then one thing you might try is to set up two projects that point to the same source code.

That is, you can make a full desktop library;

image

Add your code;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyLibrary
{
  public class MyClass
  {
    public void MyMethod()
    {
    }
  }
}
and then create another Silverlight library project;
 

and then link the source files from one project to the other as in;

image

and then add the source file as a linked item;

image

and you end up with the same source file in 2 projects;

image

What I’d actually prefer is to be able to get one project that would build the same source in both a Silverlight Configuration and a “Desktop” configuration producing 4 outputs;

  • Desktop Debug
  • Desktop Release
  • Silverlight Debug
  • Silverlight Release

I made an attempt at this but I don’t think my attempt really cuts it so if someone has a proper way of doing this with Visual Studio and MSBuild then that’d be interesting to know about although I have a feeling that it’s going to prove to be an unrealistic approach.

Here’s my attempt anyway.

Create a new desktop library project in Visual Studio;

image

Add in to this library a couple of new project configurations – Debug Silverlight  and Release Silverlight;

image

image

image

Reopen the project file as an MSBuild file and attempt to hack settings from a Silverlight project into the MSBuild file. This has me ending up with a rather clunky looking MSBuild file like;

 

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{FE8C29DB-40B4-48EE-9CC0-4A8273E99DCA}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>DualPurposeLibrary</RootNamespace>
    <AssemblyName>DualPurposeLibrary</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup Condition=" '$(Configuration)' != 'Release Silverlight' AND '$(Configuration)' != 'Debug Silverlight' ">
    <Reference Include="System" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.Linq">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data.DataSetExtensions">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Class1.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  
  <!-- BEGIN SILVERLIGHT COPIED SETTINGS -->
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Silverlight|AnyCPU' ">
    <OutputPath>bin\Debug Silverlight\</OutputPath>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <DefineConstants>DEBUG;TRACE;SILVERLIGHT</DefineConstants>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Silverlight|AnyCPU' ">
    <OutputPath>bin\Release Silverlight\</OutputPath>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <DefineConstants>TRACE;SILVERLIGHT</DefineConstants>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup Condition=" '$(Configuration)' == 'Release Silverlight' OR '$(Configuration)' == 'Debug Silverlight' ">
    <Reference Include="System.Windows" />
    <Reference Include="mscorlib" />
    <Reference Include="system" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Net" />
    <Reference Include="System.Windows.Browser" />
  </ItemGroup>

  <Import 
    Condition=" '$(Configuration)' == 'Release Silverlight' OR '$(Configuration)' == 'Debug Silverlight' "
    Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" />
  
  <!-- END SILVERLIGHT COPIED SETTINGS -->
  
  <Import Condition=" '$(Configuration)' != 'Release Silverlight' AND '$(Configuration)' != 'Debug Silverlight' "
          Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  
  
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

and I seriously suspect that this isn’t anything near to being correct at this point.

It does seem to “sort of work” from the point of view of being able to walk up to MSBuild from a command line and say;

  • msbuild dualpurposelibrary.csproj /p:Configuration=”Debug”
  • msbuild dualpurposelibrary.csproj /p:Configuration=”Release”
  • msbuild dualpurposelibrary.csproj /p:Configuration=”Debug Silverlight”
  • msbuild dualpurposelibrary.csproj /p:Configuration=”Release Silverlight”

but loading this project into Visual Studio looks to confuse Visual Studio.

You’ll notice that I ended up putting hacky looking Conditions into my MSBuild file in order to keep MSBuild happy and I suspect that Visual Studio doesn’t apply conditions in the same way – there’s a forum post up here;

http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/a48b5b57-215c-44f9-b750-106efe3ea023/

which highlights some of the differences around conditions and if I build the 4 configurations of this project in Visual Studio then what I look to end up with is 4 non-Silverlight assemblies rather than 2 Silverlight assemblies and 2 non-Silverlight assemblies.

So…I figure for the minute that perhaps the 2 separate projects with the linked files is the way to go but I’m guessing that someone out there has enough MSBuild/Visual Studio Project knowledge to put this together and make it work?