|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using System.Drawing;
namespace System.Windows.Forms;
public partial class ToolStripPanel
{
private partial class FeedbackRectangle
{
private class FeedbackDropDown : ToolStripDropDown
{
private const int MaxPaintsToService = 20;
private int _numPaintsServiced; // member variable to protect against reentrancy
public FeedbackDropDown(Rectangle bounds) : base()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, false);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.CacheText, true);
AutoClose = false;
AutoSize = false;
DropShadowEnabled = false;
Bounds = bounds;
// Set a clipping region with the center excluded.
Region region = new(bounds);
bounds.Inflate(-1, -1);
region.Exclude(bounds);
Region = region;
}
// ForceSynchronousPaint - peeks through the message queue, looking for WM_PAINTs
// calls UpdateWindow on the hwnd to force the paint to happen now.
//
// When we're changing the location of the feedback dropdown, we need to
// force WM_PAINTS to happen, as things that don't respond to WM_ERASEBKGND
// have bits of the dropdown region drawn all over them.
private unsafe void ForceSynchronousPaint()
{
if (IsDisposed || _numPaintsServiced != 0)
{
return;
}
// Protect against reentrancy.
try
{
MSG msg = default;
while (PInvokeCore.PeekMessage(
&msg,
HWND.Null,
PInvokeCore.WM_PAINT,
PInvokeCore.WM_PAINT,
PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
{
PInvoke.UpdateWindow(msg.hwnd);
// Infinite loop protection.
if (_numPaintsServiced++ > MaxPaintsToService)
{
Debug.Fail("Somehow we've gotten ourself in a situation where we're pumping an unreasonable number of paint messages, investigate.");
break;
}
}
}
finally
{
_numPaintsServiced = 0;
}
}
protected override void OnPaint(PaintEventArgs e)
{
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Respond to everything in WM_ERASEBKGND.
Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(e.Graphics, this));
Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(e.Graphics, this));
}
protected override void OnOpening(CancelEventArgs e)
{
base.OnOpening(e);
e.Cancel = false;
}
public void MoveTo(Point newLocation)
{
Location = newLocation;
// If we don't force a paint here, we'll only send WM_ERASEBKGNDs right away
// and leave rectangles all over controls that don't respond to that window message.
ForceSynchronousPaint();
}
protected override void WndProc(ref Message m)
{
if (m.MsgInternal == PInvokeCore.WM_NCHITTEST)
{
m.ResultInternal = (LRESULT)PInvoke.HTTRANSPARENT;
}
base.WndProc(ref m);
}
}
}
}
|