When developing desktop .NET forms projects the toolbox contains a number of components you can drag across to your form and setup via the designer. A component in this case is any class which inherits from System.ComponentModel.Component. Visual Studio generates the appropriate code for you, in just the same way as forms controls except your components sit in a tray below your form.
Over the past few versions of the SDF we have steadily built up a number of components which directly match those available on the desktop, so I decided it would be great to offer exactly the same designer experience.
There are two key points to design time controls, firstly they are compiled against the full .NET framework (since that is what Visual Studio’s form designer uses), secondly they include a number of special attributes to setup the correct behaviour of the component and the properties window.
Setting Up the Build Process
There are two ways to build your designer assembly, either via the command line usually by building a batch file to call the C# compiler with your code files (That’s correct no VB.NET design time controls at this stage) this article was probably the first to describe this process and is still just as relevant today. The other way is to create a desktop Class Library project type and insert your existing code files.
This is the approach I took this time as I’ve used command line build before and fancied a change. I started with a Solution which contains all the affected SDF assemblies – OpenNETCF, OpenNETCF.Drawing, OpenNETCF.Windows.Forms and OpenNETCF.WindowsCE.Forms. OpenNETCF.Drawing exposes no designable types but is used by the two forms assemblies. Within this solution file I created 5 new desktop Class Library projects – OpenNETCF.CF, OpenNETCF.CF.Drawing, OpenNETCF.CF.Windows.Forms and OpenNETCF.CF.WindowsCE.Forms, I then removed these new projects from the solution and moved the project files (.csproj) into the respective folder with their device equivalent (note that device projects have the .csdproj extension). I then opened each of the runtime projects in a text editor and copied all the entries into the design time projects. Finally back in Visual Studio I add these new desktop projects into the same solution. Next in order to allow both sets of files to build side-by-side I create a new configuration for the project called “Designer”. Then in my default “Debug” configuration for the entire solution I set the design time projects (OpenNETCF.CF.*) to use the Designer configuration and all the runtime projects to use the Debug configuration.
Next I go into the project properties for each of the design time libraries and set the output folder to binDesigner and set a conditional compilation constant for “DESIGN”. We have already used this constant for previous versions when adding designer support to the controls so this already makes much of the libraries “designer safe”.
These OpenNETCF libraries include dependencies to each other so OpenNETCF.CF.Drawing requires a reference to OpenNETCF.CF, OpenNETCF.CF.Windows.Forms requires a reference to both these and so on. These references are easily added with the Add Reference dialog and switching to the Projects pane. Next each of the design time projects needs a couple of standard references to the .NETCF designer assemblies:-
Additionally a reference to Microsoft.CF.WindowsCE.Forms was required in OpenNETCF.CF.WindowsCE.Forms since InputPanelEx derives from InputPanel. All of these designer assemblies is found within the Visual Studio installation e.g.
C:Program FilesMicrosoft Visual Studio .NET 2003CompactFrameworkSDKv1.0.5000Windows CEDesigner
The advantage of going through all these hoops is that both the design time and runtime projects point to exactly the same source code and you can build both assemblies from within your visual studio solution.
For each component or control which is exposed to the toolbox an image is required. This should be a 16×16 bitmap with the name of the control including full namespace e.g.
This is added to the design time project as an Embedded Resource in the root of the project (otherwise folder names are added to the resource name). We could try building the solution now, and will probably come across errors.
A quick way to simplify the process is to remove any code files which are not required by any of your designable components, alternatively you could exclude entire files or code blocks from the design time build process by encapsulating them in if blocks using the compilation constant we setup e.g.
public class NativeStuffWeDontNeedAtDesignTime
Concentrating on our components you can actually be quite ruthless with this conditional compilation as long as you expose the public properties which will be displayed in the properties window in the designer. Once you have successfully built the whole solution you’ll have a full set of runtime and design time assemblies, however they are not ready just yet as with all new builds they need decorating before we can move in…