diff --git a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
index 7e1bf5fdf..49786563a 100644
--- a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
+++ b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
@@ -29,6 +29,7 @@ using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Controls.TreeView;
+using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.Util;
namespace ICSharpCode.ILSpy.Analyzers
@@ -60,18 +61,21 @@ namespace ICSharpCode.ILSpy.Analyzers
| ConversionFlags.ShowTypeParameterList;
RichText? richText;
- bool richTextComputed;
+ // The build key: the signature's colours depend on the active theme and its formatting on the
+ // active language, so the cache is rebuilt when either changes (not just once).
+ (string? theme, object language)? richTextKey;
///
- /// The highlighted, type-name-emboldened signature for this row, computed once and cached.
- /// Falls back to (plain Text) for non-entity rows.
+ /// The highlighted, type-name-emboldened signature for this row, cached until the theme or
+ /// language changes. Falls back to (plain Text) for non-entity rows.
///
public RichText? CreateRichText()
{
- if (!richTextComputed)
+ var key = (ThemeManager.Current.Theme, (object)Language);
+ if (richTextKey != key)
{
richText = BuildRichText();
- richTextComputed = true;
+ richTextKey = key;
}
return richText;
}
diff --git a/ILSpy/Controls/TreeView/RichNodeText.cs b/ILSpy/Controls/TreeView/RichNodeText.cs
index 3cb2d3bcc..83bfd75d7 100644
--- a/ILSpy/Controls/TreeView/RichNodeText.cs
+++ b/ILSpy/Controls/TreeView/RichNodeText.cs
@@ -16,13 +16,16 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
+using System;
using System.ComponentModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
+using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.TextView;
+using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpyX.TreeView;
namespace ICSharpCode.ILSpy.Controls.TreeView
@@ -39,9 +42,25 @@ namespace ICSharpCode.ILSpy.Controls.TreeView
public static readonly AttachedProperty NodeProperty =
AvaloniaProperty.RegisterAttached("Node", typeof(RichNodeText));
- // Holds the live PropertyChanged subscription so a recycled TextBlock detaches cleanly.
- static readonly AttachedProperty HandlerProperty =
- AvaloniaProperty.RegisterAttached("Handler", typeof(RichNodeText));
+ // Live PropertyChanged subscription on the bound node, so a recycled TextBlock detaches cleanly.
+ static readonly AttachedProperty TextHandlerProperty =
+ AvaloniaProperty.RegisterAttached("TextHandler", typeof(RichNodeText));
+
+ // Live ThemeChanged / language-settings subscriptions (rich nodes only): the signature colours
+ // and per-language formatting are baked into the node's cached RichText, so the inlines must be
+ // rebuilt when the theme or the active language changes.
+ static readonly AttachedProperty ThemeHandlerProperty =
+ AvaloniaProperty.RegisterAttached("ThemeHandler", typeof(RichNodeText));
+
+ static readonly AttachedProperty LanguageHandlerProperty =
+ AvaloniaProperty.RegisterAttached("LanguageHandler", typeof(RichNodeText));
+
+ static readonly AttachedProperty CleanupHookedProperty =
+ AvaloniaProperty.RegisterAttached("CleanupHooked", typeof(RichNodeText));
+
+ static LanguageSettings? languageSettings;
+ static LanguageSettings? GetLanguageSettings()
+ => languageSettings ??= AppComposition.TryGetExport()?.SessionSettings.LanguageSettings;
static RichNodeText()
{
@@ -53,31 +72,65 @@ namespace ICSharpCode.ILSpy.Controls.TreeView
static void OnNodeChanged(TextBlock textBlock, AvaloniaPropertyChangedEventArgs e)
{
- if (e.OldValue is INotifyPropertyChanged oldNode
- && textBlock.GetValue(HandlerProperty) is { } oldHandler)
- {
- oldNode.PropertyChanged -= oldHandler;
- textBlock.SetValue(HandlerProperty, null);
- }
+ Detach(textBlock, e.OldValue as SharpTreeNode);
var node = e.NewValue as SharpTreeNode;
- if (node is INotifyPropertyChanged newNode)
+ if (node is INotifyPropertyChanged notify)
{
// Mirror the {Binding Text} the cell used to have: refresh when Text changes
// (e.g. a lazily-loaded node swapping its placeholder for the real label).
- void Handler(object? sender, PropertyChangedEventArgs args)
+ void TextHandler(object? sender, PropertyChangedEventArgs args)
{
if (args.PropertyName is nameof(SharpTreeNode.Text) or null or "")
Render(textBlock, node);
}
- newNode.PropertyChanged += Handler;
- textBlock.SetValue(HandlerProperty, Handler);
+ notify.PropertyChanged += TextHandler;
+ textBlock.SetValue(TextHandlerProperty, (PropertyChangedEventHandler)TextHandler);
+ }
+
+ if (node is IRichTextNode)
+ {
+ // Rebuild on theme change (palette) and on language change (signature syntax/colours).
+ void ThemeHandler(object? sender, EventArgs args) => Render(textBlock, GetNode(textBlock));
+
+ ThemeManager.Current.ThemeChanged += ThemeHandler;
+ textBlock.SetValue(ThemeHandlerProperty, (EventHandler)ThemeHandler);
+
+ if (GetLanguageSettings() is { } settings)
+ {
+ void LanguageHandler(object? sender, PropertyChangedEventArgs args) => Render(textBlock, GetNode(textBlock));
+
+ settings.PropertyChanged += LanguageHandler;
+ textBlock.SetValue(LanguageHandlerProperty, (PropertyChangedEventHandler)LanguageHandler);
+ }
+ }
+
+ if (!textBlock.GetValue(CleanupHookedProperty))
+ {
+ textBlock.SetValue(CleanupHookedProperty, true);
+ textBlock.DetachedFromVisualTree += (sender, args) => Detach(textBlock, GetNode(textBlock));
}
Render(textBlock, node);
}
+ // Drops the live subscriptions for ; safe to call repeatedly.
+ static void Detach(TextBlock textBlock, SharpTreeNode? node)
+ {
+ if (node is INotifyPropertyChanged notify && textBlock.GetValue(TextHandlerProperty) is { } textHandler)
+ notify.PropertyChanged -= textHandler;
+ textBlock.SetValue(TextHandlerProperty, null);
+
+ if (textBlock.GetValue(ThemeHandlerProperty) is { } themeHandler)
+ ThemeManager.Current.ThemeChanged -= themeHandler;
+ textBlock.SetValue(ThemeHandlerProperty, null);
+
+ if (textBlock.GetValue(LanguageHandlerProperty) is { } languageHandler && GetLanguageSettings() is { } settings)
+ settings.PropertyChanged -= languageHandler;
+ textBlock.SetValue(LanguageHandlerProperty, null);
+ }
+
static void Render(TextBlock textBlock, SharpTreeNode? node)
{
if (node is IRichTextNode richNode && richNode.CreateRichText() is { } rich)