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 @@ -2782,12 +2782,16 @@ private unsafe void CustomDraw(ref Message m)
if (renderinfo is not null && renderinfo.Font is not null)
{
// Mess with the DC directly...
PInvokeCore.SelectObject(nmtvcd->nmcd.hdc, renderinfo.FontHandle);
Debug.Assert(node._propBag is not null);
if (node._propBag is not null && node._propBag.Font is not null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The node._propBag.Font is not null part is redundant with the higher-level check renderinfo.Font is not null. GetItemRenderStyles sets retval.Font = node._propBag.Font (same reference) and returns an empty bag (Font == null) whenever node/node._propBag is null, so once renderinfo.Font is not null holds, node._propBag.Font is guaranteed non-null too and this inner check can be dropped. (Keep the node._propBag is not null half only to satisfy the compiler's nullable analysis, which can't infer that invariant across the method boundary.)

{
PInvokeCore.SelectObject(nmtvcd->nmcd.hdc, node._propBag.FontHandle);

// There is a problem in winctl that clips node fonts if the fontSize
// is larger than the treeView font size. The behavior is much better in comctl 5 and above.
m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_NEWFONT;
return;
// There is a problem in winctl that clips node fonts if the fontSize
// is larger than the treeView font size. The behavior is much better in comctl 5 and above.
m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_NEWFONT;
return;
}
}

// fall through and do the default drawing work
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace System.Windows.Forms;
[Serializable] // This class is participating in resx serialization scenarios for listview/treeview items.
public class OwnerDrawPropertyBag : MarshalByRefObject, ISerializable
{
private Font? _font;
private Control.FontHandleWrapper? _fontWrapper;
private static readonly Lock s_internalSyncObject = new();

Expand All @@ -38,7 +39,19 @@ internal OwnerDrawPropertyBag()
{
}

public Font? Font { get; set; }
public Font? Font
{
get => _font;
set
{
if (!ReferenceEquals(_font, value))
{
_font = value;
_fontWrapper?.Dispose();
_fontWrapper = null;
}
}
}

public Color ForeColor { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#nullable disable

using System.Drawing;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms.TestUtilities;

Expand Down Expand Up @@ -56,6 +57,24 @@ public void OwnerDrawPropertyBag_Font_Set_GetReturnsExpected(Font value)
Assert.Equal(value is null, bag.IsEmpty());
}

[WinFormsFact]
public void OwnerDrawPropertyBag_Font_SetDifferentValue_ResetsCachedFontWrapper()
{
using SubTreeView treeView = new();
OwnerDrawPropertyBag bag = treeView.GetItemRenderStyles(null, 0);
using Font firstFont = new(SystemFonts.MenuFont, FontStyle.Regular);
using Font secondFont = new(SystemFonts.MenuFont, FontStyle.Bold);

bag.Font = firstFont;
_ = bag.GetType().GetProperty("FontHandle", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(bag);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ontHandle  is internal and directly accessible in the test assembly (InternalsVisibleTo), so the reflection lookup for it is unnecessary


FieldInfo fontWrapperField = bag.GetType().GetField("_fontWrapper", BindingFlags.Instance | BindingFlags.NonPublic)!;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using TestAccessor.Dynamic instead of reflection.

Assert.NotNull(fontWrapperField.GetValue(bag));

bag.Font = secondFont;
Assert.Null(fontWrapperField.GetValue(bag));
}

[WinFormsTheory]
[CommonMemberData(typeof(CommonTestHelper), nameof(CommonTestHelper.GetColorWithEmptyTheoryData))]
public void OwnerDrawPropertyBag_ForeColor_Set_GetReturnsExpected(Color value)
Expand Down