搶先目睹

適用於 SharePoint 開發的 Visual Studio 2010 工具

Steve Fox

對於許多開發人員來說,SharePoint 的平台開發既麻煩又難搞,因此常常摸不著頭緒。就連要使用哪種工具組,開發人員社群的意見也多分歧。例如,有些開發人員搭配使用類別庫、手動專案資料夾與 XML 組態檔,以及建置後輸出事件,來產生 SharePoint 的功能和方案。也有開發人員使用 STSDEV (一種社群工具),或 Visual Studio Extensions for Windows SharePoint Services (VSeWSS),來建置不同的應用程式和方案,然後將它們部署到 SharePoint。換句話說,開發人員將功能和方案套件部署到 SharePoint 可採用的方法不勝枚舉。儘管有這麼多挑戰,SharePoint 開發人員社群擴增的速度驚人 (目前約有 60 萬名開發人員),而且還在繼續擴大當中。展望未來,Visual Studio 2010 將透過隨附的 SharePoint 新工具,幫助開發人員踏入 SharePoint 開發的大門。

就開發平台而言,SharePoint 2010 向前邁進了一大步,不光是因為該平台支援豐富的功能組,也要歸功於針對工具組的設計所投入的大量投資,使得開發程序更具效率,也更方便各種程度的開發人員存取。SharePoint 2010 的兩種核心開發人員工具分別是 SharePoint Designer 2010 和 Visual Studio 2010 (適用於設計人員的附屬工具組為 Expression 套件)。本文將先探討 SharePoint 2010 開發,向您介紹 Visual Studio 2010 中的 SharePoint 工具 (包括一窺全新的專案範本),並說明建立和部署範例視覺化網頁組件的方法。

Visual Studio 2010 中的 SharePoint 工具

Visual Studio 2010 中有不少領域值得向 SharePoint 開發人員一提。首先,您有內建的 SharePoint 專案範本可用,而能夠直接著手進行方案開發。其次,工具已依 Windows SharePoint Package (WSP) 套件標準加以標準化,因此當您將方案匯入或部署到 SharePoint 時,Visual Studio 會將它視為方案套件。第三,Visual Studio 2010 中的 SharePoint 工具隨附了一些絕佳的部署和封裝功能,例如方案撤銷和自訂部署設定。最後,全新的 SharePoint 方案總管可讓您檢視存在 SharePoint 伺服器上的原生和自訂成品 (例如,清單和工作流程)。這些只代表 Visual Studio 工具組重大擴充的一小部分,此工具組的設計旨在深入社群,方便 SharePoint 開發人員著手進行開發。

另外值得一提的是 SharePoint 2010 幾項可明確運用在 Visual Studio 2010 環境內的增強功能。例如,全新的用戶端物件模型可讓您透過參考的 DLL,而不是 Web 服務呼叫,來存取 SharePoint 物件 (譬如,在 SharePoint 2007 中,您使用 ASP.NET Web 服務存取 SharePoint 清單資料)。另外,LINQ for SharePoint 也將 LINQ 的威力帶入 SharePoint,例如,讓您將清單視為強型別物件。再者,Silverlight (尤其是搭配用戶端物件模型) 在 SharePoint 2010 中也受到原生支援,而不用再管 web.config,即可直接投入開發工作。還有沙箱化方案也提供了一種方法建置 SharePoint 網頁組件,並且完全不需要系統管理的介入,即可將它們部署到網站,也就是說,您可以將網頁組件部署到 SharePoint 網站,然後使用裝載的 SharePoint 版本,在該網站的環境下,經由 SharePoint 內部執行個體或是雲端來執行該網頁組件。最後,外部資料清單使得與企業營運系統的互動如同讀/寫程序一樣單純,這看似微不足道,卻是向前躍進一大步,因為這樣的工具支援可讓您迅速且有效率地建置企業營運整合。對於 SharePoint 2010 中所有這些革新,Visual Studio 2010 無論是透過專案範本或 API,都為專業開發人員提供某種程度的支援。還在等什麼,現在正是學習 SharePoint 開發的大好時機。

開發視覺化網頁組件專案

開發人員在 SharePoint 中最常建置和部署的成品之一,便是網頁組件。這很理所當然,畢竟網頁組件是 SharePoint 的核心建置區塊之一。由於 SharePoint 是以 ASP.NET 為基礎建置而成,因此網頁組件也繼承了 ASP.NET 網頁組件架構的重要特性。

