Skip to content

Commit 972bef0

Browse files
committed
Allowed styles to be extended by consumers.
1 parent 15e9fde commit 972bef0

File tree

6 files changed

+117
-103
lines changed

6 files changed

+117
-103
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Microsoft.VisualStudio.PlatformUI;
2+
using Microsoft.VisualStudio.Shell;
3+
4+
namespace Community.VisualStudio.Toolkit
5+
{
6+
/// <summary>
7+
/// Re-defines resource keys and other properties used in XAML files, where the assembly that
8+
/// they are originally defined in has a different name depending on the version of the toolkit.
9+
/// </summary>
10+
internal static class InternalResourceKeys
11+
{
12+
// Defined in "clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.xx.0"
13+
public static object VsResourceKeys_TextBoxStyleKey => VsResourceKeys.TextBoxStyleKey;
14+
public static object VsResourceKeys_ComboBoxStyleKey => VsResourceKeys.ComboBoxStyleKey;
15+
16+
// Defined in "clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.xx.0"
17+
public static object CommonControlsColors_TextBoxBackgroundBrushKey => CommonControlsColors.TextBoxBackgroundBrushKey;
18+
public static object CommonControlsColors_TextBoxTextBrushKey => CommonControlsColors.TextBoxTextBrushKey;
19+
public static object CommonControlsColors_TextBoxBorderBrushKey => CommonControlsColors.TextBoxBorderBrushKey;
20+
public static object CommonControlsColors_TextBoxBackgroundFocusedBrushKey => CommonControlsColors.TextBoxBackgroundFocusedBrushKey;
21+
public static object CommonControlsColors_TextBoxTextFocusedBrushKey => CommonControlsColors.TextBoxTextFocusedBrushKey;
22+
public static object CommonControlsColors_TextBoxBorderFocusedBrushKey => CommonControlsColors.TextBoxBorderFocusedBrushKey;
23+
public static object CommonControlsColors_TextBoxBackgroundDisabledBrushKey => CommonControlsColors.TextBoxBackgroundDisabledBrushKey;
24+
public static object CommonControlsColors_TextBoxTextDisabledBrushKey => CommonControlsColors.TextBoxTextDisabledBrushKey;
25+
public static object CommonControlsColors_TextBoxBorderDisabledBrushKey => CommonControlsColors.TextBoxBorderDisabledBrushKey;
26+
}
27+
}
Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,72 @@
11
<ResourceDictionary
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4-
xmlns:local="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
5-
xmlns:shell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
6-
xmlns:platform="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
4+
xmlns:local="clr-namespace:Community.VisualStudio.Toolkit"
75
xmlns:system="clr-namespace:System;assembly=mscorlib"
86
>
97

108
<!-- This is the same padding used by WatermarkedTextBox. -->
119
<Thickness x:Key="{x:Static local:ToolkitResourceKeys.InputPaddingKey}">6,8,6,8</Thickness>
12-
10+
1311
<!-- This is the same height used in the IVsThreadedWaitDialog. -->
1412
<system:Double x:Key="{x:Static local:ToolkitResourceKeys.ThickProgressBarHeight}">16</system:Double>
1513

16-
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Static shell:VsResourceKeys.TextBoxStyleKey}}">
14+
<Style x:Key="{x:Static local:ToolkitResourceKeys.TextBoxStyleKey}" TargetType="TextBox" BasedOn="{StaticResource {x:Static local:InternalResourceKeys.VsResourceKeys_TextBoxStyleKey}}">
1715
<Setter Property="Padding" Value="{StaticResource {x:Static local:ToolkitResourceKeys.InputPaddingKey}}" />
1816
</Style>
1917

20-
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Static shell:VsResourceKeys.ComboBoxStyleKey}}">
18+
<Style x:Key="{x:Static local:ToolkitResourceKeys.ComboBoxStyleKey}" TargetType="ComboBox" BasedOn="{StaticResource {x:Static local:InternalResourceKeys.VsResourceKeys_ComboBoxStyleKey}}">
2119
<Setter Property="Padding" Value="{StaticResource {x:Static local:ToolkitResourceKeys.InputPaddingKey}}" />
2220
</Style>
2321

