Open XML 개체 모델을 사용하여  서버측 문서 생성 솔루션 구축 (2부)

요약 :  서버측 문서 통합 솔루션의 아키텍처에 대해 설명합니다. 새로운 Open XML 개체 모델을 사용하여 문서 패키지를 생성하여 문서 조작의 장점을 보여줍니다.

Erika Ehrli, Microsoft Corporation

2007 년 8 월

적용 대상 : 2007 Microsoft Office Suites, Microsoft Office Word 2007, Microsoft ASP.NET 2.0, Microsoft Visual Studio 2005, Microsoft SQL Server 2005

목차

  • 소개

  • 비즈니스 시나리오 : 서버측 판매 보고서 생성

  • 서버측 문서 생성 솔루션 구축

  • 요약

소개

관련 예제는 「2007 Office Sample: Building a Server-Side Document Generation Solution Using the Open XML Object Model (영어)」에서 다운로드하여  주세요.

Open XML 개체 모델을 사용한  서버측 문서 생성 솔루션 구축 (파트 1/2)」을 참조해 주세요.

Open XML 개체 모델을 사용한  서버측 문서 생성 솔루션 구축 (1부)」에서는 Microsoft .NET Framework 에 기반한 Open XML 형식 및 Open XML 개체 모델을 사용하여  Microsoft Office 문서를 생성하는 서버측 솔루션을 설계 및 구축 방법을 설명합니다. Open XML 형식의 아키텍처 및 Open XML 개체 모델을 사용한 문서 패키지 생성, 문서 조작, WordprocessingML 코드 기술을 설명합니다. 또, 서버측 문서 통합 솔루션의 아키텍처에 대해서 해설합니다.

Open XML 개체 모델을 사용한  서버측 문서 생성 솔루션 구축 (2부)」에서는 비즈니스 시나리오를 보여주고, Microsoft ASP.NET 2.0 응용 프로그램을 사용하여  Microsoft Office Word 2007에서 판매 보고서를 생성하는 방법에 대해 해설합니다.

2007 Office Sample: Building a Server-Side Document Generation Solution Using the Open XML Object Model (영어)」에서는 Open XML 개체 모델을 사용하여  서버측 응용 프로그램에서 문서를 생성하는 Microsoft Visual Basic 2005와 Microsoft Visual C# 예제파일을 제공합니다.

비즈니스 시나리오 : 서버측 판매 보고서 생성

Adventure Works Cycles 는 AdventureWorks 예제 데이터베이스에서 사용하는 가상의 다국적 대기업 제조기업입니다. 이 기업은 북미, 유럽 및 아시아 시장을 대상으로, 금속제 자전거나 복합재제 자전거 제조 및 판매를 실시합니다. 미국 워싱턴주 보셀에 직원 290 명의 본사가 있으며, 전세계에 다수의 지역 판매 거점을 배포합니다. 매출 관련정보는 Adventure Works 의 비즈니스의 중요한 부분입니다.

올해 실적이 좋아서 Adventure Works Cycles에서는 실적에 따라 판매 담당자에게 수당을 지급하려고 합니다. 경영진은 각 판매 담당자 실적이 필요하여, 각판매 담당자의 연락처 정보, 연간 총매출 및 지역별 각판매 담당자 실적의 세부 사항이 포함된 문서 생성을 경영진에게서 의뢰받았습니다. 경영진은 각개인의 실적을 다른 담당자 실적과 비교하고 싶어합니다. 

판매 보고서 소개 템플릿 설계

문서 통합 솔루션을 설계할 경우에는 예제 문서 템플릿이 도움이 됩니다. 템플릿으로 생성해야 리포트의 컨텐츠와 레이아웃을 시각화할 수 있습니다.

Adventure Works 의 판매 보고서에서는 문서 생성 솔루션에 의해서 다음 컨텐츠 및 정보를 제공하는 문서를 생성합니다.

  • 기업의 로고

  • 제목 : 판매 보고서 - 직원 이름**

  • 연락처 정보

  • 매출 소개

  • 지역별 매출 : 지역별 모든 판매 담당자 소개 정보를 포함한 표

일반적인 프로젝트에서는 이 순서를 생략할 수 있습니다. 그림 1 에 문서 템플릿을 보여줍니다.

그림 1. 판매 보고서 템플릿

Sales Report Summary document template 

템플릿을 설계해서 문서 패키지 구조, 생성 문서 파트 및 같은 문서를 프로그램에서 재생성하기 위해 만드는 WordprocessingML 마크 업에 대한 이해도 깊어집니다.

템플릿을 생성한 후, 표준적인 ZIP 유틸리티 또는 패키지 탐색기 도구 (Open XML Package Explorer, XMLSpy 등)을 사용하여, 문서 패키지 안에 저장된 파일 구조나 필요한 마크 업을 확인할 수 있습니다.

서버측 문서 생성 솔루션 구축

Adventure Works Cycles 의 경영진은 인트라넷상에서 사용하여  매출 정보를 표시할 수 있는 ASP.NET 2.0 응용 프로그램 생성을 의뢰합니다. 그림 2 와 같이, 웹 페이지에서  직원을 선택하여 판매 담당자 실적 정보를 포함한 리포트를 Microsoft Office Word 2007 로 보내도록 요구합니다. 

그림 2. Adventure Works Cycles 경영진 대상 ASP.NET 2.0 Web 응용 프로그램

Adventure Works Cycles management ASP.NET 2.0 Web

Adventure Works 의 기간 업무 (LOB) 데이터에 접속할 수 있는 ASP.NET 2.0 Web 사이트 생성 방법과 Word 2007 리포트 생성 및 export의 방법을 보여주기 위해 여기에서는 다음 3 개의 중요한 순서에 대해 설명합니다.

  1. 프론트엔드 Web 서버를 구축합니다.

    • Microsoft Visual Studio 2005에서  ASP.NET 2.0 Web 사이트 프로젝트를 생성합니다.

    • 사용자가 직원을 선택하여 소개 리포트를 Word 2007 에 export 할 수 있도록 사이트의 Web 폼을 생성합니다.

  2. AdventureWorks 데이터베이스 서버에 접속해 매출 정보를 취득하는 사용자 지정 데이터 도우미 클래스를 추가합니다.

  3. Open XML 개체 모델 및 WordprocessingML 를 사용해서 Word 2007 판매 보고서 생성 기능을 생성하는 사용자 지정 리포트 생성 도우미 클래스를 추가합니다.

프론트엔드 Web 서버를 구축한다

ASP.NET 2.0 또는 Microsoft SharePoint 제품과 기술을 사용 해서 응용 프로그램의 프론트엔드 Web 서버를 구축할 수 있습니다. 이 경우, ASP.NET 2.0에서 Visual Studio 2005 를 사용하여, Adventure Works Cycles 용의 리포트 생성 솔루션을 생성합니다.

