« Custom file templates for Visual Studio (Mac)
November 15, 2017 • ☕️☕️ 8 min read
Recently, we’ve been doing a little restructuring of our Xamarin-based mobile apps. We wound up with a stricter pattern for building out views and their elements. Coincidentally, that’s providing faster development and more freedom to focus on the meat of making the app work really well. With the new structure, we have a more standard way of building things. As that common pattern has became more obvious, though, we’ve wanted a first-class way to encourage a common style of adding to the app. Rather than truly enforcing it, though — and boxing ourselves in — we decided to make the most desirable path the easiest. We want to provide snippets or file templates to make new development as easy as possible within our internal framework. The simplest starting point is to just provide templates that are available when you create a new file in Visual Studio. Let’s talk about the research it took to create our own custom file templates for Visual Studio for Mac, how we actually created and distribute them internally, and finally, where I think Visual Studio for Mac could improve the state of things.
Side note: The Story Time is a lengthy explanation of my journey to a solution, not the solution itself. If you really just want a small guide to Custom Multi-File templates on Visual Studio for Mac, skip on down.
Story Time
Before we get into the specifics of how we built our custom templates, I’d like to discuss the work it took to learn enough to write our own custom templates. Not to disparage Microsoft, or complain about the work — as an explanation of what it took and what I’d like to help improve in the future.
The first thing I do in a situation such as this is hit Google with “Custom File Templates for Visual Studio.” Great, here’s a bunch of useful and relevant looking links — jackpot:
Too bad all of these are for either Visual Studio Code or Visual Studio for Windows. I learned over the course of trying to implement these templates, that the Windows version of this is entirely incompatible with the Mac version of how you’d add custom file templates. That was somewhat frustrating to realize, but I can understand. Things have changed a lot in this part of Microsoft recently and it’s hard to keep everything straight all the way down to documentation. So moving forward, we’ll have to be sure to scope our searches to Mac.
I was convinced, when I walked into this, that custom file templates would be like a solution configuration item or something. Perhaps there’d be an option to provide a few working templates via configuration and voila. Through my reading, it quickly became apparent that on the Mac Visual Studio, you create a full-fledged Extension and distribute that. That seems heavy handed, but I wasn’t about to walk away from this without custom templates.
Eventually I found my way to this Stack Overflow question which references the MonoDevelop Extension documentation. While reading through everything it became useful to know that Visual Studio for Mac is a continuation of Xamarin Studio which is (I think?) a superset — or perhaps just a distribution with extensions — of MonoDevelop. Once on the MonoDevelop documentation, things start to make a little more sense. Reading the docs, it looks like I’ll need to install this Addin Maker Extension to Visual Studio, create a new solution, then follow the steps in these docs.
It’s at this point, though, that I realize the documentation, on Microsoft, on Xamarin, and on MonoDevelop all reference how to add a Command tool — which seems to basically execute some arbitrary code and place it in the active editor. That’s not what I want. Further reading shows a way to create Project Templates — which for some time I pursue, only to realize that they are truly project templates, not file templates. At this point I had been hoping it would apply to both situations.
I’ve seen enough to realize that it’s time to go looking in the source. In the Addin Maker Extension source, since it’s an extension of its own. After some digging, I come across their File Templates in their Addin Manifest (don’t worry about fully understanding this yet, I’ll expand on actually creating an Addin in the next section). This is all well and good, but the files they reference in the manifest (like Templates/FileTemplate.xft.xml
) are no where to be found. I’m not fully versed in how this project works, so pocket that for a little later. Looking back at notes, I was able to recall a forum post on Xamarin’s Forums that linked to an example in MonoDevelop itself. Finally, an example of the Manifest format for my use case as well as the template format.
So after pursuing implementing the distilled version of what I learned above — I had some final things to implement. I wanted to create 3 files in my template: A Xaml page, its code-behind, and a matching PageModel. Since we’re thin on documentation here, I found and perused the Schema definitions to learn that we can specify multiple files for a template — success! But how do I recreate the Xaml/Code-behind behavior that’s so automatic within Xamarin tools? At this point I recall, also from notes, that we have access to some of the Addins installed on the computer (which is missing some of the manifest and template definitions — otherwise all of my searching would have stopped here, a while ago). Opening /Applications/Visual\ Studio.app/Contents/Resources/lib/monodevelop/AddIns/Xamarin.Forms.Addin/Templates
reveals details about how to build templates that include Xaml and code-behinds. There, we’ve got it. I’d link to the source of this, if I could find it anywhere.
On top of building the templates, we have to somehow distribute this extension. The main things I know: I don’t want to distribute this publicly, since it’s somewhat specific to us, and I want it to be simple to both distribute and stay up-to-date. Once again, during my short explorations, I wasn’t able to track down much on how to host your own repository. To get this over with, for now, I’ve decided to create a script that packages up the `mpack` (extension file) and pushes it to our Github releases on the Extension’s project page. Once published, we just need to tell developers to download it and install it from file. This is less than ideal, but I’ve already sunk more time into this than I had hoped.
The point of the above simply to illustrate that this was harder than I expected. It’s not meant to just be me complaining about the state of the art, it’s a note that this is what it took me to achieve what I set out to do. If that results in further interest to improve the system, or simply the documentation — Awesome! It’s meant to help remind myself what I’d like to help improve.
Microsoft is doing a ton lately — that’s one of the reasons Xamarin and the tooling around it was so attractive when we set out to decide how we’d build our mobile apps. They’ve got deep changes going on in the core of how .NET operates, Visual Studio Code is changing things, Roslyn is changing things, Visual Studio for Mac (and consequently Xamarin) is changing things, .NET Core — the list seriously could take me the rest of the day to write-up. So much of what’s going on seems to be aiming at better standardization across the board — everything is operating on a different history, different scale. Things are converging and diverging at the same time because of the speed and breadth of what’s going on.
It’s all a mix of exciting, confusing, frustrating, and fun. I hope that Microsoft reaches a point where some of these things standardize in the next couple of years. I don’t say that with a bad taste in my mouth — I say that because that simply be amazing to use and would change the face of software as we know it, I think. That’s a cool future to look forward to.
How To
Enough of the soap-box, let’s talk about how to actually get some file templates. For our example, let’s build what I mentioned above: a template that comes with a Xaml page, with code-behind and separate PageModel class. Open Visual Studio for Mac, Open the Extensions Menu, and install the Addin Maker:
Create a new IDE Extension solution:
Open Properties/AddinInfo.cs
and change the values to whatever you’d like. I do this because otherwise I forget and end up installing a bunch of oddly named extensions. This way we can better identify it as we work on it. Next, open Properties/Manifest.addin.xml
and change it to:
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
<Extension path="/MonoDevelop/Ide/FileTemplates">
<FileTemplate id="FunPageTemplate" file="Templates/FunPage.xml" />
</Extension>
</ExtensionModel>
Next, create a Templates
directory at the top of the project (not solution). We can now define the template configuration. In Templates/FunPage.xml
we’ll explain the whole template:
<?xml version="1.0"?>
<Template>
<!-- Template Header -->
<TemplateConfiguration>
<_Name>Fun Page with PageModel</_Name>
<_Category>Fun Pages</_Category>
<Icon>md-html-file-icon</Icon>
<LanguageName>C#</LanguageName>
<DefaultFilename>MyPage</DefaultFilename>
<_Description>Creates a Fun Xaml Page and a paired PageModel.</_Description>
</TemplateConfiguration>
<!-- Template Content -->
<TemplateFiles>
<File name="${Name}.xaml"
DefaultExtension=".xaml"
AddStandardHeader="False"
BuildAction="EmbeddedResource"
CustomTool="MSBuild:UpdateDesignTimeXaml"
SubType="Designer"
src="./FunPage.xaml"/>
<File name="${Name}.xaml.cs"
DependsOn="${Name}.xaml"
AddStandardHeader="True"
src="./FunPage.xaml.cs"/>
<File name="${Name}Model.cs"
AddStandardHeader="True"
src="./FunModel.cs"/>
</TemplateFiles>
</Template>
Note the use of the Name
variable in the File definitions, as well as the attributes on the xaml
file that explains the properties we’re used to seeing in Visual Studio. The src
path is relative to this XML file, so inside of Templates/
we’ll create a FunPage.xaml
, FunPage.xaml.cs
, and FunPageModel.cs
. To keep the example going, I’ll put in each of those files, respectively:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="${Namespace}.${EscapedIdentifier}">
<ContentPage.Content>
</ContentPage.Content>
</ContentPage>
---
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace ${Namespace}
{
public partial class ${EscapedIdentifier} : ContentPage
{
public ${EscapedIdentifier} ()
{
InitializeComponent();
}
}
}
---
using System;
using System.Windows.Input;
using System.Collections.Generic;
using Async = System.Threading.Tasks;
using Xamarin.Forms;
namespace ${Namespace}
{
public class ${EscapedIdentifier}Model
{
public ICommand ViewContentCommand { get; }
public ${EscapedIdentifier}Model() { }
}
}
At this point, you should be able to build with Release
configuration. You can then find the mpack
(the file you can install to Visual Studio to get your template) in bin/Release/net461/<your-addin-name>.mpack
.
I don’t love relying on Visual Studio for building, since at some point I’d like to fully automate build and private distribution. To prep for that a little, I converted this to work better with msbuild
and push to Github, so let’s go over that.
The project csproj
didn’t work for me out of the box, so I deleted the CustomCommand
section and added:
<Target Name="AfterBuilding" AfterTargets="Build">
<Exec Command="'/Applications/Visual Studio.app/Contents/MacOS/vstool' setup pack <your-project>.dll" WorkingDirectory="$(TargetDir)" Condition="'$(Configuration)' == 'Release'" />
</Target>
Be sure to change <your-project>
. This is the command that takes the dll
from the build and generates the mpack
necessary to make your poject work like an extension. Now to build the project we can just run:
msbuild /t:Restore
msbuild /p:Configuration=Release
Finally, I wanted to use our Github Project’s releases to share the mpack
with the team, for now. If you have the [hub](https://github.com/github/hub)
command, you can use this little script I created to “release” your built code to Github:
And that’s it for an extension that provides Multi-File Templates to Visual Studio for Mac. Now, adding more should be a piece of cake. Next, let’s chat about my wishlist for how some of these things improve to make things simpler for the next person.
Improving the State of the Art
Part of the reason I wanted to tell the story of how I found the information it would take to solve the problem, was that it felt like a lot — more than I would have expected. I haven’t been a full time IDE user in some time, but I expected one of the benefits to be simple sharing of templates within a solution to promote keeping things standard.
That being said, shoving the solution to that problem into extensions makes enough sense — adds the amount of power you might want, and it’s not that complicated once you know the “right” things. In the end it was just hard to track down the “correct” information for this problem— it seems like tribal knowledge at the moment.
Taking the extension critique a step further — extensions are traditionally installed and updated from the Extension gallery. The gallery allows you to specify new repositories. However, I haven’t found a way to easily create our own extension repository that can be somewhat private. And really, I don’t actually care to force them to be “private,” they’re just wildly specific to our project. In my specific case, I don’t even need a repository available on the internet. I just want a solution-wide set of templates (or extensions, I suppose) that you just “get” when you’re developing on that solution.
Finally, as I’m sure almost everyone would love, it’d be simply amazing if there was deeper overlap between the Mac and Windows Visual Studios. Code would be kind of neat as well, but that is a wholly different thing, as far as I’m aware. It might even be silly to suggest that they could overlap with the Code app.
I do want to finish by expressing how, at the core of all of this, the power and extensibility that exists today is almost unfathomable. That’s one of the reasons I was a little surprised I had trouble finding the necessary guides to get going — it feels like this should be a huge feature, given attention and deeper effort to promote the open community. As soon as I had the necessary pieces for my use-case, several open source ideas came to mind. I look forward to pursuing those ideas and trying to add to the improvements I mention above.
Cheers Microsoft and everyone involved that created the tools and libraries that made it possible at all.
Do you have Visual Studio for Mac Extensions you can share with us? I’d love to know what community or open source tools exist as examples that I simply didn’t come across.
Follow me on Twitter for more rantings or check out my website for more info.