What is the best practice for compiling Silverlight and WPF in one project? What is the best practice for compiling Silverlight and WPF in one project? wpf wpf

What is the best practice for compiling Silverlight and WPF in one project?


Update: goes to show that there is almost always an easier way. :-)

The first step is to use conditional compilation to segregate the Silverlight specific code. (I'm assuming that your "default" target with be WPF.)

Secondly, you'll need a build script that will compile the code for each platform, setting the appropriate defines and assembly references.

Take a look at the open-source Caliburn project. It does all this.

Here's an example from Caliburn's ExtensionMethods class.

    public static T GetResource<T>(this FrameworkElement element, object key)        {            DependencyObject currentElement = element;            while (currentElement != null)            {                var frameworkElement = currentElement as FrameworkElement;                if (frameworkElement != null && frameworkElement.Resources.Contains(key))                    return (T)frameworkElement.Resources[key];#if !SILVERLIGHT                currentElement = (LogicalTreeHelper.GetParent(currentElement) ??                    VisualTreeHelper.GetParent(currentElement));#else                currentElement = VisualTreeHelper.GetParent(currentElement);#endif            }            if (Application.Current.Resources.Contains(key))                return (T)Application.Current.Resources[key];            return default(T);        }

If you open Caliburn in VS and compile it, it complies against the standard framework. The references are for .NET 3.5 and WPF, not Silverlight. That is also why the pre-processing directives are "!SILVERLIGHT".

In your build script (Caliburn uses NAnt), you'll have a target that sets the defines for each platform, for example, Caliburn's Silverlight target is:

<target name="config-platform-silverlight20">    <property name="nant.settings.currentframework" value="silverlight-2.0"/>    <property name="build.platform" value="silverlight-2.0"/>    <property name="build.defines" value="${global.build.defines},SILVERLIGHT,SILVERLIGHT_20,NO_WEB,NO_REMOTING,NO_CONVERT,NO_PARTIAL_TRUST,NO_EXCEPTION_SERIALIZATION,NO_SKIP_VISIBILITY,NO_DEBUG_SYMBOLS"/>    <property name="current.path.bin" value="${path.bin}/silverlight-2.0/${build.config}"/>    <property name="current.path.test" value="${path.bin}/silverlight-2.0/tests" />    <property name="current.path.lib" value="${path.lib}/Silverlight" /></target>

Then here is the target that invoke the actual Silverlight build:

<target name="platform-silverlight20" depends="config">    <if test="${framework::exists('silverlight-2.0')}">        <echo message="Building Caliburn ${build.version} for Silverlight v2.0."/>        <call target="config-platform-silverlight20"/>        <copy todir="${current.path.bin}">            <fileset basedir="${current.path.lib}">                <include name="*.dll"/>                <include name="*.xml"/>            </fileset>        </copy>        <call target="core"/>        <call target="messaging"/>        <call target="actions"/>        <call target="commands"/>        <call target="package-platform"/>    </if>    <if test="${not(framework::exists('silverlight-2.0'))}">        <echo message="Silverlight v2.0 is not available. Skipping platform."/>    </if></target>

Finally, here is an example of the "core" target that is responsible for producing the Caliburn.Core.dll:

<target name="core" depends="config, ensure-platform-selected">    <mkdir dir="${current.path.bin}"/>    <csc keyfile="${path.src}/Caliburn.snk" noconfig="true" warnaserror="false" target="library" debug="${build.debug}" optimize="${build.optimize}" define="${build.defines}"     output="${current.path.bin}/Caliburn.Core.dll"     doc="${current.path.bin}/Caliburn.Core.xml">        <sources basedir="${path.src}">            <include name="${build.asminfo}"/>            <include name="Caliburn.Core/**/*.cs"/>        </sources>        <references basedir="${current.path.bin}">            <include name="mscorlib.dll"/>            <include name="System.dll"/>            <include name="System.Core.dll"/>            <!--WPF-->            <include name="PresentationCore.dll"/>            <include name="PresentationFramework.dll"/>            <include name="WindowsBase.dll"/>            <!--Silverlight-->            <include name="System.Windows.dll" />        </references>        <nowarn>            <warning number="1584"/>        </nowarn>    </csc></target>

Notice the way it's referencing the necessary assemblies.

You'll probably need to edit your NAnt.exe.config (if you are using NAnt) to match the correct version of the Silverlight framework. For Silverlight RTW, the framework version will be 2.0.31005.0.


I haven't tried it myself (still trying to find the time to play with Silverlight), but couldn't you have one solution with two projects, one targetting Silverlight and the other targetting .NET 3.5, and add the common class files to each project as Links (right-click the project, Add Existing Item..., Add as Link)?

** Update: See Mark's answer below regarding the Project Linker. I've been using this in my multi-targetted composite application with the PRISM 2.0 CAL and it's a beautiful thing. I don't think this existed in PRISM 1.0?


You should check out "patterns & practices: Composite WPF and Silverlight"

http://www.codeplex.com/CompositeWPF/Wiki/View.aspx?title=Home

It has quick starts with WPF/Silvelight versions of the same app in one solution.Also a "Project Linker" that updates the source of your WPF app when you change Silverlight code (or vice versa) using linking. It can be overridden when you have version specific code.

The examples are still a little rough around the edges but it may give you an idea of how to go about your project.

HTH