Skip to content

Commit 9e037e2

Browse files
Merge pull request #316 from reduckted/master
Allow styles to be extended
2 parents 15e9fde + 64d647c commit 9e037e2

File tree

11 files changed

+213
-103
lines changed

11 files changed

+213
-103
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<platform:DialogWindow
2+
x:Class="TestExtension.CustomizedStylesDialog"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:platform="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
8+
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
9+
xmlns:shell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
10+
mc:Ignorable="d"
11+
d:DesignHeight="450"
12+
d:DesignWidth="800"
13+
toolkit:Themes.UseVsTheme="True"
14+
Width="300"
15+
Height="400"
16+
Title="Customized Styles"
17+
WindowStartupLocation="CenterOwner"
18+
ShowInTaskbar="False"
19+
>
20+
21+
<Window.Resources>
22+
<ResourceDictionary>
23+
<!--
24+
To customize a style provided by the toolkit, you need to make the resources
25+
available by including the resource dictionary from the toolkit:
26+
-->
27+
<ResourceDictionary.MergedDictionaries>
28+
<ResourceDictionary Source="{x:Static toolkit:ToolkitResourceKeys.ThemeResourcesUri}"/>
29+
</ResourceDictionary.MergedDictionaries>
30+
31+
<!--
32+
Now you can create your own styles that are based on the toolkit's styles.
33+
-->
34+
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Static toolkit:ToolkitResourceKeys.TextBoxStyleKey}}">
35+
<Setter Property="BorderBrush" Value="Red" />
36+
</Style>
37+
38+
<!--
39+
The toolkit only defines a few customized styles. Most styles come from the default Visual Studio
40+
styles. These can be referenced using the keys from `Microsoft.VisualStudio.Shell.VsResourceKeys`.
41+
-->
42+
<Style TargetType="Button" BasedOn="{StaticResource {x:Static shell:VsResourceKeys.ThemedDialogButtonStyleKey}}">
43+
<Setter Property="Foreground" Value="Green" />
44+
</Style>
45+
</ResourceDictionary>
46+
</Window.Resources>
47+
48+
<StackPanel Orientation="Vertical" Margin="10">
49+
<TextBox
50+
Text="This TextBox uses the style from the toolkit, but extends it to have a red border."
51+
AcceptsReturn="True"
52+
Height="50"
53+
TextWrapping="Wrap"
54+
/>
55+
56+
<Button
57+
Content="Green text on a button"
58+
Margin="0,10,0,0"
59+
/>
60+
</StackPanel>
61+
</platform:DialogWindow>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.VisualStudio.PlatformUI;
2+
3+
namespace TestExtension
4+
{
5+
public partial class CustomizedStylesDialog : DialogWindow
6+
{
7+
public CustomizedStylesDialog()
8+
{
9+
InitializeComponent();
10+
}
11+
}
12+
}

demo/VSSDK.TestExtension/ToolWindows/ThemeWindow/ThemeWindowControl.xaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<ColumnDefinition Width="Auto"/>
2525
<ColumnDefinition Width="*"/>
2626
<ColumnDefinition Width="Auto"/>
27+
<ColumnDefinition Width="Auto"/>
2728
</Grid.ColumnDefinitions>
2829

2930
<StackPanel
@@ -56,6 +57,14 @@
5657
Content="Open Dialog"
5758
Command="{Binding OpenDialogCommand}"
5859
VerticalAlignment="Center"
60+
Margin="10,0"
61+
/>
62+
63+
<Button
64+
Grid.Column="3"
65+
Content="Customized Styles"
66+
Command="{Binding OpenCustomStylesCommand}"
67+
VerticalAlignment="Center"
5968
/>
6069
</Grid>
6170

demo/VSSDK.TestExtension/ToolWindows/ThemeWindow/ThemeWindowControlViewModel.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public bool UseVsTheme
6464

6565
#pragma warning disable VSTHRD012 // Provide JoinableTaskFactory where allowed
6666
public ICommand OpenDialogCommand => new DelegateCommand(() => OpenDialog());
67+
public ICommand OpenCustomStylesCommand => new DelegateCommand(() => OpenCustomStyles());
6768
#pragma warning restore VSTHRD012 // Provide JoinableTaskFactory where allowed
6869

6970
private void OpenDialog()
@@ -72,6 +73,12 @@ private void OpenDialog()
7273
dialog.ShowModal();
7374
}
7475

76+
private void OpenCustomStyles()
77+
{
78+
CustomizedStylesDialog dialog = new CustomizedStylesDialog();
79+
dialog.ShowModal();
80+
}
81+
7582
private async Task ApplyThemeAsync(Theme theme)
7683
{
7784
// There doesn't appear to be a way to set the theme programatically,

demo/VSSDK.TestExtension/VSSDK.TestExtension.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@
7979
<DependentUpon>RunnerWindowControl.xaml</DependentUpon>
8080
</Compile>
8181
<Compile Include="ToolWindows\RunnerWindowMessenger.cs" />
82+
<Compile Include="ToolWindows\ThemeWindow\CustomizedStylesDialog.xaml.cs">
83+
<DependentUpon>CustomizedStylesDialog.xaml</DependentUpon>
84+
</Compile>
8285
<Compile Include="ToolWindows\ThemeWindow\ThemedControl.cs" />
8386
<Compile Include="ToolWindows\ThemeWindow\ThemeWindow.cs" />
8487
<Compile Include="ToolWindows\ThemeWindow\ThemeWindowControl.xaml.cs">
@@ -141,6 +144,10 @@
141144
<SubType>Designer</SubType>
142145
<Generator>MSBuild:Compile</Generator>
143146
</Page>
147+
<Page Include="ToolWindows\ThemeWindow\CustomizedStylesDialog.xaml">
148+
<SubType>Designer</SubType>
149+
<Generator>MSBuild:Compile</Generator>
150+
</Page>
144151
<Page Include="ToolWindows\ThemeWindow\ThemedControl.xaml">
145152
<SubType>Designer</SubType>
146153
<Generator>MSBuild:Compile</Generator>
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
}

0 commit comments

Comments
 (0)