Click here to Skip to main content
14,660,427 members
Articles » General Programming » Tools and IDE » General
Article
Posted 3 Mar 2015

Tagged as

Stats

30.2K views
419 downloads
12 bookmarked

Creating custom UI property pages/sheets in Visual Studio Part 1

Rate this:
5.00 (8 votes)
Please Sign up or sign in to vote.
5.00 (8 votes)
3 Mar 2015CPOL
Using DynamicEnumProperty, EnumProperty, IntProperty, ProjectSchemaDefinitions, Rule, StringListProperty, and StringProperty elements for creation of property sheets in Visual Studio (MSBuild)

Introduction

Anyone who worked with Visual Studio knows what property settings dialog looks like (one with the pink frame):

Image 1

It allows developer to set all available options for all the tools used in build process. Not everybody knows that this dialog is not a monolithic, built-in dialog of Visual Studio but rather modular construct, built from configuration files upon loading of the project. The configuration and features of these property pages are project type dependent and come from XML files located at $(VCTargetsPath)\...

In this article I’d like to go over steps required to create property pages for use in Visual Studio with custom projects. In my second article I'll explain how these properties could be used to generate commant line options for custom builds.

Background

MSBuild

Microsoft Build Engine, also known as MSBuild, is a build platform for native and managed code and is part of Windows Software Development Kit. Visual Studio depends on MSBuild and uses it for project builds, but MSBuild does not depend on Visual Studio and could be used stand alone. In depth information about MSBuild is available at MSBuild reference.

Rationale

Lately I had to integrate several open source libraries into Visual Studio project and found it quite challenging. Each of these required multiple steps to build and had tons of switches and command line attributes to deal with. I’ve creates MSBuild projects to perform these steps but was still left with the issue of setting up all the build parameters. So I had to create UI for these settings.

As I immediately discovered the documentation on this topic is not easily available. I had to spend some time trying to figure out what goes where and why. This article is a short summary of my findings. It is in no way exhaustive coverage of the subject but rather work in progress…

Project

Property pages are loaded by the projects. Projects created with Visual Studio have all the necessary components included and loaded by default. But if you starting from scratch, you need to create a project yourself.

Creating project

MSBuild project is just a simple XML file. You can learn more about it at MSDN. When project is created in Visual Studio from one of provided templates VS adds all necessary includes, default properties and targets. It also loads all the appropriate property sheets for the project type.

If project created manually, none of that is loaded by default, it have to be done explicitly. In reality it takes quite a few steps to do it correctly but creating a project file is outside of the scope of this article. For more information on the subject I can recommend excellent MSBuild books or MSDN library.

Project Properties UI

After creating all the necessary targets, items and properties in the project, loading it into Visual Studio, and opening Properties dialog we would have something like this:

Image 2

There would be no UI property sheets loaded for the project. We would have to create and load them into the project.

Property Page

Each property page shows up at the Property Pages dialog under References box. Adding property page to the project requires two steps: creating page’s schema and importing it into the project. Schema is loaded by Visual Studio to create all the UI controls described in it and is ignored during the build process.

Importing Property Page Schema into the project

Visual Studio keeps track of all property pages in special Item called PropertyPageSchema. To add new page we could do something like this:

<ItemGroup>
  <PropertyPageSchema Include="boost.xml" >
</ItemGroup>

If we are extending default project type (created in Visual Studio 'New Project' wizard) and there are default property sheets already loaded, this would be sufficient to display the page. But if this is the only page in the project, the page will not be displayed. Configuration subsystem requires at least one property page covering entire project to show any pages at all. We have to specify that this page applies to entire project. This is done by specifying Context metadata to be 'Project'.

<ItemGroup>
  <PropertyPageSchema Include="boost.xml" >
    <Context>Project</Context>
  </PropertyPageSchema>
</ItemGroup>

In general property page could apply to whole project, any file or specific type of file, and etc. Valid values for Context property are: Project, File, ProjectSubscriptionService, BrowseObject, or comma separated combination of these. 

Creating Property Page Schema

Property Page Schema is a regular XML file. It has following structure:

<?xml version="1.0" encoding="utf-8"?>
<Rule ... xmlns="http://schemas.microsoft.com/build/2009/properties">
...
</Rule>