24-
<Style TargetType="PasswordBox">
25-
<Setter Property="Padding" Value="{StaticResource {x:Static local:ToolkitResourceKeys.InputPaddingKey}}" />
26-
<Setter Property="Background" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBackgroundBrushKey}}" />
27-
<Setter Property="Foreground" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxTextBrushKey}}" />
28-
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBorderBrushKey}}" />
22+
<ControlTemplate x:Key="{x:Static local:ToolkitResourceKeys.PasswordBoxControlTemplateKey}" TargetType="{x:Type PasswordBox}">
23+
<!--
24+
The default template for a PasswordBox defines a trigger for IsMouseOver that changes the
25+
border brush. To get our style triggers to apply, we need to override the template.
26+
-->
27+
<Border
28+
x:Name="border"
29+
BorderBrush="{TemplateBinding BorderBrush}"
30+
BorderThickness="{TemplateBinding BorderThickness}"
31+
Background="{TemplateBinding Background}"
32+
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
33+
>
2934

30-
<Setter Property="Template">
31-
<Setter.Value>
32-
<!--
33-
The default template defines a trigger for IsMouseOver that changes the border
34-
brush. To get our style triggers to apply, we need to override the template.
35-
-->
36-
<ControlTemplate TargetType="{x:Type PasswordBox}">
37-
<Border
38-
x:Name="border"
39-
BorderBrush="{TemplateBinding BorderBrush}"
40-
BorderThickness="{TemplateBinding BorderThickness}"
41-
Background="{TemplateBinding Background}"
42-
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
43-
>
35+
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
36+
</Border>
37+
</ControlTemplate>
4438

45-
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
46-
</Border>
47-
</ControlTemplate>
48-
</Setter.Value>
49-
</Setter>
39+
<Style x:Key="{x:Static local:ToolkitResourceKeys.PasswordBoxStyleKey}" TargetType="PasswordBox">
40+
<Setter Property="Padding" Value="{StaticResource {x:Static local:ToolkitResourceKeys.InputPaddingKey}}" />
41+
<Setter Property="Background" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBackgroundBrushKey}}" />
42+
<Setter Property="Foreground" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxTextBrushKey}}" />
43+
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBorderBrushKey}}" />
5044

45+
<Setter Property="Template" Value="{StaticResource {x:Static local:ToolkitResourceKeys.PasswordBoxControlTemplateKey}}"/>
46+
5147
<Style.Triggers>
5248
<Trigger Property="IsMouseOver" Value="True">
53-
<Setter Property="Background" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBackgroundBrushKey}}" />
54-
<Setter Property="Foreground" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxTextBrushKey}}" />
55-
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBorderBrushKey}}" />
49+
<Setter Property="Background" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBackgroundBrushKey}}" />
50+
<Setter Property="Foreground" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxTextBrushKey}}" />
51+
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBorderBrushKey}}" />
5652
</Trigger>
5753

5854
<Trigger Property="IsFocused" Value="True">
59-
<Setter Property="Background" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBackgroundFocusedBrushKey}}" />
60-
<Setter Property="Foreground" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxTextFocusedBrushKey}}" />
61-
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBorderFocusedBrushKey}}" />
55+
<Setter Property="Background" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBackgroundFocusedBrushKey}}" />
56+
<Setter Property="Foreground" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxTextFocusedBrushKey}}" />
57+
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBorderFocusedBrushKey}}" />
6258
</Trigger>
6359

6460
<Trigger Property="IsEnabled" Value="False">
65-
<Setter Property="Background" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBackgroundDisabledBrushKey}}" />
66-
<Setter Property="Foreground" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxTextDisabledBrushKey}}" />
67-
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static platform:CommonControlsColors.TextBoxBorderDisabledBrushKey}}" />
61+
<Setter Property="Background" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBackgroundDisabledBrushKey}}" />
62+
<Setter Property="Foreground" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxTextDisabledBrushKey}}" />
63+
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static local:InternalResourceKeys.CommonControlsColors_TextBoxBorderDisabledBrushKey}}" />
6864
</Trigger>
6965
</Style.Triggers>
7066
</Style>
67+
68+
<!-- Default styles. -->
69+
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Static local:ToolkitResourceKeys.TextBoxStyleKey}}" />
70+
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Static local:ToolkitResourceKeys.ComboBoxStyleKey}}" />
71+
<Style TargetType="PasswordBox" BasedOn="{StaticResource {x:Static local:ToolkitResourceKeys.PasswordBoxStyleKey}}" />
7172
</ResourceDictionary>

src/toolkit/Community.VisualStudio.Toolkit.Shared/Themes/Themes.cs

