Removing the button's background? - c#

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.

Related

how to make touchscreen-like mainform C#

I want to make my mainform draggable like smartphone touchscreen.
So i put up a bunifugradientpanel in it and dock it in mainform. also put a bunifudragcontrol and set targetcontrol property = 'bunifugradientpanel' and vertical property to 'false' also fixed property to 'false'. however, whenever i drag my panel to the right in rumtime, the portion of the mainform is showing which is the white part in the picture.
The white part of the screen is the mainform. what i want is to stop the dragging activity if bunifugradientpanel location on mainform is = (x=0,y=0) so the the portion of the mainform wont appear. thanks for your help guys.
BunifuGradientPanel is a Third Party control and that's why it can present drawing and flickering problems, my recommendation is that you use a common Panel System.Windows.Forms.Panel
That said, I created this code for you, makes a control draggable only from right to left and from left to right while the mouse button is pressed:
using System;
using System.Windows.Forms;
namespace JeremyHelp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,
true);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.SetDraggable(this.bunifuGradientPanel1, vertical: false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return handleParam;
}
}
private void SetDraggable(Control target, bool horizontal = true, bool vertical = true)
{
bool IsDraggable = false;
int
X = 0, Y = 0,
A = 0, B = 0;
target.MouseUp += (s, e) =>
{
IsDraggable = false;
X = 0; Y = 0;
A = 0; B = 0;
};
target.MouseDown += (s, e) =>
{
IsDraggable = true;
A = Control.MousePosition.X - target.Left;
B = Control.MousePosition.Y - target.Top;
};
target.MouseMove += (s, e) =>
{
X = Control.MousePosition.X;
Y = Control.MousePosition.Y;
if (IsDraggable)
{
if (horizontal) target.Left = X - A;
if (vertical) target.Top = Y - B;
}
};
}
}
}

double buffer customer drawn list box

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);
}
}

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...
}

Categories