The schema could contain single rule as above or it could have multiple rules alone with other types defined in a single file. If file contains more than one definition it has to be wrapped by ProjectSchemaDefinitions element. Note that all namespace declarations should be moved into ProjectSchemaDefinitions element.

<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions  xmlns="http://schemas.microsoft.com/build/2009/properties"

                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

                           xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <Rule ... Name="Link1">
       ...
  </Rule>
  <Rule ... Name="Link2">
       ...
  </Rule>
  <ContentType>...<ContentType>
  <ItemType Name="..." DisplayName="..." />
  <FileExtension Name=".res" ContentType="PRIResource"/>
</ProjectSchemaDefinitions>

Every item described by your XML ultimately will be processed and instantiated as an instance of .NET class from Microsoft.Build.Framework.XamlTypes namespace by XAML deserializers. Think of it as creating XAML templates for WPF engine.

Information on this topic is rather scarce. Basic reference information for the Rule is available at MSDN. A bit more in-depth explanation of properties is available at blog post by Pavan Adharapurapu called “Platform extensibility - Part 2”. Here I will present compilation of material from both sources as well as my own discoveries

Rule

A Rule element is where all the magic takes place. It describes property page in its entirety including location of stored data, categories and subcategories, controls and editors, and etc. Typical Rule would look like this:

<Rule Name="..." DisplayName="..." Description="..." PageTemplate="..." ...

      xmlns="http://schemas.microsoft.com/build/2009/properties">

  <Rule.Categories>      
    <Category Name="..." DisplayName="..." />
  </Rule.Categories>

  <Rule.DataSource>
    <DataSource Persistence="ProjectFile" ... />
  </Rule.DataSource>

  <StringProperty ... />
  <StringListProperty ... />
  <IntProperty ... />
  <BoolProperty ... />
  <EnumProperty  />
  <DynamicEnumProperty .. />
</Rule>

Complete list of Rule element properties is available at MSDN. I will only describe most relevant properties and attributes.

Rule Attributes

namespaces (xmlns)

This is a standard XML element. In examples above you can see three namespaces listed. These correspond to the namespaces for the XAML deserialization classes, XAML schema and system namespace, and configuration properties. Rule is required to include at least this namespace: xmlns="http://schemas.microsoft.com/build/2009/properties".

Name

The Name attribute is an id for the Rule. It needs to be unique among all the property page xml files for a project.

DisplayName

This is the name that is shown on the property page UI for the Rule node. This value is localized. We created DisplayName as a child element of Rule rather than as an attribute (like Name or SwitchPrefix) because of internal localization tool requirements. From XAML's perspective, both are equivalent. So, you can just make it an attribute to reduce clutter or leave it as it is.

Description

Describes this Rule to the user.

PageTemplate

The value of this attribute is used by the UI to choose from a collection of UI templates.

generic The generic template displays different properties and categories all in one page.  Image 3
tool The tool template renders category as a sub-nodes under the Rule main node. The properties are rendered in a standard grid format for each category. 
Tool template allows adding All Options and Command Line categories.
Image 4
debugger The UI for debugger page template uses a drop-down box to switch between the properties of different debuggers Image 5

The xml file is designed to be UI independent. So, a different UI could use this attribute for different purposes.

Order

This is a suggestion to a prospective UI client on the relative location of this Rule compared to all other Rules in the system

DataSource

The DataSource property specifies the default location to store data for the properties in this Rule. This location could be overridden for any property by specifying data source within the property. Like in this example:

  ...
  <Rule.DataSource>
    <DataSource Persistence="ProjectFile" ItemType="" Label="" HasConfigurationCondition="true" ... />
  </Rule.DataSource>

  <StringListProperty Name="StringName" ... />
  
  <BoolProperty Name="BoolName" ... />
    <BoolProperty.DataSource>
      <DataSource Persistence="ProjectFile" PersistedName="OtherBoolName" ... />
    </BoolProperty.DataSource>
  </BoolProperty>

  <EnumProperty Name="EnumName" ... />
