double buffer customer drawn list box - c#

I have a custom draw method so I can make items in a list box different colours. The problem is that I redraw the listbox every 500ms to check if the values have changed. This makes the listbox flicker and I am not sure how to double buffer the code. Can anyone help please?
private void listBox_DrawItem(object sender, DrawItemEventArgs e)
{
ListBox sendingListBox = (ListBox)sender;
CustomListBoxItem item = sendingListBox.Items[e.Index] as CustomListBoxItem; // Get the current item and cast it to MyListBoxItem
if (item != null)
{
e.Graphics.DrawString( // Draw the appropriate text in the ListBox
item.Message, // The message linked to the item
zone1ListBox.Font, // Take the font from the listbox
new SolidBrush(item.ItemColor), // Set the color
0, // X pixel coordinate
e.Index * zone1ListBox.ItemHeight // Y pixel coordinate. Multiply the index by the ItemHeight defined in the listbox.
);
}
else
{
// The item isn't a MyListBoxItem, do something about it
}
}

If you need to enable the double buffer of the ListBox you should inherit a class from it, because the property is private and set it to true, or use the SetStyle() method and apply the WS_EX_COMPOSITED flag:
public class DoubleBufferedListBox : ListBox {
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}
public DoubleBufferedListBox( ) {
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}

Related

Removing the button's background?

