Skip to content

Commit 936ca97

Browse files
Fix dark mode ListView ColumnHeader bug where recreating ListView Handle doesn't apply DarkMode.
1 parent bc59bd4 commit 936ca97

File tree

1 file changed

+38
-23
lines changed
  • src/System.Windows.Forms/System/Windows/Forms/Controls/ListView

1 file changed

+38
-23
lines changed

src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,10 @@ protected override CreateParams CreateParams
627627
{
628628
get
629629
{
630+
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
631+
SetStyle(ControlStyles.ApplyThemingImplicitly, true);
632+
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
633+
630634
CreateParams cp = base.CreateParams;
631635

632636
cp.ClassName = PInvoke.WC_LISTVIEW;
@@ -2553,6 +2557,8 @@ private int CompensateColumnHeaderResize(int columnIndex, bool columnResizeCance
25532557

25542558
protected override unsafe void CreateHandle()
25552559
{
2560+
base.CreateHandle();
2561+
25562562
if (!RecreatingHandle)
25572563
{
25582564
using ThemingScope scope = new(Application.UseVisualStyles);
@@ -2563,8 +2569,6 @@ protected override unsafe void CreateHandle()
25632569
});
25642570
}
25652571

2566-
base.CreateHandle();
2567-
25682572
if (BackgroundImage is not null)
25692573
{
25702574
SetBackgroundImage();
@@ -2582,7 +2586,7 @@ protected override unsafe void CreateHandle()
25822586
/// </remarks>
25832587
private unsafe void CustomDraw(ref Message m)
25842588
{
2585-
bool dontmess = false;
2589+
bool lockReturnValue = false;
25862590
bool itemDrawDefault = false;
25872591

25882592
try
@@ -2678,7 +2682,7 @@ private unsafe void CustomDraw(ref Message m)
26782682
if (_viewStyle is View.Details or View.Tile)
26792683
{
26802684
m.ResultInternal = (LRESULT)(nint)(PInvoke.CDRF_NOTIFYSUBITEMDRAW | PInvoke.CDRF_NEWFONT);
2681-
dontmess = true; // don't mess with our return value!
2685+
lockReturnValue = true; // Let's make sure we're not changing this.
26822686

26832687
// ITEMPREPAINT is used to work out the rect for the first column!!! GAH!!!
26842688
// (which means we can't just do our color/font work on SUBITEM|ITEM_PREPAINT)
@@ -2760,7 +2764,7 @@ private unsafe void CustomDraw(ref Message m)
27602764
// get the node
27612765
ListViewItem item = Items[(int)nmcd->nmcd.dwItemSpec];
27622766
// if we're doing the whole row in one style, change our result!
2763-
if (dontmess && item.UseItemStyleForSubItems)
2767+
if (lockReturnValue && item.UseItemStyleForSubItems)
27642768
{
27652769
m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_NEWFONT;
27662770
}
@@ -2911,7 +2915,7 @@ private unsafe void CustomDraw(ref Message m)
29112915
PInvokeCore.SelectObject(nmcd->nmcd.hdc, _odCacheFontHandleWrapper.Handle);
29122916
}
29132917

2914-
if (!dontmess)
2918+
if (!lockReturnValue)
29152919
{
29162920
m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_NEWFONT;
29172921
}
@@ -4539,14 +4543,15 @@ protected override void OnHandleCreated(EventArgs e)
45394543

45404544
UpdateExtendedStyles();
45414545
RealizeProperties();
4546+
45424547
PInvokeCore.SendMessage(this, PInvoke.LVM_SETBKCOLOR, (WPARAM)0, (LPARAM)BackColor);
45434548
PInvokeCore.SendMessage(this, PInvoke.LVM_SETTEXTCOLOR, (WPARAM)0, (LPARAM)ForeColor);
45444549

45454550
// The native list view will not invalidate the entire list view item area if the BkColor is not CLR_NONE.
45464551
// This not noticeable if the customer paints the items w/ the same background color as the list view itself.
45474552
// However, if the customer paints the items w/ a color different from the list view's back color
45484553
// then when the user changes selection the native list view will not invalidate the entire list view item area.
4549-
PInvokeCore.SendMessage(this, PInvoke.LVM_SETTEXTBKCOLOR, (WPARAM)0, (LPARAM)PInvokeCore.CLR_NONE);
4554+
// PInvokeCore.SendMessage(this, PInvoke.LVM_SETTEXTBKCOLOR, (WPARAM)0, (LPARAM)PInvokeCore.CLR_NONE);
45504555

45514556
// LVS_NOSCROLL does not work well when the list view is in View.Details or in View.List modes.
45524557
// we have to set this style after the list view was created and before we position the native list view items.
@@ -4590,18 +4595,22 @@ protected override void OnHandleCreated(EventArgs e)
45904595

45914596
// Use a copy of the list items array so that we can maintain the (handle created || listItemsArray is not null) invariant
45924597
ListViewItem[]? listViewItemsToAdd = null;
4598+
45934599
if (_listViewItems is not null)
45944600
{
45954601
listViewItemsToAdd = [.. _listViewItems];
45964602
_listViewItems = null;
45974603
}
45984604

4599-
int columnCount = _columnHeaders is null ? 0 : _columnHeaders.Length;
4605+
int columnCount = _columnHeaders is null
4606+
? 0
4607+
: _columnHeaders.Length;
46004608

46014609
if (columnCount > 0)
46024610
{
46034611
int[] indices = new int[columnCount];
46044612
int index = 0;
4613+
46054614
foreach (ColumnHeader column in _columnHeaders!)
46064615
{
46074616
indices[index] = column.DisplayIndex;
@@ -4655,16 +4664,16 @@ protected override void OnHandleCreated(EventArgs e)
46554664
}
46564665
}
46574666

4658-
if (!RecreatingHandle)
4659-
{
4660-
ApplyDarkModeOnDemand();
4661-
}
4667+
// We need to wait for the next message loop
4668+
// to apply dark mode on demand.
4669+
BeginInvoke(ApplyDarkModeOnDemand);
46624670
}
46634671

46644672
#pragma warning disable WFO5001
46654673
private void ApplyDarkModeOnDemand()
46664674
{
4667-
if (Application.IsDarkModeEnabled)
4675+
if (Application.IsDarkModeEnabled
4676+
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
46684677
{
46694678
// Enable double buffering when in dark mode to reduce flicker.
46704679
uint exMask = PInvoke.LVS_EX_ONECLICKACTIVATE | PInvoke.LVS_EX_TWOCLICKACTIVATE |
@@ -4689,10 +4698,17 @@ private void ApplyDarkModeOnDemand()
46894698
null);
46904699

46914700
// Get the ListView's ColumnHeader handle:
4692-
HWND columnHeaderHandle = (HWND)PInvokeCore.SendMessage(this, PInvoke.LVM_GETHEADER, (WPARAM)0, (LPARAM)0);
4701+
HWND columnHeaderHandle = (HWND)PInvokeCore.SendMessage(
4702+
this,
4703+
PInvoke.LVM_GETHEADER,
4704+
(WPARAM)0,
4705+
(LPARAM)0);
46934706

46944707
// Apply dark mode theme to the ColumnHeader
4695-
PInvoke.SetWindowTheme(columnHeaderHandle, $"{DarkModeIdentifier}_{ItemsViewThemeIdentifier}", null);
4708+
PInvoke.SetWindowTheme(
4709+
columnHeaderHandle,
4710+
$"{DarkModeIdentifier}_{ItemsViewThemeIdentifier}",
4711+
null);
46964712
}
46974713
}
46984714
#pragma warning restore WFO5001
@@ -4971,11 +4987,9 @@ private void RealizeAllSubItems()
49714987

49724988
protected void RealizeProperties()
49734989
{
4974-
// Realize state information
4975-
Color c;
4976-
4977-
c = BackColor;
49784990
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
4991+
Color c = BackColor;
4992+
49794993
if (c != SystemColors.Window || Application.IsDarkModeEnabled)
49804994
{
49814995
PInvokeCore.SendMessage(this, PInvoke.LVM_SETBKCOLOR, (WPARAM)0, (LPARAM)c);
@@ -4989,6 +5003,7 @@ protected void RealizeProperties()
49895003
}
49905004
#pragma warning restore WFO5001
49915005

5006+
// Realize state information
49925007
if (_imageListLarge is not null)
49935008
{
49945009
PInvokeCore.SendMessage(this, PInvoke.LVM_SETIMAGELIST, (WPARAM)PInvoke.LVSIL_NORMAL, (LPARAM)_imageListLarge.Handle);
@@ -6441,16 +6456,16 @@ private int UpdateGroupCollapse(MessageId clickType)
64416456

64426457
internal void RecreateHandleInternal()
64436458
{
6444-
// For some reason, if CheckBoxes are set to true and the list view has a state imageList, then the native
6445-
// listView destroys the state imageList.
6446-
// (Yes, it does exactly that even though our wrapper sets LVS_SHAREIMAGELISTS on the native listView.)
6459+
// For some reason, if CheckBoxes are set to true and the list view has a state imageList,
6460+
// then the native listView destroys the state imageList.
6461+
// (Yes, it does exactly that even though our wrapper sets LVS_SHAREIMAGELISTS
6462+
// on the native listView.)
64476463
if (IsHandleCreated && StateImageList is not null)
64486464
{
64496465
PInvokeCore.SendMessage(this, PInvoke.LVM_SETIMAGELIST, (WPARAM)PInvoke.LVSIL_STATE);
64506466
}
64516467

64526468
RecreateHandle();
6453-
ApplyDarkModeOnDemand();
64546469
}
64556470

64566471
private unsafe void WmReflectNotify(ref Message m)

0 commit comments

Comments
 (0)