Launch Profile UIs are defined by instances of the Rule class. Visual Studio uses these Rule
objects to dynamically build the UI and bind the controls to launch settings. This HOW TO describes three options for defining and distributing these Rule
objects:
Rule
objects read from a .xaml file on diskRule
objects read from an embedded .xaml file resourceRule
objects defined in code and exposed viaIRuleObjectProvider
At the end your Launch Profile will look something like this:
This HOWTO is concerned with how to create a Rule
that defines a launch profile UI; however, Rule
objects are used for various purposes in the project system. For a more general discussion of Rule
s, please see the following:
In this option, the XAML resides in a standalone .xaml file on disk and is included in the end user's project as a specific kind of MSBuild item, PropertyPageSchema
. Visual Studio reads these items from the project to determine which kinds of Launch Profiles it can display.
This may be an attractive option if you already have a NuGet package or Visual Studio extension that injects MSBuild .props and .targets files into the end user's project, or if you want to use MSBuild Condition
s to control when the Launch Profile UI is available to a project.
Use the NuGet Package Manager to add the Microsoft.Build.Framework package to your project. This is an optional step, but it will allow the XAML editor to find the Rule type (and related types) and provide code completion, tool tips, Go to Definition, and other features while you type.
Add a new XAML file named "MyLaunchProfile.xaml" to your project. Depending on how the file is created you may end up with a <Page>
item in your project but this is not what we want as we're not using the file to describe a piece of WPF UI.
Update your project to replace the <Page>
item with one of the following:
- SDK-style projects:
<None Update="MyPropertyPage.xaml"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None>
- Non-SDK-style projects:
<None Include="MyPropertyPage.xaml"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None>
Now VS won't do anything with this file but copy it to the output directory when you build.
Next you need to update the .props or .targets files imported by the end users' projects to properly reference the property page so Visual Studio can find it. Note that the creation and distribution of the .props and .targets files (as well as the distribution of MyPropertyPage.xaml itself) is beyond the scope of this document.
Add the following item to your .props or .targets file:
<PropertyPageSchema Include="path\to\MyLaunchProfile.xaml">
<Context>Project</Context>
</PropertyPageSchema>
Note that the Context
metadata must be present, and must have the value "Project". Rule objects such as the one defined in these .xaml files are used for multiple purposes in the project system; failing to specify the correct context may prevent your property page from being shown and/or cause bugs elsewhere.
Replace the contents of MyLaunchProfile.xaml with the following:
<?xml version="1.0" encoding="utf-8" ?>
<Rule Name="MyLaunchProfile"
Description="Properties associated with launching and debugging a custom debug target."
DisplayName="My Custom Debug Target"
PageTemplate="commandNameBasedDebugger"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/build/2009/properties">
<Rule.Metadata>
<sys:String x:Key="CommandName">MyCustomDebugTarget</sys:String>
<!-- KnownImageIds.ImageCatalogGuid -->
<sys:Guid x:Key="ImageMonikerGuid">AE27A6B0-E345-4288-96DF-5EAF394EE369</sys:Guid>
<!-- KnownImageIds.Execute -->
<sys:Int32 x:Key="ImageMonikerId">1173</sys:Int32>
</Rule.Metadata>
<Rule.DataSource>
<DataSource Persistence="LaunchProfile"
HasConfigurationCondition="False"
ItemType="LaunchProfile"/>
</Rule.DataSource>
<StringProperty Name="ExecutablePath"
DisplayName="Executable"
Description="Path to the executable to run."
Subtype="file" />
</Rule>
The format of the file is described in detail in Property Specification, but the most important points are:
- The
Name
must be unique. - The
PageTemplate
attribute must have the value"commandNameBasedDebugger"
. - The
Rule.Metadata
element must specify aCommandName
: this is the debug command name (also known as a debug target) corresponding to this UI. Debug commands and how they are handled by VS are beyond the scope of this document, - The
ImageMonikerGuid
andImageMonikerId
inRule.Metadata
together define an ImageMoniker designating the icon to show for this Launch Profile UI. These are optional and if they are not defined a default icon will be supplied. - The
Rule.DataSource
element must be specified as shown above.- The
ItemType
attribute indicates that thisRule
only pertains to launch profiles, as opposed to other types of items that are typically found in a project (Compile
,Reference
, etc.). - The
Persistence
attribute indicates that values for the properties should be read and written through the launch profile persistence mechanism, as opposed to trying to read data from the MSBuild file.
- The
Further notes:
- Properties are ordered in the UI as they are ordered in the .xaml file.
- Properties can be placed in categories, just like the properties on property pages. However, the Launch Profiles UI currently ignores categories.
You should now be able to build and see the MyLaunchProfile.xaml copied as-is to the output directory.
And you're done. Projects that import the .targets file will now show this UI when editing the Launch Profiles.
In this option the XAML file is embedded in an assembly as a resource and discovered by means of a MEF export. Compared to Option 1, this requires more initial setup but does not require you to distribute an additional file. This may be an attractive option if you are already exporting MEF components for use in Visual Studio--for example, via an extension.
Use the NuGet Package Manager to add the Microsoft.VisualStudio.ProjectSystem.SDK package package to your project.
Use the NuGet Package Manager to add the Microsoft.Build.Framework package to your project. This is an optional step, but it will allow the XAML editor to find the Rule type (and related types) and provide code completion, tool tips, Go to Definition, and other features while you type.
Use the NuGet Package Manager to add the Microsoft.VisualStudio.ProjectSystem.Sdk.Tools package to your project. This is required to correctly embed the XAML rule at build time.
Add a new XAML file named "MyLaunchProfile.xaml" to your project. Depending on how the file is created you may end up with a <Page>
item in your project but this is not what we want as we're not using the file to describe a piece of WPF UI.
Update your project to replace the <Page>
item with the following:
<ItemGroup>
<XamlPropertyRule Include="MyLaunchProfile.xaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
<DataAccess>None</DataAccess>
<RuleInjection>None</RuleInjection>
</XamlPropertyRule>
</ItemGroup>
This is the same as Step 4 under "Option 1: XAML file on disk" (above). Please see that step for details.
Add a class to hold the MEF metadata describing the embedded XAML Rule
:
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Properties;
namespace MyNamespace
{
internal static class RuleExporter
{
[ExportPropertyXamlRuleDefinition(
xamlResourceAssemblyName: "<Assembly Name>, Version=<Assembly Version>, Culture=<Assembly Culture>, PublicKeyToken=<Assembly Public Key Token>",
xamlResourceStreamName: "XamlRuleToCode:MyLaunchProfile.xaml",
context: PropertyPageContexts.Project)]
[AppliesTo("MyUniqueProjectCapability")]
[Order(orderPrecedence: 0)]
public static int MyLaunchProfileRule;
}
}
Important points:
- The
MyLaunchProfileRule
field itself is never used; it exists solely as a place to park the relevant attributes. - The
xamlResourceAssemblyName
parameter is the fully-qualified name of the assembly holding the embedded .xaml file. - The
xamlResourceStreamName
parameter specifies the name of the embedded resource. This will be of the form "XamlRuleToCode:". - The
context
parameter should always bePropertyPageContexts.Project
. - The
AppliesTo
attribute is required. TheRule
will only be available to projects with the matching capability. - The
Order
attribute is also required. Generally anorderPrecedence
of "0" is fine as there are limited situations where you would want to have multiple exports with the samexamlResourceAssemblyName
andxamlResourceStreamName
and so the order won't matter. Generally this would only be done to override the other metadata, such as theAppliesTo
attribute, on an existingExportPropertyXamlRuleDefinitionAttribute
.
Using the VSIX Manifest designer, ensure the assembly containing the embedded file is included as a "MefComponent" asset.
In order for Visual Studio to properly load the embedded Rule
it needs to be able to load the containing assembly. This requires that the assembly be on the Visual Studio binding path. A full discussion of binding paths is beyond the scope of this document, but this is often accomplished by tagging the class defining your VS package with the ProvideBindingPath
attribute, e.g.:
[ProvideBindingPath]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(MyPackage.PackageGuidString)]
public sealed class MyPackage : AsyncPackage
{
...
}
And you're done. This Launch Profile UI will be available in any project with matching capabilities.
Note this option is only available starting in VS 2022 Update 1 (Dev17.1).
In this option, Rule
objects are defined in code and provided to the project system via MEF-exported implementations of the IRuleObjectProvider
type. This will be an attractive option if you already have an assembly participating in the VS MEF composition, don't have a .props or .targets file to hold PropertyPageSchema
items, or prefer to define your Rule
s in code rather than XAML.
This is also the simplest option if you need to localize your launch profile, as you can read the localized text from .resx files or similar rather than translating the entire .xaml file and distributing the translations.
Use the NuGet Package Manager to add the Microsoft.VisualStudio.ProjectSystem.Sdk package package to your project.
Create a new class with the following contents:
using System;
using System.Collections.Generic;
using Microsoft.Build.Framework.XamlTypes;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Properties;
namespace RuleObjectProviderDemo
{
[ExportRuleObjectProvider(name: "MyRuleObjectProvider", context: "Project")]
[AppliesTo("MyUniqueProjectCapability")]
[Order(0)]
public class MyRuleObjectProvider : IRuleObjectProvider
{
private Lazy<List<Rule>> rules = new Lazy<List<Rule>>(CreateRules);
private static List<Rule> CreateRules()
{
Rule rule = new Rule();
rule.BeginInit();
rule.Name = "MyLaunchProfile";
rule.Description = "Properties associated with launching and debugging a custom debug target.";
rule.DisplayName = "My Custom Debug Target";
rule.PageTemplate = "commandNameBasedDebugger";
rule.Metadata["CommandName"] = "MyCustomDebugTarget";
// KnownImageIds.ImageCatalogGuid
rule.Metadata["ImageMonikerGuid"] = Guid.Parse("AE27A6B0-E345-4288-96DF-5EAF394EE369");
// KnownImageIds.Execute
rule.Metadata["ImageMonikerId"] = 1173;
rule.DataSource = new DataSource
{
Persistence = "LaunchProfile",
HasConfigurationCondition = false,
ItemType = "LaunchProfile"
};
rule.Properties.Add(new StringProperty
{
Name = "ExecutablePath",
DisplayName = "Executable",
Description = "Path to the executable to run.",
Subtype = "file"
});
rule.EndInit();
List<Rule> rules = new List<Rule>();
rules.Add(rule);
return rules;
}
public IReadOnlyCollection<Rule> GetRules()
{
return this.rules.Value;
}
}
}
Important points:
- The
Rule.Name
must be unique. - The
Rule.PageTemplate
property must have the value"commandNameBasedDebugger"
. - The
Rule.Metadata
dictionary must include a value forCommandName
: this is the debug command name (also known as a debug target) corresponding to this UI. Debug commands and how they are handled by VS are beyond the scope of this document. - The
ImageMonikerGuid
andImageMonikerId
inRule.Metadata
together define an ImageMoniker designating the icon to show for this Launch Profile UI. These are optional and if they are not defined a default icon will be supplied. - The
Rule.DataSource
property must be specified as shown above.- The
ItemType
property indicates that thisRule
only pertains to launch profiles, as opposed to other types of items that are typically found in a project (Compile
,Reference
, etc.). - The
Persistence
property indicates that values for the properties should be read and written through the launch profile persistence mechanism, as opposed to trying to read data from the MSBuild file.
- The
- The
ExportRuleObjectProvider
attribute exposes the class through MEF and allows the project system to find it.- The
name
parameter must be unique to thisIRuleObjectProvider
. Duplicate names will cause one or the other to be ignored. - The
context
paraemter should always be "Project".
- The
- The
AppliesTo
attribute is required. TheRule
will only be available to projects with the matching capability. - The
Order
attribute is also required. Generally anorderPrecedence
of "0" is fine, unless you are going to return aRule
object that extends an existingRule
from a differentIRuleObjectProvider
. In that case theOrder
of yourIRuleObjectProvider
must be higher than the other one. - The set of
Rule
objects and theRule
s themselves should not depend on any VS state. That is, do not dynamically generate different sets ofRule
s, properties, etc., in theGetRules
method based on the state of your project or any VS setting. They should be static in the same way thatRule
s loaded from .xaml files are static.- Exception: It is fine for user-facing strings to vary based on the VS locale settings, as the locale cannot change while VS is running.
CreateRules
must handle concurrent access, hence the use ofSystem.Lazy
in the example above.
Using the VSIX Manifest designer, ensure the assembly containing your IRuleObjectProvider
implementation is included as a "MefComponent" asset.
And you're done. This Launch Profile UI will be available in any project with matching capabilities.