Visual Studio 2010 的其中一個新專案範本是「視覺化網頁組件」專案範本,它可讓開發人員透過視覺的方式設計能夠部署到 SharePoint 的網頁組件。如果您剛接觸 SharePoint,這是開始針對 SharePoint 2010 建置自訂應用程式的絕佳方法。我要示範的視覺化網頁組件有一些獨立程式碼,會計算產品成本並列出一個簡單的網頁組件 UI 內的資訊。


[圖 1] 全新的 SharePoint 專案範本


[圖 2] 視覺化網頁組件的設計工具檢視

請確定將 Visual Studio 2010 的 Beta 2 版和 SharePoint 2010 的 Beta 2 版安裝在 64 位元 Windows Server 2008 上。開啟 Visual Studio 2010,依序按一下 [檔案]、[新增專案],然後在 [已安裝的範本] 區段中瀏覽至 SharePoint 節點。[圖 1] 中顯示了各種不同類型的可用專案範本。例如,「匯入 VSeWSS 專案」範本可讓您升級目前的 VSeWSS 專案;工作流程範本可讓您建立工作流程專案,並將它部署到 SharePoint;「網站定義」範本提供網站層級基礎結構,讓您能夠向外建置和部署;而「匯入 SharePoint 方案套件」是一種可讓您匯入 WSP 以重新部署到本機伺服器執行個體的範本。針對此逐步說明的範例,請選取「視覺化網頁組件」專案範本,為您的專案提供名稱 (例如,SampleWebPartProject) 和位置,然後按一下 [確定]。

建立專案之後,Visual Studio 2010 會建立一些預設檔案。在方案總管中展開該專案節點,來查看所有檔案。您在本文中要使用的主要檔案位於 SampleWebPartProject 節點中。請注意,預設的視覺化網頁組件稱為 VisualWebPart1。若要變更此名稱,在方案總管的 VisualWebPart1 節點上按滑鼠右鍵,選取 [重新命名],然後為您的網頁組件輸入新名稱。

也請留意方案總管中的 [功能和套件] 節點。這些都是 Visual Studio 2010 的新基礎結構組件,它們使用 SharePoint 功能來封裝 SharePoint 方案。對於 SharePoint 的開發新手,所謂的功能會以 SharePoint 可理解的方式來組織應用程式。譬如,您可以在網站或 Web 層級將功能部署到 SharePoint。功能是透過一組 XML 組態檔建構而成,它也會參考 (根據應用程式的信任層級而定) 全域組件快取 (GAC) 的組件。更具體地說,每項功能在 SharePoint 資料夾階層中都有自己的資料夾,而且組態檔是存在資料夾內,並且提供功能必要的中繼資料。套件則包含功能和其他資產,並且會在您將方案部署到 SharePoint 時用到。套件也會判定組件部署的位置。Visual Studio 2010 推出了套件設計工具,更方便檢視和管理套件。按兩下 [套件] 節點,即可開啟設計工具。設計工具讓您能夠從可部署的套件新增和移除功能。此設計工具在幫助開發人員透過新增功能塑造其 SharePoint 方案方面有相當大的功勞。

切換回方案總管檢視,在 ProductInfoUserControl.ascx 檔案上按滑鼠右鍵,然後選取 [在設計工具中檢視]。此動作會開啟一個檢視,讓您能夠將控制項從工具箱拖放到網頁組件設計工具表面上。您會看到三個檢視:[設計]、[分割] 和 [程式碼]。在本例中,我新增了 (用輸入的方式) 標題和一些控制項,包括一些文字方塊和一個用來計算產品成本的按鈕。我還為加入網頁的控制項輸入了標籤 (請參閱 [圖 2])。

在完成視覺化網頁組件的配置後,您可為按鈕新增事件處理常式。但在進行此動作之前,先讓我們迅速看一下視覺化網頁組件的原始程式碼。您可以從 [圖 3] 中的程式碼摘錄看出,Visual Studio 以 CSS 語法的形式自動在 UI 中加入樣式。您也看得到構成 UI 的實際控制項 (以下拉式清單為例,即項目集合)。請注意,為了簡潔起見,我移除了自動產生並包含在原始碼上方的指示詞。

若要新增事件處理常式到網頁組件,按兩下按鈕。這會把您帶到後置程式碼。它還會新增 onClick 事件到 ASCX 控制項設計。例如,注意 [圖 3] 中包含在 btnCalcPrice 內的 onclick="btnCalcPrice_Click" 事件。[圖 4] 中所列的後置程式碼包含一些簡單的程式碼,可讓您計算清單方塊中所選產品的價格。程式碼重要的地方在於類別層級變數 (雙精度),代表我普通用來計算產品成本的方法;List of Products 集合 (容納新增至清單方塊的眾多 Products 物件);以及 btnCalcPrice_Click 事件。當網頁在 SharePoint 中載入時,此程式碼會呼叫 generateProductList 方法,而此方法會填入清單方塊。btnCalcPrice_Click 事件接著會根據使用者的選擇,計算特定產品的成本,並將該資訊顯示在 UI 的清單方塊中。

