Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public partial class ListView : Control
private ImageList? _imageListSmall;
private ImageList? _imageListState;
private ImageList? _imageListGroup;
private ImageList? _darkModeStateImageList;

private MouseButtons _downButton;
private int _itemCount;
Expand Down Expand Up @@ -571,6 +572,12 @@ public bool CheckBoxes
}
}
}

if (Application.IsDarkModeEnabled
&& DarkModeRequestState is true)
{
UpdateDarkModeCheckBoxImages();
}
}
}

Expand Down Expand Up @@ -3066,6 +3073,9 @@ protected override void Dispose(bool disposing)
DetachGroupImageListHandlers();
_imageListGroup = null;

_darkModeStateImageList?.Dispose();
_darkModeStateImageList = null;

// Remove any ColumnHeaders contained in this control
if (_columnHeaders is not null)
{
Expand Down Expand Up @@ -4703,6 +4713,81 @@ private void ApplyDarkModeOnDemand()
columnHeaderHandle,
$"{DarkModeIdentifier}_{ItemsViewThemeIdentifier}",
null);

if (CheckBoxes)
{
UpdateDarkModeCheckBoxImages();
}
}
}

private void UpdateDarkModeCheckBoxImages()
{
if (_imageListState is not null || !CheckBoxes || !IsHandleCreated)
{
return;
}

_darkModeStateImageList?.Dispose();

int size = ScaleHelper.ScaleToInitialSystemDpi(16);

ImageList imageList = new()
{
ImageSize = new Size(size, size),
ColorDepth = ColorDepth.Depth32Bit,
TransparentColor = Color.Transparent
};

using Bitmap uncheckedBitmap = new(size, size);
using (Graphics g = Graphics.FromImage(uncheckedBitmap))
{
CheckBoxRenderer.DrawCheckBoxWithVisualStyles(g, new Point(0, 0), CheckBoxState.UncheckedNormal, HWNDInternal);
}

using Bitmap checkedBitmap = new(size, size);
using (Graphics g = Graphics.FromImage(checkedBitmap))
{
CheckBoxRenderer.DrawCheckBoxWithVisualStyles(g, new Point(0, 0), CheckBoxState.CheckedNormal, HWNDInternal);
}

imageList.Images.Add(uncheckedBitmap);
imageList.Images.Add(checkedBitmap);

// Ensure the checkbox extended style is active before assigning our custom state imagelist,
// otherwise the style refresh may overwrite the list with the default light-themed images.
ForceCheckBoxUpdate();

PInvokeCore.SendMessage(
this,
PInvoke.LVM_SETIMAGELIST,
(WPARAM)PInvoke.LVSIL_STATE,
(LPARAM)imageList.Handle);

_darkModeStateImageList = imageList;

if (VirtualMode)
{
return;
}

if (Items.Count > 0)
{
const LIST_VIEW_ITEM_STATE_FLAGS stateMask = LIST_VIEW_ITEM_STATE_FLAGS.LVIS_STATEIMAGEMASK;

for (int i = 0; i < Items.Count; i++)
{
bool isChecked = Items[i].Checked;
int stateIndex = isChecked ? 2 : 1;

SetItemState(i, (LIST_VIEW_ITEM_STATE_FLAGS)(stateIndex << 12), stateMask);
}

PInvokeCore.SendMessage(
this,
PInvoke.LVM_REDRAWITEMS,
(WPARAM)0,
(LPARAM)(Items.Count - 1));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ public bool CheckBoxes
if (CheckBoxes)
{
UpdateStyles();
UpdateCheckBoxImages();
}
else
{
Expand Down Expand Up @@ -1869,6 +1870,18 @@ protected override void OnHandleCreated(EventArgs e)
int style = (int)PInvokeCore.GetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
style |= (int)PInvoke.TVS_CHECKBOXES;
PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);

if (Application.IsDarkModeEnabled
&& DarkModeRequestState is true
&& RecreatingHandle)
{
_ = PInvoke.SetWindowTheme(
hwnd: HWND,
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
pszSubIdList: null);
}

UpdateCheckBoxImages();
}

if (ShowNodeToolTips && !DesignMode)
Expand Down Expand Up @@ -1991,6 +2004,44 @@ private void UpdateNativeStateImageList()
_internalStateImageList = newImageList;
}

private void UpdateCheckBoxImages()
{
if (!Application.IsDarkModeEnabled || DesignMode || !IsHandleCreated || !CheckBoxes || _stateImageList is not null)
{
return;
}

int size = StateImageSize?.Width ?? ScaleHelper.ScaleToInitialSystemDpi(16);
ImageList imageList = new()
{
ImageSize = new Size(size, size),
ColorDepth = ColorDepth.Depth32Bit
};

using Bitmap uncheckedBitmap = new(size, size);
using (Graphics g = Graphics.FromImage(uncheckedBitmap))
{
CheckBoxRenderer.DrawCheckBoxWithVisualStyles(g, new Point(0, 0), CheckBoxState.UncheckedNormal, HWNDInternal);
}

using Bitmap checkedBitmap = new(size, size);
using (Graphics g = Graphics.FromImage(checkedBitmap))
{
CheckBoxRenderer.DrawCheckBoxWithVisualStyles(g, new Point(0, 0), CheckBoxState.CheckedNormal, HWNDInternal);
}

using Bitmap placeholderBitmap = new(size, size);
// TreeView uses 1-based indexing: index 0=placeholder, 1=unchecked, 2=checked
imageList.Images.Add(placeholderBitmap); // Placeholder
imageList.Images.Add(uncheckedBitmap);
imageList.Images.Add(checkedBitmap);

SetStateImageList(imageList.Handle);

_internalStateImageList?.Dispose();
_internalStateImageList = imageList;
}

private void SetStateImageList(IntPtr handle)
{
// In certain cases (TREEVIEWSTATE_checkBoxes) e.g., the Native TreeView leaks the imageList
Expand Down Expand Up @@ -3385,6 +3436,7 @@ protected override unsafe void WndProc(ref Message m)
case PInvokeCore.WM_SYSCOLORCHANGE:
PInvokeCore.SendMessage(this, PInvoke.TVM_SETINDENT, (WPARAM)Indent);
base.WndProc(ref m);
UpdateCheckBoxImages();
break;
case PInvokeCore.WM_SETFOCUS:
// If we get focus through the LButtonDown .. we might have done the validation...
Expand Down