I wanted to define some custom colors for an extension I'm writing. It's a pretty convoluted process, so I created some wrapper classes and thought it belongs in the toolkit, so here it is. :grin:
Defining Custom Font and Color Settings
There are three parts to defining custom font and color settings.
A provider defines the categories that the extension provides.
A category is what appears in the "Show settings for" dropdown list in the Fonts and Colors options page. For example, "Text Editor", "Environment", and so on. A category defines one default font and zero or more colors.
A color definition appears in the "Display items" list in the Fonts and Colors options page. A color definition defines the default foreground and background colors and font style. A user can customize this.
Font And Color Provider
You tell Visual Studio about the custom font and color definitions by using a class inheriting from BaseFontAndColorProvider:
[Guid("26442428-2cd7-xxxx-xxxx-f9b14087ca50")]
public class MyFontAndColorProvider : BaseFontAndColorProvider { }
ℹ️ Visual Studio uses a GUID to identify the provider, so make sure you add a GuidAttribute to the provider class.
The provider will find all categories defined in the same assembly as itself, and tell Visual Studio about them.
There are two things you need to do with the provider:
You must declare that your package provides fonts and colors by using the ProvideFontsAndColorsAttribute:
[ProvideFontsAndColors(typeof(MyFontAndColorProvider))]
public class MyExtensionPackage : ToolkitPackage { }
You must register the font and color providers when your package is initialized:
If you don't do both of these things, then Visual Studio won't know about your font and colors.
Font And Color Category
A category defines a default font and the colors that can be configured by the user. You define a category by inheriting from BaseFontAndColorCategory<T>. Just like commands and tool windows, the T type is the type of the inheriting class.
[Guid("e977c587-c06e-xxxx-xxxx-cbf9da1bdafa")]
public class MyFontAndColorCategory : BaseFontAndColorCategory<MyFontAndColorCategory>
{
public override string Name => "My First Category";
}
ℹ️ Visual Studio uses a GUID to identify the category, so make sure you add a GuidAttribute to the category class.
Default Font
When you define a category, you can specify a default font or you can let it use an "Automatic" font. The "Automatic" font is the font that is used by Visual Studio for most of the UI elements. It can be configured by setting the font for the Environment category.
[Guid("e977c587-c06e-xxxx-xxxx-cbf9da1bdafa")]
public class MyFontAndColorCategory : BaseFontAndColorCategory<MyFontAndColorCategory>
{
// Use Times New Roman as the default font for the category.
public MyFontAndColorCategory() : base(new FontDefinition("Times New Roman", 14)) { }
// ... snip ...
}
Color Definitions
The colors for a category are specified by defining properties of type ColorDefinition on the category class. The category will find these properties and tell Visual Studio about the colors.
[Guid("e977c587-c06e-xxxx-xxxx-cbf9da1bdafa")]
public class MyFontAndColorCategory : BaseFontAndColorCategory<MyFontAndColorCategory>
{
// ... snip ...
public ColorDefinition Primary { get; } = new(
"Primary",
defaultBackground: VisualStudioColor.Indexed(COLORINDEX.CI_RED),
defaultForeground: VisualStudioColor.Indexed(COLORINDEX.CI_WHITE)
);
public ColorDefinition Secondary { get; } = new(
"Secondary",
defaultBackground: VisualStudioColor.Indexed(COLORINDEX.CI_YELLOW),
defaultForeground: VisualStudioColor.Indexed(COLORINDEX.CI_BLACK),
);
}
Color Definitions
A ColorDefinition defines the default values for an item that appears in the Fonts and Colors options page. You can also control what a user can customize about the item.
Every color needs a name. You can optionally specify a localized name and a description, though I have no idea where the description is shown.
You can specify the default foreground and background colors, and the default font style. You can also specify options that control what the user can customize. For example, you can prevent a bold font from being used, or prevent custom colors being selected, or prevent the background or foreground color from being customized at all.
There is also a line style and "marker visual style", though I'm not sure where they are used in Visual Studio. They're part of the underlying AllColorableItemInfo that Visual Studio uses, so I figured it was best to include them just in case someone needed them.
Colors
A specific color is specified using the VisualStudioColor class. There are five different ways that you can create a VisualStudioColor.
Indexed
"Indexed" colors are the predefined colors like Yellow, Red, Green, etc.
VisualStudioColor.Indexed(COLORINDEX.CI_RED);
VsColor
This allows you to use a color that is defined by the Visual Studio theme. The theme colors are defined in three separate enums: __VSSYSCOLOREX, __VSSYSCOLOREX2 and __VSSYSCOLOREX3. If you're trying to match other parts of Visual Studio, this is probably what you want to be using.
This creates a color using a Windows system color. There's no enum for the system colors, but you can find their values here. These types of colors probably aren't that useful because they don't take into account the Visual Studio theme. You're better off using the VsColor method.
VisualStudioColor.SysColor(13); // System highlight color.
Rgb
This lets you create any color that you want. You can use red, green and blue components, or use a System.Windows.Media.Color.
This defines an "automatic" color. I think is the best way to use them:
In the ColorDefinition, set the AutomaticBackground color to a VsColor (for example, VisualStudioColor.VsColor(__VSSYSCOLOREX.VSCOLOR_ENVIRONMENT_BACKGROUND)).
Set the DefaultBackground color to Automatic.
When you select Automatic for the color in the Fonts and Colors options page, the VSCOLOR_ENVIRONMENT_BACKGROUND will be used.
Getting Fonts and Colors
Once you've defined the font and color categories, you'll probably want to get the actual font and color values that the user has customized. This can be done via the VS.FontsAndColors object.
To get the configured font and colors for a category, use the GetConfiguredFontAndColorsAsync method. Specify the type of the category as a generic parameter:
ConfiguredFontAndColorSet<MyFontAndColorCategory> set;
set = VS.FontsAndColors.GetConfiguredFontAndColorsAsync<MyFontAndColorCategory>();
// ... use it ...
set.Dispose();
The ConfiguredFontAndColorSet<T> provides access to the font and each of the color definitions. This is a "live" object - if the user customizes the font or color, the changes will be reflected in this object - so you don't need to get a new set each time you want to get the configured font or colors. Just make sure you dispose of it when you no longer need it.
Font
The font is available in the Font property as a ConfiguredFont. If the user customizes the font for the category, this object will be updated with the new font family and size.
This type is geared towards being used in WPF. It implements INotifyPropertyChanged, so you can bind directly to it in XAML.
class MyViewModel {
public ConfiguredFont Font => set.Font;
}
Family is a FontFamily object. If you're not using the font in WPF, then the font family is also available as a string via the FamilyName property.
ℹ️ The font can be "automatic". In that case, the Family property will be null and the FamilyName property will be the same as FontDefinition.Automatic.FamilyName.
Colors
The configured colors can be accessed via the GetColor method. You pass in the ColorDefinition of the color that you need. The ConfiguredFontAndColorSet<T> conveniently has a Category property that provides access to the category, so you can use that to access the ColorDefinition objects.
set.GetColor(set.Category.Primary);
The color is a ConfiguredColor. Just like the ConfiguredFont, this object will update if the user customizes the color settings. It also implements INotifyPropertyChanged, so you can bind directly to it in XAML as well.
class MyViewModel {
public ConfiguredColor Primary { get; } = set.GetColor(set.Category.Primary);
}
Brushes are available from the ForegroundBrush and BackgroundBrush properties. If you're not using it in WPF, there's also ForegroundColor and BackgroundColor properties that provide the color as a Color object.
The font style is also available. This is a FontStyle enum, but it essentially defines whether or not the font should be bold, as that's the only thing the user can customize. If you're using it in WPF, there's also a FontWeight property that you can bind to.
Events
As mentioned above, the ConfiguredFont and ConfiguredColor classes implement INotifyPropertyChanged, so you could add event handlers to them to detect when fonts and colors are customized, but there is an easier way. The ConfiguredFontAndColorSet<T> has two events that are raised whenever the font or any of the colors are changed:
FontChanged is raised when something about the font is changed (font family or size).
ColorChanged is raised when something about a ConfiguredColor is changed (foreground, background or font style). The specific ConfiguredColor that changed is included in the event arguments.
Code Analyzers
Since there are a few things that you need to do to get Visual Studio to be aware of the font and color definitions, I've also added some code analyzers to detect mistakes.
Code
Description
CVST006
The provider does not have a GuidAttribute. Visual Studio identifies the providers by GUID, so it's important that the provider has an explicitly defined GUID.
CVST007
The category does not have a GuidAttribute. Just like providers, the categories are also identified by GUID.
CVST008
You've defined a category but not a provider. Without a provider, Visual Studio has no way of knowing about the category.
CSVT009
You've defined a provider, but haven't added the ProvideFontsAndColorsAttribute to your package.
CVST010
You've added the ProvideFontsAndColorsAttribute to your package, but you haven't called RegisterFontAndColorProvidersAsync when initializing the package.
Demo
And to finish things off, the demo extension has an example of using font color definitions. Use View -> Other Windows -> Fonts and Colors Window to open the tool window. This window has four boxes. Each box uses a custom color definition.
In the Fonts and Colors options page, select the "Fonts and Colors Demo" item in the dropdown (it's towards the bottom). From there you can customize the four colors that are used on the tool window. When you save, the tool window will update with the new colors, and shows the events that were emitted.
I wanted to define some custom colors for an extension I'm writing. It's a pretty convoluted process, so I created some wrapper classes and thought it belongs in the toolkit, so here it is. :grin:
Defining Custom Font and Color Settings
There are three parts to defining custom font and color settings.
Font And Color Provider
You tell Visual Studio about the custom font and color definitions by using a class inheriting from
BaseFontAndColorProvider
:ℹ️ Visual Studio uses a GUID to identify the provider, so make sure you add a
GuidAttribute
to the provider class.The provider will find all categories defined in the same assembly as itself, and tell Visual Studio about them.
There are two things you need to do with the provider:
ProvideFontsAndColorsAttribute
:If you don't do both of these things, then Visual Studio won't know about your font and colors.
Font And Color Category
A category defines a default font and the colors that can be configured by the user. You define a category by inheriting from
BaseFontAndColorCategory<T>
. Just like commands and tool windows, theT
type is the type of the inheriting class.ℹ️ Visual Studio uses a GUID to identify the category, so make sure you add a
GuidAttribute
to the category class.Default Font
When you define a category, you can specify a default font or you can let it use an "Automatic" font. The "Automatic" font is the font that is used by Visual Studio for most of the UI elements. It can be configured by setting the font for the Environment category.
Color Definitions
The colors for a category are specified by defining properties of type
ColorDefinition
on the category class. The category will find these properties and tell Visual Studio about the colors.Color Definitions
A
ColorDefinition
defines the default values for an item that appears in the Fonts and Colors options page. You can also control what a user can customize about the item.Every color needs a name. You can optionally specify a localized name and a description, though I have no idea where the description is shown.
You can specify the default foreground and background colors, and the default font style. You can also specify options that control what the user can customize. For example, you can prevent a bold font from being used, or prevent custom colors being selected, or prevent the background or foreground color from being customized at all.
There is also a line style and "marker visual style", though I'm not sure where they are used in Visual Studio. They're part of the underlying
AllColorableItemInfo
that Visual Studio uses, so I figured it was best to include them just in case someone needed them.Colors
A specific color is specified using the
VisualStudioColor
class. There are five different ways that you can create aVisualStudioColor
.Indexed
"Indexed" colors are the predefined colors like Yellow, Red, Green, etc.
VsColor
This allows you to use a color that is defined by the Visual Studio theme. The theme colors are defined in three separate enums:
__VSSYSCOLOREX
,__VSSYSCOLOREX2
and__VSSYSCOLOREX3
. If you're trying to match other parts of Visual Studio, this is probably what you want to be using.SysColor
This creates a color using a Windows system color. There's no enum for the system colors, but you can find their values here. These types of colors probably aren't that useful because they don't take into account the Visual Studio theme. You're better off using the
VsColor
method.Rgb
This lets you create any color that you want. You can use red, green and blue components, or use a
System.Windows.Media.Color
.Automatic
This defines an "automatic" color. I think is the best way to use them:
ColorDefinition
, set theAutomaticBackground
color to aVsColor
(for example,VisualStudioColor.VsColor(__VSSYSCOLOREX.VSCOLOR_ENVIRONMENT_BACKGROUND)
).DefaultBackground
color toAutomatic
.When you select Automatic for the color in the Fonts and Colors options page, the
VSCOLOR_ENVIRONMENT_BACKGROUND
will be used.Getting Fonts and Colors
Once you've defined the font and color categories, you'll probably want to get the actual font and color values that the user has customized. This can be done via the
VS.FontsAndColors
object.To get the configured font and colors for a category, use the
GetConfiguredFontAndColorsAsync
method. Specify the type of the category as a generic parameter:The
ConfiguredFontAndColorSet<T>
provides access to the font and each of the color definitions. This is a "live" object - if the user customizes the font or color, the changes will be reflected in this object - so you don't need to get a new set each time you want to get the configured font or colors. Just make sure you dispose of it when you no longer need it.Font
The font is available in the
Font
property as aConfiguredFont
. If the user customizes the font for the category, this object will be updated with the new font family and size.This type is geared towards being used in WPF. It implements
INotifyPropertyChanged
, so you can bind directly to it in XAML.Family
is aFontFamily
object. If you're not using the font in WPF, then the font family is also available as a string via theFamilyName
property.ℹ️ The font can be "automatic". In that case, the
Family
property will be null and theFamilyName
property will be the same asFontDefinition.Automatic.FamilyName
.Colors
The configured colors can be accessed via the
GetColor
method. You pass in theColorDefinition
of the color that you need. TheConfiguredFontAndColorSet<T>
conveniently has aCategory
property that provides access to the category, so you can use that to access theColorDefinition
objects.The color is a
ConfiguredColor
. Just like theConfiguredFont
, this object will update if the user customizes the color settings. It also implementsINotifyPropertyChanged
, so you can bind directly to it in XAML as well.Brushes are available from the
ForegroundBrush
andBackgroundBrush
properties. If you're not using it in WPF, there's alsoForegroundColor
andBackgroundColor
properties that provide the color as aColor
object.The font style is also available. This is a
FontStyle
enum, but it essentially defines whether or not the font should be bold, as that's the only thing the user can customize. If you're using it in WPF, there's also aFontWeight
property that you can bind to.Events
As mentioned above, the
ConfiguredFont
andConfiguredColor
classes implementINotifyPropertyChanged
, so you could add event handlers to them to detect when fonts and colors are customized, but there is an easier way. TheConfiguredFontAndColorSet<T>
has two events that are raised whenever the font or any of the colors are changed:FontChanged
is raised when something about the font is changed (font family or size).ColorChanged
is raised when something about aConfiguredColor
is changed (foreground, background or font style). The specificConfiguredColor
that changed is included in the event arguments.Code Analyzers
Since there are a few things that you need to do to get Visual Studio to be aware of the font and color definitions, I've also added some code analyzers to detect mistakes.
CVST006
GuidAttribute
. Visual Studio identifies the providers by GUID, so it's important that the provider has an explicitly defined GUID.CVST007
GuidAttribute
. Just like providers, the categories are also identified by GUID.CVST008
CSVT009
ProvideFontsAndColorsAttribute
to your package.CVST010
ProvideFontsAndColorsAttribute
to your package, but you haven't calledRegisterFontAndColorProvidersAsync
when initializing the package.Demo
And to finish things off, the demo extension has an example of using font color definitions. Use View -> Other Windows -> Fonts and Colors Window to open the tool window. This window has four boxes. Each box uses a custom color definition.
In the Fonts and Colors options page, select the "Fonts and Colors Demo" item in the dropdown (it's towards the bottom). From there you can customize the four colors that are used on the tool window. When you save, the tool window will update with the new colors, and shows the events that were emitted.