diff --git a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
index 5b728b3472a..5492ea09bad 100644
--- a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
+++ b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
@@ -26,6 +26,7 @@ internal static partial class LocalAppContextSwitches
internal const string EnableMsoComponentManagerSwitchName = "Switch.System.Windows.Forms.EnableMsoComponentManager";
internal const string TreeNodeCollectionAddRangeRespectsSortOrderSwitchName = "System.Windows.Forms.TreeNodeCollectionAddRangeRespectsSortOrder";
internal const string MoveTreeViewTextLocationOnePixelSwitchName = "System.Windows.Forms.TreeView.MoveTreeViewTextLocationOnePixel";
+ internal const string PreserveUnassignedTreeNodeImagesSwitchName = "System.Windows.Forms.TreeView.PreserveUnassignedTreeNodeImages";
private static int s_scaleTopLevelFormMinMaxSizeForDpi;
private static int s_anchorLayoutV2;
@@ -39,6 +40,7 @@ internal static partial class LocalAppContextSwitches
private static int s_treeNodeCollectionAddRangeRespectsSortOrder;
private static int s_moveTreeViewTextLocationOnePixel;
+ private static int s_preserveUnassignedTreeNodeImages;
private static FrameworkName? s_targetFrameworkName;
@@ -231,4 +233,13 @@ public static bool MoveTreeViewTextLocationOnePixel
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(MoveTreeViewTextLocationOnePixelSwitchName, ref s_moveTreeViewTextLocationOnePixel);
}
+
+ ///
+ /// Indicates whether keep the node image that has not set an image when the ImageList changes dynamically.
+ ///
+ public static bool PreserveUnassignedTreeNodeImages
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue(PreserveUnassignedTreeNodeImagesSwitchName, ref s_preserveUnassignedTreeNodeImages);
+ }
}
diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeNode.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeNode.cs
index d97e3d70b82..e3ae94eb408 100644
--- a/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeNode.cs
+++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeNode.cs
@@ -2195,24 +2195,75 @@ static bool IsSpecialImageIndex(int actualIndex)
internal unsafe void UpdateImage()
{
- TreeView tv = TreeView!;
- if (tv.IsDisposed)
+ if (TreeView is not { IsDisposed: false } treeView)
{
return;
}
+ int imageIndex = 0;
+ int selectedImageIndex = 0;
+
+ if (treeView.ImageList is { } imageList)
+ {
+ if (AppContextSwitches.PreserveUnassignedTreeNodeImages)
+ {
+ imageIndex = GetEffectiveImageIndex(treeView, imageList, ImageKey);
+ selectedImageIndex = GetEffectiveImageIndex(treeView, imageList, SelectedImageKey);
+ }
+ else
+ {
+ imageIndex = Math.Clamp(ImageIndexer.ActualIndex, 0, imageList.Images.Count - 1);
+ }
+ }
+
TVITEMW item = new()
{
mask = TVITEM_MASK.TVIF_HANDLE | TVITEM_MASK.TVIF_IMAGE,
hItem = HTREEITEM,
- iImage = Math.Max(
- 0,
- tv.ImageList is { } imageList && ImageIndexer.ActualIndex >= imageList.Images.Count
- ? imageList.Images.Count - 1
- : ImageIndexer.ActualIndex)
+ iImage = imageIndex
};
- PInvokeCore.SendMessage(tv, PInvoke.TVM_SETITEMW, 0, ref item);
+ if (AppContextSwitches.PreserveUnassignedTreeNodeImages)
+ {
+ item.mask |= TVITEM_MASK.TVIF_SELECTEDIMAGE;
+ item.iSelectedImage = selectedImageIndex;
+ }
+
+ PInvokeCore.SendMessage(treeView, PInvoke.TVM_SETITEMW, 0, ref item);
+ }
+
+ private unsafe int GetEffectiveImageIndex(TreeView treeView, ImageList imageList, string effectiveImageKey)
+ {
+ int imageIndex;
+
+ // If the current node does not set ImageKey, try inheriting TreeView's ImageKey
+ if (string.IsNullOrEmpty(effectiveImageKey))
+ {
+ effectiveImageKey = treeView.ImageKey;
+ }
+
+ // If the current node has an ImageKey set, prefer using the valid ImageKey
+ if (!string.IsNullOrEmpty(effectiveImageKey) && imageList.Images.ContainsKey(effectiveImageKey))
+ {
+ imageIndex = imageList.Images.IndexOfKey(effectiveImageKey);
+ }
+ else if (ImageIndexer.ActualIndex >= 0 && ImageIndexer.ActualIndex < imageList.Images.Count)
+ {
+ // Otherwise use the node's own ImageIndex
+ imageIndex = ImageIndexer.ActualIndex;
+ }
+ else if (treeView.ImageIndexer.ActualIndex >= 0 && treeView.ImageIndexer.ActualIndex < imageList.Images.Count)
+ {
+ // Then try using TreeView's ImageIndex
+ imageIndex = treeView.ImageIndexer.ActualIndex;
+ }
+ else
+ {
+ // Fallback to default image
+ imageIndex = 0;
+ }
+
+ return imageIndex;
}
///