다음을 통해 공유


연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 2부

Visual Studio에서 SharePoint 프로젝트 항목의 사용자 지정 형식을 정의하여 프로젝트 템플릿과 연결한 후에는 템플릿에 대한 마법사를 제공할 수 있습니다.마법사를 사용하면 사용자가 템플릿을 사용하여 해당 프로젝트 항목이 포함된 새 프로젝트를 만들 때 사용자에게서 정보를 수집할 수 있습니다.수집한 정보는 프로젝트 항목을 초기화하는 데 사용될 수 있습니다.

이 연습에서는 연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 1부에서 설명한 사이트 열 프로젝트 템플릿에 마법사를 추가합니다.사용자가 사이트 열 프로젝트를 만들 때 마법사에서는 사이트 열에 대한 정보(예: 기본 형식 및 그룹)를 수집하고 이 정보를 새 프로젝트의 Elements.xml 파일에 추가합니다.

이 연습에서는 다음 작업을 수행합니다.

  • 프로젝트 템플릿과 연결된 사용자 지정 SharePoint 프로젝트 항목 형식을 위한 마법사 만들기

  • 사용자 지정 마법사 Visual Studio SharePoint 프로젝트에 대 한 기본 마법사와 비슷한 UI를 정의 합니다.

  • 마법사가 실행되는 동안 로컬 SharePoint 사이트를 호출하는 데 사용되는 두 가지 SharePoint 명령 만들기.SharePoint 명령은 Visual Studio 확장이 SharePoint 서버 개체 모델에서 API를 호출하기 위해 사용할 수 있는 메서드입니다.자세한 내용은 SharePoint 개체 모델 호출을 참조하십시오.

  • 대체 가능한 매개 변수를 사용하여 마법사에서 수집한 데이터로 SharePoint 프로젝트 파일 초기화

  • 각각의 새 사이트 열 프로젝트 인스턴스에서 .snk 파일 새로 만들기이 파일은 SharePoint 솔루션 어셈블리가 전역 어셈블리 캐시에 배포될 수 있도록 프로젝트 출력에 서명하는 데 사용됩니다.

  • 마법사 디버깅 및 테스트

[!참고]

https://go.microsoft.com/fwlink/?LinkId=191369에서 이 연습에 대한 완료된 프로젝트, 코드 및 기타 파일이 포함된 샘플을 다운로드할 수 있습니다.

사전 요구 사항

이 연습을 수행하려면 먼저 연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 1부를 완료하여 SiteColumnProjectItem 솔루션을 만들어야 합니다.

또한 개발 컴퓨터에 다음 구성 요소가 있어야 이 연습을 완료할 수 있습니다.

다음 개념을 알고 있으면 연습을 완료하는 데 도움이 되지만 반드시 필요하지는 않습니다.

마법사 구성 요소 이해

이 연습에서 보여 주는 마법사에는 몇 가지 구성 요소가 포함되어 있습니다.다음 표에서는 이러한 구성 요소에 대해 설명합니다.

구성 요소

설명

마법사 구현

IWizard 인터페이스를 구현하는 SiteColumnProjectWizard라는 클래스입니다.이 인터페이스는 마법사가 시작되고 완료될 때와 마법사 실행 중의 특정한 때에 Visual Studio에서 호출하는 메서드를 정의합니다.

마법사 UI

WizardWindow라는 WPF 기반 창입니다.이 창에는 Page1 및 Page2라는 두 가지 사용자 정의 컨트롤이 포함되어 있습니다.이러한 사용자 정의 컨트롤은 마법사의 두 페이지를 나타냅니다.

이 연습에서 마법사 구현의 RunStarted 메서드는 마법사 UI를 표시합니다.

마법사 데이터 모델

마법사 UI와 마법사 구현 간의 계층을 제공하는 SiteColumnWizardModel이라는 중간 클래스입니다.이 샘플에서는 이 클래스를 사용하여 마법사 구현과 마법사 UI를 서로 추상화합니다. 이 클래스는 모든 마법사의 필수 구성 요소가 아닙니다.

이 연습에서 마법사 구현은 마법사 UI를 표시할 때 SiteColumnWizardModel 개체를 마법사 창에 전달합니다.마법사 UI는 이 개체의 메서드를 사용하여 UI의 컨트롤 값을 저장하고 입력 사이트 URL이 유효한지 확인하는 등의 작업을 수행합니다.사용자가 마법사를 완료한 후 마법사 구현은 SiteColumnWizardModel 개체를 사용하여 UI의 최종 상태를 확인합니다.

프로젝트 서명 관리자

마법사 구현이 각각의 새 프로젝트 인스턴스에서 key.snk 파일을 새로 만드는 데 사용하는 ProjectSigningManager라는 도우미 클래스입니다.

SharePoint 명령

마법사가 실행되는 동안 마법사 데이터 모델에서 로컬 SharePoint 사이트를 호출하는 데 사용하는 메서드입니다.SharePoint 명령이 .NET Framework 3.5를 대상으로 해야 하기 때문에 이러한 명령은 마법사 코드의 나머지 부분과 다른 어셈블리에서 구현됩니다.

프로젝트 만들기

이 연습을 완료하려면 연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 1부에서 만든 SiteColumnProjectItem 솔루션에 몇 가지 프로젝트를 추가해야 합니다.

  • WPF 프로젝트.이 프로젝트에서 IWizard 인터페이스를 구현하고 마법사 UI를 정의합니다.

  • SharePoint 명령을 정의하는 클래스 라이브러리 프로젝트.이 프로젝트는 .NET Framework 3.5를 대상으로 해야 합니다.

먼저 프로젝트를 만들어 연습을 시작합니다.

WPF 프로젝트를 만들려면

  1. Visual Studio, SiteColumnProjectItem 솔루션을 엽니다.

  2. 솔루션 탐색기, 바로 가기 메뉴를 열고를 SiteColumnProjectItem 솔루션 노드를 선택 추가, 다음 선택 새 프로젝트.

    [!참고]

    Visual Basic 프로젝트에서는 General, Projects and Solutions, Options Dialog Box에서 솔루션 항상 표시 확인란을 선택한 경우에만 솔루션 노드가 표시됩니다.

  3. 상단에 있는 새 프로젝트 추가 대화 상자에서 있는지 확인 하십시오 .NET Framework 4.5 .NET Framework 버전의 목록에서 선택 됩니다.

  4. 확장은 C# 노드 또는 Visual Basic 노드를 선택 하 고 있는 Windows 노드.

  5. 프로젝트 템플릿 목록에서 선택 WPF 사용자 컨트롤 라이브러리, 프로젝트의 이름을 ProjectTemplateWizard, 다음 선택은 확인 단추입니다.

    Visual Studio추가 된 ProjectTemplateWizard 프로젝트를 솔루션 및 기본 UserControl1.xaml 파일을 엽니다.

  6. 프로젝트에서 UserControl1.xaml 파일을 삭제합니다.

SharePoint 명령 프로젝트를 만들려면

  1. 솔루션 탐색기, SiteColumnProjectItem 솔루션 노드에 대 한 바로 가기 메뉴를 열고 추가, 다음 선택 새 프로젝트.

  2. 상단에는 새 프로젝트 추가 대화 상자에서 선택 .NET Framework 3.5 .NET Framework 버전의 목록에서입니다.

  3. 확장 된 C# 노드 또는 Visual Basic 노드를 다음 선택은 Windows 노드.

  4. 선택은 클래스 라이브러리 : 프로젝트 이름, 프로젝트 템플릿 SharePointCommands, 다음 선택은 확인 단추.

    Visual Studio추가 된 SharePointCommands 프로젝트를 솔루션 및 기본 Class1 코드 파일을 엽니다.

  5. 프로젝트에서 Class1 코드 파일을 삭제합니다.

프로젝트 구성

마법사를 만들기 전에 일부 코드 파일과 어셈블리 참조를 프로젝트에 추가 해야 합니다.

마법사 프로젝트를 구성하려면

  1. 솔루션 탐색기, 바로 가기 메뉴를 열고를 ProjectTemplateWizard 프로젝트 노드 및 다음 선택 속성.

  2. 프로젝트 디자이너, 선택의 응용 프로그램 C# 프로젝트에 대 한 탭 또는 컴파일 Visual Basic 프로젝트에 대 한 탭입니다.

  3. .NET Framework 4.5.NET Framework 4.5 클라이언트 프로필이 아닌 대상 프레임 워크 설정 되어 있는지 확인 하십시오.

    자세한 내용은 방법: 한 버전의 .NET Framework를 대상으로 지정을 참조하십시오.

  4. 바로 가기 메뉴를 열고는 ProjectTemplateWizard 선택, 프로젝트 추가, 다음 선택 새 항목.

  5. 선택은 창 (WPF) : 항목 이름, 항목 WizardWindow, 다음 선택은 추가 단추.

  6. 두 개의 추가 사용자 컨트롤 (WPF) 항목을 프로젝트에 하 고 이름을 1 페이지 및 2 페이지.

  7. 네 코드 파일을 프로젝트에 추가 하 고 다음 이름 들이므로.

    • SiteColumnProjectWizard

    • SiteColumnWizardModel

    • ProjectSigningManager

    • CommandIds

  8. 바로 가기 메뉴를 열고를 ProjectTemplateWizard 프로젝트 노드 및 다음 선택 참조 추가.

  9. 확장은 어셈블리 노드를 선택 된 확장 노드 및 다음 다음 어셈블리 옆에 있는 확인란을 선택:

    • EnvDTE

    • Microsoft.VisualStudio.OLE.Interop

    • Microsoft.VisualStudio.SharePoint

    • Microsoft.VisualStudio.Shell.11.0

    • Microsoft.VisualStudio.Shell.Interop.10.0

    • Microsoft.VisualStudio.Shell.Interop.11.0

    • Microsoft.VisualStudio.TemplateWizardInterface

  10. 선택은 확인 어셈블리를 프로젝트에 추가 하는 단추입니다.

  11. 솔루션 탐색기아래에서 참조 폴더에는 ProjectTemplateWizard 프로젝트를 선택 EnvDTE.

    [!참고]

    Visual Basic 프로젝트에서는 General, Projects and Solutions, Options Dialog Box에서 솔루션 항상 표시 확인란을 선택한 경우에만 참조 폴더가 표시됩니다.

  12. 속성 창에서 값을 변경의 Interop 형식 포함 속성을 False.

  13. Visual Basic 프로젝트를 개발 하는 경우 가져오기는 ProjectTemplateWizard 네임 스페이스를 사용 하 여 프로젝트에 있는 프로젝트 디자이너.

    자세한 내용은 방법: 가져온 네임스페이스 추가 또는 제거(Visual Basic)을 참조하십시오.