[圖 3] SalaryCalcWebPartUserControl.ascx 的原始程式碼

<style type="text/css">
.style1
{
font-family: Calibri;
font-size: medium;
font-weight: bold;
}
.style2
{
font-family: Calibri;
font-size: small;
font-weight: bold;
}
</style>
<p class="style1">
Product Catalog</p>
<p class="style2">
Product:  
<asp:DropDownList ID="dropdwnProducts" 
runat="server" Height="20px"
style="margin-left: 21px" Width="200px">
<asp:ListItem>Helmet</asp:ListItem>
<asp:ListItem>Stick</asp:ListItem>
<asp:ListItem>Skates</asp:ListItem>
<asp:ListItem>Elbow Pads</asp:ListItem>
<asp:ListItem>Kneepads</asp:ListItem>
</asp:DropDownList>
</p>
<p class="style2">
Description: <asp:TextBox ID="txtbxDescription" runat="server"
Width=”200px” Enabled=”False”></asp:TextBox>
</p>
<p class="style2">
SKU:
<asp:TextBox ID="txtbxSKU" runat="server" style="margin-left: 48px"
Width="200px" Enabled="False"></asp:TextBox>
</p>
<p class="style2">
Price:<asp:TextBox ID="txtbxPrice" runat="server"
style="margin-left: 48px"
Width="200px" Enabled="False"></asp:TextBox>
</p>
<p class="style2">
Quantity:
<asp:TextBox ID="txtbxQuantity" runat="server" 
Width="200px" Enabled="False"></asp:TextBox>
</p>
<p class="style1">
<asp:Button ID="btnCalcPrice" runat="server"
onclick="btnCalcPrice_Click"
Text="Calc." />
</p>

當使用者按下按鈕時,網頁組件會執行回傳來執行事件,在此處即計算產品成本。比 [圖 4] 中的程式碼 (基本上相當簡單) 更有趣的地方,其實在於網頁組件如何將此程式碼呈現在實際的網頁組件中。我們目前為止所做的,是為包含面板和後置程式碼的網頁組件建立一個 ASP 使用者控制項,但是專案結構還是必須有實際的網頁組件來呈現此控制項。為了這麼做,Visual Studio 建立一個稱為 _ascxPath 的字串,來代表 ASCX 使用者控制項位在 SharePoint 2010 資料夾階層內的路徑。另外也請注意 CreateChildControls 方法中建立了一個控制項的執行個體,並且將路徑設定為使用者控制項 (使用 LoadControl 方法)。隨後使用 Add 方法將其新增至 Controls 集合中。這容許網頁組件在 SharePoint 的網頁組件內呈現 ASP 使用者控制項。[圖 5] 顯示該程式碼。

[圖 4] ProductInfoUserControl.ascx.cs 的原始程式碼

