zoukankan      html  css  js  c++  java
  • Visual Studio项目模板与向导开发

    在【Xamarin+Prism开发详解系列】里面经常使用到【Prism unity app】的模板创建Prism.Forms项目:

    备注:由于Unity社区已经不怎么活跃,下一个版本将会有Autofac,DryIOC,Ninject的项目模板。

    image

    自动弹出选择框:

    image

    对于我这类还没有动手写过模板的人来说,确实挺新奇的。于是就决定自己也写个类似的试试,目的就是通过向导创建跨平台Plugin项目,类似Plugin for xamarin,不过是针对Prism,对应平台可以自由选择创建。试了之后才发现也有不少注意的地方,特写下此文做备忘。

    项目地址:https://github.com/NewBLife/Prism.Forms.Plugin

    插件下载地址:TemplatesForPrism.vsix

    1、安装插件开发用的Extensibility模板与工具

    image

    2、新建VSIX Project插件项目

    source.extension.vsixmanifest 这个文件相当重要,里面可以指定安装目标,模板,向导等。

    最后我实现的例子:

    安装目标:VS2013以上(2017估计不行)

    image

    资产:Project模板,Item模板,Wizard向导

    image

    3、从【C# Item Template】与【C# Project Template】模板创建项目。

    4、从【类库】模板创建Wizard项目。(Wizard向导只能是类库)

    以上步骤之后的项目结构:

    image

    介绍:

    • Prism.Forms.Plugin.Nuspec:Plugin打包文件模板
    • Prism.Forms.Plugin:Plugin项目模板
    • Prism.Forms.Plugin.Wizard:Plugin创建向导
    • TemplatesForPrism:VSIX插件项目

    5、添加引用

    • Prism.Forms.Plugin.Nuspec:Microsoft.VisaulStudio.CoreUtility
    • Prism.Forms.Plugin:Microsoft.VisaulStudio.CoreUtility
    • Prism.Forms.Plugin.WizardMicrosoft.VisaulStudio.TemplateWizardinterface,envdte
    • TemplatesForPrism:Prism.Forms.Plugin.Nuspec,Prism.Forms.Plugin,Prism.Forms.Plugin.Wizard

    6、添加生成向导

    6.1、NewProjectWizard项目选择向导创建新建一个WinForm选择框,返回选择的结果。

    image

    继承IWiazrd向导接口实现:

    using EnvDTE;
    using Microsoft.VisualStudio.TemplateWizard;
    using System;
    using System.Collections.Generic;
    using System.IO;
    
    namespace Prism.Forms.Plugin.Wizard
    {
        public class NewProjectWizard : IWizard
        {
            private DTE _dte = null;
    
            private string _solutionDir = null;
    
            private string _templateDir = null;
    
            private string _projectName = null;
    
            PluginNewProjectDialogResult _dialogResult;
    
            public void BeforeOpeningFile(ProjectItem projectItem)
            {
            }
    
            public void ProjectFinishedGenerating(Project project)
            {
    if
    (_dialogResult.CreateAndroid) CreateProject(Target.Droid.ToString()); if (_dialogResult.CreateiOS) CreateProject(Target.iOS.ToString()); if
     (_dialogResult.CreateUwp)
                    CreateProject(Target.UWP.ToString());
    
            }
    
    
    void CreateProject(string platform) { string name = $"{_projectName}.{platform}"; string projectPath = System.IO.Path.Combine(_solutionDir, Path.Combine(_projectName, name)); string templateName = $"Prism.Forms.Plugin.{platform}"; string templatePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(_templateDir), $"{templateName}.zip\{templateName}.vstemplate"
    );
    
                _dte.Solution.AddFromTemplate(templatePath, projectPath, name);
    
            }
    
            public void ProjectItemFinishedGenerating(ProjectItem projectItem)
            {
            }
    
            public void RunFinished()
            {
            }
    
            public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
            {
                try
                {
                    _dte = automationObject as DTE;
    
                    _projectName = replacementsDictionary["$safeprojectname$"];
    
                    _solutionDir = System.IO.Path.GetDirectoryName(replacementsDictionary["$destinationdirectory$"]);
    
                    _templateDir = System.IO.Path.GetDirectoryName(customParams[0] as string);
    
                    PluginNewProjectDialog dialog 
    = new PluginNewProjectDialog(); dialog.ShowDialog(); _dialogResult =
     dialog.Result;
    
    
                    if (_dialogResult.Cancelled)
    
                        throw new WizardBackoutException();
    
                }
                catch (Exception)
                {
                    if (Directory.Exists(_solutionDir))
    
                        Directory.Delete(_solutionDir, true);
                    throw;
                }
            }
    
            public bool ShouldAddProjectItem(string filePath)
            {
                return true;
            }
        }
    }

    模板选择后执行RunStarted==》弹出Winform选择框==》执行ProjectFinishedGenerating创建子项目

    6.2、项目名称safeprojectgroupname向导创建

    在NewProjectWizard向导的CreateProject方法里面我们设置了每个子项目的名称为"{_projectName}.{platform}",所以到达子项目的时候$safeprojectname$里面已经带了iOS,Droid,UWP等名称,所以有必要进行处理。

    using EnvDTE;
    using Microsoft.VisualStudio.TemplateWizard;
    using System.Collections.Generic;
    using System.Threading;
    
    namespace Prism.Forms.Plugin.Wizard
    {
        public class SafeProjectGroupName : IWizard
        {
            public const string ParameterName = "$safeprojectgroupname$";
    
            static ThreadLocal<string> projectGroupName = new ThreadLocal<string>();
            bool isRootWizard;
    
            public void BeforeOpeningFile(ProjectItem projectItem)
            {
            }
    
            public void ProjectFinishedGenerating(Project project)
            {
            }
    
            public void ProjectItemFinishedGenerating(ProjectItem projectItem)
            {
            }
    
            public void RunFinished()
            {
                // If we were the ones to set the value, we must clear it.
                if (isRootWizard)
                    projectGroupName.Value = null;
            }
    
            public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
            {
                if (projectGroupName.Value == null)
                {
                    var name = replacementsDictionary["$safeprojectname$"];
                    if (name.EndsWith(Target.Abstractions.ToString()))
                    {
                        projectGroupName.Value = name.Replace($".{Target.Abstractions.ToString()}", string.Empty);
                    }
                    else if (name.EndsWith(Target.Droid.ToString()))
                    {
                        projectGroupName.Value = name.Replace($".{Target.Droid.ToString()}", string.Empty);
                    }
                    else if (name.EndsWith(Target.iOS.ToString()))
                    {
                        projectGroupName.Value = name.Replace($".{Target.iOS.ToString()}", string.Empty);
                    }
                    else if (name.EndsWith(Target.UWP.ToString()))
                    {
                        projectGroupName.Value = name.Replace($".{Target.UWP.ToString()}", string.Empty);
                    }
                    // If there wasn't a value already, it means we're the root wizard itself.
                    isRootWizard = true;
                }
    
                replacementsDictionary[ParameterName] = projectGroupName.Value;
            }
    
            public bool ShouldAddProjectItem(string filePath)
            {
                return true;
            }
        }
    }

    Debug测试方法:

    • 给类库添加署名,否则无法注册
    • 管理员权限启动VS2015 开发人员命令提示工具
    • 导航到wizard生成目录
    • 执行gacutil -if Prism.Forms.Plugin.Wizard.dll 命令注册类库
    • 执行gacutil –l  列出类库的Token信息(Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da, processorArchitecture=MSIL)
    • 模板文件的WizarExtension节点添加以上Token信息与入口类名称
    • 安装模板,设置断点
    • 启动另一个Visual Studio程序
    • 调试-》附加到进程-》附加新启动的Visual Studio进程
    • 新Visual Studio窗口选择建立的模板,将进入断点

    7、添加模板文件并设置

    星星Prism.Forms.Plugin.Nuspec模板设置

    <?xml version="1.0" encoding="utf-8"?>
    <VSTemplate Version="3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
      <TemplateData>
        <Name>Plugin nuspec for Prism.Forms</Name>
        <Description>Create a nuspec file for Prism.Forms.Plugin solution</Description>
        <Icon>Prism.Forms.Plugin.Nuspec.ico</Icon>
        <TemplateID>325a0391-d11c-4432-8658-b70405881e87</TemplateID>
        
    <ProjectType>General</ProjectType>
        <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
        <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
        <DefaultName>PrismFormsPlugin.nuspec</DefaultName>
      </TemplateData>
      <TemplateContent>
        <ProjectItem TargetFileName="$fileinputname$.nuspec" ReplaceParameters="true">Prism.Forms.Plugin.nuspec</ProjectItem>
      </TemplateContent>
    </VSTemplate>

    注意点:

    • Item模板的ProjectType默认为CSharp,有时候没法找到,最好设置为General。
    • 模板参照文件的生成操作都设置为无。
    • 记得设置分类Category,这样好找。

    image

    效果如下:(在添加新项里面)

    image

    星星Prism.Forms.Plugin模板设置

    image

    每个文件夹下面为相应的模板文件

    • Prism.Forms.Plugin.Abstractions:接口定义
    • Prism.Forms.Plugin.Droid:Android平台特性的接口实现
    • Prism.Forms.Plugin.iOS:iOS平台特性的接口实现
    • Prism.Forms.Plugin.UWP:UWP平台特性的接口实现

    最外面的入口模板文件Prism.Forms.Plugin.vstemplate设置:

    <?xml version="1.0" encoding="utf-8"?>
    <VSTemplate Version="3.0.0" 
    Type="ProjectGroup"
     xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
      <TemplateData>
        <Name>Plugin for Prism.Forms</Name>
        <Description>Creates all files necessary to create a plugin for Prism.Forms</Description>
        <Icon>Prism.Forms.Plugin.ico</Icon>
        <ProjectType>CSharp</ProjectType>
        <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
        <SortOrder>30</SortOrder>
        <TemplateID>16bac5e1-199d-4e08-9ed3-2ef287221be1</TemplateID>
        <DefaultName>PrismFormsPlugin</DefaultName>
        <ProvideDefaultName>true</ProvideDefaultName>
        <PromptForSaveOnCreation>true</PromptForSaveOnCreation>
        <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
      </TemplateData>
      <TemplateContent>
    
    <ProjectCollection> <ProjectTemplateLink ProjectName="$safeprojectname$.Abstractions">
    
            Prism.Forms.Plugin.AbstractionsPrism.Forms.Plugin.Abstractions.vstemplate
          
    </ProjectTemplateLink> </ProjectCollection>
      </TemplateContent>
    
      <WizardExtension>     <Assembly>Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da</Assembly>     <FullClassName>Prism.Forms.Plugin.Wizard.NewProjectWizard</FullClassName>   </WizardExtension>
    </VSTemplate>

    注意点:

    • 入口模板文件的Type必须为ProjectGroup,否则只创建一个工程项目
    • 模板内容必须使用ProjectTemplateLink添加每个子项目的模板文件设置
    • 由于使用向导可选择方式创建子项目,所有这里只添加了接口的模板文件
    • WizarExtension节点为向导程序设置,调用Prism.Forms.Plugin.Wizard.NewProjectWizard

    子项目模板Prism.Forms.Plugin.Abstractions.vstemplate设置:

    <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" 
    Type="Project">
      <TemplateData>
        <Name>Prism.Forms.Plugin.Abstractions</Name>
        <Description>Implementation Interface for Prism.Forms.Plugin </Description>
        <ProjectType>
    CSharp
    </ProjectType>
        <ProjectSubType>
        </ProjectSubType>
        <SortOrder>1000</SortOrder>
        
    <CreateNewFolder>true</CreateNewFolder>
        <DefaultName>Prism.Forms.Plugin.Abstractions</DefaultName>
        <ProvideDefaultName>true</ProvideDefaultName>
        <LocationField>Enabled</LocationField>
        <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
        <Icon>__TemplateIcon.ico</Icon>
        <PromptForSaveOnCreation>true</PromptForSaveOnCreation>
      </TemplateData>
      <TemplateContent>
        <Project TargetFileName="$safeprojectname$.Abstractions.csproj" File="Prism.Forms.Plugin.Abstractions.csproj" ReplaceParameters="true">
          <ProjectItem ReplaceParameters="true" TargetFileName="I$safeprojectgroupname$.cs">IPrismFormsPlugin.cs</ProjectItem>
          <Folder Name="Properties" TargetFolderName="Properties">
            <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
          </Folder>
        </Project>
      </TemplateContent>
    
    <WizardExtension> <Assembly>Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da</Assembly> <FullClassName>Prism.Forms.Plugin.Wizard.SafeProjectGroupName</FullClassName> </WizardExtension>
    </VSTemplate>

    注意点:

    • Type为Project。
    • safeprojectgroupname是Prism.Forms.Plugin.Wizard.SafeProjectGroupName向导提供的参数,不是VS内部参数。值为用户选择模板时输入的工程名称。
    • 模板参照文件(IPrismFormsPlugin.cs,AssemblyInfo.cs等)的生成操作设置为无,否则编译出错。

    IPrismFormsPlugin.cs设置:使用safeprojectgroupname

    using System;
    
    namespace $safeprojectgroupname$
    {
        public interface I$safeprojectgroupname$
    {
    }
    }

    Prism.Forms.Plugin.Abstractions.csproj设置:使用safeprojectgroupname

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props')" />
      <PropertyGroup>
        <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{$guid1$}</ProjectGuid>
        <OutputType>Library</OutputType>
        <AppDesignerFolder>Properties</AppDesignerFolder>
        
    <RootNamespace>$safeprojectgroupname$</RootNamespace> <AssemblyName>$safeprojectname$</AssemblyName>
        <DefaultLanguage>en-US</DefaultLanguage>
        <FileAlignment>512</FileAlignment>
        <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
        <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
        <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>binDebug</OutputPath>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <OutputPath>binRelease</OutputPath>
        <DefineConstants>TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        
    <DocumentationFile>binRelease$safeprojectname$.XML</DocumentationFile>
      </PropertyGroup>
      <ItemGroup>
        <!-- A reference to the entire .NET Framework is automatically included -->
      </ItemGroup>
      <ItemGroup>
        <Compile Include="I$safeprojectgroupname$.cs" />
        <Compile Include="PropertiesAssemblyInfo.cs" />
      </ItemGroup>
      <Import Project="$(MSBuildExtensionsPath32)MicrosoftPortable$(TargetFrameworkVersion)Microsoft.Portable.CSharp.targets" />
      <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
           Other similar extension points exist, see Microsoft.Common.targets.
      <Target Name="BeforeBuild">
      </Target>
      <Target Name="AfterBuild">
      </Target>
      -->
    </Project>

    总体文件结构如下:

    image

    备注:

    IPrismFormsPlugin.cs,AssemblyInfo.cs,Prism.Forms.Plugin.Abstractions.csproj等都是通过VS自动生成的,不是手工创建的。具体方法就是事先建立后想要的项目结构与文件,然后使用VS的导出模板功能就可以自动生成了。添加进模板项目的时候一定记得将生成操作设置为无。

    image

    8、插件source.extension.vsixmanifest文件设置

    添加安装目标,资产。

    image

    9、生成发布

    使用效果如下:

    image

    image

    这回再也不用每次都添加好几次工程了,一次搞定。懒人就是这样想办法偷懒微笑

  • 相关阅读:
    Mach-O iOS
    IPA的构建原理 iOS
    输入网址进入网页按回车刷新网页都发生了什么?URL 输入到显示的过程?
    LeetCode另一棵树的子树Swift
    如何解决静态库的冲突问题 iOS
    LeetCode下一个更大元素 I Swift -- 单调递增栈
    UITapGestureRecognizer 和 UICollectionView/UITableView等点击事件冲突问题
    UITableView/UICollectionView调用reloadData刷新时界面闪烁
    C#绘制折线图或曲线图,直接返回Image对象或图片文件
    C#反射+继承+接口的应用
  • 原文地址:https://www.cnblogs.com/lixiaobin/p/VSIX.html
Copyright © 2011-2022 走看看