SharePointCommands 프로젝트를 구성하려면

  1. 솔루션 탐색기, 선택은 SharePointCommands 프로젝트 노드.

  2. 메뉴 표시줄에서 선택 프로젝트, 기존 항목 추가.

  3. 기존 항목 추가 대화 상자 ProjectTemplateWizard 프로젝트에 대 한 코드 파일이 들어 있는 폴더를 찾은 다음 선택은 CommandIds 코드 파일입니다.

  4. 옆에 있는 화살표를 선택한는 추가 단추를 클릭 한 다음 선택은 링크로 추가 표시 된 메뉴에서 옵션을.

    Visual Studio코드 파일에 추가 된 SharePointCommands 프로젝트에 링크로.코드 파일은 ProjectTemplateWizard 프로젝트, 하지만 파일의 코드 또한 컴파일할에서 SharePointCommands 프로젝트.

  5. SharePointCommands 프로젝트에 명령 이라고 하는 다른 코드 파일을 추가 합니다.

  6. SharePointCommands 프로젝트를 선택 하 고 메뉴 표시줄에서 선택 프로젝트, 참조 추가.

  7. 확장은 어셈블리 노드를 선택 된 확장 노드 및 다음 다음 어셈블리 옆에 있는 확인란을 선택:

    • Microsoft.SharePoint

    • Microsoft.VisualStudio.SharePoint.Commands

  8. 선택은 확인 어셈블리를 프로젝트에 추가 하는 단추입니다.

마법사 모델, 서명 관리자 및 SharePoint 명령 ID 만들기

샘플에서 다음 구성 요소를 구현하는 코드를 ProjectTemplateWizard 프로젝트에 추가합니다.

  • SharePoint 명령 ID.이러한 문자열 마법사를 사용 하 여 SharePoint 명령을 식별 합니다.이 연습의 뒷부분에서 명령을 구현 하는 SharePointCommands 프로젝트에 코드를 추가 합니다.

  • 마법사 데이터 모델

  • 프로젝트 서명 관리자

이러한 구성 요소에 대한 자세한 내용은 마법사 구성 요소 이해를 참조하십시오.

SharePoint 명령 ID를 정의하려면

  • ProjectTemplateWizard 프로젝트 CommandIds 코드 파일을 열고이 파일의 전체 내용을 다음 코드로 대체 합니다.

    Namespace Contoso.SharePoint.Commands
        Public Class CommandIds
            Public Const GetFieldTypes As String = "Contoso.Commands.GetFieldTypes"
            Public Const ValidateSite As String = "Contoso.Commands.ValidateSite"
        End Class
    End Namespace
    
    namespace Contoso.SharePoint.Commands
    {
        public static class CommandIds
        {
            public const string GetFieldTypes = "Contoso.Commands.GetFieldTypes";
            public const string ValidateSite = "Contoso.Commands.ValidateSite";
        }
    }
    

마법사 모델을 만들려면

  • SiteColumnWizardModel 코드 파일을 열고이 파일의 전체 내용을 다음 코드로 대체 합니다.

    Imports EnvDTE
    Imports Microsoft.VisualStudio.SharePoint
    Imports Microsoft.VisualStudio
    Imports Microsoft.VisualStudio.Shell
    Imports Microsoft.VisualStudio.Shell.Interop
    Imports IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider
    
    Public Class SiteColumnWizardModel
        Private dteObject As DTE
        Private projectServiceValue As ISharePointProjectService
        Private validatedUrls As New List(Of String)
    
        Friend Sub New(ByVal dteObject As DTE, ByVal requiresFarmPriveleges As Boolean)
            Me.dteObject = dteObject
    
            ' Initialize default values for wizard choices.
            IsSandboxed = Not requiresFarmPriveleges
            IsSecondPagePopulated = False
            FieldType = "Text"
            FieldGroup = "Custom Columns"
            FieldName = "My Custom Column"
            CurrentSiteUrl = GetLocalHostUrl()
        End Sub
    
    #Region "Helper methods used by the wizard UI"
    
        ' Specifies whether the current site URL is valid. Uses the ValidateSite SharePoint command to do this.
        Friend Function ValidateCurrentUrl(ByVal errorMessage As String) As Boolean
            Dim isValid As Boolean = False
            errorMessage = String.Empty
    
            If validatedUrls.Contains(CurrentSiteUrl) Then
                isValid = True
            Else
                Dim uriToValidate As Uri = New Uri(CurrentSiteUrl, UriKind.Absolute)
                Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = Nothing
    
                Try
                    vsThreadedWaitDialog = ShowProgressDialog("Connect to SharePoint",
                        "Connecting to SharePoint site " + CurrentSiteUrl)
                    isValid = Me.ProjectService.SharePointConnection.ExecuteCommand(Of Uri, Boolean)(
                        Contoso.SharePoint.Commands.CommandIds.ValidateSite, uriToValidate)
                Catch ex As Exception
                    errorMessage = "An error occurred while validating the site. " + ex.Message
                Finally
                    If isValid Then
                        validatedUrls.Add(CurrentSiteUrl)
                    End If
                    If vsThreadedWaitDialog IsNot Nothing Then
                        CloseProgressDialog(vsThreadedWaitDialog)
                    End If
                End Try
            End If
            Return isValid
        End Function
    
        ' Gets the available field types from the SharePoint site. Uses the GetFieldTypes SharePoint command to do this.
        Friend Function GetFieldTypes() As ArrayList
            ' If we have not yet validated this site, do it now.
            Dim errorMessage As String = String.Empty
            If Not ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    CurrentSiteUrl, errorMessage), "SharePoint Connection Error")
                Return Nothing
            End If
    
            ' Site is valid, so go ahead and get the available field types.
            Dim siteUri As Uri = New Uri(CurrentSiteUrl, UriKind.Absolute)
            Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = ShowProgressDialog(
                "Connect to SharePoint", "Connecting to SharePoint site " + CurrentSiteUrl)
            Dim fieldTypesArray As String() = Me.ProjectService.SharePointConnection.ExecuteCommand(Of Uri, String())(
                Contoso.SharePoint.Commands.CommandIds.GetFieldTypes, siteUri)
    
            If vsThreadedWaitDialog IsNot Nothing Then
                CloseProgressDialog(vsThreadedWaitDialog)
            End If
    
            Return New ArrayList(fieldTypesArray)
        End Function
    
        ' Returns the default column group names in SharePoint.
        Friend Function GetFieldGroups() As List(Of String)
            Dim groups As List(Of String) = New List(Of String)()
            groups.Add("Base Columns")
            groups.Add("Core Contact and Calendar Columns")
            groups.Add("Core Document Columns")
            groups.Add("Core Task and Issue Columns")
            groups.Add("Extended Columns")
            Return groups
        End Function
    #End Region
    
    #Region "Properties shared by the wizard implementation and the wizard UI"
    
        Friend ReadOnly Property ProjectService As ISharePointProjectService
            Get
                If projectServiceValue Is Nothing Then
                    projectServiceValue = GetProjectService()
                End If
                Return projectServiceValue
            End Get
        End Property
    
        Friend Property IsSecondPagePopulated As Boolean
        Friend Property IsSandboxed As Boolean
        Friend Property FieldType As String
        Friend Property FieldGroup As String
        Friend Property FieldName As String
        Friend Property CurrentSiteUrl As String
    #End Region
    
    #Region "Private methods"
    
        Private Function GetLocalHostUrl() As String
            Const HttpScheme As String = "http"
            Dim builder As UriBuilder = New UriBuilder(HttpScheme, Environment.MachineName.ToLowerInvariant())
            Return builder.ToString()
        End Function
    
        Private Function GetProjectService() As ISharePointProjectService
            Dim serviceProvider As ServiceProvider = New ServiceProvider(CType(dteObject, IOleServiceProvider))
            Return CType(serviceProvider.GetService(GetType(ISharePointProjectService)), ISharePointProjectService)
        End Function
    
        Private Function ShowProgressDialog(ByVal caption As String, ByVal message As String) As IVsThreadedWaitDialog2
            Dim oleServiceProvider As IOleServiceProvider = CType(dteObject, IOleServiceProvider)
            Dim dialogFactory As IVsThreadedWaitDialogFactory = CType(New ServiceProvider(oleServiceProvider).GetService(
                GetType(SVsThreadedWaitDialogFactory)), IVsThreadedWaitDialogFactory)
    
            If dialogFactory Is Nothing Then
                Throw New InvalidOperationException("The IVsThreadedWaitDialogFactory object could not be retrieved.")
            End If
    
            Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = Nothing
            ErrorHandler.ThrowOnFailure(dialogFactory.CreateInstance(vsThreadedWaitDialog))
            ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.StartWaitDialog(caption, message,
                Nothing, Nothing, String.Empty, 0, False, True))
            Return vsThreadedWaitDialog
        End Function
    
        Private Sub CloseProgressDialog(ByVal vsThreadedWaitDialog As IVsThreadedWaitDialog2)
            If vsThreadedWaitDialog Is Nothing Then
                Throw New ArgumentNullException("vsThreadedWaitDialog")
            End If
            Dim canceled As Integer
            ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.EndWaitDialog(canceled))
        End Sub
    #End Region
    End Class
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Windows;
    using EnvDTE;
    using Microsoft.VisualStudio.SharePoint;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
    
    namespace ProjectTemplateWizard
    {
        internal class SiteColumnWizardModel
        {
            private DTE dteObject;
            private ISharePointProjectService projectServiceValue;
            private List<string> validatedUrls = new List<string>();
    
            internal SiteColumnWizardModel(DTE dteObject, bool requiresFarmPriveleges)
            {
                this.dteObject = dteObject;
    
                // Initialize default values for wizard choices.
                IsSandboxed = !requiresFarmPriveleges;
                IsSecondPagePopulated = false;
                FieldType = "Text";
                FieldGroup = "Custom Columns";
                FieldName = "My Custom Column";
                CurrentSiteUrl = GetLocalHostUrl();
            }
    
            #region Helper methods used by the wizard UI
    
            // Specifies whether the current site URL is valid. Uses the ValidateSite SharePoint command to do this.
            internal bool ValidateCurrentUrl(out string errorMessage)
            {
                bool isValid = false;
                errorMessage = String.Empty;
    
                if (validatedUrls.Contains(CurrentSiteUrl))
                {
                    isValid = true;
                }
                else
                {
                    Uri uriToValidate = new Uri(CurrentSiteUrl, UriKind.Absolute);
                    IVsThreadedWaitDialog2 vsThreadedWaitDialog = null;
    
                    try
                    {
                        vsThreadedWaitDialog = ShowProgressDialog("Connect to SharePoint",
                            "Connecting to SharePoint site " + CurrentSiteUrl);
                        isValid = this.ProjectService.SharePointConnection.ExecuteCommand<Uri, bool>(
                            Contoso.SharePoint.Commands.CommandIds.ValidateSite, uriToValidate);
                    }
                    catch (Exception ex)
                    {
                        errorMessage = "An error occurred while validating the site. " + ex.Message;
                    }
                    finally
                    {
                        if (isValid)
                        {
                            validatedUrls.Add(CurrentSiteUrl);
                        }
    
                        if (vsThreadedWaitDialog != null)
                        {
                            CloseProgressDialog(vsThreadedWaitDialog);
                        }
                    }
                }
    
                return isValid;
            }
    
            // Gets the available field types from the SharePoint site. Uses the GetFieldTypes SharePoint command to do this.
            internal ArrayList GetFieldTypes()
            {
                // If we have not yet validated this site, do it now.
                string errorMessage;
                if (!ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        CurrentSiteUrl, errorMessage), "SharePoint Connection Error");
                    return null;
                }
    
                // Site is valid, so go ahead and get the available field types.
                Uri siteUri = new Uri(CurrentSiteUrl, UriKind.Absolute);
                IVsThreadedWaitDialog2 vsThreadedWaitDialog = ShowProgressDialog(
                    "Connect to SharePoint", "Connecting to SharePoint site " + CurrentSiteUrl);
                string[] fieldTypesArray = this.ProjectService.SharePointConnection.ExecuteCommand<Uri, string[]>(
                        Contoso.SharePoint.Commands.CommandIds.GetFieldTypes, siteUri);
    
                if (vsThreadedWaitDialog != null)
                {
                    CloseProgressDialog(vsThreadedWaitDialog);
                }
    
                return new ArrayList(fieldTypesArray);
            }
    
            // Returns the default column group names in SharePoint.
            internal List<string> GetFieldGroups()
            {
                List<string> groups = new List<string>();
                groups.Add("Base Columns");
                groups.Add("Core Contact and Calendar Columns");
                groups.Add("Core Document Columns");
                groups.Add("Core Task and Issue Columns");
                groups.Add("Extended Columns");
                return groups;
            }
    
            #endregion
    
            #region Properties shared by the wizard implementation and the wizard UI
    
            internal ISharePointProjectService ProjectService
            {
                get
                {
                    if (projectServiceValue == null)
                    {
                        projectServiceValue = GetProjectService();
                    }
                    return projectServiceValue;
                }
            }
    
            internal bool IsSecondPagePopulated { get; set; }
            internal bool IsSandboxed { get; set; }
            internal string FieldType { get; set; }
            internal string FieldGroup { get; set; }
            internal string FieldName { get; set; }
            internal string CurrentSiteUrl { get; set; }
    
            #endregion
    
            #region Private methods
    
            private string GetLocalHostUrl()
            {
                const string HttpScheme = "http";
                UriBuilder builder = new UriBuilder(HttpScheme, Environment.MachineName.ToLowerInvariant());
                return builder.ToString();
            }
    
            private ISharePointProjectService GetProjectService()
            {
                ServiceProvider serviceProvider = new ServiceProvider(dteObject as IOleServiceProvider);
                return serviceProvider.GetService(typeof(ISharePointProjectService)) as ISharePointProjectService;
            }
    
            private IVsThreadedWaitDialog2 ShowProgressDialog(string caption, string message)
            {
                IOleServiceProvider oleServiceProvider = dteObject as IOleServiceProvider;
                IVsThreadedWaitDialogFactory dialogFactory = new ServiceProvider(oleServiceProvider).GetService(
                    typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
    
                if (dialogFactory == null)
                {
                    throw new InvalidOperationException("The IVsThreadedWaitDialogFactory object could not be retrieved.");
                }
    
                IVsThreadedWaitDialog2 vsThreadedWaitDialog = null;
                ErrorHandler.ThrowOnFailure(dialogFactory.CreateInstance(out vsThreadedWaitDialog));
                ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.StartWaitDialog(caption, message,
                     null, null, String.Empty, 0, false, true));
                return vsThreadedWaitDialog;
            }
    
            private void CloseProgressDialog(IVsThreadedWaitDialog2 vsThreadedWaitDialog)
            {
                if (vsThreadedWaitDialog == null)
                {
                    throw new ArgumentNullException("vsThreadedWaitDialog");
                }
    
                int canceled;
                ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.EndWaitDialog(out canceled));
            }
    
            #endregion
        }
    }
    

