I've just completed a Silverlight project and it's time to have a little clean up. I'd like to take my core files and put them into a separate project which I will reference from my main Silverlight app. Some of these classes are compatible with WPF and I would quite like to be able to have Silverlight / WPF code all in one project. My ideal solution would be a single project that allows multiple configurations. So,
Configuration: Silverlight would generate: Company.Controls.Silverlight.dll
Configuration: WPF would generate: Company.Controls.Wpf.dll
Is it possible to have the same source in the same file just seperated via defines?
Has anyone done this before?
Edit: I've created a solution per project, like MyCompany.Windows.Controls, which then contains 2 projects, MyCompany.Windows.Controls & MyCompany.Windows.Controls.Silverlight. Alongside those 2 folders I have a "Shared" folder, which contains files used by both projects. It works well so far :)
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With