I am trying to make a Visual Studio Project Template. I can easily replace the names of things I want to have the same name as the project using $safeprojectname$
So if my project was named WidgetHunter and I named a file $safeprojectname$.js it would be called WidgetHunter.js when the project was created.
But what if I wanted to name the file widgetHunter.js or widget-hunter.js?
Is there away to make a new variable that does not have a static value? (I need to perform a string operation on the supplied project name.)
So this will not be as straight forward as you allude. Project Templating is not very powerful, it's basic replacement, you can't really run code AFAIK.
However, you can "easily" add a Wizard Step (IWizard) that adds additional keys to the replacement dictionary. For example, you could add $safeprojectnameforjs$ which has the manipulated value.
public class ExampleWizard : IWizard
{
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
replacementsDictionary.Add("safeprojectnameforjs",
YourCustomMethodForManipulatingName(replacementsDictionary["safeprojectname"])
}
// there are a few other IWizard methods you'll need to
// implement but don't need to do anything in
}
To wire in your ExampleWizard, you'll need to add a tag in your .vstemplate
<VSTemplate>
<WizardExtension>
<Assembly>ExampleWizard.ExampleWizard, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=a76e3e75702e3ee4</Assembly>
<FullClassName>ExampleWizard.ExampleWizard</FullClassName>
</WizardExtension>
</VSTemplate>
Note: You'll need to have your Wizard in an assembly. Easiest way is to create a new Class Library project. Also, it will need to be signed (at least, pretty sure that's still a requirement).
Finally, you'll need to update your VSIX manifest, so that you have the Wizard assembly wired up as an assembly dependency:
<PackageManifest>
<Assets>
<Asset Type="Microsoft.VisualStudio.Assembly" d:Source="Project" d:ProjectName="ExampleWizard.ExampleWizard" Path="|ExampleWizard.ExampleWizard|" AssemblyName="|ExampleWizard.ExampleWizard;AssemblyName|" />
</Assets>
</PackageManifest>
Related
I am creating a Visual Studio extension and have added a custom configuration-specific property to an existing Microsoft Visual C++ (vcxproj) project using the IVsBuildPropertyStorage interface.
I would like to have that custom property show up in the Project Designer's property pages, as part of a new property page. There is some information on how to achieve that for a custom project type but the approach relies on reimplementing the project system which is not appropriate for all situations. Is there a way to extend the Project Designer for an existing project type?
One way to customise the Project Designer pages for a vcxproj is to include an additional property schema by adding the following:
<ImportGroup Label="PropertySheets" />
<ItemGroup>
<PropertyPageSchema Include="proppage.xml">
<Context>Project</Context>
</PropertyPageSchema>
</ItemGroup>
to a project which can be done by either copying this snippet into the project file directly or by putting it into a custom props file and including that into the project. Details on the structure of a property page schema are available here.
The main issue with this approach is that it requires manual intervention or an explicit step for setting up a project to use the custom property page, making it unsuitable for my use case.
It is also bit cumbersome as it requires shipping one-two additional files with the extension and it leaves a footprint in the project file. I would like to come across a more minimalistic solution that automatically applies to all MS VC++ projects but does not modify the project files unless the user inputs a property into the custom property page.
I'd like to create a Visual Studio extension to allow the user to create a json file and automatically apply a custom JSON schema to it. I followed this article and so far, I succeeded in allowing the user to create the JSON file, but I have no idea how to automatically bind the custom schema to it.
I'd like this schema to be in the extension solution, this way, when I update the extension, the schema gets updated. As I'm using SideWaffle, I noticed that there is this "JSON Schema Selector Extension" item template but it does not work. It requires "Microsoft.Json.Core.Schema" that is nowhere to be found (not even in the Microsoft.JSON Nuget package...).
Late to this question, but the solution involves referencing a non-SDK VS assembly as of VS2019 (and everything prior). To prove it is non-SDK, it even changed identity in VS2019, it is now Microsoft.WebTools.Languages.Json as of VS2019 (whereas it was indeed Microsoft.Json.Core for older versions), and of course, it may change again in the future.
Knowing this, if you search online opensource repos, you will find some ideas for how to achieve the reference. Some people bundle a copy of the DLL in their repo (which is probably against some licensing terms). Personally, I did not go that route and I instead went for an overcomplicated path relative to $(MSBuildToolsPath):
<Reference Include="Microsoft.WebTools.Languages.Json">
<HintPath>$(MSBuildToolsPath)\..\..\..\Common7\IDE\Extensions\Microsoft\Web Tools\Languages\Microsoft.WebTools.Languages.Json.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
<Private>false</Private>
</Reference>
Just looking at this, it is obvious that this approach is brittle, and the path may move in the future, but it avoids bootstrapping the dll.
Anyway, whichever way you choose to reference the assembly, as for everything else that is meant to come from VS itself, make sure it is Private=false, as you do not want your final VSIX bundle to include it.
Then it is a simple matter of implementing IJsonSchemaSelector:
[Export(typeof(IJsonSchemaSelector))]
public class LaunchConfigJsonSchemaProvider : IJsonSchemaSelector
{
private static readonly string Schema = #"<somehow resolve your extension install path and find your schema json>";
public event EventHandler AvailableSchemasChanged { add { } remove { } }
public Task<IEnumerable<string>> GetAvailableSchemasAsync()
{
return Task.FromResult((IEnumerable<string>)new[] { Schema });
}
public string GetSchemaFor(string fileLocation)
{
string ext = Path.GetExtension(fileLocation);
if (!".greatjson".Equals(ext, StringComparison.OrdinalIgnoreCase))
return null;
return Schema;
}
}
I am trying to create a Visual Studio Item Template that will create a WPF Window with an attached file for a view model
Like the following
VMWindow.xaml
---VMWindow.xaml.cs
---VMWindow.vm.cs
I am able to create the template with the following .vstemplate file
<VSTemplate Type="Item" Version="2.0.0"
xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>Viewmodel Dialog Box</Name>
<Description>Viewmodel Dialog Box</Description>
<Icon>Icon.ico</Icon>
<ProjectType>CSharp</ProjectType>
<DefaultName>VMDialog</DefaultName>
</TemplateData>
<TemplateContent>
<ProjectItem TargetFileName="$fileinputname$.xaml" SubType="Window">ViewModelDialogTemplate.xaml</ProjectItem>
<ProjectItem TargetFileName="$fileinputname$.xaml.cs">ViewModelDialogTemplate.xaml.cs</ProjectItem>
<ProjectItem TargetFileName="$fileinputname$.vm.cs">ViewModelDialogTemplate.vm.cs</ProjectItem>
</TemplateContent>
</VSTemplate>
I would like for the template to create itself with the .vm.cs file nested inside the main Window file when displayed in Solution Explorer.
I have found the following howto, I am having trouble following it with Visual Studio 2010 though. It was written in 2008, does this still apply?
Code Project article
It's actually very easy...
<ProjectItem TargetFileName="$fileinputname$.xaml" SubType="Window">ViewModelDialogTemplate.xaml</ProjectItem>
<ProjectItem TargetFileName="$fileinputname$.xaml/$fileinputname$.xaml.cs">ViewModelDialogTemplate.xaml.cs</ProjectItem>
<ProjectItem TargetFileName="$fileinputname$.xaml/$fileinputname$.vm.cs">ViewModelDialogTemplate.vm.cs</ProjectItem>
There's a much easier way. You can pull in the same wizard that VS uses to construct composite items. You do this by adding an element at the end of your template, after <TemplateContent>...
<WizardExtension>
<Assembly>Microsoft.VisualStudio.Web.Application, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
<FullClassName>Microsoft.VisualStudio.Web.Application.WATemplateWizard</FullClassName>
</WizardExtension>
Then you need to tell the wizard the extension of the parent, and the extension of the children...
<CustomParameters>
<CustomParameter Name="$ParentExtension$" Value=".xaml"/>
<CustomParameter Name="$ChildExtension$" Value=".cs"/>
</CustomParameters>
This element goes inside <TemplateContent>.
This solution is tested and working in VS2012, and you can see the version hardcoded in the call to the wizard. If you have version problems, look for the file webform.vstemplate (visual studio's .aspx template), and inspire yourself.
As it turns out the same method works for VS 2010. Required a bit of adaptation but this Code Project article covers the basic idea.
You need to implement the Microsoft.VisualStudio.TemplateWizard.IWizard interface, and write a little bit of code to remove the new item from the project and re-add it as the child of another item. Here is a working example from QueryFirst that takes any file with the extension .gen.cs and makes it a child of the same-named .sql file...
public void ProjectItemFinishedGenerating(ProjectItem
item)
{
string path = item.FileNames[0];
string parentPath = null;
if (path.EndsWith(".gen.cs"))
parentPath = path.Replace(".gen.cs", ".sql");
if (path.EndsWith("Results.cs"))
parentPath = path.Replace("Results.cs", ".sql");
if (!string.IsNullOrEmpty(parentPath))
{
ProjectItem parent = item.DTE.Solution.FindProjectItem(parentPath);
if (parent == null)
return;
item.Remove();
parent.ProjectItems.AddFromFile(path);
}
}
To attach the code to the template, you'll need something like this in your .vstemplate file...
<WizardExtension>
<Assembly>QueryFirst, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4688a60b10e39f04</Assembly>
<FullClassName>QueryFirst.WizardImplementation</FullClassName>
</WizardExtension>
I have a solution that contains several projects, lets call them ProjectA and ProjectB. I need ProjectA to have a reference to ProjectB, however when I "Export Template" I can't seem to keep the reference. What I am trying to do is to have it create the reference for me when I create a new project and then automatically name it to what I entered.
This is slightly out of scope for pure Visual Studio templates.
Visual Studio templates supports replacements parameters for templates, so what you could do is:
Inside your ProjectA.zip template file you will find ProjectA.csproj, which is the template for the project
In ProjectA.csproj you will find an <ItemGroup> containing <Reference> entries
Among them should be an entry like <Reference Include="ProjectB">
When you create a project from your ProjectB-template, it's name will be user-specified, so you want to replace ProjectB with a replacement parameter like $Foo$.
This is where it gets out of scope for pure templates: How do you tell visual studio to replace $Foo$? And what would the replacement be?
Your only choice here it to use a custom wizard, which either looks for the ProjectB-type project or asks the user for it.
Here's an outline for how to get the template and custom wizard to get along:
Create a VSIX package project.
Be sure to add the [ProvideBindingPath] attribute to your package. This is required to get the custom wizard registered (otherwise you would need to install it to the GAC, so VS can find it)
Create your wizard, it should implement Microsoft.VisualStudio.TemplateWizard.IWizard
Add your template to the solution and add it as a ProjectTemplate asset in your package's vsixmanifest
In the .vstemplate file of your template, add the following snippet below the <TemplateContent> block:
<WizardExtension>
<Assembly>Your.Packages.Assembly</Assembly>
<FullClassName>Your.Wizard</FullClassName>
</WizardExtension>
That will bring up your wizard when the project is created. You could, in the RunStarted method bring up a dialog which asks for the reference or you could use the automationObject parameter (which actually is a EnvDTE.DTE instance) to try and find the ProjectB project and supply it's name in the replacementsDictionary.
First, I know Silverlight project can't reference to non-Silverlight based project like Windows class library or Asp.net MVC project. But I need to create my projects which can support both Silverlight-based project & Asp.net MVC project.
So, I created Silverlight-based project for my sharing source code. It works fine on VS.net 2008 & .Net 3.5 SP1. However, I found some error when I try to use some method of Silverlight-based project from .Net-based project like the following code.
Silverlight-based Method
public static void InitializeInstance(object obj)
{
// Initialize Field Value
foreach (FieldInfo fi in obj.GetType().GetFields())
{
foreach (Attribute attr in fi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
fi.SetValue(obj, dv.Value);
}
}
}
// Initialize Property Value
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
foreach (Attribute attr in pi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
if (pi.CanWrite)
{
pi.SetValue(obj, dv.Value, null);
}
}
}
}
}
.Net-based Method
private void Form1_Load(object sender, EventArgs e)
{
InitializeInstance(this);
}
Error Detail
System.IO.FileNotFoundException:
Could not load file or assembly
'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' or
one of its dependencies. The system
cannot find the file specified. File
name: 'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' at
InitializeInstance(Object obj)
Finally, I try to solve this problem by copying system.dll of Silverlight to output directory and reference it. It still shows same error. So, I think this error may be limitation of both .Net & Silverlight platform. Do you have any idea for avoid this issue?
PS. I know I can use this technique for a few sharing code. But it’s impossible to do this for my projects. Because it’s very complicate & very complex more than directly create Silverlight-based or .Net-based class library.
Thanks,
The trouble here is that those types share an assembly with a different strong name: System.Windows in Silverlight, PresentationFramework or PresentationCore on the desktop CLR.
So at runtime, the intended type cannot be loaded, and there are no type forwarders for the Silverlight-to-desktop types.
My recommended solution
Consider using file links, instead of actually trying to reference the same built binary.
This way, you can have a source structure for your project that may look like this:
MyApp\
Silverlight\
Page.xaml
Page.xaml.cs
(link) ..\AspMvc\MySharedDataFile.cs
AspMvc\
MySharedDataFile.cs
MyApp.cs
This way, the source will be re-compiled with both projects. We use this on the Silverlight Toolkit to build many controls, including the charting and data visualization controls, for both WPF and Silverlight. This is by rebuilding for each platform, instead of referencing the binaries from both.
To insert a link in Visual Studio, just right-click on your project or one of its folder, Add Existing Item, then find it in the explorer open file dialog. however, instead of just clicking the button, click on the little down arrow drop-down on the Add file button, and select the "Add as link" option.
Then, it simply builds that file from another location, but it is not a copy, so you can maintain it in one place and use in both.
A crazy solution
You can use .NET reflection from your desktop app that is of a much higher trust to actually create a new app domain, hook up to the assembly resolution event, and see what that does. You may be able to instead return the type from the desktop CLR, or just no-op these warnings.
No clue if it works.