shuhelohelo’s blog

Xamarin.Forms多めです.

VSIXプロジェクトテンプレートを作る

以前にASP.NET Core(Generic Host)のDIの仕組みをXamarin.Formsで利用する手順を書きました.

shuhelohelo.hatenablog.com

しかし,毎回この手順を行うのは苦痛なので,プロジェクトのテンプレートを作成したいと思います.

Xamarin.Formsは通常,複数プロジェクト(共有プロジェクト,Android,iOS,UWPなど)からなります.

複数プロジェクトのテンプレートを作成するには以下の公式ドキュメントの「既存のソリューションから複数プロジェクトのテンプレートを作成する」に従います.

docs.microsoft.com

雛形となるソリューションを作成する

作成したものがこちらになります.これが「既存のソリューション」に当たります

github.com

各プラットフォームのプロジェクトをテンプレートとしてエクスポートする

各プラットフォームのプロジェクトそれぞれについて「Project -> Export Template...」します.

f:id:shuhelohelo:20200618130510p:plain

Export Template Wizardが開くので,作成するテンプレートの種類にはProject templateを選択し,エクスポート対象のプロジェクトを下のドロップダウンリストから選択してNextボタンを押します.

f:id:shuhelohelo:20200618155346p:plain

次の画面ではテンプレート名などの情報を入力します.

テンプレート名,説明,アイコン,出力先などを指定できますが,ここでは基本デフォルトのままで,一つだけAutomatically import the template into Visual Studioのチェックを外してFinishボタンを押します. これがついていると,プロジェクトをエクスポートすると同時にVisual Studioにテンプレートとして追加されてしまうためです. 今回はまだVisual Studioに追加されてほしくないので,チェックを外しておきます.

f:id:shuhelohelo:20200618194952p:plain

これを各プラットフォームの分だけ行います.今回は共有プロジェクト,Android,iOSの3つです.

出力先はデフォルトでは%USERPROFILE%\Documents\Visual Studio {バージョン:2019とか2017}\My Exported Templatesです.

テンプレートファイルを解凍する

zip形式で出力された各テンプレートファイルですが,これをそれぞれ解凍します.

解凍されてできたフォルダごと,適当なフォルダに移動させます.私はデスクトップに「XFAspNetCoreDITemplate」という名前のフォルダを作って,そこに3つのフォルダを移動させました.

f:id:shuhelohelo:20200618161939p:plain

.vstemplateファイルを作成する

各テンプレートのフォルダを置いたフォルダに.vstemplateという拡張子のファイルを作成します.

空のテキストファイルを作成して名前を変更して作ればOkです.

f:id:shuhelohelo:20200618162333p:plain

ここではXFAspNetCoreDITemplate.vstemplateという名前にしました.中身はまだ空です.

vstemplateファイルの中身を書く

vstemplateファイルにはXMLで以下のように書きます.

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="ProjectGroup">
  <TemplateData>
    <Name>Xamarin.Forms with ASP.NET Core DI (.NET Standard 2.1)</Name>
    <Description>Xamarin.Forms with ASP.NET Core DI template.</Description>
    <ProjectType>CSharp</ProjectType>
    <Icon></Icon>
    <DefaultName>XFAppAspDI</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
  </TemplateData>
  <TemplateContent PreferedSolusionConfigration="Debug|Any CPU">
    <ProjectCollection>
      <ProjectTemplateLink ProjectName="$safeprojectname$" CopyParameters="true">
        XFAspNetCoreDITemplate\MyTemplate.vstemplate
      </ProjectTemplateLink>
      <ProjectTemplateLink ProjectName="$safeprojectname$.Android" CopyParameters="true">
        XFAspNetCoreDITemplate.Android\MyTemplate.vstemplate
      </ProjectTemplateLink>
      <ProjectTemplateLink ProjectName="$safeprojectname$.iOS" CopyParameters="true">
        XFAspNetCoreDITemplate.iOS\MyTemplate.vstemplate
      </ProjectTemplateLink>
    </ProjectCollection>
  </TemplateContent>
</VSTemplate>

vstemplateファイル内で各プロジェクトの名前なども指定するのですが,実際に使うときはプロジェクトの名前はユーザーが作成時に入力するソリューション名が使用されるようにしなければなりません.

それをどうするかというと,田淵さんのサンプルにあるように$safeprojectname$という変数(テンプレートパラメータ)を使います.

github.com

テンプレートパラメータに関する公式ドキュメントはこちら.

docs.microsoft.com

これで,アプリ作成時に指定したソリューション名に合わせて{ソリューション名}.Android,{ソリューション名}.iOSのように各プラットフォームのプロジェクト名がつけられます.

f:id:shuhelohelo:20200729220516p:plain

が,プロジェクトの中ではソリューション名が変わらないままです.

例えば,Android,iOSの各プロジェクトから共有コードのプロジェクトを参照しているのですが,その参照先のプロジェクト名が変更されないため,以下のように参照エラーになります.これではいけません.

f:id:shuhelohelo:20200729220337p:plain

各プロジェクトの中でソリューション名に指定された文字列を使うためにはここでも先程と同じようにテンプレートパラメータを使用します.

どのようなテンプレートパラメータを使用するかというと$ext_safeprojectnameというパラメータです.