using System;
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Collections.Generic;
using System.Data;
namespace SampleWebPartProject.ProductInfo
{
public partial class ProductInfoUserControl : UserControl
{
double tax = .11;
double totalCost = 0.0;
List<Products> lstOfProducts = new List<Products>();
protected void Page_Load(object sender, EventArgs e)
{
generateProductList();
}
private void generateProductList()
{
lstOfProducts.Add(new Products()
{ strName = "Helmet", strDescr = "Hockey helmet.", strSKU =
"KLSONHELMT1224", dblPrice = 59.00, intQuantity = 28 });
lstOfProducts.Add(new Products()
{ strName = "Skates", strDescr = "Hockey skates.", strSKU =
"SKATWOKSH0965", dblPrice = 438.00, intQuantity = 88 });
lstOfProducts.Add(new Products()
{ strName = "Stick", strDescr = "Composite hockey stick.",
strSKU = "STIK82910JJKS", dblPrice = 189.99, intQuantity =
35 });
lstOfProducts.Add(new Products()
{ strName = "Elbow Pads", strDescr = "Hockey elbow pads.",
strSKU = "ELBOP563215NN", dblPrice = 34.00, intQuantity =
12 });
lstOfProducts.Add(new Products()
{ strName = "Knee Pads", strDescr = "Hockey knee pads.",
strSKU = "KPDS7827NNJS1", dblPrice = 47.99, intQuantity =
44 });
}
protected void btnCalcPrice_Click(object sender, EventArgs e)
{
double dblCost = 0;
string strPrice = "";
if (dropdwnProducts.SelectedValue == "Helmet")
{
dblCost = lstOfProducts[0].dblPrice;
totalCost = dblCost + (dblCost * tax);
System.Math.Round(totalCost, 2);
strPrice = "$" + totalCost.ToString();
txtbxDescription.Text = lstOfProducts[0].strDescr.
ToString();
txtbxSKU.Text = lstOfProducts[0].strSKU.ToString();
txtbxPrice.Text = strPrice;
txtbxQuantity.Text = lstOfProducts[0].intQuantity.
ToString();
}
else if (dropdwnProducts.SelectedValue == "Skates")
{
dblCost = lstOfProducts[1].dblPrice;
totalCost = dblCost + (dblCost * tax);
System.Math.Round(totalCost, 2);
strPrice = "$" + totalCost.ToString();
txtbxDescription.Text = lstOfProducts[1].strDescr.
ToString();
txtbxSKU.Text = lstOfProducts[1].strSKU.ToString();
txtbxPrice.Text = strPrice;
txtbxQuantity.Text = lstOfProducts[1].intQuantity.
ToString();
}
else if (dropdwnProducts.SelectedValue == "Stick")
{
dblCost = lstOfProducts[2].dblPrice;
totalCost = dblCost + (dblCost * tax);
System.Math.Round(totalCost, 2);
strPrice = "$" + totalCost.ToString();
txtbxDescription.Text = lstOfProducts[2].strDescr.
ToString();
txtbxSKU.Text = lstOfProducts[2].strSKU.ToString();
txtbxPrice.Text = strPrice;
txtbxQuantity.Text = lstOfProducts[2].intQuantity.
ToString();
}
else if (dropdwnProducts.SelectedValue == "Elbow Pads")
{
dblCost = lstOfProducts[3].dblPrice;
totalCost = dblCost + (dblCost * tax);
System.Math.Round(totalCost, 2);
strPrice = "$" + totalCost.ToString();
txtbxDescription.Text = lstOfProducts[3].strDescr.
ToString();
txtbxSKU.Text = lstOfProducts[3].strSKU.ToString();
txtbxPrice.Text = strPrice;
txtbxQuantity.Text = lstOfProducts[3].intQuantity.
ToString();
}
else if (dropdwnProducts.SelectedValue == "Knee Pads")
{
dblCost = lstOfProducts[4].dblPrice;
totalCost = dblCost + (dblCost * tax);
System.Math.Round(totalCost, 2);
strPrice = "$" + totalCost.ToString();
txtbxDescription.Text = lstOfProducts[4].strDescr.
ToString();
txtbxSKU.Text = lstOfProducts[4].strSKU.ToString();
txtbxPrice.Text = strPrice;
txtbxQuantity.Text = lstOfProducts[4].intQuantity.
ToString();
}
}
}
}

[圖 5] ProductInfo.cs 的原始程式碼

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
namespace SampleWebPartProject.ProductInfo
{
public class ProductInfo : WebPart
{
private const string _ascxPath =
@"~/CONTROLTEMPLATES/SampleWebPartProject/ProductInfo/" +
@"ProductInfoUserControl.ascx";
public ProductInfo()
{
}
protected override void CreateChildControls()
{
Control control = this.Page.LoadControl(_ascxPath);
Controls.Add(control);
base.CreateChildControls();
}
protected override void Render(HtmlTextWriter writer)
{
base.RenderContents(writer);
}
}
}

[圖 6] ProductInfo.webpart XML 檔案

<?xml version="1.0" encoding="utf-8"?>
<webParts>
<webPart xmlns="https://schemas.microsoft.com/WebPart/v3">
<metaData>
<type name="SampleWebPartProject.ProductInfo.ProductInfo,
SampleWebPartProject, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=db3a9f914308c42a" />
<importErrorMessage>
$Resources:core,ImportErrorMessage;
</importErrorMessage>
</metaData>
<data>
<properties>
<property name="Title" type="string">
Product Info Web Part</property>
<property name="Description" type="string">Provides some
information about hockey products.</property>
</properties>
</data>
</webPart>
</webParts>

視覺化網頁組件建置好之後,就可以將它部署到 SharePoint 伺服器。當建立專案時,您是將它設定為與特定伺服器執行個體相關聯。這是暗指會進行一些程式設計的整合工作,把您剛剛撰寫的程式碼與 SharePoint 伺服器結合起來。如果您檢閱方案總管內的檔案,就不難發現促進這項整合的 XML 檔案其實還不少。例如,Feature.xml 檔案 (請參閱下列的程式碼) 提供功能的定義。您可以在 XML 中看到該檔案參考其他幾個同樣提供網頁組件相關特定資訊的 XML 檔案。您在這裡可以看到參考的是 Elements.xml 和 ProductInfo.webpart:

<?xml version="1.0" encoding="utf-8"?>
<Feature xmlns="https://schemas.microsoft.com/sharepoint/" 
Id="416172c1-cfa7-4d7a-93ba-fe093b037fab" 
ImageUrl="" Scope="Site" Title="SampleWebPartProject Feature1">
  <ElementManifests>
    <ElementManifest Location="ProductInfo\Elements.xml" />
    <ElementFile Location="ProductInfo\ProductInfo.webpart" />
  </ElementManifests>

Elements.xml 提供包含在功能內之核心組件的相關資訊,而 ProductInfo.webpart 則定義與網頁組件相關的中繼資料,例如其標題和描述等。比如,[圖 6] 顯示預設的 Title 和 Description 屬性。您可以更新這些屬性,以確保網頁組件庫內公開的網頁組件中繼資料既符合直覺而且有意義。以此網頁組件為例,您應該把標題修改為「產品資訊網頁組件」,把描述改為「提供計算的產品價格和相關資訊的網頁組件」。


[圖 7] 網頁組件頁面上的網頁組件

另外還有其他 XML 組態檔,如果您是 SharePoint 開發新手,我鼓勵您一一檢閱專案中的檔案,進一步了解它們的用途。現在讓我們繼續討論將網頁組件部署到 SharePoint 伺服器。

部署視覺化網頁組件專案

在 SharePoint 2010 之前,Stsadm 這個命令列導向的系統管理工具,一般是用來部署應用程式至 SharePoint。而在 Visual Studio 2010 中 (加上 Window PowerShell 的推出,不過這個主題值得另闢文章加以探討) 已經完全不需要這麼做。因為您的專案與 SharePoint 伺服器已經有關聯性,而此關聯具有已定義的信任層級,您只需要在專案上按滑鼠右鍵,然後選取 [建置],確定方案確實已建置,再按一次右鍵然後選取 [部署] 就大功告成了。當然啦,在偵錯 SharePoint 方案時,使用 F5 鍵也可以達成同樣的效果。藉由這麼做,偵錯時可包含不同的步驟,例如附加適當的處理序,以及重設 IIS 等。

順利部署網頁組件後,您必須開啟 SharePoint 網站,建立一個新的網頁組件頁面。如果您按下 F5 來偵錯應用程式,在預設的情況下會叫用 [建立網頁組件] 頁面。否則請按一下 [檢視所有網站內容],再按一下 [建立]。按一下 [網頁組件頁面] 選項,然後針對該特定網頁組件頁面提供所要求的相關資訊。例如,為該網頁提供名稱和配置範本。輸入這些資訊之後,按一下 [建立],而 SharePoint 便會建立您的網頁組件頁面。

現在您必須將建立和部署好的視覺化網頁組件新增到伺服器。要這麼做,請瀏覽至 [網頁組件] 頁面,按一下 [網站動作],再按 [編輯頁面]。在您想要放置視覺化網頁組件的地方按一下網頁組件區域,按一下 [插入] 索引標籤,再按 [插入] 索引標籤上的 [網頁組件]。

完成此動作之後,SharePoint 會公開數個網頁組件類別,供您瀏覽並選取特定的網頁組件,以便新增至您在頁面上所選的網頁組件區域。瀏覽至 [自訂] 類別,就可以在 [網頁組件] 窗格中看到您建立和部署的視覺化網頁組件。如果您有跟著使用本文中的程式碼,請按一下 ProductInfo 網頁組件,再按 [新增] 按鈕。

該網頁組件現在會新增到 [網頁組件] 頁面上的網頁組件區域,如 [圖 7] 所示。您在此時可以透過 [工具] 窗格設定 [網頁組件] 選項,或者您可以直接接受預設選項,並按一下 [停止編輯]。

投入 SharePoint 開發

對於 SharePoint 開發人員來說,Visual Studio 2010 不僅提供一組原生工具套件,更提供投入 SharePoint 開發的難得機會。我建議您不妨試試這些工具。對於想要全權掌控其程式碼的開發人員,以及喜愛 SharePoint 中建置和部署方案的設計經驗的人來說,都可以找到一些實用的選項。

Steve Fox* 是 Microsoft 開發人員與平台推廣者 (Developer and Platform Evangelist) 小組的資深技術推廣者。他把大部分時間花在與客戶合作開發 Office 和 SharePoint 上。Fox 發行過不少書籍和文章,並且經常在世界各地的開發人員研討會中發表演說。*