Based on my previous question (Making the panel transparent?), I wished to create a button with transparent background around it:
internal class TransparentButton : Button
{
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public TransparentButton()
{
SetStyle(ControlStyles.Opaque | ControlStyles.SupportsTransparentBackColor, true);
}
}
Now, when I add this button to my form:
I wish it to have transparent background, i.e. to not draw the gray border on top of the black background.
Can I do it with my TransparentButton class, without major changes (like drawing the text and background, and border manually)?
This is my desired goal to reach, so the button itself shall stay as is, but the rectangular background with the color of "Control" shall be transparent:
This edited TransparentButton improves on my previous answer and no longer overrides OnPaint. It requires no custom drawing. Instead, it uses Graphics.CopyFromScreen to make a screenshot of the rectangle behind the button and sets its own Button.BackgroundImage to the snipped bitmap. This way it's effectively camouflaged and appears transparent while still drawing as a Standard styled button.
Requirements:
Create a button with transparent background.
Do it without drawing the background manually in OnPaint.
Keep the Button.FlatStyle as FlatStyle.Standard.
Do not disturb the rounded edges of the standard button.
class TransparentButton : Button
{
public TransparentButton() => Paint += (sender, e) =>
{
// Detect size/location changes
if ((Location != _prevLocation) || (Size != _prevSize))
{
Refresh();
}
_prevLocation = Location;
_prevSize = Size;
};
Point _prevLocation = new Point(int.MaxValue, int.MaxValue);
Size _prevSize = new Size(int.MaxValue, int.MaxValue);
public new void Refresh()
{
if (!DesignMode)
{
bool isInitial = false;
if ((BackgroundImage == null) || !BackgroundImage.Size.Equals(Size))
{
isInitial = true;
BackgroundImage = new Bitmap(Width, Height);
}
if (MouseButtons.Equals(MouseButtons.None))
{
// Hide button, take screenshot, show button again
Visible = false;
BeginInvoke(async () =>
{
Parent?.Refresh();
if (isInitial) await Task.Delay(250);
using (var graphics = Graphics.FromImage(BackgroundImage))
{
graphics.CopyFromScreen(PointToScreen(new Point()), new Point(), Size);
}
Visible = true;
});
}
else
{
using (var graphics = Graphics.FromImage(BackgroundImage))
graphics.FillRectangle(Brushes.LightGray, graphics.ClipBounds);
}
}
else base.Refresh();
}
protected override void OnMouseUp(MouseEventArgs mevent)
{
base.OnMouseUp(mevent);
Refresh();
}
/// <summary>
/// Refresh after a watchdog time delay. For example
/// in response to parent control mouse moves.
/// </summary>
internal void RestartWDT(TimeSpan? timeSpan = null)
{
var captureCount = ++_wdtCount;
var delay = timeSpan ?? TimeSpan.FromMilliseconds(250);
Task.Delay(delay).GetAwaiter().OnCompleted(() =>
{
if (captureCount.Equals(_wdtCount))
{
Debug.WriteLine($"WDT {delay}");
Refresh();
}
});
}
int _wdtCount = 0;
}
Design Mode Example
Main form BackgroundImage to main form with a Stretch layout. Then overlay a TableLayoutPanel whose job it is to keep the button scaled correctly as the form resizes. TransparentButton is now placed in one of the cells.
Test
Here's the code I used to test the basic transparent button as shown over a background image:
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
buttonTransparent.ForeColor = Color.White;
buttonTransparent.Click += onClickTransparent;
}
private void onClickTransparent(object? sender, EventArgs e)
{
MessageBox.Show("Clicked!");
buttonTransparent.RestartWDT();
}
protected override CreateParams CreateParams
{
get
{
const int WS_EX_COMPOSITED = 0x02000000;
// https://stackoverflow.com/a/36352503/5438626
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
}
Revision 2.0 Demo
I've uploaded a demo to my GitHub repo that has a draggable, clickable transparent button over a RichTextBox control.

Move transparent image over another transparent image

I define SelecterControl user control (with transparent image in background) that contain a Roll control with transparent image.
I want to move front control over SelecterControl.
when do that, transparent area of SelecterControl don't clear.
My complete code is below:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
namespace TestControl
{
public partial class SelecterControl : UserControl
{
private Roll roll;
public SelecterControl()
{
InitializeComponent();
BackgroundImage = Image.FromStream(new MemoryStream(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAPsAAAAWCAYAAAAGqOr6AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgFDRwUWvPXdAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACxklEQVR42u3cv0sbYRwG8McgucnXpiBCThyCpUMqGa4RobhlKDjKBSLt0EU7tbQQmv4FjQj94aRZOhSSkiKdQqFNJxGkIUOKGQohFMmlBEHrOSVQ0yE5aYJW7jyVO54PhCwX3vDePfd+733vbgBn88G6fRCRWReSuYEzGlQABM7RaJ6BJzId9Mg5Al8FUDwpd4MnNXZj6Lby+uH7pPB7Fb3egvB7TbWm11uYXZ5Y5H4jMicXr+xZyZvwe6HXW9X7b2ZSe61fgW7gq/+Gvj/svsx8OSlkaWEsPITxaYFhWTL9hz8lqtxrRBbdeTRm+jcHWhM7W3rg3eON5PfPP/PPSxGjSjge5Qf7ziprwu9VrTRGRFdnWJYwOTeCybkRCL83kimUfbF0MNUNehEAPMbGG692njHoRO6oDMbCQ8qLUF5FZ97N1z+yJ2/Njdg90eBzWD9d46HS4ze7wJm6WY7gCfLdwOcHu6P6mpAlS9fn/6F0v500Gz/Dw6THBrvAuWW9kCVk5stqLB2sAvAZI7s6Pi1sbezpzbdqWJlShb0nELpc99gFzjU+LaBrTQXp3jLe9nI7rEzhbjJgd7VA5GoXuZLlATrrdAwlkftKeb3e6g07EbmfBwCE34sDrcneIHKRA61p3P3ac1PNvt3X7YXiNyABcIKO6Mrt5+KVzqpYKdtIlrKN9tGfI1s+uXilDWABzltnJ7pSuXjFthyWso12Zr78xcihBwBC0dGErjVZyhO5qITXtSZi6WDCKOOPJ+iELC1ur++yl4hcYHt9F7XC4RI6T76hJ+yh6GhKyFJqc6XGniJysM2VGvR660MsHVzCaY+4hqKji6Vso7i5UlsTsmT5EVdjUoDdTnR5ZfvOlo5a4RAAlmaXJxL925z6pppStpHVtaZ6jpdXXGfgiczJxSttqy+vAJD/uPo1+vLHA+aOiIjI9f4CBEpvEw5QVnUAAAAASUVORK5CYII=")));
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
protected override void OnPaint(PaintEventArgs e)
{
if (BackgroundImage != null)
e.Graphics.DrawImage(BackgroundImage, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
}
#region Roll Horizontal Movement
int strtX;
private void roll_MouseDown(object sender, MouseEventArgs e)
{
strtX = e.X;
}
private void roll_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
roll.Left += e.X - strtX;
}
#endregion
#region Designer generated code
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SelecterControl));
this.roll = new MapControl.Roll();
this.SuspendLayout();
//
// roll
//
this.roll.BackColor = System.Drawing.Color.Transparent;
//this.roll.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("roll.BackgroundImage")));
this.roll.Location = new System.Drawing.Point(87, 1);
this.roll.Name = "roll";
this.roll.Size = new System.Drawing.Size(13, 18);
this.roll.TabIndex = 0;
this.roll.MouseDown += new System.Windows.Forms.MouseEventHandler(this.roll_MouseDown);
this.roll.MouseMove += new System.Windows.Forms.MouseEventHandler(this.roll_MouseMove);
//
// SelecterControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
//this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.Controls.Add(this.roll);
this.Name = "SelecterControl";
this.Size = new System.Drawing.Size(251, 22);
this.ResumeLayout(false);
}
#endregion
}
class Roll : Control
{
public Roll()
{
BackgroundImage = Image.FromStream(new MemoryStream(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAA0AAAASCAYAAACAa1QyAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgFDR0RM4ISugAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAA5UlEQVQoz2NkQAWCUMyARMPAexjNAhORjTul5GEhd5cBC1jc4h/249nJ9zCNjAwMDAwaufeVbDU57zIwMLyfnCKKYkPunNcwjek/np28x8DAcI+JgYGBAVnDozd/4BoevfnDMDlFlIGBgYEhtmbjTJizmWAKYBrkROAuZpATYUHRCANM2PygU/iIQafwEQMuwMRABhjVRIkmFmyCV/rl8Ol5z4SUxt7DUgByMpITYYGnP1iChWkSYmBgEIRpRE5GMA3LJ2abQDW9Z4QpSJ32QpCBgeEdNvesnt+p/OF0/3ukPEU6AADyU1AuXNEg+gAAAABJRU5ErkJggg==")));
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
protected override void OnPaint(PaintEventArgs e)
{
if (BackgroundImage != null)
e.Graphics.DrawImage(BackgroundImage, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
}
}
}
What is the cause of this problem?
Thanks for any help.
this.roll.Size = new System.Drawing.Size(13, 18);
That's the core problem in your code, the "roll" control is too small. When you use the WS_EX_TRANSPARENT flag in the CreateParams property, you ask Windows to ensure that the window behind your control always paints first. Which sets the background pixels for your "roll" control so it can just paint its own stuff on top of them.
Trouble is, your roll control is so small that after you moved it, it isn't large enough to overlap the stray pixels that were drawn previously. They are no longer inside the roll control window. So they'll stay there, producing the smearing effect you see.
The only way to avoid this is to make your roll control bigger so it always catches those pixels. Realistically, as large as the range of the slider. Problem is, you now lose all benefit of making "roll" a control of its own. Things like hit testing get complicated, you need to know where the thumb is located to know that the user clicked it.
You might as well create one control that draws both the slider and the thumb. Required code is exactly the same. And it will be more efficient since you use fewer system resources, windows are expensive objects.

.NET Custom UserControl's contents are disappearing when the parent is resized

I have derived the .NET UserControl, to make my own PictureList class.
However, when I resize the parent of the control, the contents of the control disappear. No Resize event is issued or whatever. I left out code not relevant to the question.
public ImageList(int width, int height)
{
ClientSize = new Size(width, height);
ResizeRedraw = true;
}
// Ensure background transparency will be handled corretly.
protected override CreateParams CreateParams
{
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
MessageBox.Show("This messagebox is only shown upon the first drawing, but not after the magically disappearing of the contents");
img = /*the image to draw*/
rect = /*the size of the image*/
e.Graphics.DrawImage(img, rect);
}
Do you need to enable AutoRedraw?

Transparent Controls In VSTO

I'm trying to add some windows forms controls to a worksheet using vsto. I'd like them to be transparent though (so that the actual content in Excel is visible).
My winforms user control constructor looks like this:
public Tag()
{
InitializeComponent();
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
I'm adding the control like this:
void Application_WorkbookOpen(Excel.Workbook Wb)
{
var nativeSheet = Wb.ActiveSheet as Excel.Worksheet;
if (nativeSheet != null)
{
var tag = new Tag();
var vstoSheet = nativeSheet.GetVstoObject();
var range = nativeSheet.Range["A1", missing];
vstoSheet.Controls.AddControl(tag, range, Guid.NewGuid().ToString());
}
}
If there's some content cell A1, it will be covered by the control (the cell will just appear as plain white).
Anybody have any ideas on this?
As mentioned over at MSDN:
When a Windows form control are hosted on Office document, the control
is not directly embedded in the document. An ActiveX control called
the Windows Forms control host is added to document surface first and
the control host acts as a host for each Windows Form control added
too the document.
You can see for yourself:
var btn = new ImageButton();
btn.Name = "link1";
btn.Text = controlText;
btn.Click += new EventHandler(btn_Click);
vstoWorksheet.Controls.AddControl(pic, nativeWorksheet.Range[address], controlText);
public class ImageButton : Control, IButtonControl
{
public ImageButton()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.ResizeRedraw, true);
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs pevent)
{
Graphics g = pevent.Graphics;
g.DrawRectangle(Pens.Black, this.ClientRectangle);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// don't call the base class
//base.OnPaintBackground(pevent);
}
protected override CreateParams CreateParams
{
get
{
const int WS_EX_TRANSPARENT = 0x20;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
}
// rest of class here...
}

DataGridView draws wrong

I have a form and it has tones of other controls(buttons, custom controls, labels, panel,gridview). You can guess i had flickering issue. I tried doublebuffering and it couldn't solve. Finally i tried this one:
protected override CreateParams CreateParams
{
get
{
// Activate double buffering at the form level. All child controls will be double buffered as well.
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}
Flickering gone but my datagridview draws wrong. It shows CellBorders, BorderColors wrong. Actually this code has some issue with background images, lines, and other stuff. Why is that and how can it be fixed?
I know this question is a little old, but better late than never...
Here's a work-around to stop flickering when a user resizes a form, but without messing up the drawing of controls such as DataGridView. Provided your form name is "Form1":
int intOriginalExStyle = -1;
bool bEnableAntiFlicker = true;
public Form1()
{
ToggleAntiFlicker(false);
InitializeComponent();
this.ResizeBegin += new EventHandler(Form1_ResizeBegin);
this.ResizeEnd += new EventHandler(Form1_ResizeEnd);
}
protected override CreateParams CreateParams
{
get
{
if (intOriginalExStyle == -1)
{
intOriginalExStyle = base.CreateParams.ExStyle;
}
CreateParams cp = base.CreateParams;
if (bEnableAntiFlicker)
{
cp.ExStyle |= 0x02000000; //WS_EX_COMPOSITED
}
else
{
cp.ExStyle = intOriginalExStyle;
}
return cp;
}
}
private void Form1_ResizeBegin(object sender, EventArgs e)
{
ToggleAntiFlicker(true);
}
private void Form1_ResizeEnd(object sender, EventArgs e)
{
ToggleAntiFlicker(false);
}
private void ToggleAntiFlicker(bool Enable)
{
bEnableAntiFlicker = Enable;
//hacky, but works
this.MaximizeBox = true;
}
I found the trick to having nice smooth resizing and show my grid-lines, was to add an additional flag if my app is running under Windows XP or Windows Server 2003:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
if (this.IsXpOr2003 == true)
cp.ExStyle |= 0x00080000; // Turn on WS_EX_LAYERED
return cp;
}
}
private Boolean IsXpOr2003
{
get
{
OperatingSystem os = Environment.OSVersion;
Version vs = os.Version;
if (os.Platform == PlatformID.Win32NT)
if ((vs.Major == 5) && (vs.Minor != 0))
return true;
else
return false;
else
return false;
}
}

Categories