Introduction
For an introduction to the SoapBox.Core
(SBC) framework, read Building an Extensible Application with MEF, WPF, and MVVM.
The demo project that is introduced with the SoapBox framework is a pinball game. It demonstrates a few things that the framework provides, but is missing a few that are integral to many applications. A toolbar, for example.
I did not find the pinball example particularly useful for my own use and started writing code to figure out how various parts of SBC work in a simple example of my own. I figured I would share my results here.
Also, as it's written, the SoapBox framework is a set of projects within solution folders, and SoapBox.Core.Host
is output as an executable. So using SoapBox requires that we include the whole set of SoapBox projects within our local solution and set SBC Host as the startup project. There are several problems with this:
- Including these extra projects is kind of messy (and can increase compile time).
- When SoapBox has an update, updating the SoapBox projects in all of our projects is messy.
- The Express editions of Visual Studio don't support solution folders (they also don't support mixed languages, so can't use the SoapBox C# sources within, say, a Visual Basic project). (As a workaround, you can use SharpDevelop or MonoDevelop (buggy?) to open projects that use solution folders or mixed languages.)
Overview
So for this article, first I will describe how to set up the SoapBox.Core.Host
so the SBC can be used as a set of DLLs. The example project also moves the DLLs into separate folders instead of having them all in the application's base folder.
Second, I will present a simple example solution containing a startup project and a "feature
" project that has some basic things most applications need (toolbar, status bar, document area, etc.).
Modifying SoapBox.Core.Host
First, change the project type from WinExe
to Library
.
Second, rename App.xaml.cs to App.cs and remove App.xaml. The attached source code has the details for the new file. It is mainly a copy of App.xaml.cs, plus some things I added for setting and accessing its catalog.
You may also need to manually remove this line from SoapBox.Core.Host.csproj:
<StartupObject>SoapBox.Core.Host.App</StartupObject>
Now you can build the SoapBox solution and then copy whichever DLLs you want to use to your own project library.
Creating the Startup Project (Example.Host)
This project outputs the executable file that starts the application. Pretty much all it does is provide references to the SBC DLLs and then set up and run the framework. Then the framework does the rest of the work. Here's a step-by-step.
- Start the editor (I am using Visual Studio C# Express 2010).
Create a new project (New > Project > WPF Application), named Example.Host
Save Example.Host
As ...
Name: Example.Host
Location: F:\Projects\Soapbox
SolutionName: Example (Create directory for solution) - In Windows Explorer (not Visual Studio), create a folder (named Lib) in the solution folder and put all of the SoapBox, NLog, and AvalonDock DLLs in it.
- Add references to all of the DLLs in the Lib folder. We need references to all of these with the Visual Studio "Copy Local" property set to
true
, even though Example.Host
doesn't explicitly need some of them, so they get copied to the output folder. (This is one way to do it; there's a number of different ways to get these copied to the output folders.) - Add a reference to
System.ComponentModel.Composition
- Delete App.xaml, MainWindow.xaml, and related .cs files.
- Add Program.cs with a
Main
that creates a new SoapBox.Core.Host.App()
object. - Add catalogs to the
app
object, so SBC knows where to look for them. - Add a call to
Run()
on the app
object. - Change the build Location to ..\bin\Debug and ..\bin\Release (Be *sure* that all projects output to the correct folders or you will be scratching your head when things don't appear in the application when you run it).
- At this point, if you run the project, you should get a plain workbench (bigger than the screenshot, but otherwise the same).
Creating an Example "Feature" Project (Example.HelloWorld)
For each main "feature" of your application, you'll want a separate project. For a more loosely coupled solution, all features would refer to common project, such as the "Contracts
" project that appears in SoapBox examples. For simplicity (and because there's only one feature), all of the contracts are in HelloWorld
(see the CompositionPoints
and ExtensionPoints
files in the source code).
You might also want to set up one of the features (or even the 'host" project above) as the main shell, to provide a "main" toolbar, about box, etc.
The example application demonstrates the following items:
Uri u = new Uri("pack://application:,,,/Example.HelloWorld;component/App/star.png", UriKind.Absolute);
mainWindow.Value.Icon = new BitmapImage(u);
- Application icon. SoapBox examples use a way that the author comments as "hacky", which creates a
Window
class and sets the icon with the VS designer... I implemented a "less hacky" one- or two-liner method that reads the image as an embedded resource. Namely, - SBC
StartupCommands
(Shutdown commands are similar). The startup command in this example is used to display the recipe document and instructions pad when the application starts. - An SBC Pad (the pinball demo uses a pad, as well).
- An SBC Document. SBC Document and Pad XAMLs are based on
ResourceDictionaries
with a DataTemplate
, which are not editable in Visual Studio designer. This example demonstrates referencing a UserControl
so that the Visual Studio designer CAN be used to edit the layout. Not a big advantage for a simple example like this, but great if the layout is more complex. - An SBC
ToolBar
with a ToolBarButton
that is linked via an IExecutableCommand
to perform some action (display a MessageBox
). - An SBC
StatusBar
with several different items in it. - Adding items to the menu (such as the About item).
Here is the most basic step-by-step, as a recipe for creating one of these projects.
- Add a New WPF project, named
Example.HelloWorld
, to the solution. I find this easier than using a library project and then having to add all of the WPF references. - Delete App.xaml, MainWindow.xaml, and associated .cs files.
- In the project properties, change the project type to Class Library.
- Add a reference to
System.ComponentModel.Composition
. - Add a reference to
Soapbox.Core.Contracts
. - Change the build Location to ..\bin\Debug.
- Start adding SBC items by using Export and Import as needed....
Points of Interest
For downloading the SoapBox Core and related projects from its author, visit SoapBox Core. For FAQs and some quick examples, visit ask.soapboxcore.com.
I messed around for a while trying to figure out how to merge the SBC DLLs into a single file. ILMerge
, the traditional method, doesn't work for WPF. Several other methods I tried didn't work and gave me various errors that I think meant MEF couldn't find the correct assemblies or that the same assembly was being loaded in different contexts.
Troubleshooting loosely coupled projects can be tricky. Here's an interesting reference: How to Debug and Diagnose MEF Failures.
History
- 2012-02-12: First edition