프로젝트 서명 관리자를 만들려면

  • ProjectSigningManager 코드 파일을 연 다음이 파일의 전체 내용을 다음 코드로 대체 합니다.

    Imports EnvDTE
    Imports System
    Imports System.IO
    Imports System.Runtime.InteropServices
    
    Friend Class ProjectSigningManager
        Private Const KEY_FILENAME As String = "key.snk"
        Private keyBuffer As Byte()
        Private strongNameGenerated As Boolean = False
    
    #Region "Methods used by the project wizard"
    
        Friend Sub GenerateKeyFile()
            If Not strongNameGenerated Then
                keyBuffer = CreateNewKeyPair()
                strongNameGenerated = True
            End If
        End Sub
    
        Friend Sub AddKeyFile(ByVal project As Project)
            If strongNameGenerated Then
                AddKeyFileToProject(project)
            End If
        End Sub
    #End Region
    
    #Region "Private members"
    
        Private Function CreateNewKeyPair() As Byte()
            Dim buffer As IntPtr = IntPtr.Zero
            Dim bufferSize As UInteger
            Dim keyBuffer As Byte()
    
            Try
                If 0 = NativeMethods.StrongNameKeyGen(IntPtr.Zero, 0, buffer, bufferSize) Then
                    Marshal.ThrowExceptionForHR(NativeMethods.StrongNameErrorInfo())
                End If
                If buffer = IntPtr.Zero Then
                    Throw New InvalidOperationException("Cannot generate the strong name key.")
                End If
    
                ' Copy generated key to managed memory.
                keyBuffer = New Byte(bufferSize) {}
                Marshal.Copy(buffer, keyBuffer, 0, CInt(bufferSize))
            Finally
                ' Free native resources.
                NativeMethods.StrongNameFreeBuffer(buffer)
            End Try
            Return keyBuffer
        End Function
    
        Private Sub AddKeyFileToProject(ByVal project As Project)
    
            ' Save the key to a file.
            If keyBuffer IsNot Nothing Then
                Try
                    Dim destinationDirectory As String = Path.GetDirectoryName(project.FullName)
                    Dim keySavePath As String = Path.Combine(destinationDirectory, KEY_FILENAME)
    
                    File.WriteAllBytes(keySavePath, keyBuffer)
                    project.ProjectItems.AddFromFile(keySavePath)
    
                    ' Add properties in the project to use the key for signing.
                    Dim projProps As EnvDTE.Properties = project.Properties
                    projProps.Item("SignAssembly").Value = True
                    projProps.Item("AssemblyOriginatorKeyFile").Value = KEY_FILENAME
                Catch e As Exception
                    Throw New Exception("Cannot add the strong name key to the project. " & e.Message, e)
                End Try
            End If
        End Sub
    
        Private Class NativeMethods
            <DllImport("mscoree.dll")>
            Friend Shared Function StrongNameFreeBuffer(ByVal pbMemory As IntPtr) As Integer
            End Function
    
            <DllImport("mscoree.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True)>
            Friend Shared Function StrongNameKeyGen(ByVal wszKeyContainer As IntPtr, ByVal dwFlags As UInteger, _
                ByRef KeyBlob As IntPtr, ByRef KeyBlobSize As UInteger) As Integer
            End Function
    
            <DllImport("mscoree.dll", CharSet:=CharSet.Unicode)>
            Friend Shared Function StrongNameErrorInfo() As Integer
            End Function
        End Class
    #End Region
    End Class
    
    using EnvDTE;
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    
    namespace ProjectTemplateWizard
    {
        internal class ProjectSigningManager
        {
            private const string KEY_FILENAME = "key.snk";
            private byte[] keyBuffer;
            private bool strongNameGenerated = false;
    
            #region Methods used by the project wizard
    
            internal void GenerateKeyFile()
            {
                if (!strongNameGenerated)
                {
                    keyBuffer = CreateNewKeyPair();
                    strongNameGenerated = true;
                }
            }
    
            internal void AddKeyFile(Project project)
            {
                if (strongNameGenerated)
                {
                    AddKeyFileToProject(project);
                }
            }
    
            #endregion
    
            #region Private members
    
            private byte[] CreateNewKeyPair()
            {
                IntPtr buffer = IntPtr.Zero;
                uint bufferSize;
                byte[] keyBuffer;
    
                try
                {
                    if (0 == NativeMethods.StrongNameKeyGen(IntPtr.Zero, 0, out buffer, out bufferSize))
                    {
                        Marshal.ThrowExceptionForHR(NativeMethods.StrongNameErrorInfo());
                    }
    
                    if (buffer == IntPtr.Zero)
                    {
                        throw new InvalidOperationException("Cannot generate the strong name key.");
                    }
    
                    // Copy generated key to managed memory.
                    keyBuffer = new byte[bufferSize];
                    Marshal.Copy(buffer, keyBuffer, 0, (int)bufferSize);
                }
                finally
                {
                    // Free native resources.
                    NativeMethods.StrongNameFreeBuffer(buffer);
                }
    
                return keyBuffer;
            }
    
            private void AddKeyFileToProject(Project project)
            {
                // Save the key to a file.
                if (keyBuffer != null)
                {
                    try
                    {
                        string destinationDirectory = Path.GetDirectoryName(project.FullName);
                        string keySavePath = Path.Combine(destinationDirectory, KEY_FILENAME);
    
                        File.WriteAllBytes(keySavePath, keyBuffer);
                        project.ProjectItems.AddFromFile(keySavePath);
    
                        // Add properties in the project to use the key for signing.
                        EnvDTE.Properties projProps = project.Properties;
                        projProps.Item("SignAssembly").Value = true;
                        projProps.Item("AssemblyOriginatorKeyFile").Value = KEY_FILENAME;
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Cannot add the strong name key to the project. " + e.Message, e);
                    }
                }
            }
    
            private static class NativeMethods
            {
                [DllImport("mscoree.dll")]
                internal extern static int StrongNameFreeBuffer(IntPtr pbMemory);
    
                [DllImport("mscoree.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
                internal static extern int StrongNameKeyGen(IntPtr wszKeyContainer, uint dwFlags, out IntPtr KeyBlob, 
                    out uint KeyBlobSize);
    
                [DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
                internal static extern int StrongNameErrorInfo();
            }
    
            #endregion
        }
    }
    

마법사 UI 만들기

마법사 창의 UI 및 마법사 페이지에 UI를 제공하는 두 가지 사용자 정의 컨트롤을 정의하는 XAML을 추가하고 창과 사용자 정의 컨트롤의 동작을 정의하는 코드를 추가합니다.만드는 마법사는 Visual Studio의 SharePoint 프로젝트에 대한 기본 제공 마법사와 유사합니다.

[!참고]

다음 단계에서는 XAML 또는 코드를 프로젝트에 추가한 후 프로젝트에서 컴파일 오류가 발생합니다.이러한 오류는 이후 단계에서 코드를 추가하면 사라집니다.

마법사 창 UI를 만들려면

  1. ProjectTemplateWizard 프로젝트의 WizardWindow.xaml 파일에 대 한 바로 가기 메뉴를 열고 선택 열기 디자이너에서를 클릭 합니다.

  2. 디자이너의 XAML 뷰에서 현재 XAML을 다음 XAML로 바꿉니다.이 XAML에서는 제목, 마법사 페이지가 포함된 Grid 및 창 아래쪽의 탐색 단추가 포함된 UI를 정의합니다.

    <ui:DialogWindow x:Class="ProjectTemplateWizard.WizardWindow"
                     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                     xmlns:ui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.11.0"        
                     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"                                
                     Title="SharePoint Customization Wizard" Height="500" Width="700" ResizeMode="NoResize" 
                     Loaded="Window_Loaded" TextOptions.TextFormattingMode="Display">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="75*" />
                <RowDefinition Height="364*" />
                <RowDefinition Height="1*" />
                <RowDefinition Height="60*" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0" Name="headingGrid" Background="White">
                <Label Height="28" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="18,0,0,0" 
                       Name="headingLabel" FontWeight="ExtraBold" />
            </Grid>
            <Grid Grid.Row="1" Name="pageGrid" />
            <Rectangle Grid.Row="2" Name="separatorRectangle" Fill="White"  />
            <StackPanel Grid.Row="3" Name="navigationPanel" Orientation="Horizontal">
                <Button Content="&lt; _Previous" Margin="300,0,0,0"  Height="25" Name="previousButton" Width="85" 
                        IsEnabled="False" Click="previousButton_Click" />
                <Button Content="_Next >" Margin="10,0,0,0" Height="25" Name="nextButton" Width="85" Click="nextButton_Click" 
                        IsDefault="True" />
                <Button Content="_Finish" Margin="10,0,0,0" Height="25" Name="finishButton" Width="85" 
                        Click="finishButton_Click" />
                <Button Content="Cancel" Margin="10,0,0,0" Height="25" Name="cancelButton" Width="85" 
                        IsCancel="True" />
            </StackPanel>
        </Grid>
    </ui:DialogWindow>
    

    [!참고]

    이 XAML에서 생성 되는 창에서 파생 되는 DialogWindow 기본 클래스입니다.사용자 지정 WPF 대화 상자 Visual Studio 추가 하면 대화 상자는 다른 Visual Studio 대화 상자와 일관성 있는 스타일 하 고 발생할 수 있는 모달 대화 상자 문제를 방지 하려면이 클래스에서 파생 하는 것이 좋습니다.자세한 내용은 방법: 작성 하 고 대화 상자를 관리 합니다.을 참조하십시오.

  3. Visual Basic 프로젝트를 개발 하는 경우 제거는 ProjectTemplateWizard 네임 스페이스에서의 WizardWindow 클래스 이름에는 x:Class 특성은 Window 요소.이 요소에는 XAML의 첫 번째 줄에 있습니다.완료 되 면 첫 번째 줄은 다음 예제와 같아야 합니다.

    <Window x:Class="WizardWindow"
    
  4. WizardWindow.xaml 파일의 코드 숨김 파일을 엽니다.

  5. 제외 하 고는이 파일의 내용을 대체 된 using 선언 다음 코드를 파일 맨 위에 있는.

    Public Class WizardWindow
        Private firstPage As Page1
        Private secondPage As Page2
        Private Const firstPageLabel As String = "Specify the site and security level for debugging"
        Private Const secondPageLabel As String = "Configure the site column"
    
        Friend Sub New(ByVal presentationModel As SiteColumnWizardModel)
            InitializeComponent()
            Me.PresentationModel = presentationModel
            firstPage = New Page1(Me)
            secondPage = New Page2(Me)
            secondPage.Visibility = Visibility.Hidden
        End Sub
    
        Friend Property PresentationModel As SiteColumnWizardModel
    
        Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            headingLabel.Content = firstPageLabel
            pageGrid.Children.Add(firstPage)
            pageGrid.Children.Add(secondPage)
        End Sub
    
        Private Sub nextButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    
            ' Initialize the second wizard page if this is the first time 
            ' it has been shown with the current site URL.
            If Not PresentationModel.IsSecondPagePopulated Then
    
                If Not ValidateUrl() Then
                    Return
                End If
    
                ' Refresh the UI in the second page.
                secondPage.ClearControls()
                secondPage.PopulateSiteColumnOptions()
    
                ' Do not do this work again until the user changes the site URL.
                PresentationModel.IsSecondPagePopulated = True
            End If
    
            ' Display the second wizard page and update related controls.
            firstPage.Visibility = Visibility.Hidden
            secondPage.Visibility = Visibility.Visible
            previousButton.IsEnabled = True
            nextButton.IsEnabled = False
            nextButton.IsDefault = False
            finishButton.IsDefault = True
            headingLabel.Content = secondPageLabel
        End Sub
    
        ' Display the first wizard page again and update related controls.
        Private Sub previousButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            secondPage.Visibility = Visibility.Hidden
            firstPage.Visibility = Visibility.Visible
            previousButton.IsEnabled = False
            finishButton.IsDefault = False
            nextButton.IsEnabled = True
            nextButton.IsDefault = True
            headingLabel.Content = firstPageLabel
        End Sub
    
        Private Sub finishButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If ValidateUrl() Then
                DialogResult = True
                Close()
            End If
        End Sub
    
        Private Function ValidateUrl() As Boolean
            Dim errorMessage As String = String.Empty
            If Not PresentationModel.ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    PresentationModel.CurrentSiteUrl, errorMessage),
                    "SharePoint Connection Error")
                Return False
            End If
            Return True
        End Function
    End Class
    
    using System;
    using System.Windows;
    using Microsoft.VisualStudio.PlatformUI;
    
    namespace ProjectTemplateWizard
    {
        public partial class WizardWindow : DialogWindow
        {
            private Page1 firstPage;
            private Page2 secondPage;
            private const string firstPageLabel = "Specify the site and security level for debugging";
            private const string secondPageLabel = "Configure the site column";
    
            internal WizardWindow(SiteColumnWizardModel presentationModel)
            {
                InitializeComponent();
                this.PresentationModel = presentationModel;
                firstPage = new Page1(this);
                secondPage = new Page2(this);
                secondPage.Visibility = Visibility.Hidden;
            }
    
            internal SiteColumnWizardModel PresentationModel { get; set; }
    
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                headingLabel.Content = firstPageLabel;
                pageGrid.Children.Add(firstPage);
                pageGrid.Children.Add(secondPage);
            }
    
            private void nextButton_Click(object sender, RoutedEventArgs e)
            {
                // Initialize the second wizard page if this is the first time 
                // it has been shown with the current site URL.
                if (!PresentationModel.IsSecondPagePopulated)
                {
                    if (!ValidateUrl())
                    {
                        return;
                    }
    
                    // Refresh the UI in the second page.
                    secondPage.ClearControls();
                    secondPage.PopulateSiteColumnOptions();
    
                    // Do not do this work again until the user changes the site URL.
                    PresentationModel.IsSecondPagePopulated = true;
                }
    
                // Display the second wizard page and update related controls.
                firstPage.Visibility = Visibility.Hidden;
                secondPage.Visibility = Visibility.Visible;
                previousButton.IsEnabled = true;
                nextButton.IsEnabled = false;
                finishButton.IsDefault = true;
                headingLabel.Content = secondPageLabel;
            }
    
            // Display the first wizard page again and update related controls.
            private void previousButton_Click(object sender, RoutedEventArgs e)
            {
                secondPage.Visibility = Visibility.Hidden;
                firstPage.Visibility = Visibility.Visible;
                previousButton.IsEnabled = false;
                finishButton.IsDefault = false;
                nextButton.IsEnabled = true;
                nextButton.IsDefault = true;
                headingLabel.Content = firstPageLabel;
            }
    
            private void finishButton_Click(object sender, RoutedEventArgs e)
            {
                if (ValidateUrl())
                {
                    DialogResult = true;
                    Close();
                }
            }
    
            private bool ValidateUrl()
            {
                string errorMessage;
                if (!PresentationModel.ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        PresentationModel.CurrentSiteUrl, errorMessage),
                        "SharePoint Connection Error");
                    return false;
                }
                return true;
            }
        }
    }
    

