C# SDI/MDI Application wizardsSupports VC#2002, 2003 and 2005Two new C# project templates
It's a fact of life that C# project templates provided by the VS.NET IDE lack SDI/MDI support. Since SDI/MDI fit many real-world requirements when writing applications, especially those being document-centric, I thought that would be nice and hopefully useful to share this stuff. In the remainder of this article, I'll show :
1. Installing the wizardThe zip files has everything you need to get the wizards installed on either VC#2002 or VC#2003. The package can be broken down as follows :
If you only want to install the stuff and are not very much interested in the details, then extract all the stuff in some folder, and then double-click on The installer does the following job :
The code for the installer is reproduced below : static void Main(string[] args) { // installation sequence // 1- get VC# install dir (regkey) // 2- copy template files in the VC# subfolder for files // 3- copy vsz files in the VC# subfolder for project wizards // 4- add two entries in the VC# vsdir file so that the IDE sees them // check out cmdline (type 2002 for VC#2002, 2003 for VC#2003 none or everything else for VC#2005) bool bIsFor2002 = args.GetLength(0)>0 && args[0] == "2002"; bool bIsFor2003 = args.GetLength(0)>0 && args[0] == "2003"; // 1- regkey RegistryKey k; if (bIsFor2002) k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\7.0", true); else if (bIsFor2003) k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\7.1", true); else k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\8.0", true); String szWizardDir = (String) k.GetValue("InstallDir") + @"..\..\VC#\"; if ( !Directory.Exists(szWizardDir) ) { System.Console.WriteLine("Make sure to install VC# first"); return; } // 2- template files String szSrcDir = AppDomain.CurrentDomain.BaseDirectory; RecurseCopyFiles(szSrcDir + @"\CSharpSDIWiz", szWizardDir + @"VC#Wizards\CSharpSDIWiz"); RecurseCopyFiles(szSrcDir + @"\CSharpMDIWiz", szWizardDir + @"VC#Wizards\CSharpMDIWiz"); // 3- vsz files if (bIsFor2002) // VC# 2002 { File.Copy(szSrcDir + @"\CSharpSDI_VS2002.vsz", szWizardDir + @"CSharpProjects\CSharpSDI.vsz",true); File.Copy(szSrcDir + @"\CSharpMDI_VS2002.vsz", szWizardDir + @"CSharpProjects\CSharpMDI.vsz",true); } else if (bIsFor2003)// VC# 2003 { File.Copy(szSrcDir + @"\CSharpSDI_VS2003.vsz", szWizardDir + @"CSharpProjects\CSharpSDI.vsz",true); File.Copy(szSrcDir + @"\CSharpMDI_VS2003.vsz", szWizardDir + @"CSharpProjects\CSharpMDI.vsz",true); } else // VC# 2005 { File.Copy(szSrcDir + @"\CSharpSDI_VS2005.vsz", szWizardDir + @"CSharpProjects\CSharpSDI.vsz",true); File.Copy(szSrcDir + @"\CSharpMDI_VS2005.vsz", szWizardDir + @"CSharpProjects\CSharpMDI.vsz",true); } // 4- update vsdir file (append 2 entries if they don't exist yet) bool bAlreadyInstalled = false; bool bFileExists = false; if (File.Exists( szWizardDir + @"CSharpProjects\CSharp.vsdir" )) { bFileExists = true; StreamReader sr = new StreamReader( szWizardDir + @"CSharpProjects\CSharp.vsdir" ); String line; while ((line = sr.ReadLine()) != null) { if (line.IndexOf("CSharpSDI.vsz") > -1) { bAlreadyInstalled = true; break; } } sr.Close(); } if (!bFileExists || !bAlreadyInstalled) { using (StreamWriter sw = File.AppendText( szWizardDir + @"CSharpProjects\CSharp.vsdir" )) { sw.WriteLine("CSharpSDI.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|SDI Application|11|" + "Builds a Windows single document interface (SDI) application|" + "{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4554| |SDIApplication"); sw.WriteLine("CSharpMDI.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|MDI Application|12|" + "Builds a Windows multiple document interface (MDI) application|" + "{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4554| |MDIApplication"); sw.Close(); } } } } static void RecurseCopyFiles(String szSrcDir, String szDestDir) { if ( !Directory.Exists(szDestDir) ) Directory.CreateDirectory(szDestDir); string [] fileEntries = Directory.GetFiles(szSrcDir); foreach(string fileName in fileEntries) File.Copy(fileName, szDestDir + fileName.Substring(fileName.LastIndexOf('\\')),true); // recurse string [] subdirectoryEntries = Directory.GetDirectories(szSrcDir + @"\"); foreach(string subdirectory in subdirectoryEntries) { String szSrcNextDir = subdirectory; String szDestNextDir = szDestDir + subdirectory.Substring(subdirectory.LastIndexOf('\\')); RecurseCopyFiles(szSrcNextDir, szDestNextDir); } } The MDI application is derived from a sample provided by MS in the VS.NET CDs, called "Scribble".
2. What is SDI/MDI ?SDI/MDI is short for Simple Document Interface / Multiple Document Interface. Those words are used by VC++ MFC programmers to refer to application wizards that are built within the VC++ IDE and which provide rich and useful application skeleton code. We could also speak about MVC (Model View Controller). In fact, the document-view paradigm is a way to provide functionalities to an application such like the ability to create a new document of a given type (file extension), open an existing document, and save a working document. Each document is rendered, whether on screen or not, using one or more views. The document orders the views to update themselves based on events or other application logic. Each view implements an A SDI application is a one document - one view application. Depending on requirements, one can add views to it. Notepad is a SDI application. A MDI application is a multiple document - multiple view application. Microsoft Word is a MDI application. Sample MDI application
3. Views and documentsBoth SDI and MDI wizards share a common "architecture". The main form acts like a container. One or more documents (instances of the SDIDoc class) hold all the documents life-cycle (open, save, print, is it dirty?, ...) as well as the application logic itself. One or more views for each document (instances of the SDIView class) hold what's draw on screen or being sent to the printer or any other device. In the MDI wizard, the main form not only acts like a container, it is really a container and uses the Winforms MDI properties associated to each class derived from In the SDI wizard, the main form is a container but is mostly a simple form by itself. In both the SDI and MDI wizards, the drawing and printing are delegated to views. Each view knows its "parent" document, and can use the properties from that document to draw itself, etc. By analogy, the serialization (load / save) is delegated to the document. Like in the MFC wizard, a document is being associated a file extension,
4. Inside the application wizardInstalling the application wizard allows to create projects using either of two new project templates. Once installed, the 2 new project templates appear with a clearly identifiable icon and label from the VS.NET project wizard. Both can be used like if you were using the default Windows Application project template. Below are explanations on how to install the application : Below are the steps to add both SDI and MDI application wizards to the list of known C# project wizards. 4.1 Find the VC# installation folder. On my machine, it's 4.2 When you bring up the VS.NET project box, VS.NET lists all existing Those two files must be edited, and the reference to the VSWIZARD 7.0 Wizard=VsWizard.VsWizardEngine Param="WIZARD_NAME = CSharpSDIWiz" Param="WIZARD_UI = FALSE" Param="PROJECT_TYPE = CSPROJ"CSharpMDIWiz.vsz // VC# 2002 VSWIZARD 7.0 Wizard=VsWizard.VsWizardEngine Param="WIZARD_NAME = CSharpMDIWiz" Param="WIZARD_UI = FALSE" Param="PROJECT_TYPE = CSPROJ"
4.3 Filling the 4.4 Filling the In short, here is how the file hierarchy should look like for each WIZ subfolder : CSharpSDIWiz + Scripts + 1033 + default.js (a copy from the one existing in the CSharpEXEWiz folder) + Templates + 1033 + Templates.inf + Form1.cs + Form1.resx + SDIDoc.cs + SDIView.cs + SDIView.resx + New.bmp + Open.bmp + Preview.bmp + Print.bmp + Save.bmp + App.icoWhere Templates.inf constains this : Form1.cs Form1.resx SDIDoc.cs SDIView.cs SDIView.resx New.bmp Open.bmp Preview.bmp Print.bmp Save.bmp App.icoLast note, to make sure namespaces are properly transferred to the target project, we have to change the content of the Form1.cs file from the original stand-alone project :
namespace SDIApp { ...with this (in the Templates \ 1033 folder) :
namespace [!output SAFE_NAMESPACE_NAME] { ... What is just done and finished for
5. Unifying both project templates ?I have to confess that, for trivial reasons, I have brought two wizards up instead of only one. Actually there is no need to have two additional icons in the project box, one for SDI and one for MDI. The idea is that, in the For those interested in HTML UI-based wizards, just check out the undocumented wizard named
History :
Stéphane Rodriguez |
Home Blog |