ASP.NET 2.0 Web 사이트 프로젝트를 생성하려면

  1. Visual Studio 2005 를 실행합니다.

  2. [File] 메뉴의 [New] 에서 [Web Site] 를 클릭합니다.

  3. [New Web Site] 대화상자에서 [ASP.NET Web Site] 템플릿을 클릭합니다.

  4. [Location] 박스 목록의 [HTTP] 또는 [File System] 을 클릭합니다.

  5. Web 사이트 경로 또는 URL 를 지정하거나 기본값을 그대로 사용합니다.

  6. [Language] 박스 목록의 [Visual C#] 를 클릭하고, [OK] 를 클릭합니다.

Default.aspx 라는 이름의 Web 폼과 Default.aspx.cs (또는 Visual Basic 2005 를 사용 한 경우는 Default.aspx.vb) 라는 이름의 코드 파일을 포함한 ASP.NET Web 사이트 프로젝트가 생성됩니다.

전제조건

이 예제 응용 프로그램의 목적은 Open XML 형식을 사용하여  Word 문서를 생성하는 방법을 살펴보는 것입니다. 이 응용 프로그램을 구축하려면, 다음이 필요합니다.

Open XML 개체 모델 및 System.IO.Packaging 네임 스페이스 네임 스페이스를 조작하려면, 유형 라이브러리 참조를 추가합니다.

프로젝트에 참조를 추가하려면

  1. 솔루션 탐색기에서 [References] 폴더를 배포합니다.

    Note메모 :

    [References] 폴더가 표시되지 않는 경우는 [Project] 노드에서  [ Show All Files] 를 클릭합니다.

  2. [References] 폴더를 오른쪽 클릭하고, [Add Reference] 를 클릭합니다.

  3. [References] 탭을 클릭하고, WindowsBase.dll 가 있는 장소로 이동해 이 파일을 선택합니다.

    Note메모 :

    이 문서의 공개 시점에서는 이 파일은 local_drive :\Program Files\Reference Assemblies\Microsoft\Framework\v3.0 에 있습니다.

  4. WindowsBase.dll 파일을 클릭하고, [OK] 를 클릭합니다.

  5. [References] 폴더를 오른쪽 클릭하고, [Add Reference] 를 클릭합니다.

  6. [References] 탭을 클릭하고, Microsoft.Office.DocumentFormat.OpenXml.dll 가 있는 장소로 이동해 이 파일을 클릭하고, [OK] 를 클릭합니다.

    Note메모 :

    Microsoft SDK for Open XML Formats Technology Preview 를 설치 한 경우, 이 파일은 일반적으로,local_drive :\Program Files\2007 Office System Developer Resources\OpenXMLSDK\1.0.0531\lib 에 있습니다.

참조를 추가하면, Web.config 파일에 다음 마크 업이 추가됩니다.

Note메모 :

이 코드 예에는 온라인 표시를 보기 쉽도록 하기 위해서 줄 바꿈이 포함되어 있습니다. 코드를 실행하기 전에 줄바꿈을 삭제해야 합니다.

<compilation debug="true">
   <assemblies>
     <add assembly="Microsoft.Office.DocumentFormat.OpenXml, Version=1.0.531.0,
Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
     <add assembly="WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
   </assemblies>
</compilation>
Note메모 :

Microsoft Office 문서를 생성하기 위해서 서버에 2007 Microsoft Office system 를 설치 할 필요는 없습니다. Open XML 형식에 의해서 자동화를 사용하지 않고 문서를 생성 및 조작할 수 있습니다.

Web 폼 생성

마지막에 그림 1 과 같은 Web 폼을 생성합니다.  Web 폼에서는 DropDownList 컨트롤을 사용하여  모든 직원명을 표시합니다. 또, 사용자가 직원마다의 판매 보고서를 Word 2007 에 export하기 위한 Button 컨트롤도 포함되어 있습니다.

Note메모 :

이 문서에서는 Microsoft Visual C# 의 예제코드를 보여줍니다. 이 문서와 함께 다운로드된 예제코드에는 Visual C# 와 Microsoft Visual Basic 2005 예제코드가 포함되어 있습니다.

Web 폼을 생성하려면

  • default.aspx 코드를 다음 예제코드에 옮겨놓습니다.

    <%@ Page Language="C#" %>
    <%@ Import Namespace="System.Data" %>
    <%@ Import Namespace="System.Data.SqlClient" %>
    <script runat="server">
        protected void btnExportToWord_Click(object sender, EventArgs e) {
            SalesReportBuilder report = new SalesReportBuilder(ddlEmployee.SelectedItem.Value);
            lblResult.Text = "Report saved at: " + report.CreateDocument();
        }
    </script>
    <html >
    <head runat="server">
        <title>Word 2007 Sales Report Generator</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <img src="Resources/headerImage.gif" style="width: 264px; height: 107px" alt="Adventure Works"/>
                <h1>
                    <span style="font-family: Verdana">Sales Report Generator</span>
                </h1>
                <hr />
                <h3>
                   <span style="font-family: Verdana; color:Maroon;">Create sales report for employee:</span>
                </h3>
                <table border="0" style="font-family: Verdana">
                    <tr>
                        <td>
                            <asp:DropDownList
                            ID="ddlEmployee"
                            runat="server"
                            DataSourceID="EmployeeDataSource"
                            AutoPostBack="True"
                            DataTextField="FullName"
                            DataValueField="SalesPersonID"
                            Width="200px">
                            </asp:DropDownList>
                            <asp:SqlDataSource
                            ID="EmployeeDataSource"
                            runat="server"
                            ConnectionString="<%$ ConnectionStrings:AWConnString %>"
                            SelectCommand="SELECT [SalesPersonID],[FirstName]
                            + ' ' + [LastName] AS [FullName] FROM [Sales].[vSalesPerson]">
                            </asp:SqlDataSource>
                        </td>
                        <td valign="top">
                            <asp:Button
                            ID="btnExportToWord"
                            runat="server"
                            Text="Export to Word 2007"
                            OnClick="btnExportToWord_Click" />
                        </td>
                    </tr>
                </table>
    
    
                <br />
                <h3>
                   <span style="font-family: Verdana; color:Blue;">
                      <asp:Label
                      ID="lblResult"
                      runat="server" />
                   </span>
                </h3>
            </div>
        </form>
    </body>
    </html>
    

사용자 지정 데이터 도우미 클래스 구축

LOB 시스템 및 데이터는 응용 프로그램의 데이터 층 논리를 캡슐화합니다. 사용자 지정 데이터 도우미 클래스를 생성하여 다양한 LOB 시스템에서 데이터를 취득하는 프로세스를 캡슐화할 수 있습니다.

LOB 데이터에 접속

Adventure Works Cycles에서는 LOB 데이터를 AdventureWorks 데이터베이스에 저장합니다. AdventureWorks 예제데이터베이스는 「SQL Server 2005 Samples and Sample Databases (February 2007) (영어)」에서 다운로드하여  설치 할 수 있습니다.

다음에 ASP.NET Web 사이트를 AdventureWorks 데이터베이스에 접속합니다.

ASP.NET 2.0 Web 사이트를 AdventureWorks 데이터베이스에 접속하려면

  1. web.config 파일을 엽니다.

  2. 이하와 같이 web.config 파일에 접속 문자열을 추가합니다.

    Note메모 :

    이 코드 예에는 온라인 표시를 보기 쉽도록 하기 위해서 줄 바꿈이 포함되어 있습니다. 코드를 실행하기 전에 줄 바꿈을 삭제해야 합니다.

    <connectionStrings>
       <add name="AWConnString" connectionString="Data Source=(local);
    Initial Catalog=AdventureWorks;Integrated Security=True" providerName=
    "System.Data.SqlClient"/>
    </connectionStrings>
    

사용자 지정 데이터 도우미 클래스 구축

AdventureWorks 데이터베이스에서 매출 정보를 취득하는 데이터 도우미 클래스를 생성합니다. 이 클래스는 2 의 public 메서드를 제공합니다.

  • GetSalesPersonData.FullName, Phone, Email, SalesQuota, SalesYTD 및 TerritoryName 등의 직원의 데이터를 취득합니다. 이 데이터를 사용하여 생성된 문서로 직원에 관한 정보를 설정합니다.

  • GetSalesByTerritory.각 직원의 판매 지역에 소속하는 모든 직원의 2003 년과 2004 년의 모든 판매 실적을 보여주는 표를 설정합니다. 이것에 의해서 매출 데이터와 실적을 비교 할 수 있습니다. 이 데이터를 사용하여 생성된 문서의 판매 지역별 표를 설정합니다.

데이터 도우미 클래스를 생성하려면

  1. 솔루션 탐색기에서 [Project] 폴더를 오른쪽 클릭하고, [Add New Item] 를 클릭합니다.

  2. [Add New Item] 대화상자에서 [Class File] 을 클릭합니다.

  3. AdventureWorksSalesData.cs 라는 파일 이름을 지정하고, [Add] 를 클릭합니다.

  4. [Microsoft Visual Studio] 메시지 박스에서 [Yes] 를 클릭하고 클래스 파일을 프로젝트의 [App_Code] 폴더에 추가합니다.

  5. AdventureWorksSalesData.cs 코드를 다음 예제코드에 옮겨놓습니다.

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Collections.Specialized;
    using System.Configuration;
    
  6. 다음 클래스, 필드 및 생성자를 추가합니다.

    /// <summary>
    /// Represents the AdventureWorks line of business sales data.
    /// </summary>
    public class AdventureWorksSalesData {
        private string _source;
    
        public AdventureWorksSalesData() {
            _source = ConfigurationManager.ConnectionStrings["AWConnString"].ConnectionString;
        }
    
  7. GetSalesPersonData 메서드를 생성합니다.

       /// <summary>
        /// Get employee's data: FullName, Phone, Email,
        /// SalesQuota, SalesYTD, TerritoryName.
        /// </summary>
        /// <param name="salesPersonID">Data for employee with SalesPersonID</param>
        /// <returns>StringDictionary</returns>
        public StringDictionary GetSalesPersonData(string salesPersonID) {
            StringDictionary SalesPerson = new StringDictionary();
    
            //Connect to a Microsoft SQL Server database and get data
            const string query =
               "SELECT [FirstName] + ' ' + [LastName] AS [FullName],[Phone],
               [EmailAddress], [TerritoryName], [SalesQuota], [SalesYTD] FROM [AdventureWorks]
               .[Sales].[vSalesPerson] WHERE ([SalesPersonID] = @SalesPersonID)";
    
            using (SqlConnection conn = new SqlConnection(_source)) {
                conn.Open();
                SqlCommand cmd = new SqlCommand(query, conn);
                cmd.Parameters.AddWithValue("@SalesPersonID", salesPersonID);
                SqlDataReader dr = cmd.ExecuteReader();
    
                if (dr.Read()) {
    
                    SalesPerson.Add("FullName", (string)dr["FullName"]);
                    SalesPerson.Add("Phone", (string)dr["Phone"]);
                    SalesPerson.Add("Email", (string)dr["EmailAddress"]);
                    if (dr["SalesQuota"] is System.DBNull) {
                        SalesPerson.Add("SalesQuota", "0");
                    }
                    else {
                        SalesPerson.Add("SalesQuota", Convert.ToString((decimal)dr["SalesQuota"]));
                    }
                    if (dr["SalesYTD"] is System.DBNull) {
                        SalesPerson.Add("SalesYTD", "0");
                    }
                    else {
                        SalesPerson.Add("SalesYTD", Convert.ToString((decimal)dr["SalesYTD"]));
                    }
                    if (dr["TerritoryName"] is System.DBNull) {
                        SalesPerson.Add("TerritoryName", "NA");
                    }
                    else {
                        SalesPerson.Add("TerritoryName", (string)dr["TerritoryName"]);
                    }
                }
    
                dr.Close();
                conn.Close();
            }
    
            return SalesPerson;
        }
    
  8. GetSalesByTerritory 메서드를 생성합니다.

       /// <summary>
        /// Get a table that shows all sales for 2003 and 2004 
        /// for all employees that belong to employee's
        /// sales territory so you can compare sales data
        /// and performance.
        /// </summary>
        /// <param name="Territory">Get all sales/employee for territory</param>
        /// <returns>Table with 2003 and 2004 sales/employee</returns>
        public DataTable GetSalesByTerritory(string Territory) {
            //Connect to a Microsoft SQL Server database and get data
            String source = ConfigurationManager.ConnectionStrings["AWConnString"].
            ConnectionString;
            string query = "SELECT [FullName], [2003], [2004] FROM [AdventureWorks].
            [Sales].[vSalesPersonSalesByFiscalYears] WHERE ([SalesTerritory] =
             '" + Territory + "')";
    
            using (SqlConnection conn = new SqlConnection(_source)) {
                conn.Open();
                SqlDataAdapter da = new SqlDataAdapter(query, conn);
                DataSet ds = new DataSet();
                da.Fill(ds, "SalesByTerritory");
                conn.Close();
    
                return ds.Tables["SalesByTerritory"];
            }
        }
    
  9. 클래스를 닫습니다.

판매 보고서를 생성하는 사용자 지정 데이터 도우미 클래스 구축

LOB 소프트웨어 및 구성요소는 응용 프로그램의 비즈니스 논리층을 캡슐화합니다. 사용자 지정 리포트 생성 도우미 클래스를 생성하고, 문서 통합의 프로세스를 캡슐화할 수 있습니다. 이러한 클래스에서는 Open XML 개체 모델을 사용하여 문서 패키지를 생성하여 LOB 데이터와 비즈니스 요건에 기반한 문서 컨텐츠를 출력합니다.

리포트 생성 도우미 클래스 구조

여기에서는 AdventureWorksSalesData 클래스에서 매출 정보를 취득하고, 그림 2 와 같은 리포트를 생성하는 리포트 생성 도우미 클래스를 생성합니다. 이 도우미 클래스에 의해서 document.xml 파트에서 공통 서식 설정, 스타일 및 쓰기 메서드의 코드를 그룹화 할 수 있습니다.

이 부분은 서버측 문서 생성 솔루션의 핵심이 되는 부분입니다. Open XML 개체 모델을 사용하여 문서 패키지와 문서 파트를 생성합니다. WordprocessingML 마크 업을 사용하고, 문서의 컨텐츠를 정의합니다.

Note메모 :

Open XML 개체 모델을 사용한 서버측 문서 생성 솔루션 구축 (파트 1/2)」에서는 이 리포트를 구축하기 위해서 사용하는 Open XML 패키지 아키텍처 및 WordprocessingML 의 기본 개념에 대해 설명합니다.

그림 1 은 리포트 생성 클래스에서 정의하는 작업을 보여줍니다. 이 순서에 대해 다음에서 설명합니다.

  1. 문서 패키지를 생성합니다.

    • 패키지를 Word 2007 문서로서 생성합니다.

    • 메인 문서 파트 document.xml 를 삽입합니다.

  2. 스타일 파트를 삽입하고, 문서 패키지에 추가합니다.

  3. 이미지 파트를 삽입하고, 문서 패키지에 추가합니다.

  4. WordprocessignML 마크 업을 사용하여  document.xml 의 컨텐츠를 생성합니다.

    • 단락내에 이미지를 삽입합니다.

    • 단락내에 제목을 입력합니다.

    • 단락내에 직원의 연락처 정보를 입력합니다.

    • 단락내에 매출 소개 정보를 입력합니다.

    • 지역별 총매출을 표 형식에서 입력합니다.

리소스 파일

이 문서에서는 Open XML 개체 모델을 사용하여  문서로 스타일이나 이미지를 사용하는 방법을 보여줍니다. 리포트 생성 도우미 클래스의 구현을 시작하기 전에 이미지나 스타일을 조작하기 위한 리소스 파일을 추가해야 합니다.

머리글 이미지 리소스 파일

Adventure Works Cycles 내의 모든 문서에는 그림 3 과 같이 문서의 머리글에 회사의 로고를 넣어야 합니다 .

그림 3. Adventure Works Cycle  로고

Adventure Works Cycle company logo 

Open XML 개체 모델을 사용하여 이미지 파트를 문서 패키지에 추가합니다. 머리글 이미지를 삽입하려면, 다음 리소스가 필요합니다.

  • 이미지 파일

  • DrawingML 템플릿 파일

WordprocessingML 마크 업을 삽입하고, 이미지를 문서 컨텐츠의 일부로서 표시하는 방법의 자세한 내용은 「Open XML 개체 모델을 사용한 서버측 문서 생성 솔루션의 구축 (파트 1/2)」의 「이미지 파트와 이미지」를 참조해 주세요. 문서에 이미지를 삽입하려면, <w:drawing> 요소를 사용합니다. 모든 DrawingML 마크 업을 생성하는 대신에 이 예제 솔루션에서는 모든 <w:drawing> 마크 업을 쓰는 drawingTemplate.xml 를 사용합니다.

프로젝트에 이미지 리소스를 추가하려면

  1. 솔루션 탐색기를 오른쪽 클릭하고, [New Folder] 를 클릭합니다.

  2. 새로운 폴더에 Resources 라는 이름을 붙입니다.

  3. 이 문서의 그림 3 을 오른쪽 클릭하고, 이미지를 하드 디스크에 저장합니다.

  4. 이미지의 파일 이름을 headerImage.gif 로 변경합니다.

  5. 솔루션 탐색기에서 [Resources] 폴더를 오른쪽 클릭하고, [Add Existing Item] 를 클릭하고 headerImage.gif 이미지를 추가합니다.

  6. 솔루션 탐색기에서 [Resources] 폴더를 오른쪽 클릭하고, [Add New Item] 를 클릭합니다.

  7. [Add New Item] 대화상자에서 [XML File] 을 클릭합니다.

  8. 파일에drawingTemplate.xml 이라고 이름을 붙이고, [Add] 를 클릭합니다.

  9. drawingTemplate.xml 의 마크 업을 다음 예제 마크 업에 옮겨놓습니다.

    Note메모 :

    다음 코드 예에는 온라인으로 표시했을 때에 보기 쉽도록 줄 바꿈이 추가 됩니다. 실제로 코드로 사용하기 전에 줄 바꿈을 삭제해야 합니다.

    <!-- report generator helper class code will replace the {0} with the reference id of the image -->
    <container xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main"
    xmlns:wp="https://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
    xmlns:r="https://schemas.openxmlformats.org/officeDocument/2006/relationships">
      <w:drawing>
        <wp:inline>
          <wp:extent cx="1628936" cy="733586" />
          <wp:docPr name="image.gif" id="1" />
          <a:graphic xmlns:a="https://schemas.openxmlformats.org/drawingml/2006/main">
            <a:graphicData uri="https://schemas.openxmlformats.org/drawingml/2006/picture">
              <pic:pic xmlns:pic="https://schemas.openxmlformats.org/drawingml/2006/picture">
                <pic:nvPicPr>
                  <pic:cNvPr id="0" name="image.gif" />
                  <pic:cNvPicPr />
                </pic:nvPicPr>
                <pic:blipFill>
                  <a:blip r:embed="{0}" />
                  <a:stretch>
                    <a:fillRect />
                  </a:stretch>
                </pic:blipFill>
                <pic:spPr>
                  <a:xfrm>
                    <a:off x="0" y="0" />
                    <a:ext cx="1630190" cy="734151" />
                  </a:xfrm>
                  <a:prstGeom prst="rect" />
                </pic:spPr>
              </pic:pic>
            </a:graphicData>
          </a:graphic>
        </wp:inline>
      </w:drawing>
    </container>
    

이 리포트 생성 도우미 클래스는 Open XML 개체 모델을 사용하여 다음의 처리를 실행합니다.

  • 이미지 파트를 생성하여 문서에 추가합니다.

  • 이미지의 참조 ID 를 취득합니다.

  • drawingTemplate.xml 파일의 **{0}**을 이미지의 참조 ID 에 옮겨놓습니다.

  • DrawingML 마크 업을 메인 문서 파트의 WordprocessingML 마크 업에 삽입합니다.

스타일 리소스 파일

Open XML 개체 모델을 사용하여 스타일 파트를 문서 패키지에 삽입합니다. WordprocessingML 마크 업을 사용하고, 단락, 텍스트, 범위, 표 스타일 및 서식 설정을 정의합니다.

스타일 파트를 삽입하려면, styles.xml 파일을 편집해야 합니다. WordprocessingML 마크 업을 생성하고, 스타일을 정의하는 방법의 자세한 내용은 「Open XML 개체 모델을 사용한 서버측 문서 생성 솔루션의 구축 (파트 1/2)」의 「이미지 파트, 스타일 및 서식 설정」을 참조해 주세요. styles.xml 파일을 사용하고, 문서 컨텐츠에 적용할 수 있는 서식 설정 속성의 특정 값집합을 정의합니다. 모든 마크 업을 수동으로 생성하는 대신에 이 예제 솔루션에서는 styles.xml 템플릿을 사용합니다.

styles.xml 리소스 파일을 프로젝트에 추가하려면

  1. 솔루션 탐색기에서 [Resources] 폴더를 오른쪽 클릭하고, [Add New Item] 를 클릭합니다.

  2. [Add New Item] 대화상자에서 [XML File] 을 클릭합니다.

  3. 파일에 styles.xml 이라고 이름을 붙이고, [Add] 를 클릭합니다.

  4. styles.xml 의 마크 업을 다음 예제 마크 업에 옮겨놓습니다.

    Note메모 :

    다음 코드 예에는 온라인으로 표시했을 때에 보기 쉽도록 줄 바꿈이 추가 됩니다. 실제로 코드로 사용하기 전에 줄 바꿈을 삭제해야 합니다.

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <w:styles xmlns:r="https://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
      <w:docDefaults>
        <w:rPrDefault>
          <w:rPr>
            <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorEastAsia" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
            <w:sz w:val="22"/>
            <w:szCs w:val="22"/>
            <w:lang w:val="en-US" w:eastAsia="en-US" w:bidi="ar-SA"/>
          </w:rPr>
        </w:rPrDefault>
        <w:pPrDefault>
          <w:pPr>
            <w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
          </w:pPr>
        </w:pPrDefault>
      </w:docDefaults>
      <w:latentStyles w:defLockedState="0" w:defUIPriority="99" w:defSemiHidden="1"
     w:defUnhideWhenUsed="1" w:defQFormat="0" w:count="267">
        <w:lsdException w:name="Normal" w:semiHidden="0" w:uiPriority="0" w:unhideWhenUsed="0" w:qFormat="1"/>
        <w:lsdException w:name="heading 1" w:semiHidden="0" w:uiPriority="9" w:unhideWhenUsed="0" w:qFormat="1"/>
        <w:lsdException w:name="Title" w:semiHidden="0" w:uiPriority="10" w:unhideWhenUsed="0" w:qFormat="1"/>
        <w:lsdException w:name="Default Paragraph Font" w:uiPriority="1"/>
        <w:lsdException w:name="Subtitle" w:semiHidden="0" w:uiPriority="11" w:unhideWhenUsed="0" w:qFormat="1"/>
        <w:lsdException w:name="Strong" w:semiHidden="0" w:uiPriority="22" w:unhideWhenUsed="0" w:qFormat="1"/>
        <w:lsdException w:name="Table Grid" w:semiHidden="0" w:uiPriority="59" w:unhideWhenUsed="0"/>
        <w:lsdException w:name="Light List Accent 2" w:semiHidden="0" w:uiPriority="61" w:unhideWhenUsed="0"/>
      </w:latentStyles>
      <w:style w:type="paragraph" w:default="1" w:styleId="Normal">
        <w:name w:val="Normal"/>
        <w:qFormat/>
      </w:style>
      <w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont">
        <w:name w:val="Default Paragraph Font"/>
        <w:uiPriority w:val="1"/>
        <w:semiHidden/>
        <w:unhideWhenUsed/>
      </w:style>
      <w:style w:type="table" w:default="1" w:styleId="TableNormal">
        <w:name w:val="Normal Table"/>
        <w:uiPriority w:val="99"/>
        <w:semiHidden/>
        <w:unhideWhenUsed/>
        <w:qFormat/>
        <w:tblPr>
          <w:tblInd w:w="0" w:type="dxa"/>
          <w:tblCellMar>
            <w:top w:w="0" w:type="dxa"/>
            <w:left w:w="108" w:type="dxa"/>
            <w:bottom w:w="0" w:type="dxa"/>
            <w:right w:w="108" w:type="dxa"/>
          </w:tblCellMar>
        </w:tblPr>
      </w:style>
      <w:style w:type="table" w:styleId="LightList-Accent2">
        <w:name w:val="Light List Accent 2"/>
        <w:basedOn w:val="TableNormal"/>
        <w:uiPriority w:val="61"/>
        <w:rsid w:val="00693473"/>
        <w:pPr>
          <w:spacing w:after="0" w:line="240" w:lineRule="auto"/>
        </w:pPr>
        <w:tblPr>
          <w:tblStyleRowBandSize w:val="1"/>
          <w:tblStyleColBandSize w:val="1"/>
          <w:tblInd w:w="0" w:type="dxa"/>
          <w:tblBorders>
            <w:top w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            <w:left w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            <w:bottom w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            <w:right w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
          </w:tblBorders>
          <w:tblCellMar>
            <w:top w:w="0" w:type="dxa"/>
            <w:left w:w="108" w:type="dxa"/>
            <w:bottom w:w="0" w:type="dxa"/>
            <w:right w:w="108" w:type="dxa"/>
          </w:tblCellMar>
        </w:tblPr>
        <w:tblStylePr w:type="firstRow">
          <w:pPr>
            <w:spacing w:before="0" w:after="0" w:line="240" w:lineRule="auto"/>
          </w:pPr>
          <w:rPr>
            <w:b/>
            <w:bCs/>
            <w:color w:val="FFFFFF" w:themeColor="background1"/>
          </w:rPr>
          <w:tblPr/>
          <w:tcPr>
            <w:shd w:val="clear" w:color="auto" w:fill="C0504D" w:themeFill="accent2"/>
          </w:tcPr>
        </w:tblStylePr>
        <w:tblStylePr w:type="lastRow">
          <w:pPr>
            <w:spacing w:before="0" w:after="0" w:line="240" w:lineRule="auto"/>
          </w:pPr>
          <w:rPr>
            <w:b/>
            <w:bCs/>
          </w:rPr>
          <w:tblPr/>
          <w:tcPr>
            <w:tcBorders>
              <w:top w:val="double" w:sz="6" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:left w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:bottom w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:right w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            </w:tcBorders>
          </w:tcPr>
        </w:tblStylePr>
        <w:tblStylePr w:type="firstCol">
          <w:rPr>
            <w:b/>
            <w:bCs/>
          </w:rPr>
        </w:tblStylePr>
        <w:tblStylePr w:type="lastCol">
          <w:rPr>
            <w:b/>
            <w:bCs/>
          </w:rPr>
        </w:tblStylePr>
        <w:tblStylePr w:type="band1Vert">
          <w:tblPr/>
          <w:tcPr>
            <w:tcBorders>
              <w:top w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:left w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:bottom w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:right w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            </w:tcBorders>
          </w:tcPr>
        </w:tblStylePr>
        <w:tblStylePr w:type="band1Horz">
          <w:tblPr/>
          <w:tcPr>
            <w:tcBorders>
              <w:top w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:left w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:bottom w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
              <w:right w:val="single" w:sz="8" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
            </w:tcBorders>
          </w:tcPr>
        </w:tblStylePr>
      </w:style>
    </w:styles>
    

이 리포트 생성 도우미 클래스에서는 Open XML 개체 모델을 사용하여 스타일 파트를 생성하여 문서에 추가합니다. 표, 단락 및 범위 등의 일부의 WordprocessingML 요소에서는 앞에서 언급한 styles.xml 문서 파트로 정의된 스타일을 사용합니다.

사용자 지정 리포트 생성 도우미 클래스 구축

다음은 경영진에서 구할 수 있던 판매 보고서를 생성하는 모든 비즈니스 논리를 포함한 리포트 생성 도우미 클래스를 생성합니다.

이 리포트 생성 도우미 클래스는 솔루션내의 [Reports] 폴더에 리포트를 저장합니다. 이것에 의해서 안전한 다운로드 하이퍼 링크를 문서에 제공할 수 있습니다.

[Reports] 폴더를 생성하려면

  1. 솔루션 탐색기를 오른쪽 클릭하고, [New Folder] 를 클릭합니다.

  2. 새로운 폴더에 Reports 라는 이름을 붙입니다.

문서 컨텐츠를 생성하려면, System.Xml.XmlWriter 를 사용하여  WordprocessingML 마크 업을 생성합니다. 생성해야 할 마크 업 코드 양은 생성하는 문서 컨텐츠에 따라 다릅니다. 다른 마크업 언어와 같이 WordprocessingML 는 상세합니다.

사용자 지정 리포트 생성 도우미 클래스를 생성하려면

  1. 솔루션 탐색기에서 [Project] 노드를 오른쪽 클릭하고, [Add New Item] 를 클릭합니다.

  2. [Add New Item] 대화상자에서 [Class File] 을 클릭합니다.

  3. 파일에 AdventureWorksSalesData.cs 라는 이름을 붙이고, [Add] 를 클릭합니다.

  4. [Microsoft Visual Studio] 메시지 박스에서 [Yes] 를 클릭하고 클래스 파일을 프로젝트의 [App_Code] 폴더에 추가합니다.

  5. SalesReportBuilder.cs 의 코드를 다음 순서로 설명하는 예제코드에 옮겨놓습니다. 우선, 다음의 using 구문을 추가합니다.

    using System;
    using System.Data;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.IO;
    using System.IO.Packaging;
    using System.Text;
    using System.Xml;
    using System.Web;
    using Microsoft.Office.DocumentFormat.OpenXml.Packaging;
    
  6. 다음 네임 스페이스, 클래스, 필드 및 생성자를 추가합니다.

    /// <summary>
    /// Represents the complete sales report document to be generated.
    /// </summary>
    public class SalesReportBuilder {
        const string drawingTemplate = @"~/resources/drawingTemplate.xml";
        const string headerImageFile = @"~/resources/headerimage.gif";
        const string stylesXmlFile = @"~/resources/styles.xml";
        const string wordNamespace = "https://schemas.openxmlformats.org/wordprocessingml/2006/main";
        const string wordPrefix = "w";
    
        private string _documentName;
        private string _salesPersonID;
        private string _imagePartRID;
    
        public SalesReportBuilder(string salesPersonID) {
            _salesPersonID = salesPersonID;
            //Generate unique filename
            _documentName = HttpContext.Current.Server.MapPath(
            @"~/reports/AdventureWorks" + DateTime.Now.ToFileTime() + ".docx");
        }
    
  7. 영역 패키지 및 파트를 생성합니다. 이 영역에는 문서 패키지 및 파트 생성에 사용되는 메서드가 포함됩니다.

    Note메모 :

    다음 코드 예에는 온라인으로 표시했을 때에 보기 쉽도록 줄 바꿈이 추가 됩니다. 실제로 코드로 사용하기 전에 줄 바꿈을 삭제해야 합니다.

    #region Create Package and Parts
        /// <summary>
        /// 1. Create a new package as a Word document.
        /// 2. Add a style.xml part.
        /// 3. Add an embedded image part.
        /// 4. Create the document.xml part content. 
        /// </summary>
        /// <returns>File path location or error message</returns>
        public string CreateDocument() {
            try {
                using (WordprocessingDocument wordDoc = WordprocessingDocument.Create
                (_documentName, WordprocessingDocumentType.Document)) {
    
                    // Set the content of the document so that Word can open it.
                    MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();
    
                    // Create a style part and add it to the document.
                    XmlDocument stylesXml = new XmlDocument();
                    stylesXml.Load(HttpContext.Current.Server.MapPath(stylesXmlFile));
    
                    StyleDefinitionsPart stylePart = mainPart.AddNewPart<StyleDefinitionsPart>();
                    //  Copy the style.xml content into the new part....
                    using (Stream outputStream = stylePart.GetStream()) {
                        using (StreamWriter ts = new StreamWriter(outputStream)) {
                            ts.Write(stylesXml.InnerXml);
                        }
                    }
    
                    // Create an image part and add it to the document.
                    ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Gif);
                    string imageFileName = System.Web.HttpContext.Current.Server.MapPath(headerImageFile);
                    using (FileStream stream = new FileStream(imageFileName, FileMode.Open)) {
                        imagePart.FeedData(stream);
                    }
    
                    // Get the reference ID for the image added to the package.
                    // You will use the image part reference ID to insert the
                    // image to the document.xml file.
                    _imagePartRID = mainPart.GetIdOfPart(imagePart);
    
                    // Create document.xml content.
                    SetMainDocumentContent(mainPart);
                }
    
                return (_documentName);
            }
            catch (Exception ex) {
                return (ex.Message);
    
            }
        }
    
        /// <summary>
        /// Set content of MainDocumentPart. 
        /// </summary>
        /// <param name="part">MainDocumentPart</param>
        public void SetMainDocumentContent(MainDocumentPart part) {
            using (Stream stream = part.GetStream()) {
                CreateWordProcessingML(stream);
            }
        }
    
       /// <summary>
        /// Generate WordprocessingML for Sales Report.
        /// The resulting XML will be saved as document.xml.
        /// </summary>
        /// <param name="stream">MainDocumentPart stream</param>
        public void CreateWordProcessingML(Stream stream) {
    
            // Get sales person data from AdventureWorks database
            // You will write this data to the document.xml file.
            AdventureWorksSalesData salesData = new AdventureWorksSalesData();
            StringDictionary SalesPerson = salesData.GetSalesPersonData(_salesPersonID);
    
            // Create an XmlWriter using UTF8 encoding.
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            settings.Indent = true;
    
            // This file represents the WordprocessingML of the Sales Report.
            XmlWriter writer = XmlWriter.Create(stream, settings);
            try {
                writer.WriteStartDocument(true);
                writer.WriteComment("This file represents the WordProcessingML of our Sales Report");
                writer.WriteStartElement(wordPrefix, "document", wordNamespace);
                writer.WriteStartElement(wordPrefix, "body", wordNamespace);
    
                WriteHeaderImage(writer);
                WriteDocumentTitle(writer, SalesPerson["FullName"]);
                WriteDocumentContactInfo(writer, SalesPerson["FullName"], SalesPerson["Phone"], SalesPerson["Email"]);
                WriteSalesSummaryInfo(writer, SalesPerson["SalesYTD"], SalesPerson["SalesQuota"]);
                WriteTerritoriesTable(writer, SalesPerson["TerritoryName"]);
    
                writer.WriteEndElement(); //body
                writer.WriteEndElement(); //document
            }
            catch (Exception e) {
                throw;
            }
            finally {
                //Write the XML to file and close the writer.
                writer.Flush();
                writer.Close();
            }
            return;
        }
        #endregion
    
  8. 영역 서식 설정 메서드를 생성합니다. 이 영역에는 문서 컨텐츠의 서식 설정에 사용되는 WordprocessingML 를 생성하기 위한 메서드가 포함됩니다.

     #region Formatting Methods
        /// <summary>
        /// Write the title paragraph properties to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the properties to.</param>
        private void WriteTitleParagraphProperties(XmlWriter writer) {
            // Create the paragraph properties element.
            // </w:pPr>
            writer.WriteStartElement(wordPrefix, "pPr",
               wordNamespace);
    
            // Create the bottom border.
            //   <w:pBdr>
            //     <w:bottom w:val=”single” w:sz=”4”
            //               w:space=”1” w:color=”auto” />
            //   </w:pBdr>
            writer.WriteStartElement(wordPrefix, "pBdr", wordNamespace);
            writer.WriteStartElement(wordPrefix, "bottom", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, "single");
            writer.WriteAttributeString(wordPrefix, "sz", wordNamespace, "4");
            writer.WriteAttributeString(wordPrefix, "space", wordNamespace, "1");
            writer.WriteAttributeString(wordPrefix, "color", wordNamespace, "blue");
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            // Define the spacing for the paragraph.
            //   <w:spacing w:line=”240” w:lineRule=”auto” />
            writer.WriteStartElement(wordPrefix, "spacing", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "line", wordNamespace, "240");
            writer.WriteAttributeString(wordPrefix, "lineRule", wordNamespace, "auto");
            writer.WriteEndElement();
    
            // Close the properties element.
            // </w:pPr>
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Write the title run properties to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the properties to.</param>
        private void WriteTitleRunProperties(XmlWriter writer) {
            // Create the run properties element.
            // <w:rPr>
            writer.WriteStartElement(wordPrefix, "rPr", wordNamespace);
    
            // Set up the spacing.
            //   <w:spacing w:val=”5” />
            writer.WriteStartElement(wordPrefix, "spacing", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, "5");
            writer.WriteEndElement();
    
            // Define the size.
            //   <w:sz w:val=”52” />
            writer.WriteStartElement(wordPrefix, "sz", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, "52");
            writer.WriteEndElement();
    
            // Close the properties element.
            // </w:rPr>
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Write the subtitle paragraph properties to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the properties to.</param>
        private void WriteSubtitleParagraphProperties(XmlWriter writer) {
            // Create the paragraph properties element.
            // <w:pPr>
            writer.WriteStartElement(wordPrefix, "pPr", wordNamespace);
    
            // Define the spacing for the paragraph.
            //   <w:spacing w:before=”200” w:after=”0” />
            writer.WriteStartElement(wordPrefix, "spacing", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "before", wordNamespace, "200");
            writer.WriteAttributeString(wordPrefix, "after", wordNamespace, "0");
            writer.WriteEndElement();
    
            // Close the properties element.
            // </w:pPr>
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Write the subtitle run properties to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the properties to.</param>
        private void WriteSubtitleRunProperties(XmlWriter writer) {
            // Create the run properties element.
            // <w:rPr>
            writer.WriteStartElement(wordPrefix, "rPr", wordNamespace);
    
            // setup as bold
            //   <w:b />
            writer.WriteElementString(wordPrefix, "b", wordNamespace, null);
    
            // Define the size.
            //   <sz w:val=”26” />
            writer.WriteStartElement(wordPrefix, "sz", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, "26");
            writer.WriteEndElement();
    
            // Close the properties element.
            // </w:rPr>
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Write the bold run property to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the properties to.</param>
        private void WriteBoldRunProperties(XmlWriter writer) {
            // Create the run properties element.
            // <w:rPr>
            //   <w:b />
            // </w:rPr>
            writer.WriteStartElement(wordPrefix, "rPr", wordNamespace);
            writer.WriteElementString(wordPrefix, "b", wordNamespace, null);
            writer.WriteEndElement();
        }
        #endregion
    
  9. 영역 스타일 메서드를 생성합니다. 이 영역에는 스타일 속성을 정의하기 위해서 사용되는 메서드가 포함됩니다.

    region Styles Methods
        /// <summary>
        /// Write the style property to the WordprocessingML paragraph element.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the style to.</param>
        public void ApplyParagraphStyle(XmlWriter writer, string styleId) {
            // Apply the style in the paragraph properties.
            // <w:pPr>
            //   <w:pStyle w:val=”MyTitle” />
            // </w:pPr>
            writer.WriteStartElement(wordPrefix, "pPr", wordNamespace);
            writer.WriteStartElement(wordPrefix, "pStyle", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, styleId);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Write the style property to the WordprocessingML table element.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the style to.</param>
        public void ApplyTableStyle(XmlWriter writer, string styleId) {
            // Apply the style in the table properties.
            // <w:tblPr>
            //   <w:tblStyle w:val="MyTableStyle" />
            //   <w:tblW w:w="0" w:type="auto" /> 
            //   <w:tblLook w:val="04A0" /> 
            // </w:tblPr>
            writer.WriteStartElement(wordPrefix, "tblPr", wordNamespace);
            writer.WriteStartElement(wordPrefix, "tblStyle", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, styleId);
            writer.WriteEndElement();
            writer.WriteStartElement(wordPrefix, "tblW", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "w", wordNamespace, "0");
            writer.WriteAttributeString(wordPrefix, "type", wordNamespace, "auto");
            writer.WriteEndElement();
            writer.WriteStartElement(wordPrefix, "tblLook", wordNamespace);
            writer.WriteAttributeString(wordPrefix, "val", wordNamespace, "04A0");
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
        #endregion
    
  10. 영역 document.xml 쓰기 메서드를 생성합니다. 이 영역에는 문서의 이미지, 제목, 연락처 정보, 매출 소개 및 판매 지역별 표의 각 섹션의 컨텐츠를 정의하는 WordprocessingML 마크 업을 생성하기 위한 메서드가 포함되어 있습니다.

      region document.xml writing methods
        /// <summary>
        /// Writes an image within a paragraph
        /// into the WordprocessingML.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the image to.</param>
        private void WriteHeaderImage(XmlWriter writer) {
            // Load the drawing template into an XML document.
            XmlDocument drawingXml = new XmlDocument();
            string drawingXmlFile = System.Web.HttpContext.Current.Server.MapPath(drawingTemplate);
            drawingXml.Load(drawingXmlFile);
    
            // Load the drawing template into an XML document and pass the reference ID parameter.
            drawingXml.LoadXml(string.Format(drawingXml.InnerXml, _imagePartRID));
    
            // Write the wrapping paragraph and the drawing fragment.
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            drawingXml.DocumentElement.WriteContentTo(writer);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Writes a document title within a paragraph
        /// into the WordprocessingML.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the image to.</param>
        /// <param name="title">Document title</param>
        private void WriteDocumentTitle(XmlWriter writer, string title) {
            // Create the title.
            // <w:p>
            //   <w:r>
            //     <w:t>Sales Report - Employee Name</w:t>
            //   </w:r>
            // </w:p>    
    
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            WriteTitleParagraphProperties(writer);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            WriteTitleRunProperties(writer);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Sales Report - " + title);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Writes a document's contact information within a paragraph
        /// into the WordprocessingML.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the image to.</param>
        /// <param name="fullname">Employee's fullname</param>
        /// <param name="phone">Employee's phone number</param>
        /// <param name="email">Employee's e-mail address</param>
        private void WriteDocumentContactInfo(XmlWriter writer, string fullname, string phone, string email) {
    
            // Create the contact information section.
            // <w:p>
            //   <w:r>
            //     <w:t>Contact</w:t>
            //   </w:r>
            // </w:p>
    
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            WriteSubtitleParagraphProperties(writer);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            WriteSubtitleRunProperties(writer);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Contact");
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            // Fill in the contact information section.
            // <w:p>
            //   <w:r>
            //     <w:t>Employee's fullname</w:t>
            //     <w:br />
            //     <w:t>sEmployee's e-mail</w:t>
            //     <w:br />
            //     <w:t>Employee's phone</w:t>
            //   </w:r>
            // </w:p>
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, fullname);
            writer.WriteElementString(wordPrefix, "br", wordNamespace, null);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, email);
            writer.WriteElementString(wordPrefix, "br", wordNamespace, null);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, phone);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Writes the sales summary information within a paragraph
        /// into the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the page break to.</param>
        /// <param name="totalsales">Employee's sales YTD</param>
        /// <param name="salesquota">Employee's sales quota</param>
        private void WriteSalesSummaryInfo(XmlWriter writer, string totalsales, string salesquota) {
            string wordNamespace = "https://schemas.openxmlformats.org/wordprocessingml/2006/main";
            string wordPrefix = "w";
    
            // Create the sales summary section.
            // <w:p>
            //   <w:r>
            //     <w:t>Sales Summary</w:t>
            //   </w:r>
            // </w:p>
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            WriteSubtitleParagraphProperties(writer);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            WriteSubtitleRunProperties(writer);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Sales Summary");
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            // Fill in the contact information section.
            // <w:p>
            //   <w:r>
            //     <w:t>Total Sales:</w:t>
            //   </w:r>
            //   <w:r>
            //     <w:t>Employee's sales YTD</w:t>
            //   </w:r>
            //   <w:br />
            //   <w:r>
            //     <w:t>Employee's sales quota</w:t>
            //   </w:r>
            //   <w:r>
            //     <w:t>$1000.00</w:t>
            //   </w:r>
            // </w:p>
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Total Sales:");
            writer.WriteElementString(wordPrefix, "tab", wordNamespace, null);
            writer.WriteEndElement();
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, totalsales);
            writer.WriteEndElement();
            writer.WriteElementString(wordPrefix, "br", wordNamespace, null);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Sales Quota:");
            writer.WriteElementString(wordPrefix, "tab", wordNamespace, null);
            writer.WriteEndElement();
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, salesquota);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    
        /// <summary>
        /// Writes the territory sales totals as a table to the WordprocessingML document.
        /// </summary>
        /// <param name="writer">The XmlWriter to write the page break to.</param>
        private void WriteTerritoriesTable(XmlWriter writer, string territory) {
            string wordNamespace = "https://schemas.openxmlformats.org/wordprocessingml/2006/main";
            string wordPrefix = "w";
    
            // Create the territory section header.
            // <w:p>
            //   <w:r>
            //     <w:t>Sales by Territory</w:t>
            //   </w:r>
            // </w:p>
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            WriteSubtitleParagraphProperties(writer);
            ApplyParagraphStyle(writer, "Heading 3");
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            WriteSubtitleRunProperties(writer);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Sales by Territory - " + territory);
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            // Open the table element.
            writer.WriteStartElement(wordPrefix, "tbl",
               wordNamespace);
            ApplyTableStyle(writer, "LightList-Accent2");
    
            // Write table headings.
            writer.WriteStartElement(wordPrefix, "tr", wordNamespace);
            writer.WriteStartElement(wordPrefix, "tc", wordNamespace);
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "Employee");
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            writer.WriteStartElement(wordPrefix, "tc", wordNamespace);
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "2003");
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            writer.WriteStartElement(wordPrefix, "tc", wordNamespace);
            writer.WriteStartElement(wordPrefix, "p", wordNamespace);
            writer.WriteStartElement(wordPrefix, "r", wordNamespace);
            writer.WriteElementString(wordPrefix, "t", wordNamespace, "2004");
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.WriteEndElement();
    
            // write the close row
            writer.WriteEndElement();
    
            // write a row for each territory
            AdventureWorksSalesData salesData = new AdventureWorksSalesData();
            DataTable dt = salesData.GetSalesByTerritory(territory);
    
            foreach (DataRow myRow in dt.Rows) {
                writer.WriteStartElement(wordPrefix, "tr", wordNamespace);
    
                foreach (DataColumn myCol in dt.Columns) {
                    writer.WriteStartElement(wordPrefix, "tc", wordNamespace);
                    writer.WriteStartElement(wordPrefix, "p", wordNamespace);
                    writer.WriteStartElement(wordPrefix, "r", wordNamespace);
                    writer.WriteElementString(wordPrefix, "t", wordNamespace, myRow[myCol].ToString());
                    writer.WriteEndElement();
                    writer.WriteEndElement();
                    writer.WriteEndElement();
    
                }
                // Write the close row.
                writer.WriteEndElement();
            }
            // end the table element
            writer.WriteEndElement();
        }
    
        #endregion
    
    }
    
  11. 클래스를 닫습니다.

응용 프로그램을 실행한다

이것으로, 응용 프로그램을 실행하여 직원을 선택하고, 판매 보고서를 Word 2007 에 보낼 준비가 되었습니다. 프로그램에 의해서 생성된 리포트는 로컬 하드 디스크에 저장됩니다.

응용 프로그램을 실행하려면

  1. F5 키를 눌러 Web 응용 프로그램을 빌드하여 실행합니다.

  2. DropDownList 컨트롤로 직원을 선택합니다.

  3. [Export to Word 2007] 을 클릭합니다.

    생성된 문서의 파일 경로가 표시됩니다.

  4. 문서를 열고, 생성된 파일을 확인합니다.

서버측 솔루션으로 문서를 생성 한 후, 2007 Microsoft Office 프로그램을 사용하고 리포트를 열고, 문서를 표시 또는 조작할 수 있습니다.

이전 버전의 Microsoft Office 를 사용하는 사용자는 Word/Excel/PowerPoint 2007 파일 형식용 Microsoft Office 호환기능 팩을 다운로드하여  설치하여 생성된 문서를 열거나 편집 및 저장할 수 있습니다. 이 호환기능 팩에 의해서 Word 2007, Excel 2007 및 PowerPoint 2007 형식의 문서를 Office 2000, Office XP 또는 Office 2003 사용자와 공유할 수 있습니다.

요약

Open XML 형식에 의해서 서버측 솔루션의 가능성이 한층 더 커집니다. 서버에 Microsoft Office 응용 프로그램을 설치하지 않아도 문서 생성 및 조작할 수 있습니다. 또, 이 형식으로 조직에서는 LOB 데이터를 보이는 리포트를 제공할 수 있습니다.

Open XML 개체 모델을 사용하면, .NET Framework 기반 응용 프로그램에서 프로그램에 의해서 Open XML 문서 패키지 및 문서 파트를 생성 및 조작 프로세스가 쉬워집니다.

이 문서에서는 Open XML 개체 모델과 ASP.NET 2.0 을 사용하여, 서버측 Word 2007 문서 생성 솔루션을 구축하는 방법을 보여주었습니다. 같은 Open XML 개체 모델과 함께, SpreadsheetML 를 사용하여  리포트를 Excel 에 보내거나 PresentationML 를 사용하여  PowerPoint 프레젠테이션을 생성하는 프로세스를 자동화할 수 있습니다.