Lines changed: 4 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
using System;
22
using System.Collections.ObjectModel;
33
using System.ComponentModel;
4-
using System.IO;
54
using System.Windows;
65
using System.Windows.Controls;
7-
using Microsoft.VisualStudio;
86
using Microsoft.VisualStudio.PlatformUI;
9-
using Microsoft.VisualStudio.Shell;
107

118
namespace Community.VisualStudio.Toolkit
129
{
@@ -127,16 +124,12 @@ private static void OnElementInitialized(object sender, EventArgs args)
127124

128125
private static void MergeStyles(FrameworkElement element)
129126
{
130-
#if DEBUG
131-
// Always reload the theme resources in DEBUG mode, because it allows
132-
// them to be edited on disk without needing to restart Visual Studio,
133-
// which makes it much easier to create and test new styles.
134-
_themeResources = null;
135-
#endif
136-
137127
if (_themeResources is null)
138128
{
139-
_themeResources = LoadThemeResources();
129+
_themeResources = new ResourceDictionary
130+
{
131+
Source = ToolkitResourceKeys.ThemeResourcesUri
132+
};
140133
}
141134

142135
Collection<ResourceDictionary> dictionaries = element.Resources.MergedDictionaries;
@@ -145,52 +138,5 @@ private static void MergeStyles(FrameworkElement element)
145138
dictionaries.Add(_themeResources);
146139
}
147140
}
148-
149-
private static ResourceDictionary LoadThemeResources()
150-
{
151-
try
152-
{
153-
return LoadResources();
154-
}
155-
catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex))
156-
{
157-
ex.Log();
158-
return new ResourceDictionary();
159-
}
160-
}
161-
162-
private static ResourceDictionary LoadResources(
163-
#if DEBUG
164-
[System.Runtime.CompilerServices.CallerFilePath] string? thisFilePath = null
165-
#endif
166-
)
167-
{
168-
#if DEBUG
169-
// Load the resources from disk in DEBUG mode, because this allows
170-
// you to edit the resources without needing to reload Visual Studio.
171-
using (StreamReader reader = new(Path.Combine(Path.GetDirectoryName(thisFilePath), "ThemeResources.xaml")))
172-
#else
173-
using (StreamReader reader = new(typeof(Themes).Assembly.GetManifestResourceStream("Community.VisualStudio.Toolkit.Themes.ThemeResources.xaml")))
174-
#endif
175-
{
176-
string content = reader.ReadToEnd();
177-
178-
// The XAML uses the `VsResourceKeys` type and needs to specify the assembly
179-
// that the type is in, but the exact assembly name differs between the
180-
// toolkit versions, so we need to replace the assembly name at runtime.
181-
content = content.Replace(
182-
"clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0",
183-
$"clr-namespace:Microsoft.VisualStudio.Shell;assembly={typeof(VsResourceKeys).Assembly.GetName().Name}"
184-
);
185-
186-
// Do the same thing for the `CommonControlsColors` namespace.
187-
content = content.Replace(
188-
"clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0",
189-
$"clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly={typeof(CommonControlsColors).Assembly.GetName().Name}"
190-
);
191-
192-
return (ResourceDictionary)System.Windows.Markup.XamlReader.Parse(content);
193-
}
194-
}
195141
}
196142
}

src/toolkit/Community.VisualStudio.Toolkit.Shared/Themes/ToolkitResourceKeys.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.Windows;
1+
using System;
2+
using System.Reflection;
3+
using System.Windows;
4+
using System.Windows.Controls;
25
using Microsoft.VisualStudio.Shell.Interop;
36

