Listing 1: Using Cached Data in a Word VSTO Customization

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;

namespace ExpenseReport
{
  public partial class ThisDocument
  {
    [Cached]
    public string EmpName;
    [Cached]
    public DataSet Expenses;

    private void ThisDocument_Startup(object sender, EventArgs e)
    {
      if (this.NeedsFill("EmpName"))
        EmpName = "Unknown Employee";
      	else
        this.EmployeeName.Text = EmpName;

      if (!this.NeedsFill("Expenses"))
      {
        // We have a cached expenses dataset, iterate over it and
        // put it into a Word table (omitted for brevity)
      }
    }

    private void ThisDocument_Shutdown(object sender, EventArgs e)
    {
    }

    #region VSTO Designer generated code
    private void InternalStartup()
    {
      this.Startup += new System.EventHandler(ThisDocument_Startup);
      this.Shutdown += new System.EventHandler(ThisDocument_Shutdown);
    }
    #endregion
  }
}  
Listing 2: An ASPX Web Form That Edits the Word Data Island on the Server

<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="System.Configuration" %>
<%@ Import Namespace="System.Web.Configuration" %>
<%@ Import Namespace="System.Data"%>
<%@ Import Namespace="System.Data.Common"%>
<%@ Import Namespace="System.Data.OleDb"%>
<%@ Import Namespace="System.IO"%>
<%@ Import Namespace= "Microsoft.VisualStudio.Tools.Applications.Runtime"%>

<script runat=server>

const int Forbidden = 403;

protected void Page_Load(object sender, EventArgs e)
{
  // If the user is not authenticated, then we do not want
  // to give the user any expense report at all.
  if (!User.Identity.IsAuthenticated)
  {
    Response.StatusCode = Forbidden;
    Response.End();
    return;
  }

  // If we do have a username, fetch the user's personal data from the
  // database (or Web service or other data source.)

  DataSet dataset = new DataSet();
  DataTable datatable = dataset.Tables.Add("Expenses");
  OleDbDataAdapter adapter = new OleDbDataAdapter();

  // Authenticated usernames are hard to malform. If there is a
  // chance that a string could be provided by a hostile caller,
  // do not use string concatenation without vetting the string
  // carefully. Better still, avoid SQL injection attacks entirely
  // by using stored procedures.

  adapter.SelectCommand = new OleDbCommand(
    "SELECT [Date], Description, Cost " +
    "FROM Expenses WHERE EmployeeName = \"" + User.Identity.Name + "\"");

  // It's a good idea to store connection strings in the web.config
  // file both for security Ñ they can be encrypted in web.config Ð
  // and for convenience Ñ you can update the config file when the
  // database server changes.

  string connectionString = ConfigurationManager.
    ConnectionStrings["expenses"].ConnectionString;
  adapter.SelectCommand.Connection = 
    new OleDbConnection(connectionString);
  adapter.Fill(datatable);

  // We do not want to modify the file on disk; instead, we'll read it
  // into memory and add the user's information to the inmemory
  // document before we serve it.

  FileStream file = new FileStream(
    @"c:\INetPub\WWWRoot\expenses\ExpenseReport.DOC", 
    FileMode.Open, FileAccess.Read);
  byte[] template;
  try {
    template = new byte[file.Length];
    file.Read(template, 0, (int)file.Length);
  }
  finally {
    file.Close();
  }

  // Finally, we'll create a ServerDocument object to manipulate the
  // in-memory copy. Because it only has a raw array of bytes to work
  // with, it needs to be told whether it is looking at an .XLS,
  // .XLT, .DOC, or .DOT.

  ServerDocument sd = new ServerDocument(template, ".DOC");
  try {
    sd.CachedData.HostItems["ExpenseReport.ThisDocument"].
      CachedData["EmpName"].SerializeDataInstance(User.Identity.Name);
    sd.CachedData.HostItems["ExpenseReport.ThisDocument"].
      CachedData["Expenses"].SerializeDataInstance(dataset);
    sd.Save();

    // "template" still has the original bytes. Get the new bytes.
    template = sd.Document;
  }
  finally {
    sd.Close();
  }
  Response.ClearContent();
  Response.ClearHeaders();
  Response.ContentType = "application/vnd.ms-word";
  Response.OutputStream.Write(template, 0, template.Length);
  Response.Flush();
  Response.Close();
}
</script>


Listing 3: Creating a Custom Handler That Edits the Data Island

<%@ WebHandler Language="C#" Class="DOCHandler" %>

using System;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.IO;
using System.Web;
using Microsoft.VisualStudio.Tools.Applications.Runtime;

public class XLSHandler : IHttpHandler {
  const int Forbidden = 403;
  public void ProcessRequest (HttpContext context) {

    if (!context.User.Identity.IsAuthenticated)
    {
      context.Response.StatusCode = Forbidden;
      context.Response.End();
      return;
    }

    DataSet dataset = new DataSet();
    DataTable datatable = dataset.Tables.Add("Expenses");
    OleDbDataAdapter adapter = new OleDbDataAdapter();

    adapter.SelectCommand = new OleDbCommand("SELECT [Date], " +
      "Description, Cost FROM Expenses WHERE EmployeeName = \"" + 
      context.User.Identity.Name + "\"");

    string connectionString = ConfigurationManager.
      ConnectionStrings["expenses"]. ConnectionString;
    adapter.SelectCommand.Connection = 
      new OleDbConnection(connectionString);
    adapter.Fill(datatable);

    FileStream file = new FileStream(
       @"c:\INetPub\WWWRoot\expenses\ExpenseReport.DOC", 
       FileMode.Open, FileAccess.Read);
    byte[] template;
    try
    {
      template = new byte[file.Length];
      file.Read(template, 0, (int)file.Length);
    }
    finally
    {
      file.Close();
    }

    ServerDocument sd = new ServerDocument(template, ".DOC");
    try
    {
      sd.CachedData.HostItems["ExpenseReport.ThisDocument"].
        CachedData["EmpName"].SerializeDataInstance(
        context.User.Identity.Name);
      sd.CachedData.HostItems["ExpenseReport.ThisDocument"].
        CachedData["Expenses"].SerializeDataInstance(dataset);
      sd.Save();

      // "template" still has the original bytes. Get the new bytes.
      template = sd.Document;
    }
    finally
    {
      sd.Close();
    }

    context.Response.ContentType = "application/vnd.msword";
    context.Response.OutputStream.Write(template, 0, template.Length);
  }
  public bool IsReusable   {
    get { return false; }
  }
}