첫 번째 마법사 페이지 UI를 만들려면

  1. ProjectTemplateWizard 프로젝트 Page1.xaml 파일의 바로 가기 메뉴를 열고 선택 열기 사용자 정의 컨트롤 디자이너에서 엽니다.

  2. 디자이너의 XAML 뷰에서 현재 XAML을 다음 XAML로 바꿉니다.XAML 디버깅에 사용할 로컬 사이트의 URL을 사용자가 입력할 수 있는 텍스트 상자를 포함 하는 UI를 정의 합니다.UI 프로젝트가 샌드박스가 적용 된 지 여부를 사용자 지정할 수 있습니다 옵션 단추가 포함 됩니다.

    <UserControl x:Class="ProjectTemplateWizard.Page1"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" d:DesignHeight="364" d:DesignWidth="700" Loaded="UserControl_Loaded">
        <Grid Height="364" HorizontalAlignment="Left" Name="page1Grid" Width="700">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="548*" />
                <ColumnDefinition Width="132*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBox Grid.Row="1" Grid.Column="1" Margin="5,0,1,0" Height="23" Name="siteUrlTextBox" 
                     TextChanged="siteUrlTextBox_TextChanged" />
            <Label Grid.Row="0" Grid.Column="1" Margin="0,20,0,0" Name="siteLabel" FontWeight="Bold" 
                   Target="{Binding ElementName=siteUrlTextBox}" 
                   Content="What local _site do you want to use for debugging?" />
            <Button Grid.Row="1" Grid.Column="2" Content="_Validate" Height="25" Name="validateButton" 
                    Width="88" Click="validateButton_Click" HorizontalAlignment="Left" 
                    Margin="5,0,0,0" VerticalAlignment="Top" />
            <Label Grid.Row="2" Grid.Column="1" Margin="0,10,0,0" Content="What is the trust level for this SharePoint solution?" 
                   Name="trustLabel" FontWeight="Bold" />
            <StackPanel Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Vertical">
                <RadioButton Content="Deploy as a sand_boxed solution" Margin="5,0,0,0" Name="sandboxedSolutionRadioButton" 
                             FontWeight="Bold" Checked="sandboxedSolutionRadioButton_Checked" />
                <TextBlock TextWrapping="WrapWithOverflow" Margin="20,7,50,0">Clicking this option causes the solution to be 
                           deployed as a Sandboxed solution. Sandboxed solutions can be deployed by the site collection owner 
                           and are run in a secure, monitored process that has limited resource access.</TextBlock>
                <RadioButton Content="Deploy as a _farm solution" Margin="5,7,0,0" Name="farmSolutionRadioButton" FontWeight="Bold" 
                             Checked="farmSolutionRadioButton_Checked" />
                <TextBlock TextWrapping="WrapWithOverflow" Margin="20,7,50,0">Clicking this option means that users must have 
                           SharePoint administrator privileges to run or deploy the solution.</TextBlock>
            </StackPanel>
        </Grid>
    </UserControl>
    
  3. Visual Basic 프로젝트를 개발하는 중이면 UserControl 요소의 x:Class 특성에 있는 Page1 클래스 이름에서 ProjectTemplateWizard 네임스페이스를 제거합니다.이 네임스페이스는 XAML의 첫 번째 줄에 있습니다.지금까지 작업을 마친 후의 첫 번째 줄은 다음과 같습니다.

    <UserControl x:Class="Page1"
    
  4. Page1.xaml 파일의 내용을 제외 하 고 대체 된 using 선언 다음 코드를 파일 맨 위에 있는.

    Public Class Page1
        Private mainWindow As WizardWindow
    
        Friend Sub New(ByVal mainWindow As WizardWindow)
            Me.mainWindow = mainWindow
            InitializeComponent()
        End Sub
    
        Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If (mainWindow.PresentationModel.IsSandboxed) Then
                sandboxedSolutionRadioButton.IsChecked = True
            Else
                sandboxedSolutionRadioButton.IsEnabled = False
                farmSolutionRadioButton.IsChecked = True
            End If
            siteUrlTextBox.Text = mainWindow.PresentationModel.CurrentSiteUrl
        End Sub
    
        ' Validate that the URL exists on the development computer.
        Private Sub validateButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim errorMessage As String = String.Empty
            validateButton.IsEnabled = False
    
            If Not mainWindow.PresentationModel.ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    mainWindow.PresentationModel.CurrentSiteUrl, errorMessage),
                    "SharePoint Connection Error")
            Else
                MessageBox.Show("Successfully connected to SharePoint site " +
                    mainWindow.PresentationModel.CurrentSiteUrl, "Connection Successful")
            End If
            validateButton.IsEnabled = True
        End Sub
    
        ' Prevent users from finishing the wizard if the URL is not formatted correctly.
        Private Sub siteUrlTextBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            Dim url As String = EnsureTrailingSlash(siteUrlTextBox.Text)
    
            ' Perform some basic error-checking on the URL here.
            If url.Length > 0 AndAlso Uri.IsWellFormedUriString(Uri.EscapeUriString(url), UriKind.Absolute) Then
    
                mainWindow.finishButton.IsEnabled = True
                mainWindow.nextButton.IsEnabled = True
                validateButton.IsEnabled = True
                mainWindow.PresentationModel.CurrentSiteUrl = url
                mainWindow.PresentationModel.IsSecondPagePopulated = False
            Else
                mainWindow.finishButton.IsEnabled = False
                mainWindow.nextButton.IsEnabled = False
                validateButton.IsEnabled = False
            End If
        End Sub
    
        Private Sub sandboxedSolutionRadioButton_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            mainWindow.PresentationModel.IsSandboxed = CBool(sandboxedSolutionRadioButton.IsChecked)
        End Sub
    
        Private Sub farmSolutionRadioButton_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            mainWindow.PresentationModel.IsSandboxed = CBool(sandboxedSolutionRadioButton.IsChecked)
        End Sub
    
        Private Function EnsureTrailingSlash(ByVal url As String)
            If Not String.IsNullOrEmpty(url) AndAlso url(url.Length - 1) <> "/" Then
                url += "/"
            End If
            Return url
        End Function
    End Class
    
    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace ProjectTemplateWizard
    {
        public partial class Page1 : UserControl
        {
            private WizardWindow mainWindow;
    
            internal Page1(WizardWindow mainWindow)
            {
                this.mainWindow = mainWindow;
                InitializeComponent();
            }
    
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                if (mainWindow.PresentationModel.IsSandboxed)
                {
                    sandboxedSolutionRadioButton.IsChecked = true;
                }
                else
                {
                    sandboxedSolutionRadioButton.IsEnabled = false;
                    farmSolutionRadioButton.IsChecked = true;
                }
    
                siteUrlTextBox.Text = mainWindow.PresentationModel.CurrentSiteUrl;
            }
    
            // Validate that the URL exists on the development computer.
            private void validateButton_Click(object sender, RoutedEventArgs e)
            {
                string errorMessage;
                validateButton.IsEnabled = false;
    
                if (!mainWindow.PresentationModel.ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        mainWindow.PresentationModel.CurrentSiteUrl, errorMessage),
                        "SharePoint Connection Error");
                }
                else
                {
                    MessageBox.Show("Successfully connected to SharePoint site " +
                        mainWindow.PresentationModel.CurrentSiteUrl, "Connection Successful");
                }
    
                validateButton.IsEnabled = true;
            }
    
            // Prevent users from finishing the wizard if the URL is not formatted correctly.
            private void siteUrlTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                string url = EnsureTrailingSlash(siteUrlTextBox.Text);
    
                // Perform some basic error-checking on the URL here.
                if ((url.Length > 0) && (Uri.IsWellFormedUriString(Uri.EscapeUriString(url), UriKind.Absolute)))
                {
                    mainWindow.finishButton.IsEnabled = true;
                    mainWindow.nextButton.IsEnabled = true;
                    validateButton.IsEnabled = true;
                    mainWindow.PresentationModel.CurrentSiteUrl = url;
                    mainWindow.PresentationModel.IsSecondPagePopulated = false;
                }
                else
                {
                    mainWindow.finishButton.IsEnabled = false;
                    mainWindow.nextButton.IsEnabled = false;
                    validateButton.IsEnabled = false;
                }
            }
    
            private void sandboxedSolutionRadioButton_Checked(object sender, RoutedEventArgs e)
            {
                mainWindow.PresentationModel.IsSandboxed = (bool)sandboxedSolutionRadioButton.IsChecked;
            }
    
            private void farmSolutionRadioButton_Checked(object sender, RoutedEventArgs e)
            {
                mainWindow.PresentationModel.IsSandboxed = (bool)sandboxedSolutionRadioButton.IsChecked;
            }
    
            private string EnsureTrailingSlash(string url)
            {
                if (!String.IsNullOrEmpty(url)
                    && url[url.Length - 1] != '/')
                {
                    url += '/';
                }
                return url;
            }
        }
    }
    

