最近在做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><没有可用的说明></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”,前者指的是一个文件(或多个文件),后者指的是一个完整的项目,不要搞混了,尤其在看文档时很容易就晕。