更多

Visual Studio自定义项模板和解决方案浏览器折叠

最近在做WinUI开发,需要大量创建Page页面,还用到了MVVM,所以需要写挺多的样板代码,除了使用“新建项”里创建的xaml和xaml.cs文件外,还需要创建对应的viewModel文件,最后还要在xaml.cs中获取并绑定viewModel。

1、解决方案浏览器折叠文件

默认情况下,VS会自动折叠 .xaml 和 .xaml.cs 文件,合并成一个选项卡,这样看起来很美观整洁,但是使用MVVM时,总是会多出来一个viewModel文件,看起来就有点杂乱。VS提供了自定义折叠规则的功能:

适用于解决方案资源管理器的文件嵌套规则 – Visual Studio (Windows) | Microsoft Learn

这里简单提一下几项规则:

  • extensionToExtension:使用此规则类型在 file.ts 下嵌套 file.js
  • fileSuffixToExtension:使用此规则类型在 file.js 下嵌套 file-vsdoc.js
  • addedExtension:使用此规则类型在 file.html 下嵌套 file.html.css
  • pathSegment:使用此规则类型在 jquery.js 下嵌套 jquery.min.js
  • allExtensions:使用此规则类型在 file.js 下嵌套 file.*
  • fileToFile:使用此规则类型在 .bowerrc 下嵌套 bower.json

使用此方法自定义的时候需要的是dependentFileProviders.add里的几个规则是有顺序的,越前边的规则越先应用,只有前边的规则都不匹配时,才会用到最后的规则,这里推荐把allExtensions、addedExtension放置在最后两个,因为这两个匹配的范围比较大。

如果创建的ViewModel文件是以 *.xaml.viewModel.cs 这样的形式命名的,那就可以使用extensionToExtension规则,具体可以这样做:

{
  "help": "https://go.microsoft.com/fwlink/?linkid=866610",
  "root": true,
  "dependentFileProviders": {
    "add": {
      "extensionToExtension": {
        "add": {
          ...
          ".xaml.model.cs": [
            ".xaml"
          ]
        }
       },
       ...
     }
  }
}

如果创建的ViewModel文件是以 *ViewModel.cs 这样的形式命名的,那就可以使用fileSuffixToExtension这个规则,具体可以这样做:

{
  "help": "https://go.microsoft.com/fwlink/?linkid=866610",
  "root": true,
  "dependentFileProviders": {
    "add": {
      ...
      "fileSuffixToExtension": {
        "add": {
          ...
          "ViewModel.cs": [
            ".xaml"
          ]
        }
       },
       ...
     }
  }
}

2、自定义项模板

为了少(手动)写样板代码,可以自己创建一个项模板,就和使用VS自带的新建空白窗口之类的,能够自动生成页面模板代码,实现同时创建 .xaml、.xaml.cs、*ViewModel.cs文件,并自动完成引用和MVVM初始化。

VS的新建多文件项模板文档如下:

创建多文件项模板 – Visual Studio (Windows) | Microsoft Learn

如果你只需要创建一个文件的项模板,可以参考这个文档:

创建项模板 – Visual Studio (Windows) | Microsoft Learn

就我的需求,创建的模板如下:

Page.xaml:

<?xml version="1.0" encoding="utf-8"?>
<Page
    x:Class="$rootnamespace$.$itemname$"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:$rootnamespace$"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>

    </Grid>
</Page>

Page.xaml.cs:

using $defaultnamespace$;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;

namespace $rootnamespace$;

public sealed partial class $itemname$ : Page
{
    public $itemname$ViewModel ViewModel => ($itemname$ViewModel)DataContext;

    public $itemname$()
    {
        InitializeComponent();
        DataContext = App.Current.Services.GetService<$itemname$ViewModel>();
    }
}

PageViewModel.cs:

using CommunityToolkit.Mvvm.ComponentModel;

namespace $rootnamespace$;

public class $itemname$ : ObservableObject
{
    
}

MyTemplate.vstemplate:

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <DefaultName>MVVM Page.xaml</DefaultName>
    <Name>MVVM Page</Name>
    <Description>&lt;没有可用的说明&gt;</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>10</SortOrder>
    <Icon>__TemplateIcon.ico</Icon>
  </TemplateData>
  <TemplateContent>
    <References />
    <ProjectItem ItemType="Page" SubType="Designer" CustomTool="MSBuild:Compile"  TargetFileName="$fileinputname$.xaml" ReplaceParameters="true">Page.xaml</ProjectItem>
    <ProjectItem SubType="" TargetFileName="$fileinputname$.xaml.cs" ReplaceParameters="true">Page.xaml.cs</ProjectItem>
    <ProjectItem SubType="" TargetFileName="$fileinputname$ViewModel.cs" ReplaceParameters="true">PageViewModel.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

需要注意的是,如果你跟着文档创建好了模板,你可能会发现在最终的模板压缩包中还有一个多出来的xaml文件,并且,假设你要使用你创建好的模板创建一个Home.xaml,VS会提示已经存在Home.xaml并不再创建其它几个文件,这个问题的原因就是模板压缩包中多出来的那个xaml文件,解决方法就是在MyTemplate.vstemplate中的TemplateContent下删除对多出来的xaml文件的引用,同时删除压缩包中的那个文件。

但是到这里还没有完成,你会发现你创建的模板和我这里贴出的MyTemplate.vstemplate文件有些许不一样,而且你会发现你创建出的 .xaml.cs 文件中的InitializeComponent方法调用会提示找不到InitializeComponent,这点微软的文档里并未提及。

这点就需要了解一下WinForm、WPF、UWP以及WinUI的界面实现了。众所周知,实际上为了完成xaml和cs的结合,微软采用了分部类的解决方案,在创建了 .xaml 和 .xaml.cs 文件后,VS还会生成一个不主动显示的.g.i.cs文件,在该文件中完成xaml绑定等工作,一般会在项目目录的obj文件夹中,InitializeComponent方法也是在该文件中生成的。

我在查看了VS自带的模板后才发现,xaml文件需要添加 SubType=”Designer” CustomTool=”MSBuild:Compile” 这两个属性才可以自动生成对应的desinger文件,因此如果没有这两个属性,VS只会添加 .xaml 文件,而不生成对应的分部类,所以会提示找不到InitializeComponent方法。所以解决方法就是如上代码所示,在xaml文件对应的ProjectItem标签上添加这两个属性。

至于ProjectItem中的其它属性,可以参考如下文档:

ProjectItem 元素 (Visual Studio 项模板) – Visual Studio (Windows) | Microsoft Learn

其余标签参考如下文档:

Visual Studio 模板架构参考 – Visual Studio (Windows) | Microsoft Learn

最后还要注意的一个小的点是,“项”和“项目”是两个不同的概念,前者是“item”,后者是“project”,前者指的是一个文件(或多个文件),后者指的是一个完整的项目,不要搞混了,尤其在看文档时很容易就晕。

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code