47
namespace Community.VisualStudio.Toolkit
@@ -8,6 +11,21 @@ public static class ToolkitResourceKeys
811
{
912
private const string _prefix = "Toolkit";
1013

14+
/// <summary>
15+
/// Gets the URI of the <see cref="ResourceDictionary"/> that contains the theme resources.
16+
/// </summary>
17+
/// <remarks>
18+
/// This can be used to load the <see cref="ResourceDictionary"/> when you would like to customize the styles.
19+
/// <code>
20+
/// &lt;ResourceDictionary&gt;
21+
/// &lt;ResourceDictionary.MergedDictionaries&gt;
22+
/// &lt;ResourceDictionary Source="{x:Static toolkit:ToolkitResourceKeys.ThemeResourcesUri}"/&gt;
23+
/// &lt;/ResourceDictionary.MergedDictionaries&gt;
24+
/// &lt;/ResourceDictionary&gt;
25+
/// </code>
26+
/// </remarks>
27+
public static Uri ThemeResourcesUri { get; } = BuildPackUri("Themes/ThemeResources.xaml");
28+
1129
/// <summary>Gets the key that can be used to get the <see cref="Thickness"/> to use for input controls.</summary>
1230
public static object InputPaddingKey { get; } = _prefix + nameof(InputPaddingKey);
1331

@@ -16,5 +34,27 @@ public static class ToolkitResourceKeys
1634
/// (similar to the height used in the <see cref="IVsThreadedWaitDialog"/>).
1735
/// </summary>
1836
public static object ThickProgressBarHeight { get; } = _prefix + nameof(ThickProgressBarHeight);
37+
38+
/// <summary>Gets the key that defines the resource for a Visual Studio-themed <see cref="TextBox"/> style.</summary>
39+
public static object TextBoxStyleKey { get; } = _prefix + nameof(TextBoxStyleKey);
40+
41+
/// <summary>Gets the key that defines the resource for a Visual Studio-themed <see cref="ComboBox"/> style.</summary>
42+
public static object ComboBoxStyleKey { get; } = _prefix + nameof(ComboBoxStyleKey);
43+
44+
/// <summary>Gets the key that defines the resource for a Visual Studio-themed <see cref="PasswordBox"/> style.</summary>
45+
public static object PasswordBoxStyleKey { get; } = _prefix + nameof(PasswordBoxStyleKey);
46+
47+
/// <summary>Gets the key that defines the resource for the <see cref="ControlTemplate"/> of a Visual Studio-themed <see cref="PasswordBox"/> style.</summary>
48+
public static object PasswordBoxControlTemplateKey { get; } = _prefix + nameof(PasswordBoxControlTemplateKey);
49+
50+
private static Uri BuildPackUri(string resource)
51+
{
52+
// Multiple versions of the toolkti assembly might be loaded, so when
53+
// loading a resource, we need to include the version number of this
54+
// assembly to ensure that the resource is loaded from the correct assembly.
55+
AssemblyName assemblyName = typeof(ToolkitResourceKeys).Assembly.GetName();
56+
string version = assemblyName.Version.ToString();
57+
return new Uri($"pack://application:,,,/{assemblyName.Name};v{version};component/{resource}");
58+
}
1959
}
2060
}

src/toolkit/Community.VisualStudio.Toolkit.Shared/VSSDK.Helpers.Shared.projitems

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<PropertyGroup>
44
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
@@ -96,6 +96,7 @@
9696
<Compile Include="$(MSBuildThisFileDirectory)Solution\SolutionItemType.cs" />
9797
<Compile Include="$(MSBuildThisFileDirectory)Solution\Solutions.cs" />
9898
<Compile Include="$(MSBuildThisFileDirectory)Solution\VirtualFolder.cs" />
99+
<Compile Include="$(MSBuildThisFileDirectory)Themes\InternalResourceKeys.cs" />
99100
<Compile Include="$(MSBuildThisFileDirectory)Themes\Themes.cs" />
100101
<Compile Include="$(MSBuildThisFileDirectory)Themes\ToolkitResourceKeys.cs" />
101102
<Compile Include="$(MSBuildThisFileDirectory)ToolkitPackage.cs" />

src/toolkit/Directory.Build.props

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,11 @@
3030
<!--
3131
Include the XAML theme resources file here rather than in the shared project, because
3232
when it's included in the shared project, you don't get Intellisense when editing the file.
33-
Also note that this is an EmbeddedResource rather than a Page, because we need to load and edit
34-
the file at runtime to account for different assembly versions that are referenced in the namespaces.
3533
-->
36-
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Community.VisualStudio.Toolkit.Shared\Themes\ThemeResources.xaml" Link="Themes\ThemeResources.xaml">
34+
<Page Include="$(MSBuildThisFileDirectory)Community.VisualStudio.Toolkit.Shared\Themes\ThemeResources.xaml" Link="Themes\ThemeResources.xaml">
35+
<SubType>Designer</SubType>
3736
<Generator>MSBuild:Compile</Generator>
38-
</EmbeddedResource>
37+
</Page>
3938
</ItemGroup>
4039

4140
<ItemGroup>

0 commit comments

Comments
 (0)