我想在Visual Studio 2012(及更高版本)中创建一个支持不同主题(深、浅、蓝)的语法突出显示器。
Visual Studio的编辑器分类器项目模板解释了如何使用Microsoft.VisualStudio.Text.Classification.ClassificationFormatDefinition
在环境中创建自己的颜色。
...直到您意识到Visual Studio 2012(及更高版本)中有不同的主题,而您实际上并不支持它们。您在浅色主题上漂亮的深蓝色标识符在深色主题环境中变得不可读。
据我所知,如果您在给定主题的工具/选项/字体和颜色中更改了 ClassificationFormatDefinition(例如:浅色),它不会影响不同主题中的相同 ClassificationFormatDefinition(例如:Dark).颜色在不同的主题中似乎是独立的。
这很好。但是**如何实现定义相同的 ClassificationFormatDefinition(例如:MyKeywords)在所有主题中具有相同的名称,但为它们提供不同的颜色?**就像Visual Studio自己的“标识符”一样,在Light主题中默认为黑色,在Black主题中默认为黑色。
我知道Microsoft.VisualStudio.PlatformUI.VSColorTheme.ThemeChanged
事件,它允许我在颜色主题更改时得到通知。我是否必须使用此事件并以某种方式获取现有的 ClassificationFormatDefinition,然后根据新主题为它们分配新颜色?但这也会引发一个问题:这些修改过的颜色是否会保留到环境中,例如,如果我重新启动Visual Studio,下次我的更改是否会出现在所有不同的主题中。
我没有找到任何属性来说明 ClassificationFormatDefinition 支持哪个主题,也没有找到很多关于这个主题的有用文章。
任何帮助都感激不尽。
5条答案
按热度按时间c0vxltue1#
好吧,我找到了一个变通的办法。它远非完美,但它已经很好了。
诀窍是在定义自己的分类类型时使用另一个基本定义。这将为不同的主题使用它们的默认颜色。重要的是,您不能在
MyKeywordsFormatDefinition
中定义自己的颜色,因为这会禁用在主题之间切换时的默认行为。因此,请尝试找到与您的颜色匹配的基本定义。请在此处查找预定义的分类类型:Microsoft.VisualStudio.Language.StandardClassification.PredefinedClassificationTypeNames我希望它对你们中的一些人有用。甚至可能有助于完善一个适当的解决方案,当你可以实际设置自己的颜色而不重用现有的颜色定义时。
mbskvtky2#
这可能对您有所帮助,F# Power Tools中的代码似乎正在侦听ThemeChanged事件并更新分类器-https://github.com/fsprojects/VisualFSharpPowerTools/blob/a7d7aa9dd3d2a90f21c6947867ac7d7163b9f99a/src/FSharpVSPowerTools/SyntaxConstructClassifierProvider.cs
wfveoks03#
使用VS SDK附带的
VsixColorCompiler
还有另一种更简洁的方法。首先,像往常一样创建一个
ClassificationTypeDefinition
和ClassificationFormatDefinition
,这将定义所有主题的默认颜色:接下来,创建一个colours.xml文件,这将允许我们覆盖特定主题的颜色:
现在编辑您的.csproj以包含一个构建后命令,将XML编译为一个.pkgdef,紧挨着您的普通包的.pkgdef(VS2015 SDK如下所示):
无论何时进行更改,请确保在两个版本之间执行clear the MEF cache以强制其更新。此外,可能还需要删除以下注册表项:
xuo3flqw4#
我也遇到过类似的问题。我已经为DSL开发了一个语法高亮器。它有两组颜色--亮和暗主题。我需要一种方法,当VS主题改变时,在运行时在这两组颜色之间切换。
经过一番搜索,我在F#github中负责与VS集成的代码中找到了一个解决方案:www.example.comhttps://github.com/dotnet/fsharp/blob/main/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs#L121
F#repo中的代码与Omer Raviv的答案中的代码非常相似,我将其翻译成C#,得到如下内容:
我已经使用上面的类作为所有ClassificationFormatDefinition类的基类。
编辑:升级到
AsyncPackage
以获得VS的新版本后,之前的代码停止工作。您需要在其他地方声明MEF导入,例如,直接在ClassificationFormatDefinition
的继承者中声明。此外,正如@Alessandro所指出的,代码中存在一个微妙的bug,如果切换VS主题,然后立即转到VS设置"字体和颜色"你会看到默认颜色值没有改变。它们会在VS重启后改变,但这仍然不是理想的。幸运的是,有一个解决办法(再次感谢@Alessandro).您需要拨打IVsFontAndColorCacheManager
's具有正确guid75A05685-00A8-4DED-BAE5-E7A50BFA929A
的ClearCache
或RefreshCache
,该guid与注册表中Fonts and Colors缓存中的MefItems类别相对应。下面是对一篇文章的引用,该文章对此进行了一些描述:https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.interop.ivsfontandcolorcachemanager?view=visualstudiosdk-2019不幸的是,我找不到guid常量的任何文档。
更新:经过更多的研究,调试和添加记录坏错误代码到VS活动日志,我发现以下几点:
1.为VS主题的单个更改多次调用主题更改处理程序
因此,我将对ClearCache的调用替换为对RefreshCache的调用。
下面是一个更新的示例:
通过检查代码编辑器的当前背景,可以确定是否需要使用适合浅色或深色主题的颜色。下面是我使用的代码的链接:www.example.comhttps://github.com/Acumatica/Acuminator/blob/dev/src/Acuminator/Acuminator.Vsix/Coloriser/Constants/VSColors.cs#L82
下面是@Alessandro的一个更简洁的片段(再次感谢!):
您也可以创建一个单独的共享ThemeUpdater类,该类将订阅
ThemeChanged
事件,并且所有ClassificationFormatDefinition
派生类都将订阅它,以便在主题更改时进行特定更改。这具有性能优势,您可以批量更新所有格式定义,并且在主题更改时仅调用EndBatchUpdate
和RefreshCache/ClearCache
一次。pdtvr36n5#
对于Visual Studio 2022,answer by SENya只能部分或“有时”工作:更改VS主题不会立即正确地更改颜色,大约有10%的情况是这样。此外,将主题从深色更改为浅色看起来很正常,但在重新启动Visual Studio后,经常使用深色而不是浅色(超过一半的情况)。所有这些都是不确定的。
经过一些调试,我理解的问题如下:调用
IVsFontAndColorCacheManager.ClearCache()
将删除注册表项"Software\Microsoft\VisualStudio\17.0_4d51a943Exp\FontAndColors\Cache\{75A05685-00A8-4DED-BAE5-E7A50BFA929A}\ItemAndFontInfo"
,该注册表项是字体和颜色该高速缓存。在自定义主题更改函数完成后,某些其他Visual Studio组件有时(但并不总是)立即更新字体和颜色缓存。也就是说,它调用类似fontAndColorStorage.OpenCategory(ref mFontAndColorCategoryGUID, (uint)__FCSTORAGEFLAGS.FCSF_READONLY | (uint)__FCSTORAGEFLAGS.FCSF_LOADDEFAULTS)
的内容。请注意FCSF_LOADDEFAULTS
。这会导致Visual Studio重新创建注册表项。但是,显然,它没有使用更新后的IClassificationFormatMap
的颜色,而是使用了ClassificationFormatDefinition
本身设置的颜色,这些颜色没有更新,因此,更改主题会立即更改显示的颜色(因为IClassificationFormatMap
得到了更新),但是注册表缓存以错误的颜色结束。在重新启动VS之后,它使用缓存的值,因此最终使用错误的颜色。通过在ClassificationFormatDefinition
示例上也更改颜色,该问题似乎得到了修复。详情
在我的VSDoxyHighlighter(Github)中,我对answer by SENya进行了如下修改:
首先,一些helper类存储默认的文本格式:
然后是处理主题内容的主类
DefaultColors
:这里需要注意几点:
DefaultColors
的一个示例不应该手工创建,而应该只由MEF创建一个示例(例如通过Import
属性)。ClassificationFormatDefinition
定义(表示Visual Studio用于各种分类的文本格式)应通过RegisterFormatDefinition()
在DefaultColors
示例上注册自身。VSColorTheme.ThemeChanged
。还请注意,每次主题更改都会多次触发该事件。由于没有必要多次执行ThemeChangedImpl()
中的所有更新代码,因此我们检查新旧主题是否不同。ThemeChangedImpl()
中,这是主要基于answer by SENya的代码,但是添加了以前通过RegisterFormatDefinition()
注册的ClassificationFormatDefinition
获得对Reinitialize()
的调用。为了完整起见,
ID_command
和ID_parameter1
是用于标识ClassificationFormatDefinition
的一些自定义标识符(参见下文):现在,实际的
ClassificationFormatDefinition
定义如下:它们继承自接口IFormatDefinition
(可以传递给DefaultColors.RegisterFormatDefinition()
函数)所有
ClassificationFormatDefinition
基本相同:它们设置文本属性(color、bold、italic等),在构造时适合当前的颜色主题,这是通过查询DefaultColors.GetDefaultFormattingForCurrentTheme()
函数来完成的,并且它们在DefaultColors
上注册自己,并实现Reinitialize()
方法(由DefaultColors
调用),因为总是一样的,所以我为它们定义了一个基类FormatDefinitionBase
:最后,实际的定义如下所示:
请注意,构造函数被标记为
ImportingConstructor
,以便MEF自动创建DefaultColors
类的单个示例并将其传递给构造函数。所以,总结一下:
ClassificationFormatDefinition
由MEF创建。同时,MEF还创建了DefaultColors
的一个示例并将其传递给ClassificationFormatDefinition
。ClassificationFormatDefinition
设置默认颜色并提供一个函数以允许其在主题更改时重新初始化。为了实现这一点,它还在DefaultColors
示例上注册了自己。DefaultColors
计算出当前主题,并包含每个主题的默认颜色。DefaultColors
侦听VSColorTheme.ThemeChanged
事件,如果触发,则清除Visual Studio的字体和颜色缓存,更新当前分类格式Map(以显示新颜色),并使用新颜色更新所有自定义ClassificationFormatDefinition
示例(以便在VS重新创建字体和颜色缓存时将正确的颜色用于该高速缓存)。