두 번째 마법사 페이지 UI를 만들려면

  1. ProjectTemplateWizard 프로젝트 Page2.xaml 파일의 바로 가기 메뉴를 열고 선택 열기.

    사용자 정의 컨트롤이 디자이너에서 열립니다.

  2. XAML 뷰에서 현재 XAML을 다음 XAML로 바꿉니다.이 XAML에서는 사이트 열의 기본 형식을 선택하기 위한 드롭다운 목록, 갤러리에 사이트 열을 표시할 기본 제공 그룹 또는 사용자 지정 그룹을 지정하기 위한 콤보 상자 및 사이트 열의 이름을 지정하기 위한 텍스트 상자가 포함된 UI를 정의합니다.

    <UserControl x:Class="ProjectTemplateWizard.Page2"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" d:DesignHeight="364" d:DesignWidth="700" Loaded="UserControl_Loaded">
        <Grid Height="364" HorizontalAlignment="Left" Name="page2Grid" Width="700">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="450*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Grid.Row="0" Grid.Column="1" Margin="0,20,0,0" Content="_Type:" Name="fieldTypeLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=fieldTypeComboBox}"/>
            <Label Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" Content="_Group:" Name="groupLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=groupComboBox}"/>
            <Label Grid.Row="2" Grid.Column="1" Margin="0,10,0,0" Content="_Name:" Name="nameLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=nameTextBox}"/>
            <ComboBox Grid.Row="0" Grid.Column="2" HorizontalAlignment="Left" Margin="0,20,0,0" Height="23" 
                      Name="fieldTypeComboBox" Width="450" SelectionChanged="fieldTypeComboBox_SelectionChanged" />
            <ComboBox Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left" Margin="0,10,0,0" Height="23" 
                      Name="groupComboBox" Width="450" IsEditable="True"  />
            <TextBox Grid.Row="2" Grid.Column="2" HorizontalAlignment="Left"  Margin="0,10,0,0" Height="23" 
                     Name="nameTextBox" Width="450" TextChanged="nameTextBox_TextChanged" />
        </Grid>
    </UserControl>
    
  3. Visual Basic 프로젝트를 개발하는 중이면 UserControl 요소의 x:Class 특성에 있는 Page2 클래스 이름에서 ProjectTemplateWizard 네임스페이스를 제거합니다.이 네임스페이스는 XAML의 첫 번째 줄에 있습니다.지금까지 작업을 마친 후의 첫 번째 줄은 다음과 같습니다.

    <UserControl x:Class="Page2"
    
  4. Page2.xaml 파일의 코드 숨김 파일의 내용을 제외 하 고 대체 된 using 선언 다음 코드를 파일 맨 위에 있는.

    Public Class Page2
        Private mainWindow As WizardWindow
        Private innerTextBoxForGroupComboBox As TextBox
    
        Friend Sub New(ByVal mainWindow As WizardWindow)
            Me.mainWindow = mainWindow
            InitializeComponent()
        End Sub
    
        Friend Sub ClearControls()
            fieldTypeComboBox.Items.Clear()
            groupComboBox.Items.Clear()
            nameTextBox.Clear()
        End Sub
    
        Friend Sub PopulateSiteColumnOptions()
            ' Add the available field type names to the combo box.
            Dim fieldTypes As System.Collections.ArrayList = mainWindow.PresentationModel.GetFieldTypes()
            If fieldTypes IsNot Nothing Then
                fieldTypes.Sort()
                For Each fieldValue As String In fieldTypes
                    fieldTypeComboBox.Items.Add(fieldValue)
                Next
                fieldTypeComboBox.SelectedIndex = 0
            End If
    
            ' Add the default group names to the combo box.
            Dim fieldGroups As List(Of String) = mainWindow.PresentationModel.GetFieldGroups()
            For Each fieldGroupValue As String In fieldGroups
                groupComboBox.Items.Add(fieldGroupValue)
            Next
            groupComboBox.SelectedIndex = 0
        End Sub
    
        Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Handle the TextChanged event of the underlying TextBox for the ComboBox. This enables us to determine 
            ' 1) when the user selects an item in the list and 2) when they type their own custom group name. 
            ' The ComboBox.SelectionChanged event is not raised when you type in an editable ComboboBox.
            innerTextBoxForGroupComboBox = CType(groupComboBox.Template.FindName(
                "PART_EditableTextBox", groupComboBox), TextBox)
            AddHandler innerTextBoxForGroupComboBox.TextChanged, AddressOf innerTextBoxForGroupComboBox_TextChanged
        End Sub
    
        Private Sub fieldTypeComboBox_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
            mainWindow.PresentationModel.FieldType = CStr(fieldTypeComboBox.SelectedItem)
        End Sub
    
        Private Sub innerTextBoxForGroupComboBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            mainWindow.PresentationModel.FieldGroup = groupComboBox.Text
        End Sub
    
        Private Sub nameTextBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            mainWindow.PresentationModel.FieldName = nameTextBox.Text
        End Sub
    End Class
    
    using System.Windows;
    using System.Windows.Controls;
    
    namespace ProjectTemplateWizard
    {
        public partial class Page2 : UserControl
        {
            private WizardWindow mainWindow;
            private TextBox innerTextBoxForGroupComboBox;
    
            internal Page2(WizardWindow mainWindow)
            {
                this.mainWindow = mainWindow;
                InitializeComponent();
            }
    
            internal void ClearControls()
            {
                fieldTypeComboBox.Items.Clear();
                groupComboBox.Items.Clear();
                nameTextBox.Clear();
            }
    
            internal void PopulateSiteColumnOptions()
            {
                // Add the available field type names to the combo box.
                System.Collections.ArrayList fieldTypes = mainWindow.PresentationModel.GetFieldTypes();
                if (fieldTypes != null)
                {
                    fieldTypes.Sort();
                    foreach (string fieldValue in fieldTypes)
                    {
                        fieldTypeComboBox.Items.Add(fieldValue);
                    }
    
                    fieldTypeComboBox.SelectedIndex = 0;
                }
    
                // Add the default group names to the combo box.
                System.Collections.Generic.List<string> fieldGroups = mainWindow.PresentationModel.GetFieldGroups();
                foreach (string fieldGroupValue in fieldGroups)
                {
                    groupComboBox.Items.Add(fieldGroupValue);
                }
    
                groupComboBox.SelectedIndex = 0;
            }
    
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                // Handle the TextChanged event of the underlying TextBox for the ComboBox. This enables us to determine 
                // 1) when the user selects an item in the list and 2) when they type their own custom group name. 
                // The ComboBox.SelectionChanged event is not raised when you type in an editable ComboboBox.
                innerTextBoxForGroupComboBox = groupComboBox.Template.FindName(
                    "PART_EditableTextBox", groupComboBox) as TextBox;
                innerTextBoxForGroupComboBox.TextChanged += innerTextBoxForGroupComboBox_TextChanged;
            }
    
            private void fieldTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldType = (string)fieldTypeComboBox.SelectedItem;
            }
    
            void innerTextBoxForGroupComboBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldGroup = groupComboBox.Text;
            }
    
            private void nameTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldName = nameTextBox.Text;
            }
        }
    }
    

