This article is the second part in the series: Building a Web Message Board using Visual Studio 2008. The first part in the series can be accessed here:
This article demonstrates the following features of Visual Studio 2008:-
- Office 2007 Document Templates - Visual Studio 2008 Tools for Office allows you to associate an assembly with an Office 2007 document or a document template. The assembly can control or access various portions of the document and add customized behavior. For example, in this article, we will see how we can extract subject and message text from specific regions in a Word document and post to a message board.
Let's get started by looking at what we build in the article, and then we can see the steps involved in building it.
In this article, we will be building a Microsoft Word document template. Users can create Word documents using this template. The Word document, so created, will have place holders where the user can type in a message subject and a message text. Once the user is done with typing the message, he can click on a ribbon button labeled Post Message. This causes the message to be posted on the MessageBoard website. The following screenshot demonstrates all the components:
The Post Message ribbon button is only available for documents developed using the custom document template. Any other Word document will appear in the normal fashion, i.e., it will not have the Post Message button. Before proceeding, let's look at the reasons why we are going through the trouble of building a custom Word document template.
It has happened to me many times that I prepare an elaborate post for a message board, such as that in the CodeProject website or the www.asp.net website, and I accidentally close the browser, or get disconnected from the internet, or accidentally navigate to a different site. The elaborate message I prepared gets lost, and I have to either re-type the entire thing or not post the particular message. So, the solution I adopted (and which many other people have adopted) is that I type the message separately in Word or Notepad and then copy and paste the message text to the appropriate form fields in the message posting page. Then I submit the form to the website. The advantage of using Word is that I can save the message offline and also get all the good features such as spelling and grammar correction. The code presented in this article provides the users with a custom document template that they can use to post a message to the MessageBoard website developed in the previous article. The users can directly post the message from Word, and they don't have to cut and paste the message contents to form fields in a web page.
Microsoft Word 2007 also has a built-in document template for blog posts. The blog post document template can post to any site that implements the meta-blog API. For the message board, one solution is that we can implement the meta-blog API and allow users to directly use the blog post word template. We will do so in one of the later articles. In this article, I want to keep things simple and also demonstrate how to build custom document templates.
Before we get started with actual code, let's look at the different types of Microsoft Office Word solutions we can develop in Visual Studio 2008.
Microsoft Office Word is a highly customizable application. VBA (Visual Basic for Applications) was available to Office Developers for a long time. VBA allowed developers to automate certain tasks in Microsoft Word such as formatting ranges, customized search and replace etc. The VBA code was saved with Microsoft Word documents or the templates. VBA macros only worked for a particular document or a template. For application level customization, Word has support for Add-Ins. Word Add-ins can be COM based, hence can be developed in Visual C++, Visual Basic 6.0, or even Managed code.
Visual Studio Tools for Office (VSTO) provides an easier, better, and secured way to create Word solutions in managed code. The VSTO runtime shields most of the COM details, and provides a more .NETish way to develop Word solutions. Also, VSTO solutions run in a separate app-domain, and therefore they provide a good degree of isolation among different solutions. VSTO has support for the following:
- Application-level Add-Ins - You can use VSTO to develop Word add-ins. These add-ins are not associated with a particular document, and can be used to provide generic services such as document sharing and collaboration and source control.
- Document-level Customizations - VSTO can also be used to customize a Word document. VSTO allows you to develop a .NET assembly and associate it with a Word document. The .NET assembly can be used to add custom behavior to the Word document.
- Document-template-level Customizations - If you want to customize a class of documents (documents having common formats, behavior, or contents), you can customize a Word document template using VSTO by associating a managed assembly with the template. The customization is automatically available to all the documents created from the template.
Now that we have seen different solutions that can be created using VSTO, let's select the right solution for the message board application.
For the message board application, we can theoretically use any of the three types of solutions supported by VSTO. We can build an add-in that allows documents to be posted to the message board. However, the problem with an add-in is that it will be available for all the documents. The format of the document we want to use to post to the message board is mostly fixed: it has an area where the user can type the subject and an area where the user can type in the message text. Thus, it does not make much sense to develop an application level add-in. We can either develop a customized Word document or a document template. The customized document template makes more sense for the following reason:
- Users can post different messages to the message board. The structure and format of the document will be the same but the contents of each message will be different. The best way to enforce the structure and format is to use a template.
- Users will mostly start off with an empty document with place holders, and type the message. It does not make any sense to have any concrete content in a document, which is another point in favor of using a document template.
Now that we have decided to use a document template, let's get our hands dirty with some coding and jump right into development.
In this article, I will be working with Word 2007. The procedure for developing a Word 2003 template will differ slightly. The main difference is that Word 2003 does not use ribbons. Depending on the requirements in a real project, you may have to support both Word 2003 and Word 2007. If you are interested in learning how to develop for multiple versions of Word, please leave a comment.
To start, download the code from Part I of the article, by clicking on this link, and follow the installation instructions to install the back-end database.
Next, extract the zip file and open the MessageBoard.sln file in Visual Studio. Right click on the solution, and click on Add New project. From the project dialog box, select:
This will cause the VSTO Wizard to appear:
Type MessageBoard as the name of the document (as shown), and click OK. This will cause Word to open within the Visual Studio window, as shown below:
You can drag and drop controls from the tool box to the Word document. Next, we will add controls to the Word document.
You can drag and drop and use most of the Windows Forms controls in a Word document. In this project, however, we will be using a special type of controls called Word controls:
These controls are also called content controls. Content controls can be used to develop form like input within a Word document. Content controls can be used to designate different areas in the Word document for a specific purpose. For example, in the message board document template, we want to designate an area for entering the message subject and the message text. The control we will use is the
PlainTextContentControl, as the name suggests, can only contain plain text, sort of like the Windows Forms
TextBox control. In our message board website, we don't have support for rich text content yet (we will be adding it in a later part in the series), so it makes sense to use the plain text control. We will drag and drop two plain text controls, one for the subject and one for the message text. Just like Windows Forms controls, we can provide a name to each of the content controls. In our example, we will name them
. There are some other properties of the controls that are useful to us:
|Represents the text of the control. We will set the value of the property to empty at design time. This property will be used at runtime to access the subject and the message text.|
|Represents an instructional text that appears to the user when the control has no text. This text appears in a slightly grayed color:
|The title appears on top of the control, and provides a quick clue on what a control does:
Here is how the properties are set for the
We also format the
subject control as
Here is how the properties look like for the
Now that we have designated areas in the document for subject and text, we need to provide a means to the end user by which he can post the text entered in the content controls to the message board. In Office 2007, a user invokes commands using the Ribbon. In the next section, we will add controls to the Ribbon so that the user can post messages.
Prior to Visual Studio 2008, adding controls to the Ribbon was done by hand editing XML files. Visual Studio 2008 includes a graphical designer for designing Ribbon controls.
Also, note that adding controls to the Ribbon for a document template project works only for Microsoft Word. It does not work for Excel. The reason behind this is that Word is not an MDI application as Excel. Word has multiple top level documents, and each of the documents have their own Ribbon. Excel, however, has a single Ribbon for all the documents.
To add a Ribbon, right click on the project and select Add Item. Select Ribbon from the list of items, as shown below:
The Ribbon appears in the designer as shown below:
The Ribbon tab consists of Ribbon groups, and each Ribbon group consists of Ribbon controls, as shown. Microsoft Word has predefined Ribbon tabs such as Home, Insert, Page Layout, Add-ins etc. The Ribbon created using the designer in the above screenshot will merge with the existing controls in the pre-existing Ribbon tab named Add-Ins. However, in our message board sample, we want the control to appear in the Home tab. To do so, we need to modify the properties of the tab. Click on the ribbon tab in the designer, and modify the properties as shown below:
We changed the
OfficeId property to
TabHome. This will make all controls and groups placed inside the tab to be merged with the Home ribbon tab. The question here is how did we get the
OfficeId for the home tab. An Excel sheet available at the Microsoft Office website has a list of IDs of all the standard Ribbon controls for all the Office applications. You can use the Excel sheet to find the IDs of the standard Ribbon tabs, groups, and controls. You can download the Excel sheets for all Office applications, from the following link:
2007 Office System Document: List of Control IDs.
Now, if you run the application, the ribbon group will correctly appear in the Home tab; it will, however, appear at the extreme right. In order to make it appear in the left, we have to modify the properties of the group as shown:
The important property to note here is the
PositionType property. We set it to
BeforeOfficeId to indicate that the group should appear before a standard Office group. We set the
OfficeId property to
GroupClipboard to place it before the clipboard group. Again, the ID of the clipboard group was obtained using the Excel sheet mentioned above.
Now, we have the ribbon group appearing in the correct tab at the correct position. Next, we need to add a button to the group.
You can add a button to the ribbon by dragging and dropping a
Button control from the Office 2007 Ribbon Controls tab in the toolbox:
A button in the ribbon has text, an ID, and an icon image. You can either specify a custom icon image for a button, or use some standard images which ship with Office. The list of standard office image IDs is available in an Excel worksheet available here.
In the case of the message board, I use the same icon that the default blog template uses for posting a message. The icon ID is
BlogPublish. Finally, here are the properties of the newly added button:
Now that we have a button, we can add code to handle its
Click event. Double clicking the button automatically generates a handler. This handler is in the Ribbon.cs file. We add code as follows:
private void post_Click(object sender, RibbonControlEventArgs e)
Globals.ThisDocument gives the reference to the Word document object that is associated with the assembly by the code. VSTO loads all assemblies in a separate AppDomain and also does a shadow copy, so even if there are multiple instances of the template in a single Microsoft Word process (winword.exe),
Globals.ThisDcoument will still give the correct document reference. The static variables are per AppDomain. Finally, we add a method called
PostMessage with the following code:
internal void PostMessage()
We first validate the subject and the text to make sure that they are not empty. If the validation is successful, we call a method called
AuthenticateUser which authenticates the user, and finally, we invoke the web service that posts the message, using the
InvokeWebService method. We will get into the details of
InvokeWebService a little later. Here is how the
ValidateSubjectAndText method looks like:
private bool ValidateSubjectAndText()
We have not yet added the actual code to post the message to the message board. Before we do that, we need to expose the message board API as a web service. This web service can then be used by the document template to post the message. Now, let's move on to the server side.
We need to add a web service that can be used by the document template project to add messages to the message board. To add a message to the message board, we use the
AddMessage method of the
public static void AddMessage(string subject, string text)
The details of the method are in the previous article in the series. We have to expose a web service that, in turn, uses this method to add the message to the message board. The first question is what kind of web service should we create? We have two options: ASP.NET (asmx) web services or WCF Web services. In the next section, we will decide which one to use.
Prior to WCF, ASP.NET Web services (asmx) was the common way web services were built in .NET. ASP.NET Web Services are not as rich as WCF. For instance, WCF has support for most of the WS Specs. The neat thing about WCF is that you can get rich web service features just by changing the configuration file. Although we will not be using a lot of WCF features in this article, it still makes sense to expose the message board as a WCF service as it will be easy to to have a rich web service support in the future. In the next section, we will create the WCF service.
Following the conventions of the previous article, we will add the actual code for the WCF service in the MessageBoard.Web project and add the svc file that exposes the web service to the MessageBoard website. The easiest way to create a web service is to use the Add New Item dialog box:
This automatically creates the Service contract -
IMessageBoardService and a class that implements this contract -
MessageBoardService. We modify the wizard generated interface,
IMessageBoardService, as follows:
public interface IMessageBoardService
void AddMessage(string subject, string text);
Next, we need to modify the
MessageBoardService class to implement this interface, as follows:
public class MessageBoardService : IMessageBoardService
public void AddMessage(string subject, string text)
AspNetCompatibilityRequirements which is set to
AspNetCompatibilityRequirementsMode.Allowed. This attribute works in conjunction with the following configuration setting in the configuration file:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
Combined together, these properties ensure that the
HttpContext.Current property returns a valid object when called from within the service code. More about this can be found in this blog post. Why do we need ASP.NET compatibility?
AddMessage calls the
Membership.GetUser method. The
GetUser method internally uses
HttpContext.Current to find the user. It has also to do with how we are authenticating the user; we will be using .NET Client Application Services as opposed to the built-in WCF authentication.
The next step is to add an svc file which will be used to access the WCF service. We create a file named MessageBoard.svc in the MessageBoard website:
<%@ ServiceHost Service="MessageBoard.Web.MessageBoardService" %>
The easiest way to do it is to add a text file to the website and rename the extension to svc. We are not fully done with the web service yet. We need to add configuration settings to the web.config file.
WCF services need to be configured before they can be used. You can either use the WCF Service Configuration Editor, or hand edit the web.config file. Either way, you need to add the following settings to the configuration file:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
system.servicemodel section may already be present in the configuration file. In that case, you need to merge the above configuration settings with the already existing contents in the web.config file.
We are exposing the service using
BasicHttpBinding, we are not using the more secure
WsHttpBinding to keep things simple. Remember, though, that we can always opt to use another binding(s) just by changing the configuration settings. Now, we have the web service configured correctly. You can test this by viewing the MessageBoard.svc file in the browser.
Now, let's go back to the document template project and see how we can invoke the web service.
In order for us to invoke the web service from the client project, we need to add a reference to the web service. This can be done by right clicking on the MessageBoard.Word project and selecting Add Service Reference. In the Add Service Reference dialog box that shows up, click on the Advanced button on the lower left corner. In the dialog box that pops up, click the Add Web Reference button. This will bring up the Add Web Reference dialog box. In the resulting dialog box, click on web services in the current solution and select the MessageBoardService. Once you select the MessageBoardService, the proxy class to invoke the service is automatically created for you.
Why is adding a Web Reference so complex?
What used to be a single step in Visual Studio 2005 is now three steps in Visual Studio 2008. The reason behind this is that Visual Studio 2008 wants you to use WCF for web service client code. We cannot use WCF client code as it does not provide as much control over the web request as the older web service proxy class did. In this article, we need some control over specifying the cookies when invoking the web service. This is not possible if we are using the WCF based client code, hence we have to use the older web service proxy.
Now, we are ready to invoke the web service.
We added a place holder method named
InvokeWebservice in the ThisDocument.cs file to invoke the web service. This can now be coded as follows:
private void InvokeWebService()
using (MessageBoardService service = new MessageBoardService())
catch (WebException ex)
catch (SoapException ex)
InvokeWebService call in the
internal void PostMessage()
To test launch the MessageBoard.Web project, enter some subject and text, and click on the Post Message button in the ribbon. If the message is posted successfully, you will get a message box indicating that the message was posted successfully. You can also view the message board website in the browser to make sure that the message appears. If everything worked correctly, you will notice that new messages are posted anonymously. That is the case because we have not yet authenticated the users. In the next section, we will use ASP.NET client application services to authenticate the user.
If you have been using WCF, you might be wondering why we are not using the WCF authentication in this article. WCF security is quite powerful, and it also integrates with ASP.NET membership. However, WCF authentication requires extra configuration work. For example, if you want to use user name/password authentication in WCF, you need to configure a certificate (or a test certificate) in order to make the authentication work. Although such high security may be needed for many projects, I decided to keep things simple and use a light weight alternative: ASP.NET Client Application Services.
What are the ASP.NET Client Application Services? Put simply, ASP.NET Client Application Services allow you to access the membership, profile and roles services on an ASP.NET web server remotely from any .NET application. So, if you have a web server configured with ASP.NET membership, you can use the web server to authenticate a user on a desktop Windows application. This is an extremely lightweight API for authentication. If you recall, the MessageBoard site was configured with ASP.NET membership, and users were authenticated using ASP.NET membership. Now, we need to do the same for the messages posted from Microsoft Word.
To achieve authentication using ASP.NET membership, first, we need to configure the server. Add the following settings to the web.config file:
Next, we need to modify the project properties of the MessageBoard.Web project:
Under the Authentication service location, enter the URL of the MessageBoard website. Also notice the Credentials provider field set to
MessageBoard.Word.LogOnForm. This is the name of a very simple
log-on form developed using Windows Forms, which implements an interface
IClientFormsAuthenticationCredentialsProvider. The code for the form is shown below:
public partial class LogOnForm : Form,
public ClientFormsAuthenticationCredentials GetCredentials()
return (this.ShowDialog() == DialogResult.OK) ?
Here is how the form looks in the designer:
When you authenticate the user using the Client Application Services, this dialog box will automatically be shown when the user name supplied to the authentication API is null or empty and if there are no saved credentials. The client application service automatically saves the user credentials in an offline SQL Server Everywhere database when indicated to do so (Remember Me is checked). The database is automatically created by default in the user's data directory. If you don't intend to save the user authentication data, you can do so by clicking the Advanced button in the Services page in the project properties. Now, let's see the steps involved in authenticating the user. Once the project properties have been set correctly, authenticating the user is as simple as calling the
private bool AuthenticateUser()
Membership.ValidateUser with an empty user name and password. This will cause the
LogOnForm to be shown automatically for the user to provide the credentials. The
ValidateUser method returns
null if the authentication was unsuccessful; in that case, we ask the user whether he wants to post the message anonymously. If yes, we return
true; otherwise (or if the user is authenticated), we return
We are not done yet. We need to make sure that the web service is invoked with the authentication done by the
ValidateUser method. The code for the
InvokeWebService method changes slightly:
using (MessageBoardService service = new MessageBoardService())
ClientFormsIdentity identity =
Thread.CurrentPrincipal.Identity as ClientFormsIdentity;
if (identity != null)
service.CookieContainer = identity.AuthenticationCookies;
catch (WebException ex)
catch (SoapException ex)
If the authentication succeeds, the
Thread's principal is set to the a principal which has an identity of type
ClientFormsIdentity. If the authentication succeeds, the server returns cookies which contain the ASP.NET Forms authentication ticket. In the subsequent calls, the client can just send the cookies to indicate the authentication data to the server. The cookies are available in the
property of the
ClientFormsIdentity object. We set the
CookieContainer property of the web service proxy to the same cookie container so that the web service gets invoked with the right authentication cookies. Thus, the web service call is now authenticated at the server. You can test it out by posting another message and verifying that the user name is correctly set in the newly posted message.
This concludes the second part in the article series. In the next part, we will go back to the website and add some AJAX support.
Please be free to leave comments about the article, especially if you think that the article is lacking. That will help me improve future articles.
- December 28, 2007 - Initially posted.
- December 31, 2007 - Added Series Navigation and Installation instructions.
- December 31, 2007 - Resolved issues with uploaded files