I'm new to C#. I'd like to create an invisible button, but they are click-able in C# windows form application. Is there a way? I tried BackColor to Transparent, but that does not change the fact that it is transparent
Its simple try this.
Click the button that you want to make transparent.
Select FlatStyle from Properties and set it to popup
Now change the BackColor property to Transparent.
This will make the button transparent.
However if you want to make it transparent over a PictureBox this method wont work..
It works only on normal backgrounds and background images.
Hope it works....
buttonLink.FlatStyle = FlatStyle.Flat;
buttonLink.BackColor = Color.Transparent;
buttonLink.FlatAppearance.MouseDownBackColor = Color.Transparent;
buttonLink.FlatAppearance.MouseOverBackColor = Color.Transparent;
The answers given just make the background color of the control you want to make transparent the same as the background color of its parent. It's not true transparency and windows forms doesn't support true transparency.
Windows Forms controls do not support true transparency. The background of a
transparent Windows Forms control is painted by its parent.
https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-give-your-control-a-transparent-background?view=netframeworkdesktop-4.8&redirectedfrom=MSDN
Reference:
Original article and code can be found at:
Displaying a ToolTip when the Mouse Hovers Over a Disabled Control
# CodeProject by tetsushmz
Code:
public class TransparentSheet : ContainerControl
{
public TransparentSheet()
{
// Disable painting the background.
this.SetStyle(ControlStyles.Opaque, true);
this.UpdateStyles();
// Make sure to set the AutoScaleMode property to None
// so that the location and size property don't automatically change
// when placed in a form that has different font than this.
this.AutoScaleMode = AutoScaleMode.None;
// Tab stop on a transparent sheet makes no sense.
this.TabStop = false;
}
private const short WS_EX_TRANSPARENT = 0x20;
protected override CreateParams CreateParams
{
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
get
{
CreateParams l_cp;
l_cp = base.CreateParams;
l_cp.ExStyle = (l_cp.ExStyle | WS_EX_TRANSPARENT);
return l_cp;
}
}
}
Explanation:
What you need to do is use the given control as an overlay on your disabled TextBox (that you mentioned in one of your comments). Sibscribe to the overlay control's Click event and you have yourself a click on a disabled control.
I strongly recommend against this approach and feel it is kind of a hack. You really should look for an alternative approach instead of having to use a disabled control with an overlay control on top of it.
Maybe a different UI or atleast wrap it up in a UserControl to isolate this messy logic.
Setting the background property of the button to transparent will still leave a border. If you want a completely transparent button, do one of 2 things:
Create a transparent panel and assign a method to the Click event
or preferably
Create a custom UserControl that is filled with only BackColor (set to transparent) and assign method to Click event.
public class Invisible_Button : UserControl
{
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
this.Cursor = Cursors.Hand;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), 0, 0, this.Width, this.Height);
}
}
Did you try button.Visible = false?
If all you want is to hide it, this will do the job.
Related
how can I make my richtext box transparent
I want this cuz I am trying to put a text on top of a graphic image (which is the background of my form).
That is why I wanted the richTextBox to be transparent,
I am using .NET ,c# and in a windows form application
I know this answer is very late, but I hope it helps others who would like an easy way to get this done.
First, create a new User Control in your project and give it a name, say CustomRTB.cs. Once done, open the partial class and change:
public partial class CustomRTB : UserControl
to:
public partial class CustomRTB : RichTextBox
This will cause an error when you open the Design file so just go to the Designer.cs file and remove/comment the lines which show errors (there will be no more than two lines with errors). Next, add the following to the partial class:
protected override CreateParams CreateParams
{
get
{
//This makes the control's background transparent
CreateParams CP = base.CreateParams;
CP.ExStyle |= 0x20;
return CP;
}
}
The class should look like this now:
public partial class CustomRTB : RichTextBox
{
public CustomRTB()
{
InitializeComponent();
}
protected override CreateParams CreateParams
{
get
{
//This makes the control's background transparent
CreateParams CP = base.CreateParams;
CP.ExStyle |= 0x20;
return CP;
}
}
}
Now build your solution and you will be able to use the control in your forms. This control will be completely transparent and you will not be able to adjust the transparency. You will also be able to create different transparent controls apart from a richtextbox by changing the first line in this code. Hope this helps :)
Edit:
The problem with the above control is that it can only be used to display text programmatically as it is problematic to edit while running or debugging the application (as #nevelis explains in the comment below). However, there is a simple workaround for this:
First, create another User Control in your project and name it TranslucentPanel.cs (Yes, it is a panel and it is going to be translucent whose opacity can be controlled programmatically). Now open the partial class and modify it as:
public partial class TranslucentPanel : Panel
{
public TranslucentPanel()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
}
}
You will have to take care of the error that crops up when you build the project by simply commenting out the line in the Designer.cs file which throws it. Once done, build your project again and the translucent panel will appear in your toolbox as before. Use this panel as a parent control to your transparent richtextbox i.e. place the panel on your form and place the RTB inside it. You can also set the BorderStyle property as None to remove any trace of the RTB from the UI.
You can also control the opacity of the translucent panel by using its BackColor property in your program:
translucentPanel1.BackColor = Color.FromArgb(50, 0, 0, 0);
Changing the arguments passed above will let you control the opacity and the colour of the panel.
This workaround will solve the cursor and scrolling problems of not only the transparent RTB, but also any other transparent control you create.
There is no such thing as true transparency in a WinForms Control. Transparent mode inherits the default background of its parent. The way I have worked around it in the past has been to use the OnPaint event and then use the Graphics.DrawString method to position the text where I want it.
Have you given this a try?
http://www.codeproject.com/KB/edit/AlphaBlendedTextControls.aspx?artkw=richTextBox%20to%20be%20transparent
There is no way to have Windows Forms controls with a transparent background. Many have tried it before and all have failed. Some came up with exotic hacks, but they all fail at some detail. Use WPF or HTML if you need more advanced rendering capabilities than the old Windows Forms can offer you.
I wanted to make my windows form transparent so removed the borders, controls and everything leaving only the forms box, then I tried to the BackColor and TransparencyKey to transparent but it didnt work out as BackColor would not accept transparent color. After searching around I found this at msdn:
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.TransparencyKey = BackColor;
Unhappyly it did not work either. I still get the grey or any other selected color background.
All I wanted to do is to have the windows form transparent so I could use a background image that would act as if it was my windows form.
I searched around here and saw many topics in regards opacity which is not what I am looking for and also saw some in regards this method I was trying but have not found an answer yet.
Hope anyone can light my path.
UPDATE:
image removed as problem is solved
The manner I have used before is to use a wild color (a color no one in their right mind would use) for the BackColor and then set the transparency key to that.
this.BackColor = Color.LimeGreen;
this.TransparencyKey = Color.LimeGreen;
A simple solution to get a transparent background in a windows form is to overwrite the OnPaintBackground method like this:
protected override void OnPaintBackground(PaintEventArgs e)
{
//empty implementation
}
(Notice that the base.OnpaintBackground(e) is removed from the function)
I've tried the solutions above (and also) many other solutions from other posts.
In my case, I did it with the following setup:
public partial class WaitingDialog : Form
{
public WaitingDialog()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
// Other stuff
}
protected override void OnPaintBackground(PaintEventArgs e) { /* Ignore */ }
}
As you can see, this is a mix of previously given answers.
Here was my solution:
In the constructors add these two lines:
this.BackColor = Color.LimeGreen;
this.TransparencyKey = Color.LimeGreen;
In your form, add this method:
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.LimeGreen, e.ClipRectangle);
}
Be warned, not only is this form fully transparent inside the frame, but you can also click through it. However, it might be cool to draw an image onto it and make the form able to be dragged everywhere to create a custom shaped form.
My solution was extremely close to Joel's (Not Etherton, just plain Joel):
public partial class WaitingDialog : Form
{
public WaitingDialog()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.TransparencyKey = Color.Transparent; // I had to add this to get it to work.
// Other stuff
}
protected override void OnPaintBackground(PaintEventArgs e) { /* Ignore */ }
}
What works for me is using a specific color instead of the real ability of .png to represent transparency.
So, what you can do is take your background image, and paint the transparent area with a specific color (Magenta always seemed appropriate to me...).
Set the image as the Form's BackgrounImage property, and set the color as the Form's TransparencyKey. No need for changes in the Control's style, and no need for BackColor.
I've tryed it right now and it worked for me...
I tried almost all of this. but still couldn't work.
Finally I found it was because of 24bitmap problems. If you tried some bitmap which less than 24bit. Most of those above methods should work.
I had drawn a splash screen (32bpp BGRA) with "transparent" background color in VS2013 and put a pictureBox in a form for display. For me a combination of above answers worked:
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = this.pictureBox1.BackColor;
this.TransparencyKey = this.pictureBox1.BackColor;
}
So make sure you use the same BackColor everywhere and set that color as the TransparencyKey.
In my application i am constantly moving from one control to another. I have created no. of user controls, but during navigation my controls gets flicker. it takes 1 or 2 sec to update. I tried to set this
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
but it didn't help... Each control has same background image with different controls.
So what is the solution for it..
Thanks.
It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a lot worse.
It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.
This is a pretty fundamental limitation of Windows Forms, it is stuck with the way Windows renders windows. Fixed by WPF btw, it doesn't use windows for child controls. What you'd want is double-buffering the entire form, including the child controls. That's possible, check my code in this thread for the solution. It has side-effects though, and doesn't actually increase painting speed. The code is simple, paste this in your form (not the user control):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
There are many things you can do to improve painting speed, to the point that the flicker isn't noticeable anymore. Start by tackling the BackgroundImage. They can be really expensive when the source image is large and needs to be shrunk to fit the control. Change the BackgroundImageLayout property to "Tile". If that gives a noticeable speed-up, go back to your painting program and resize the image to be a better match with the typical control size. Or write code in the UC's OnResize() method to create a properly sized copy of the image so that it doesn't have to be resized every time the control repaints. Use the Format32bppPArgb pixel format for that copy, it renders about 10 times faster than any other pixel format.
Next thing you can do is prevent the holes from being so noticeable and contrasting badly with the image. You can turn off the WS_CLIPCHILDREN style flag for the UC, the flag that prevents the UC from painting in the area where the child controls go. Paste this code in the UserControl's code:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
The child controls will now paint themselves on top of the background image. You might still see them painting themselves one by one, but the ugly intermediate white or black hole won't be visible.
Last but not least, reducing the number of child controls is always a good approach to solve slow painting problems. Override the UC's OnPaint() event and draw what is now shown in a child. Particular Label and PictureBox are very wasteful. Convenient for point and click but their light-weight alternative (drawing a string or an image) takes only a single line of code in your OnPaint() method.
This is a real issue, and the answer Hans Passant gave is great for saving the flicker. However, there are side effects as he mentioned, and they can be ugly (UI ugly). As stated, "You can turn off the WS_CLIPCHILDREN style flag for the UC", but that only turns it off for a UC. The components on the main form still have issues.
Example, a panel scroll bar doesn't paint, because it is technically in the child area. However the child component doesn't draw the scroll bar, so it doesn't get painted until mouse over (or another event triggers it).
Also, animated icons (changing icons in a wait loop) doesn't work. Removing icons on a tabPage.ImageKey doesn't resize/repaint the other tabPages appropriately.
So I was looking for a way to turn off the WS_CLIPCHILDREN on initial painting so my Form will load nicely painted, or better yet only turn it on while resizing my form with a lot of components.
The trick is to get the application to call CreateParams with the desired WS_EX_COMPOSITED/WS_CLIPCHILDREN style. I found a hack here (https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) and it works great. Thanks AngryHacker!
I put the TurnOnFormLevelDoubleBuffering() call in the form ResizeBegin event and TurnOffFormLevelDoubleBuffering() call in the form ResizeEnd event (or just leave it WS_CLIPCHILDREN after it is initially painted properly.)
int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;
protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;
CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;
return cp;
}
}
public void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}
If you are doing any custom painting in the control (i.e. overriding OnPaint) you can try the double buffering yourself.
Image image;
protected override OnPaint(...) {
if (image == null || needRepaint) {
image = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(image)) {
// do any painting in image instead of control
}
needRepaint = false;
}
e.Graphics.DrawImage(image, 0, 0);
}
And invalidate your control with a property NeedRepaint
Otherwise the above answer with SuspendLayout and ResumeLayout is probably what you want.
Put the code bellow in your constructor or OnLoad event and if you're using some sort of custom user control that having sub controls, you'll need to make sure that these custom controls are also double buffered (even though in MS documentation they say it's set to true by default).
If you're making a custom control, you might want to add this flag into your ctor:
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Optionally you can use this code in your Form/Control:
foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}
We iterating through all the controls in the form/control and accessing their DoubleBuffered property and then we change it to true in order to make each control on the form double buffered. The reason we do reflection here, is because imagine you have a control that has child controls that are not accessible, that way, even if they're private controls, we'll still change their property to true.
More information about double buffering technique can be found here.
There is another property I usually override to sort this problem:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
return parms;
}
}
WS_EX_COMPOSITED - Paints all descendants of a window in bottom-to-top painting order using double-buffering.
You can find more of these style flags here.
Hope that helps!
Try BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayout methods.
See following
How to fix nested winform control flicker issues
Flickering during updates to Controls in WinForms (e.g. DataGridView)
Just to add to the answer Hans gave:
(TLDR version: Transparency is heavier than you think, use only solid colors everywhere)
If WS_EX_COMPOSITED, DoubleBuffered and WS_CLIPCHILDREN did not solve your flicker (for me WS_CLIPCHILDREN made it even worse), try this: go through ALL your controls and all your code, and wherever you have Any transparency or semi-transparency for BackColor, ForeColor, or any other color, just remove it, use only solid colors. In most of the cases where you think you just have to use transparency, you don't. Re-design your code and controls, and use solid colors.
I had terrible, terrible flickering and the program was running sluggish. Once I removed transparency it sped up significantly, and there is 0 flicker.
EDIT: To add further, I just discovered that WS_EX_COMPOSITED doesn't have to be window-wide, it could be applied just to specific controls! This saved me a lot of trouble. Just make a custom control inherited from whatever control you need, and paste the already posted override for WS_EX_COMPOSITED. This way you get low-level double-buffer on this control only, avoiding the nasty side-effects in the rest of the application!
On the main form or user control where background image resides set the BackgroundImageLayout property to Center or Stretch. You will notice a big difference when the user control is rendering.
I tried to add this as a comment but I don't have enough points. This is the only thing that's ever helped my flickering problems so many thanks to Hans for his post. For anyone that's using c++ builder like myself here's the translation
Add the CreateParams declaration to your application's main form .h file e.g.
class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}
and add this to your .cpp file
void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}
I know this question is very old, but want to give my experience on it.
I had a lot of problems with Tabcontrol flickering in a form with overrided OnPaint and/or OnPaintBackGround in Windows 8 using .NET 4.0.
The only think that worked has been NOT USE the Graphics.DrawImage method in OnPaint overrides, in other words, when draw was done directly to the Graphics provided by the PaintEventArgs, even painting all the rectangle, the flickering dissapeared. But if call the DrawImage method, even drawing a clipped Bitmap, (created for double buffering) the flicker appears.
Hope it helps!
I combined this flicker fix and this font fix, then I had to add a bit of my own code to start a timer on paint to Invalidate the TabControl when it goes offscreen and back, etc..
All three make this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_PAINT = 0x0f;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private System.Drawing.Bitmap buffer;
private Timer timer = new Timer();
public TabControlEx()
{
timer.Interval = 1;
timer.Tick += timer_Tick;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
void timer_Tick(object sender, EventArgs e)
{
this.Invalidate();
this.Update();
timer.Stop();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT) timer.Start();
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs pevent)
{
this.SetStyle(ControlStyles.UserPaint, false);
base.OnPaint(pevent);
System.Drawing.Rectangle o = pevent.ClipRectangle;
System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
if (o.Width > 0 && o.Height > 0)
DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
buffer = new System.Drawing.Bitmap(Width, Height);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
}
I'm not the creator but from what I understand the bitmap does all the bug bypassing.
This was the only thing that definitively solved TabControl (with Icons) flicker for me.
difference result video: vanilla tabcontrol vs tabcontrolex
http://gfycat.com/FineGlitteringDeermouse
ps. you will need to set HotTrack = true, because this fixes that bug too
Did you try Control.DoubleBuffered Property?
Gets or sets a value indicating whether this control should redraw its surface using a secondary buffer to reduce or prevent flicker.
Also this and this might help.
There is no need of any Double buffering and all that stuff guys...
A Simple solution...
If you are using MDI Interface, just paste the code below in the main form. It will remove all flickering from the pages. However some pages which require more time for loading will showup in 1 or 2 secs. But this is better than showing a flickering page in which each item comes one by one.
This is the only best solution for whole application. See the code to put in the main form:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
I want to move the form title, icon and close, and help buttons from left side to right side (change the layout).
I moved the form controls manually to keep background image but now I want to change the form title.
When I set rightToLeft property to yes and rightToLeftLayout to true in the form properties the background image disappears, but it uses the property "BackColor"
My code is as follows:
if (_lang == 'Arabic')
{
this.RightToLeft = RightToLeft.Yes;
this.RightToLeftLayout = true;
}
But it keeps buttons image.
So why is that?
To further Blounty's answer, the MSDN specs clearly state that BackgroundImage, Opacity and others aren't supported when using RightToLeftLayout:
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.righttoleftlayout(vs.80).aspx:
Owner draw is not supported when RightToLeftLayout is set to Yes. The owner draw events will still occur, but the behavior of any code you author in these events is not defined. Additionally, BackgroundImage, Opacity, TransparencyKey, and the painting events are not supported.
BackgroundImage, Opacity, TransparencyKey, and the painting events are not supported when RightToLeftLayout is set to yes.
It is pretty easy to replace the lost functionality:
protected override void OnPaintBackground(PaintEventArgs e) {
Rectangle rc = new Rectangle(Point.Empty, this.ClientSize);
e.Graphics.DrawImage(Properties.Resources.SampleImage, rc);
}
You'll need to do a bit more work if you need to tile the image.
I have a couple of buttons of which I modified how they look. I have set them as flat buttons with a background and a custom border so they look all pretty and nothing like normal buttons anymore (actually, they look like Office 2003 buttons now ;-). The buttons have a border of one pixel.
However when the button gets selected (gets the focus through either a click or a keyboard action like pressing the tab key) the button suddenly gets and extra border around it of the same colour, so making it a two pixel border. Moreover when I disable the one pixel border, the button does not get a one pixel border on focus.
On the net this question is asked a lot like 'How can I disable focus on a Button', but that's not what I want: the focus should still exist, just not display in the way it does now.
Any suggestions? :-)
Is this the effect you are looking for?
public class NoFocusCueButton : Button
{
protected override bool ShowFocusCues
{
get
{
return false;
}
}
}
You can use this custom button class just like a regular button, but it won't give you an extra rectangle on focus.
I had the same issue with the annoying double border, and stumbled across this thread looking for an answer...
The way I solved this was to set the BorderSize to 0 then draw my own border in OnPaint
Note: Not the entire button, just the border
A simple example would be:
public class CustomButton : Button
{
public CustomButton()
: base()
{
// Prevent the button from drawing its own border
FlatAppearance.BorderSize = 0;
FlatStyle = System.Windows.Forms.FlatStyle.Flat;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw Border using color specified in Flat Appearance
Pen pen = new Pen(FlatAppearance.BorderColor, 1);
Rectangle rectangle = new Rectangle(0, 0, Size.Width - 1, Size.Height - 1);
e.Graphics.DrawRectangle(pen, rectangle);
pen.Dispose();
}
}
In my case, this is how I made a button that mimics a ToolStripButton, where the border is only visible when you hover over the button:
public class ToolButton : Button
{
private bool ShowBorder { get; set; }
public ToolButton()
: base()
{
// Prevent the button from drawing its own border
FlatAppearance.BorderSize = 0;
// Set up a blue border and back colors for the button
FlatAppearance.BorderColor = Color.FromArgb(51, 153, 255);
FlatAppearance.CheckedBackColor = Color.FromArgb(153, 204, 255);
FlatAppearance.MouseDownBackColor = Color.FromArgb(153, 204, 255);
FlatAppearance.MouseOverBackColor = Color.FromArgb(194, 224, 255);
FlatStyle = System.Windows.Forms.FlatStyle.Flat;
// Set the size for the button to be the same as a ToolStripButton
Size = new System.Drawing.Size(23, 22);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
// Show the border when you hover over the button
ShowBorder = true;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
// Hide the border when you leave the button
ShowBorder = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// The DesignMode check here causes the border to always draw in the Designer
// This makes it easier to place your button
if (DesignMode || ShowBorder)
{
Pen pen = new Pen(FlatAppearance.BorderColor, 1);
Rectangle rectangle = new Rectangle(0, 0, Size.Width - 1, Size.Height - 1);
e.Graphics.DrawRectangle(pen, rectangle);
pen.Dispose();
}
}
// Prevent Text from being set on the button (since it will be an icon)
[Browsable(false)]
public override string Text { get { return ""; } set { base.Text = ""; } }
[Browsable(false)]
public override ContentAlignment TextAlign { get { return base.TextAlign; } set { base.TextAlign = value; } }
}
Make a custom button:
public partial class CustomButton: Button
{
public ButtonPageButton()
{
InitializeComponent();
this.SetStyle(ControlStyles.Selectable, false);
}
}
That'll get rid of that annoying border! ;-)
Another option (although a bit hacktastic) is to attach an event-handler to the button's GotFocus event. In that event-handler, pass a value of False to the button's NotifyDefault() method. So, for instance:
void myButton_GotFocus(object sender, EventArgs e)
{
myButton.NotifyDefault(false);
}
I'm assuming this will work every time, but I haven't tested it extensively. It's working for me for now, so I'm satisfied with that.
There is another way which works well for flat styled buttons. Don't use buttons but labels. As you are completely replacing the UI for the button it does not matter whether your use a button control or a label. Just handle the click in the same way.
This worked for me, although not great practice it is a good hack and as long as you name the button obviously (and comment the source) other coders will pick up the idea.
Ryan
The second border which gets added is the Windows standard "default button" border. You may have noticed that if you tab through most dialog boxes with multiple buttons (such as any Control Panel properties window), the original "double-bordered" button becomes "normal," and the in-focus button becomes "double-bordered."
This isn't necessarily focus at work, but rather a visual indication of the action undertaken by hitting the Enter key.
It sounds, to me, like you don't really care about that internal working. You want the display to not have two borders -- totally understandable. The internal working is to explain why you're seeing this behavior. Now ... To try and fix it.
The first thing I'd try -- and bear in mind, I haven't validated this -- is a hack. When a button receives focus (thereby getting the double-border), turn off your single border. You might get the effect you want, and it's pretty simple. (Hook into the Focus event. Even better, subclass Button and override OnFocus, then use that subclass for your future buttons.)
However, that might introduce new, awkward visual side effects. In that vein -- and because hacks are rarely the best answer -- I have to "officially" recommend what others have said: Custom paint the button. Although the code here may be overkill, this link at CodeProject discusses how to do that (VB link; you'll need translate). You should, in a full-on custom mode, be able to get rid of that second border completely.
Certainly you can draw the button yourself. One of the state flags is focused.
So on the draw event if the flag is focused go ahead and draw the button how you like, otherwise just pass it on to the base method.
Consider implementing your own drawing code for the button. That way you have full control. In the past, I've implemented my own Control derivative that custom paints my button and implements all the button characteristics for my purposes, but you should be able to override the button's painting and do it yourself, thereby controlling how it draws in every state, including when focused.
Set the FocusVisualStyle dependency property to null in your style, and the dotted border will be gone.
From MSDN: Styling for Focus in Controls, and FocusVisualStyle
Windows Presentation Foundation (WPF)
provides two parallel mechanisms for
changing the visual appearance of a
control when it receives keyboard
focus. The first mechanism is to use
property setters for properties such
as IsKeyboardFocused within the style
or template that is applied to the
control. The second mechanism is to
provide a separate style as the value
of the FocusVisualStyle property; the
"focus visual style" creates a
separate visual tree for an adorner
that draws on top of the control,
rather than changing the visual tree
of the control or other UI element by
replacing it. This topic discusses the
scenarios where each of these
mechanisms is appropriate.
The extra border you see is defined by the FocusVisualStyle and not in the control template, so you need to remove or override the style to remove the border.
If you have a textbox and a button
then on textchange event of textbox
write button1.focus();
It will work.
You can also create an invisible button and make it active whenever you press another button.
I've had good luck merely setting the Focusable property of the button to be false:
<Button HorizontalAlignment="Left" Margin="0,2"
Command="{Binding OpenSuspendedJobCommand, Mode=OneWay}"
Focusable="False"
Style="{StaticResource ActionButton}" Content="Open Job..." />