마법사 구현

IWizard 인터페이스를 구현하여 마법사의 기본 기능을 정의합니다.이 인터페이스는 마법사가 시작되고 완료될 때와 마법사 실행 중의 특정한 때에 Visual Studio에서 호출하는 메서드를 정의합니다.

마법사를 구현하려면

  1. ProjectTemplateWizard 프로젝트에서 SiteColumnProjectWizard 코드 파일을 엽니다.

  2. 이 파일의 전체 내용을 다음 코드로 바꿉니다.

    Imports EnvDTE
    Imports Microsoft.VisualStudio.SharePoint
    Imports Microsoft.VisualStudio.TemplateWizard
    Imports System
    Imports System.Collections.Generic
    
    Public Class SiteColumnProjectWizard
        Implements IWizard
    
        Private wizardUI As WizardWindow
        Private dteObject As DTE
        Private presentationModel As SiteColumnWizardModel
        Private signingManager As ProjectSigningManager
    
        Public Sub New()
            signingManager = New ProjectSigningManager()
        End Sub
    
        Public Sub RunStarted(ByVal automationObject As Object, ByVal replacementsDictionary As Dictionary(Of String, String), _
            ByVal runKind As WizardRunKind, ByVal customParams() As Object) Implements IWizard.RunStarted
    
            dteObject = CType(automationObject, DTE)
            presentationModel = New SiteColumnWizardModel(dteObject, False)
    
            If Not presentationModel.ProjectService.IsSharePointInstalled Then
                Dim errorString As String = "A SharePoint server is not installed on this computer. A SharePoint server " &
                    "must be installed to work with SharePoint projects."
                System.Windows.MessageBox.Show(errorString, "SharePoint Not Installed", System.Windows.MessageBoxButton.OK,
                    System.Windows.MessageBoxImage.Error)
                Throw New WizardCancelledException(errorString)
            End If
    
            wizardUI = New WizardWindow(presentationModel)
            Dim dialogCompleted? As Boolean = wizardUI.ShowModal()
    
            If (dialogCompleted = True) Then
                replacementsDictionary.Add("$selectedfieldtype$", presentationModel.FieldType)
                replacementsDictionary.Add("$selectedgrouptype$", presentationModel.FieldGroup)
                replacementsDictionary.Add("$fieldname$", presentationModel.FieldName)
                signingManager.GenerateKeyFile()
            Else
                Throw New WizardCancelledException()
            End If
        End Sub
    
        ' Populate the SiteUrl and IsSandboxedSolution properties in the new project, and add a new 
        ' key.snk file to the project.
        Public Sub ProjectFinishedGenerating(ByVal project As Project) _
            Implements IWizard.ProjectFinishedGenerating
            Dim sharePointProject As ISharePointProject = presentationModel.ProjectService.Convert(Of Project, ISharePointProject)(project)
            sharePointProject.SiteUrl = New Uri(presentationModel.CurrentSiteUrl, UriKind.Absolute)
            sharePointProject.IsSandboxedSolution = presentationModel.IsSandboxed
            signingManager.AddKeyFile(project)
        End Sub
    
        ' Always return true; this IWizard implementation throws a WizardCancelledException
        ' that is handled by Visual Studio if the user cancels the wizard.
        Public Function ShouldAddProjectItem(ByVal filePath As String) As Boolean _
            Implements IWizard.ShouldAddProjectItem
            Return True
        End Function
    
        ' The following IWizard methods are not used in this example.
        Public Sub BeforeOpeningFile(ByVal projectItem As ProjectItem) _
            Implements IWizard.BeforeOpeningFile
        End Sub
    
        Public Sub ProjectItemFinishedGenerating(ByVal projectItem As ProjectItem) _
            Implements IWizard.ProjectItemFinishedGenerating
        End Sub
    
        Public Sub RunFinished() Implements IWizard.RunFinished
        End Sub
    End Class
    
    using EnvDTE;
    using Microsoft.VisualStudio.SharePoint;
    using Microsoft.VisualStudio.TemplateWizard;
    using System;
    using System.Collections.Generic;
    
    namespace ProjectTemplateWizard
    {
        public class SiteColumnProjectWizard : IWizard
        {
            private WizardWindow wizardUI;
            private DTE dteObject;
            private SiteColumnWizardModel presentationModel;
            private ProjectSigningManager signingManager;
    
            public SiteColumnProjectWizard()
            {
                signingManager = new ProjectSigningManager();
            }
    
            public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, 
                WizardRunKind runKind, object[] customParams)
            {
                dteObject = automationObject as DTE;
                presentationModel = new SiteColumnWizardModel(dteObject, false);
    
                if (!presentationModel.ProjectService.IsSharePointInstalled)
                {
                    string errorString = "A SharePoint server is not installed on this computer. A SharePoint server " +
                        "must be installed to work with SharePoint projects.";
                    System.Windows.MessageBox.Show(errorString, "SharePoint Not Installed", System.Windows.MessageBoxButton.OK,
                        System.Windows.MessageBoxImage.Error);
                    throw new WizardCancelledException(errorString);
                }
    
                wizardUI = new WizardWindow(presentationModel);
                Nullable<bool> dialogCompleted = wizardUI.ShowModal();
    
                if (dialogCompleted == true)
                {
                    replacementsDictionary.Add("$selectedfieldtype$", presentationModel.FieldType);
                    replacementsDictionary.Add("$selectedgrouptype$", presentationModel.FieldGroup);
                    replacementsDictionary.Add("$fieldname$", presentationModel.FieldName);
                    signingManager.GenerateKeyFile();
                }
                else
                {
                    throw new WizardCancelledException();
                }
            }
    
            // Populate the SiteUrl and IsSandboxedSolution properties in the new project, and add a new 
            // key.snk file to the project.
            public void ProjectFinishedGenerating(Project project)
            {
                ISharePointProject sharePointProject = presentationModel.ProjectService.Convert<Project, ISharePointProject>(project);
                sharePointProject.SiteUrl = new Uri(presentationModel.CurrentSiteUrl, UriKind.Absolute);
                sharePointProject.IsSandboxedSolution = presentationModel.IsSandboxed;
                signingManager.AddKeyFile(project);
            }
    
            // Always return true; this IWizard implementation throws a WizardCancelledException
            // that is handled by Visual Studio if the user cancels the wizard.
            public bool ShouldAddProjectItem(string filePath)
            {
                return true;
            }
    
            // The following IWizard methods are not used in this example.
            public void BeforeOpeningFile(ProjectItem projectItem)
            {
            }
    
            public void ProjectItemFinishedGenerating(ProjectItem projectItem)
            {
            }
    
            public void RunFinished()
            {
            }
        }
    }
    