</Rule>
Persistence This property tells the project system where to store values for the Rule. It could be either: 
ProjectFile - properties should be written to the project file or;
UserFile - properties go to  $(ProjectName).user file.
ResolvedReference - result of executing ResolveProjectReferencesDesignTime target specified in MSBuildTarget attribute.
PersistedName This property specifies a name used to read/write the value of this property in project file. Normally properties are saved using name of the property such as: StringName, BoolName, EnumName, and etc. (See code sample above.) But when PersistedName is defined it would be that name instead: StringName, OtherBoolName, EnumName, and etc.
Setting this attribute on the default DataSource does not make much sense because it will save all properties with the same overridden name losing all the data.
ItemType When this attribute is empty all the properties are stored as regular MSBuild Properties within some PropertyGroup in the project file.
Specifying ItemType (such as ClCompile for example) forces properties to be stored as ItemDefinition metadata or item metadata (the latter only if the property pages were spawned from a file node in solution explorer) of this item type.
HasConfigurationCondition Tells the project system to affix a configuration condition to the value so that it takes effect only for the current project configuration or not.
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
  ...
</ItemDefinitionGroup>
SourceType Specifies type of the data source. Valid types are:
Item - When data source is an entity defined within ItemDefinitionGroup.
TargetResults - When source of data is a target specified in MSBuildTarget attribute. 
MSBuildTarget The semicolon-delimited list of MSBuild targets that must be executed before reading the read-only properties or items of this data source. Data returned by the targets becomes the DataSource.
SourceOfDefaultValue The value of DefaultValueSourceLocation which indicates the location of the default value for this data source.
Categories

Categories is an optional property containing a list of included categories. Sequence of entries establishes order of categories in the References window of the Properties Dialog.

Body of Rule element

Nested inside the Rule element are the configuration properties which describe the property sheet.
Following elements are available to be included in the Rule:

StringProperty
StringListProperty
IntProperty
EnumProperty
DynamicEnumProperty
BoolProperty

Configuration Properties

All of these elements are derived from same basic class and as such share common set of attributes.

Common Attributes

Most of these properties are self-explanatory. Complete list of common properties is available at MSDN. I will list several properties which are not so obvious:

Category

Specifies the category for the property. In generic template this property is grouped under this category shown in bold. Tool template renders this category as sub-category of the Rule node in References window of the dialog. This category will be displayed in the UI even if it is not listed in Categories element of the Rule.  

DataSource

As been mentioned earlier each property could override DataSource defined in parent Rule. Once specified this property will store its value into that DataSource.

IncludeInCommandLine

If PageTemplate is tool and Categories has CommandLine sub-type included like this:

<Rule 

  ...

  <Rule.Categories>
    <Category Name="Command Line" Subtype="CommandLine" DisplayName="Command Line" />
  </Rule.Categories>
  ...

IncludeInCommandLine determines if switch for this Property is included into generated display.

SwitchPrefix

Overrides SwitchPrefix defined in Rule element. 

Switch

This value contains a string used in the command line switch. For example if Switch="I" command line property sheet will display is as /I. The format is $(SwitchPrefix)$(Switch). This attribute is used by the CommandLine property sheet to generate current set of switches. 

Visible

This attribute determines if this property is displayed in the property sheet and command line sheet.

ValueEditors

Allows you to associate specific value editors for this property. 

Separator

Specifies the token used to separate a switch from its value. The format is as follows: $(SwitchPrefix)$(Switch)$(Separator)$(Value)

For more information please see Microsoft.Build.Framework.XamlTypes.BaseProperty

 

StringProperty Element

This property allows entering and editing of text data. Value of the string is held in variable with the same name as the property, unless overridden in PersistedName. When rendered in CommandLine property sheet following format is used: $(SwitchPrefix)$(Switch)$(Separator)$(StringProperty). 

Subtype

Qualifies this string property to give it a more specific classification. A well-known subtypes are:

Folder - Value of the property represents path to the folder without the file info.
File - Value of the property is path to a file

Sample:
<StringProperty Name="OutDir" SwitchPrefix="-" Switch="outdir" Separator="=" ... />

Entering value "C:\Temp\" into property sheet will store it as <OutDir>C:\Temp\</OutDir> and renders it
as -outdir="C:\Temp\" in Command Line window.  

For more information see Microsoft.Build.Framework.XamlTypes.StringProperty

 

StringListProperty Element

This property is the same as StringProperty except it holds list of string values. It shares the same behavior with StringProperty when SubType is specified and follows the same formatting as StringProperty with addition of separator between string values. StringListProperty concatenates list into string with individual string values divided by separator. Default separator if semicolon ";". Separator is held in following attribute:

CommandLineValueSeparator

Specifies a separator to use in delineating individual values of this string list property.

Sample:
<StringListProperty Name="DisableSpecificWarnings" CommandLineValueSeparator="," Switch="wd" ... />

Entering value 1234, 4567 and 5678 into property sheet will store it as <DisableSpecificWarnings>1234,4567,5678</DisableSpecificWarnings>.  

For more information see Microsoft.Build.Framework.XamlTypes.StringListProperty

 

IntProperty Element

This property allows entering and editing numerical data. It defines two attributes to store Max and Min values allowed for this property:

MaxValue

Maximum allowed value for this property.

MinValue

Minimum allowed value for this property.

For more information see Microsoft.Build.Framework.XamlTypes.IntProperty

 

BoolProperty Element

This property allows entering and editing Boolean data. It could be 'true', 'false' or empty. It defines additional attribute ReverseSwitch to represent 'false' state:

ReverseSwitch

A flag that forces the logical negation of the value of a Boolean switch.

Sample:
<BoolProperty SwitchPrefix="/" ReverseSwitch="sdl-" Name="SDLCheck" Switch="sdl" ... />  

Selecting Yes in property sheet will store it as <SDLCheck>true</SDLCheck> and renders it  as  /sdl
Selecting No in property sheet will store it as <SDLCheck>false</SDLCheck> and renders it  as  /sdl-

For more information see Microsoft.Build.Framework.XamlTypes.BoolProperty

 

EnumProperty Element

EnumProperty allows selecting one option from a set of possible values. It stores name of EnumValue element when one of them is selected. Possible choices are defined by <EnumValue> elements. EnumProperty also defines AdmissibleValue attribute to store acceptable values. 

AdmissibleValues

Specifies the list of possible values for this property.

Sample:

  <EnumProperty Name="WarningLevel" SwitchPrefix="/" ... >
    <EnumValue Name="TurnOffAllWarnings" Switch="W0" DisplayName="Turn Off All Warnings" />
    <EnumValue Name="Level1" Switch="W1" DisplayName="Level 1" />
    <EnumValue Name="Level2" Switch="W2" DisplayName="Level 2" />
    <EnumValue Name="Level3" Switch="W3" DisplayName="Level 3" />
    <EnumValue Name="Level4" Switch="W4" DisplayName="Level 4" />
    <EnumValue Name="EnableAllWarnings" Switch="Wall" DisplayName="EnableAllWarnings" />
  </EnumProperty>

Selecting first option in property sheet will store <WarningLevel>TurnOffAllWarnings</WarningLevel> and renders it as  /w0  in Command Line window.  
Selecting second  option stores <WarningLevel>Level1</WarningLevel> and renders it as  /w1  
Selecting last  option stores <WarningLevel>EnableAllWarnings</WarningLevel> and renders it as  /w1 

For more information see Microsoft.Build.Framework.XamlTypes.EnumProperty

 

DynamicEnumProperty Element

DynamicEnumProperty is very similar to EnumProperty with only difference that Enum values are coming from dynamic enum provider.  This property defines two attributes EnumProvider and ProviderSettings to specify provider data.

EnumProvider

Specifies the provider that produces the list of possible values for this property.

ProviderSettings

Specifies a provider-specific set of options to pass to the provider.

Sample:

<DynamicEnumProperty Name="PlatformToolset" DisplayName="Platform Toolset" EnumProvider="Toolsets" />

For more information see Microsoft.Build.Framework.XamlTypes.EnumProperty

 

For more in-depth coverage of these elements see Creating custom UI property pages/sheets in Visual Studio Part 2

History

03/03/2015 - Published.
03/09/2015 - Fixed few incorrect statements and added reference to Part 2

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Eugene Sadovoi
Software Developer (Senior)
United States United States
Senior Software Engineer with over 20+ years of experience in variety of technologies, development tools and programming languages.

Microsoft Certified Specialist programming in C#, JavaScript, HTML, CSS

Comments and Discussions

 
GeneralIMAGES! Pin
Carlos19073-Mar-15 23:20
professionalCarlos19073-Mar-15 23:20 
GeneralRe: IMAGES! Pin
Eugene Sadovoi4-Mar-15 21:22
MemberEugene Sadovoi4-Mar-15 21:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.