Android,iOSの各プロジェクトの.csprojファイルの中で,プロジェクトの参照情報の記述があるので,それを以下のように変更します.

  <ItemGroup>
    <ProjectReference Include="..\XFAspNetCoreDITemplate\XFAspNetCoreDITemplate.csproj">
      <Project>{7DD7EBB5-8DCB-428D-A7B2-FE93EE1EFC89}</Project>
      <Name>XFAspNetCoreDITemplate</Name>
    </ProjectReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\$ext_safeprojectname$\$ext_safeprojectname$.csproj">
      <Project>{7DD7EBB5-8DCB-428D-A7B2-FE93EE1EFC89}</Project>
      <Name>$ext_safeprojectname$</Name>
    </ProjectReference>
  </ItemGroup>

あともう一点,変更箇所がありました.基本的にはAndroidiOSも同じです.

  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{$guid1$}</ProjectGuid>
    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <TemplateGuid>{c9e5eea5-ca05-42a1-839b-61506e0a37df}</TemplateGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>$ext_safeprojectname$</RootNamespace>
    <AssemblyName>$ext_safeprojectname$.Android</AssemblyName>

ピックアップすると以下の箇所です.

  • <ProjectGuid>{$guid1$}</ProjectGuid>
  • <RootNamespace>$ext_safeprojectname$</RootNamespace>
  • <AssemblyName>$ext_safeprojectname$.Android</AssemblyName>

以上のように修正して保存します.

フォルダとvstemplateファイルをzip形式に圧縮する

以下のようにフォルダとファイルを選択してzipにします.

f:id:shuhelohelo:20200618163400p:plain

テンプレートフォルダに配置

以下のフォルダに先程作成したzipファイルを配置します.

%USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates

これで準備完了です.

Visual Studioを閉じてから再び起動します.

試してみる.

プロジェクトの新規作成でテンプレートを検索すると,以下のように先程作成したテンプレートが見つかります.

f:id:shuhelohelo:20200618164135p:plain

デフォルトのプロジェクト名も指定したとおりになっています.

f:id:shuhelohelo:20200618164242p:plain

ソースコード

テンプレートのもとにしたプロジェクト

github.com

テンプレート(zip前)

github.com

これをzipで固めると,プロジェクトテンプレートになります.

テンプレートの公開

作成したテンプレートを他の端末や,多くの人に使ってもらいたい場合,テンプレートを拡張機能として公開すると便利でしょう.

その場合は以下の手順を行います.

VSIXファイルを作成する

docs.microsoft.com

VSIXプロジェクトを作成するのですが,VSIXプロジェクトを作成するにはVisual Studio SDKをインストールしておく必要があるので,インストールします.

Visual Studio SDKのインストール

インストールはVisual Studio InstallerからVisual Studio extension developmentというワークロードを選択することでインストールされます.

f:id:shuhelohelo:20200618134306p:plain

約3GBあるので,インストールの完了を気長に待ちます.

f:id:shuhelohelo:20200618131820p:plain

VSIXプロジェクトの作成

Visual Studio SDKのインストールが完了したら,Visual Studioを起動してC#Empty VSIX Projectを新規作成します.

f:id:shuhelohelo:20200618140023p:plain

プロジェクト名はこのようにしました.

f:id:shuhelohelo:20200618140103p:plain

テンプレートファイル(zip)をプロジェクトに追加する

ソリューションエクスプローラでVSIXプロジェクトを右クリックしてコンテキストメニューからAdd -> Existing Itemを選択します.

f:id:shuhelohelo:20200618132450p:plain

ファイル選択ダイアログが開くのでファイルの種類をAll Filesにして,先程出力したテンプレートファイル(zip)を選択します.

f:id:shuhelohelo:20200709135450p:plain

ソリューションエクスプローラ上で,追加したテンプレートファイルを右クリックしてコンテキストメニューからPropertyを選択し,Copy to Output Directoryの項目にCopy alwaysを指定します.

f:id:shuhelohelo:20200618140349p:plain

source.extension.vsixmanifestを編集する

VSIXプロジェクトの中にsource.extension.vsixmanifestというファイルがあるので,ダブルクリックしてデザイナを開きます.

デザイナではMetadataのページで以下のように入力します.

f:id:shuhelohelo:20200618141046p:plain

次にAssetsのページでNewボタンを押します. 以下のウィンドウが表示されるので以下のように各項目を指定します.

f:id:shuhelohelo:20200618141408p:plain

  • TypeにはMicrosoft.VisualStudio.ProjectTemplateを指定.

  • SourceFile on filesystemを指定.

  • Pathにテンプレートファイル(zip)を指定します.

OKボタンを押します.

すると,ソリューションには以下のようにフォルダが作成され,その中にテンプレートファイルがインポートされます.

f:id:shuhelohelo:20200709151212p:plain

VSIXプロジェクトをビルドする

ビルドすると,VSIXプロジェクト内のbin\Debug(またはRelease)フォルダ内に以下のような.vsixファイルが生成されます.

f:id:shuhelohelo:20200618142131p:plain

拡張機能としてテンプレートをインストールする

この中のvsixファイルをダブルクリックするとインストールが始まります.

インストールが完了したらVisual Studioを起動します. f:id:shuhelohelo:20200709150525p:plain

プロジェクト新規作成でテンプレートを検索すると,以下のようにインストールしたテンプレートが見つかります.

f:id:shuhelohelo:20200709150756p:plain

20200715追記

Releaseビルドしたvsixファイルをインストールしようとすると,以下のようにサインがない,と言われてインストールできないことがあります.

f:id:shuhelohelo:20200715020421p:plain