SharePoint 명령 만들기

SharePoint 서버 개체 모델을 호출하는 두 개의 사용자 지정 명령을 만듭니다.한 명령은 사용자가 마법사에서 입력하는 사이트 URL이 유효한지 여부를 확인합니다.다른 명령은 사용자가 새 사이트 열의 기반으로 사용할 필드 형식을 선택할 수 있도록 지정된 SharePoint 사이트에서 모든 필드 형식을 가져옵니다.

SharePoint 명령을 정의하려면

  1. SharePointCommands 프로젝트에서 Commands 코드 파일을 엽니다.

  2. 이 파일의 전체 내용을 다음 코드로 바꿉니다.

    Imports Microsoft.SharePoint
    Imports Microsoft.VisualStudio.SharePoint.Commands
    
    Namespace Contoso.SharePoint.Commands
    
        Friend Class Commands
    
            <SharePointCommand(CommandIds.ValidateSite)> _
            Private Function ValidateSite(ByVal context As ISharePointCommandContext, ByVal url As Uri) As Boolean
                Using site As SPSite = New SPSite(url.AbsoluteUri)
                    Dim webUrl As String = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl)
                    If webUrl IsNot Nothing Then
                        Using web As SPWeb = site.OpenWeb(webUrl, True)
                            Return web.Exists
                        End Using
                    End If
                End Using
                Return False
            End Function
    
            ' For simplicity, this command does not check to make sure the provided Uri is valid. 
            ' Use the ValidateSite command to verify that the Uri is valid first.
            <SharePointCommand(CommandIds.GetFieldTypes)> _
            Private Function GetFieldTypes(ByVal context As ISharePointCommandContext, ByVal url As Uri) As String()
                Dim columnDefinitions As List(Of String) = New List(Of String)()
                Using site As SPSite = New SPSite(url.AbsoluteUri)
                    Dim webUrl As String = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl)
                    Using web As SPWeb = site.OpenWeb(webUrl, True)
                        For Each columnDefinition As SPFieldTypeDefinition In web.FieldTypeDefinitionCollection
                            columnDefinitions.Add(columnDefinition.TypeName)
                        Next
                        ' SharePoint commands cannot serialize List<string>, so return an array.
                        Return columnDefinitions.ToArray()
                    End Using
                End Using
            End Function
    
            Private Function DetermineWebUrl(ByVal serverRelativeInputUrl As String, ByVal serverRelativeSiteUrl As String) As String
                ' Make sure both URLs have a trailing slash.
                serverRelativeInputUrl = EnsureTrailingSlash(serverRelativeInputUrl)
                serverRelativeSiteUrl = EnsureTrailingSlash(serverRelativeSiteUrl)
    
                Dim webUrl As String = Nothing
                Dim isSubString As Boolean = serverRelativeInputUrl.StartsWith(serverRelativeSiteUrl, StringComparison.OrdinalIgnoreCase)
    
                If isSubString Then
                    ' The Web URL cannot have escaped characters.
                    webUrl = Uri.UnescapeDataString(serverRelativeInputUrl.Substring(serverRelativeSiteUrl.Length))
                End If
                Return webUrl
            End Function
    
            Private Function EnsureTrailingSlash(ByVal url As String)
                If Not String.IsNullOrEmpty(url) AndAlso url(url.Length - 1) <> "/" Then
                    url += "/"
                End If
                Return url
            End Function
        End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using Microsoft.SharePoint;
    using Microsoft.VisualStudio.SharePoint.Commands;
    
    namespace Contoso.SharePoint.Commands
    {
        internal class Commands
        {
            [SharePointCommand(CommandIds.ValidateSite)]
            private bool ValidateSite(ISharePointCommandContext context, Uri url)
            {
                using (SPSite site = new SPSite(url.AbsoluteUri))
                {
                    string webUrl = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl);
                    if (webUrl != null)
                    {
                        using (SPWeb web = site.OpenWeb(webUrl, true))
                        {
                            return web.Exists;
                        }
                    }
                }
    
                return false;
            }
    
            // For simplicity, this command does not check to make sure the provided Uri is valid. 
            // Use the ValidateSite command to verify that the Uri is valid first.
            [SharePointCommand(CommandIds.GetFieldTypes)]
            private string[] GetFieldTypes(ISharePointCommandContext context, Uri url)
            {
                List<string> columnDefinitions = new List<string>();
                using (SPSite site = new SPSite(url.AbsoluteUri))
                {
                    string webUrl = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl);
                    using (SPWeb web = site.OpenWeb(webUrl, true))
                    {
                        foreach (SPFieldTypeDefinition columnDefinition in web.FieldTypeDefinitionCollection)
                        {
                            columnDefinitions.Add(columnDefinition.TypeName);
                        }
    
                        // SharePoint commands cannot serialize List<string>, so return an array.
                        return columnDefinitions.ToArray();
                    }
                }
            }
    
            private string DetermineWebUrl(string serverRelativeInputUrl, string serverRelativeSiteUrl)
            {
                // Make sure both URLs have a trailing slash.
                serverRelativeInputUrl = EnsureTrailingSlash(serverRelativeInputUrl);
                serverRelativeSiteUrl = EnsureTrailingSlash(serverRelativeSiteUrl);
    
                string webUrl = null;
                bool isSubString = serverRelativeInputUrl.StartsWith(serverRelativeSiteUrl, StringComparison.OrdinalIgnoreCase);
    
                if (isSubString)
                {
                    // The Web URL cannot have escaped characters.
                    webUrl = Uri.UnescapeDataString(serverRelativeInputUrl.Substring(serverRelativeSiteUrl.Length));
                }
    
                return webUrl;
            }
    
            private string EnsureTrailingSlash(string url)
            {
                if (!String.IsNullOrEmpty(url)
                    && url[url.Length - 1] != '/')
                {
                    url += '/';
                }
                return url;
            }
        }
    }
    

검사점

이 연습의 이전 단계를 통해 마법사를 위한 모든 코드가 프로젝트에 포함되었습니다.프로젝트를 빌드하여 오류 없이 컴파일되는지 확인합니다.

프로젝트를 빌드하려면

  • 메뉴 표시줄에서 선택 빌드, 솔루션 빌드.

프로젝트 템플릿에서 key.snk 파일 제거

연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 1부에서 만든 프로젝트 템플릿에는 각 사이트 열 프로젝트 인스턴스에 서명하는 데 사용되는 key.snk 파일이 포함되어 있습니다.이제 마법사에서 각 프로젝트에 대한 새 key.snk 파일을 생성하기 때문에 이 key.snk 파일은 더 이상 필요하지 않습니다.프로젝트 템플릿에서 key.snk 파일을 제거하고 이 파일에 대한 참조를 제거합니다.

프로젝트 템플릿에서 key.snk 파일을 제거하려면

  1. 솔루션 탐색기아래에서 SiteColumnProjectTemplate 노드, 바로 가기 메뉴를 엽니다의 key.snk 파일을 및 다음 선택 삭제.

  2. 확인 대화 상자에서 선택 된 확인 단추입니다.

  3. 아래는 SiteColumnProjectTemplate 노드를 SiteColumnProjectTemplate.vstemplate 파일을 연 다음 요소를 제거 합니다.

    <ProjectItem ReplaceParameters="false" TargetFileName="key.snk">key.snk</ProjectItem>
    
  4. 파일을 저장한 후 닫습니다.

  5. 아래는 SiteColumnProjectTemplate 노드를 ProjectTemplate.csproj 또는 ProjectTemplate.vbproj 파일을 열고 다음을 제거 PropertyGroup 요소를.

    <PropertyGroup>
      <SignAssembly>true</SignAssembly>
      <AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
    </PropertyGroup>
    
  6. 다음 None 요소를 제거합니다.

    <None Include="key.snk" />
    
  7. 파일을 저장한 후 닫습니다.

마법사를 프로젝트 템플릿과 연결

마법사를 구현했으므로 이 마법사를 사이트 열 프로젝트 템플릿과 연결해야 합니다.이렇게 하려면 세 가지 절차를 완료해야 합니다.

  1. 강력한 이름으로 마법사 어셈블리에 서명합니다.

  2. 마법사 어셈블리의 공개 키 토큰을 가져옵니다.

  3. 사이트 열 프로젝트 템플릿의 .vstemplate 파일에서 마법사 어셈블리에 대한 참조를 추가합니다.

강력한 이름으로 마법사 어셈블리에 서명하려면

  1. 솔루션 탐색기, 바로 가기 메뉴를 열고를 ProjectTemplateWizard 프로젝트를 하 고 선택 속성.

  2. 서명 탭에서 어셈블리 서명 확인란을 선택합니다.

  3. 에 있는 강력한 이름 키 파일 선택 목록에서 선택 < 새로... >.

  4. 강력한 이름 키 만들기 대화 상자에서 선택을 취소에서 새 키 파일의 이름을 입력은 암호로 내 키 파일 보호 확인란을 선택한 다음 선택은 확인 단추.

  5. 바로 가기 메뉴를 열고는 ProjectTemplateWizard 프로젝트를 하 고 선택 빌드 ProjectTemplateWizard.dll 파일을 만들 수.

