vendredi 13 mars 2015

Custom Transparent TrackBar in ToolStrip fails to redraw

As the title says, when I put the below control in a ToolStrip, the control sometimes disappears for no good reason. I've been able to narrow this down to a couple scenarios (out of several) that are repeatable:



  1. When the control goes offscreen (even momentarily during a window move)

  2. When the user presses "Alt" and something that can have an accelerator on it is in the same window as the toolbar.


The TrackBar appears to do the PrePaint stage, but that's it until a full redraw is called for. Surely I'm missing something obvious...


Note: I'm deriving ToolStripTrackBarControl from ContainerControl because the real project puts buttons on either side of the TrackBar.


Code:



[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip)]
class ToolStripTrackBar : ToolStripControlHost
{
class ToolStripTrackBarControl : ContainerControl
{
class TransparentTrackBar : TrackBar
{
public TransparentTrackBar()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}

bool _hasTransparentBackground;
protected override void OnBackColorChanged(EventArgs e)
{
if (base.BackColor == Color.Empty || base.BackColor.A < 255)
{
_hasTransparentBackground = true;
}
else
{
_hasTransparentBackground = false;
}
base.OnBackColorChanged(e);
}

const int WM_ERASEBKGND = 0x0014;
const int WM_NOTIFY = 0x004E;
const int WM_REFLECT = 0x2000;

const int NM_CUSTOMDRAW = -12;

const int CDDS_PREPAINT = 0x0001;

const int CDRF_DODEFAULT = 0x0000;

protected override void WndProc(ref Message m)
{
// only do the custom logic if the background is transparent
if (_hasTransparentBackground)
{
// if we're erasing the background, do the transparency logic
if (m.Msg == WM_ERASEBKGND)
{
// tell the sender not to do anything
m.Result = (IntPtr)(-1);
}
else
{
// here we actually paint our background with the transparent color
if (m.Msg == WM_NOTIFY + WM_REFLECT)
{
// LParam points to a NMCUSTOMDRAW structure... we only need three fields: hdr.code, dwDrawStage, & hdc

// IntPtr, int, <int> (hdr.code)
if (System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, IntPtr.Size + 4) == NM_CUSTOMDRAW)
{
// IntPtr, int, int, <int> (dwDrawStage)
if (System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, IntPtr.Size + 8) == CDDS_PREPAINT)
{
// IntPtr, int, int, int, <IntPtr> (hdc)
var hdc = System.Runtime.InteropServices.Marshal.ReadIntPtr(m.LParam, IntPtr.Size + 12);
using (var graphics = Graphics.FromHdc(hdc))
{
// get our positioning right
graphics.TranslateTransform(-this.Left, -this.Top);

// tell our parent to paint over us
var ea = new PaintEventArgs(graphics, this.Bounds);
ToolStripManager.Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(graphics, (ToolStrip)this.Parent.Parent));

// paint our background
using (var brush = new SolidBrush(base.BackColor))
{
graphics.FillRectangle(brush, this.Bounds);
}

// reset our positioning back to default (just in case)
graphics.ResetTransform();
}

// now tell the trackbar that we're done with the custom stuff
m.Result = (IntPtr)CDRF_DODEFAULT;
}
}
}
else // m.Msg == WM_NOTIFY + WM_REFLECT
{
// default processing
base.WndProc(ref m);
}
}
}
else // _hasTransparentBackground
{
// default processing
base.WndProc(ref m);
}
}
}

public ToolStripTrackBarControl()
{
this.AutoSize = false;
this.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.Margin = new System.Windows.Forms.Padding(0);
this.TabStop = false;

TrackBar = new TransparentTrackBar();
TrackBar.AutoSize = false;
TrackBar.BackColor = Color.Transparent;
TrackBar.Dock = DockStyle.Fill;
TrackBar.Margin = new System.Windows.Forms.Padding(0);
TrackBar.TabStop = false;
TrackBar.TickStyle = TickStyle.None;
Controls.Add(TrackBar);

// other controls go here
}

internal TrackBar TrackBar { get; private set; }
}

ToolStripTrackBarControl _control;

public ToolStripTrackBar()
: base(CreateControl())
{
this.AutoSize = false;
this.AutoToolTip = false;
this.Size = DefaultSize;

_control = Control as ToolStripTrackBarControl;
}

static Control CreateControl()
{
return new ToolStripTrackBarControl();
}

protected override Size DefaultSize
{
get
{
return new Size(150, 22);
}
}

public int Maximum
{
get { return _control.TrackBar.Maximum; }
set { _control.TrackBar.Maximum = value; }
}

public int Minimum
{
get { return _control.TrackBar.Minimum; }
set { _control.TrackBar.Minimum = value; }
}

public override Size GetPreferredSize(Size constrainingSize)
{
var preferredSize = base.GetPreferredSize(constrainingSize);
preferredSize.Width = Math.Max(preferredSize.Width, 150);
if (preferredSize.Height == 0)
{
if (this.Owner != null)
{
preferredSize.Height = Math.Max(this.Owner.Height - 3, 10);
}
else
{
preferredSize.Height = 22;
}
}
return preferredSize;
}
}

Aucun commentaire:

Enregistrer un commentaire