마법사 어셈블리의 공개 키 토큰을 가져오려면

  1. 시작 메뉴, 선택 모든 프로그램, 선택 Microsoft Visual Studio 2012, 선택 Visual Studio 도구, 다음 선택 개발자가 명령 프롬프트에 VS2012.

    Visual Studio 명령 프롬프트 창이 열립니다.

  2. 다음 명령을 실행 대체 PathToWizardAssembly ProjectTemplateWizard.dll 빌드된 어셈블리 ProjectTemplateWizard 프로젝트 개발 컴퓨터에 대 한 전체 경로:

    sn.exe -T PathToWizardAssembly
    

    ProjectTemplateWizard.dll 어셈블리의 공개 키 토큰은 Visual Studio 명령 프롬프트 창에 기록됩니다.

  3. Visual Studio 명령 프롬프트 창을 계속 열어 둡니다.공개 키 토큰은 다음 절차 중에 필요합니다.

마법사 어셈블리에 대한 참조를 .vstemplate 파일에 추가하려면

  1. 솔루션 탐색기에서 SiteColumnProjectTemplate 프로젝트 노드를 확장하고 SiteColumnProjectTemplate.vstemplate 파일을 엽니다.

  2. 파일의 끝 부분에서 </TemplateContent> 및 </VSTemplate> 태그 사이에 다음 WizardExtension 요소를 추가합니다.PublicKeyToken 특성의 your token 값을 이전 절차에서 가져온 공개 키 토큰으로 바꿉니다.

    <WizardExtension>
      <Assembly>ProjectTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=your token</Assembly>
      <FullClassName>ProjectTemplateWizard.SiteColumnProjectWizard</FullClassName>
    </WizardExtension>
    

    WizardExtension 요소에 대한 자세한 내용은 WizardExtension 요소(Visual Studio 템플릿)를 참조하십시오.

  3. 파일을 저장한 후 닫습니다.

프로젝트 템플릿의 Elements.xml 파일에 대체 가능한 매개 변수 추가

대체 가능한 매개 변수 여러 개를 SiteColumnProjectTemplate 프로젝트의 Elements.xml 파일에 추가합니다.이러한 매개 변수는 앞에서 정의한 SiteColumnProjectWizard 클래스의 RunStarted 메서드에서 초기화됩니다.사용자가 사이트 열 프로젝트를 만들면 Visual Studio에서는 자동으로 새 프로젝트의 Elements.xml 파일에 있는 이러한 매개 변수를 마법사에서 지정한 값으로 바꿉니다.

대체 가능한 매개 변수는 달러 기호($) 문자로 시작하고 끝나는 토큰입니다.대체 가능한 매개 변수를 직접 정의할 뿐 아니라 SharePoint 프로젝트 시스템에 의해 정의되고 초기화되는 기본 제공 매개 변수를 사용할 수도 있습니다.자세한 내용은 대체 가능 매개 변수을 참조하십시오.

대체 가능한 매개 변수를 Elements.xml 파일에 추가하려면

  1. SiteColumnProjectTemplate 프로젝트의 Elements.xml 파일의 내용을 다음 XML로 바꿉니다.

    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="https://schemas.microsoft.com/sharepoint/">
      <Field ID="{$guid5$}" 
             Name="$fieldname$" 
             DisplayName="$fieldname$" 
             Type="$selectedfieldtype$" 
             Group="$selectedgrouptype$">
      </Field>
    </Elements>
    

    새로운 XML에서는 Name, DisplayName, Type 및 Group 특성의 값을 대체 가능한 사용자 지정 매개 변수로 변경합니다.

  2. 파일을 저장한 후 닫습니다.

VSIX 패키지에 마법사 추가

사이트 열 프로젝트 템플릿이 포함된 VSIX 패키지를 사용하여 마법사를 배포하려면 마법사 프로젝트와 SharePoint 명령 프로젝트에 대한 참조를 VSIX 프로젝트의 source.extension.vsixmanifest 파일에 추가합니다.

VSIX 패키지에 마법사를 추가하려면

  1. 솔루션 탐색기SiteColumnProjectItem 프로젝트에 대 한 바로 가기 메뉴를 엽니다의 source.extension.vsixmanifest 파일을 및 다음 선택 열기.

    매니페스트 편집기에서 파일이 열립니다.

  2. 자산 탭 편집기의 선택은 New 단추.

    를 추가 하는 새로운 자산 대화 상자가 열립니다.

  3. 에 있는 유형 목록에서 선택 Microsoft.VisualStudio.Assembly.

  4. 에 있는 원본 목록에서 선택 현재 솔루션의 프로젝트에.

  5. 프로젝트 목록에서 선택 ProjectTemplateWizard, 다음 선택은 확인 단추입니다.

  6. 자산 탭 편집기의 선택은 New 단추를 다시 클릭.

    를 추가 하는 새로운 자산 대화 상자가 열립니다.

  7. 에 있는 유형 목록에서 입력 SharePoint.Commands.v4.

  8. 에 있는 원본 목록에서 선택 현재 솔루션의 프로젝트에.

  9. 프로젝트 목록에서 선택의 SharePointCommands 프로젝트를 하 고 선택은 확인 단추.

  10. 메뉴 표시줄에서 선택 빌드, 솔루션 빌드, 및 다음 솔루션이 오류 없이 빌드되는지 확인 합니다.

마법사 테스트

이제 마법사를 테스트할 준비가 되었습니다.우선 실험 모드의 Visual Studio 인스턴스에서 SiteColumnProjectItem 솔루션 디버깅을 시작합니다.그런 다음 실험 모드의 Visual Studio 인스턴스에서 사이트 열 프로젝트에 대한 마법사를 테스트합니다.마지막으로, 프로젝트를 빌드 및 실행하여 사이트 열이 예상대로 작동하는지 확인합니다.

솔루션 디버깅을 시작하려면

  1. Visual Studio 관리자 자격 증명으로 다시 시작 하 고 SiteColumnProjectItem 솔루션을 엽니다.

  2. ProjectTemplateWizard 프로젝트에 SiteColumnProjectWizard 코드 파일을 열고 다음 코드의 첫째 줄에 중단점을 추가 된 RunStarted 메서드입니다.

  3. 메뉴 표시줄에서 선택 디버깅, 예외.

  4. 예외 대화 상자에서 있는지 확인 하십시오의 throw 됨사용자가 처리 하지 않음 확인란을 공용 언어 런타임 예외 선택을 하 고 선택의 확인 단추.

  5. 선택 하 여 디버깅을 시작 된 F5 키 또는 메뉴 모음을 선택 디버깅, 디버깅 시작.

    Visual Studio Column\1.0 %userprofile%\appdata\local\microsoft\visualstudio\11.0exp\extensions\contoso\site에 있는 확장을 설치 및 Visual Studio 실험 인스턴스를 시작 합니다.프로젝트 항목에 Visual Studio이 인스턴스를 테스트 합니다.

Visual Studio에서 마법사를 테스트하려면

  1. Visual Studio 실험적 인스턴스에서 메뉴 표시줄에서 선택 파일, New, 프로젝트.

  2. 확장은 C# 노드 또는 Visual Basic (프로젝트 템플릿을 지 원하는 언어에 따라), 노드를 확장은 SharePoint 노드를 다음 선택은 2010 노드.

  3. 프로젝트 템플릿 목록에서 선택 사이트 열, 프로젝트의 이름을 SiteColumnWizardTest, 다음 선택은 확인 단추입니다.

  4. 다른 Visual Studio 인스턴스의 코드가 이전에 RunStarted 메서드에 설정한 중단점에서 중지하는지 확인합니다.

  5. 계속을 선택 하 여 프로젝트를 디버깅 하는 F5 키 또는 메뉴 모음을 선택 디버깅, 계속.

  6. SharePoint 사용자 지정 마법사디버깅에 사용 하려는 사이트의 URL을 입력 하 고 다음 선택의 다음 단추.

  7. SharePoint 사용자 지정 마법사의 두 번째 페이지에서 다음과 같이 선택합니다.

    • 에 있는 유형 목록에서 선택 부울.

    • 에 있는 그룹 목록에서 선택 사용자 지정 예/아니요 열.

    • 에 있는 이름 상자 내 예/아니요 열을 입력 하 고 다음을 선택의 마침 단추.

    솔루션 탐색기, 프로젝트를 새로 나타나고 라는 프로젝트 항목이 포함 된 Field1, Visual Studio 프로젝트의 Elements.xml 파일을 편집기에서 엽니다.

  8. 마법사에서 지정한 값이 Elements.xml에 포함되어 있는지 확인합니다.

SharePoint에서 사이트 열을 테스트하려면

  1. Visual Studio 실험적 인스턴스에서 F5 키를 선택 합니다.

    사이트 열을 포장 하는 Sharepoint에 배포는 사이트는 사이트 URL 프로젝트 속성을 지정 합니다.웹 브라우저가이 사이트의 기본 페이지를 엽니다.

    [!참고]

    경우는 스크립트 디버깅 사용 안 함 대화 상자가 나타나면 선택은 프로젝트에 디버깅을 계속 하려면 단추.

  2. 에 있는 사이트 작업 메뉴를 선택 사이트 설정.

  3. 사이트 설정 페이지에서 아래에서 갤러리, 선택은 사이트 열 링크입니다.

  4. 사이트 열의 목록에 있는지 확인은 사용자 지정 예/아니요 열 라는 열을 포함 하는 그룹 내 예/아니요 열을 하 고 웹 브라우저를 닫습니다.

개발 컴퓨터 정리

프로젝트 항목의 테스트를 마쳤으면 실험 모드의 Visual Studio 인스턴스에서 프로젝트 템플릿을 제거합니다.

개발 컴퓨터를 정리하려면

  1. Visual Studio 실험적 인스턴스에서 메뉴 표시줄에서 선택 도구, 확장 및 업데이트.

    확장 및 업데이트 대화 상자가 열립니다.

  2. 확장명 목록에서 선택한 사이트 열, 다음 선택은 제거 단추입니다.

  3. 나타나는 대화 상자에서 선택 된 확장명을 제거 하 고 선택 확인 단추는 지금 다시 시작 단추 제거를 완료 합니다.

  4. Visual Studio 실험적 인스턴스에서 CustomActionProjectItem 솔루션에 열려 있는 인스턴스를 모두 닫습니다.

    배포 하는 방법에 대 한 Visual Studio 확장을 참조 하십시오. Visual Studio Extension 배포.

참고 항목

작업

연습: 프로젝트 템플릿을 사용하여 사이트 열 프로젝트 항목 만들기, 1부

방법: 프로젝트 템플릿에 마법사 사용

참조

Visual Studio 템플릿 스키마 참조

IWizard

개념

사용자 지정 SharePoint 프로젝트 항목 형식 정의

SharePoint 프로젝트 항목에 대한 항목 템플릿 및 프로젝트 템플릿 만들기