1

I created a flat Button with a transparent border, also setting FlatAppearance.BorderSize = 0.
The border is hidden on a mouse click and the Button background uses a custom Color when the Mouse button is pressed.

My problem is that cannot remove the border that is drawn when the Button becomes active, for example pressing the Tab key.
I can't use the TabStop property (set it to false) because I want the functionalities I've designed.

I just want to paint the background color and hide the border (the same as mouse click colors).

Picture with the issue

The Button properties in the Form Designer:

this.importBtn.BackgroundImage = global::CompetitionManager.Properties.Resources.Open;
this.importBtn.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.importBtn.Cursor = System.Windows.Forms.Cursors.Hand;
this.importBtn.Delta = 5;
this.importBtn.Dock = System.Windows.Forms.DockStyle.Fill;
this.importBtn.FlatAppearance.BorderSize = 0;
this.importBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.SteelBlue;
this.importBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Transparent;
this.importBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.importBtn.ForeColor = System.Drawing.Color.Transparent;
this.importBtn.Location = new System.Drawing.Point(3, 50);
this.importBtn.MoveXDirection = false;
this.importBtn.MoveYDirection = true;
this.importBtn.Name = "importBtn";
this.importBtn.Size = new System.Drawing.Size(183, 162);
this.importBtn.TabIndex = 0;
this.ToolTip.SetToolTip(this.importBtn, "Import Competitors (Excel/XML)");
this.importBtn.UseMargin = true;
this.importBtn.UseVisualStyleBackColor = true;
this.importBtn.Click += new System.EventHandler(this.ImportFile_Click);

 
Jimi
  • 29,621
  • 8
  • 43
  • 61
alex
  • 31
  • 6
  • Do you mean you're running synchronous code on a Button.Click handler and the Button's rendering remains *stuck* ? Try to `BeginInvoke()` that code (if you have it all in the event handler, move it to a dedicated method). – Jimi Oct 03 '20 at 00:05
  • But, since you handler is named `ImportFile`, are you maybe working with files on disk? If so, you have async methods you can call and so you can probably make your handler async. If you don't want any of this, try to just `Refresh()` your Button before calling any sync code. – Jimi Oct 03 '20 at 00:24
  • @Jimi I don't know what you are talking about. Forget the click action. I have 10 buttons with this design, Every click do a different operation but this is not related to the topic. When I press the tab button to update the active control, the focus is updated to a new button. Then, I see a black border around the button. This is not related to the click event – alex Oct 03 '20 at 09:23
  • @Jimi Look at the attached picture... – alex Oct 03 '20 at 09:26
  • Well, this: *How can I remove the border when I press a long click* doesn't really describe the simple focus rectangle you're seeing when the Button receives the focus. Since it's a custom class, just add `protected override bool ShowFocusCues => false;` and `public override void NotifyDefault(bool value) => base.NotifyDefault(false);`. If you're already overriding OnPaint, you can paint over it. – Jimi Oct 03 '20 at 10:01
  • @Jimi ShowFocusCues = false hide to the border but I still want to paint the background on focus because I can't know which button is focused. How can I do that? – alex Oct 03 '20 at 19:39

1 Answers1

4

As described in the question, the custom control - a Button, here - is showing its standard Focus Cues when it becomes the ActiveControl. The default rendering doesn't appears to fit in, because the Background color is rendered transparent in a specific context, causing the standard Focus Cue to become obnoxious.

▶ The standard Focus Cue rendering is disabled overriding Control.ShowFocusCues to always return false (except when the handle is not yet created).

▶ The NotifyDefault method is also overridden, to avoid a similar effect when the Button is used to open a Windows that becomes active: in this case, the Button is rendered with a border meant as a visual clue that it's the ActiveControl of that Window.

▶ Some properties that define the Button specialization are removed from the PropertyGrid using a custom ControlDesigner, to avoid unwanted tampering with specific defining properties.

Finally, a custom Focus Cue is drawn at the bottom of the Custom Control's ClientRectangle, to give some feedback, otherwise a User would have no clue what the current Button/Control is.
The custom cue is not shown when the Mouse is hovering or the Button is being clicked.

It's an example of a possible custom rendering. Of course you can now paint whatever you want: a different border, background, translucent overlay etc.

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;

[ToolboxItem(true)]
[DesignerCategory("code")]
[Designer(typeof(CustomDesigner))]
public class ImportButton : Button
{
    private Color m_FocusColor = Color.LightBlue;
    private bool m_DrawFocusCue = false;

    public ImportButton() {
        Cursor = Cursors.Hand;
        Image = new Bitmap(Properties.Resources.[SomeImage]);
        FlatAppearance.BorderSize = 0;
        FlatAppearance.MouseDownBackColor = Color.SteelBlue;
        FlatAppearance.MouseOverBackColor = Color.Transparent;
        FlatStyle = FlatStyle.Flat;
    }

    protected override bool ShowFocusCues {
        get {
            m_DrawFocusCue = !ClientRectangle.Contains(PointToClient(MousePosition));
            return !IsHandleCreated;
        }
    }

    public override void NotifyDefault(bool value) => base.NotifyDefault(false);

    // Make it public if this value should be customizable
    private int FocusBorderSize { get; set; } = 2;

    protected override void OnPaint(PaintEventArgs e) {
        base.OnPaint(e);
        
        if (Focused && m_DrawFocusCue) {
            var rect = ClientRectangle;
            rect.Inflate(-FocusBorderSize, -FocusBorderSize);
            using (var pen = new Pen(FlatAppearance.MouseDownBackColor, FocusBorderSize)) {
                e.Graphics.DrawLine(pen, 0, rect.Bottom, rect.Right, rect.Bottom);
            }
        }
    }

    protected override void Dispose(bool disposing) {
        if (disposing) {
            Image?.Dispose();
        }
        base.Dispose(disposing);
    }

    public class CustomDesigner : ControlDesigner
    {
        private static string[] RemovedProperties = new[] {
            "AutoEllipsis", "AutoSize", "AutoSizeMode",
            "BackColor", "Cursor", "FlatAppearance", "FlatStyle",
            "ForeColor", "Text", "TextAlign", "TextImageRelation"
        };

        public CustomDesigner() { }

        protected override void PreFilterProperties(IDictionary properties) {
            foreach (string prop in RemovedProperties) {
                properties.Remove(prop);
            }
            base.PreFilterProperties(properties);
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Sorry but I didn't understand, what part of the code I need to paste to remove the border when the tab button is pressed? – alex Oct 17 '20 at 17:01
  • This is a complete Custom Control. Just add a new class object to your Project, name the class file `ImportButton`. When Visual Studio presents you the new class object, paste everything you find here inside it. Build the Project. Now you'll find your new Custom Control in the ToolBox, as any other control (just on top of the List). Drop it on a Form and you're good to go. – Jimi Oct 17 '20 at 17:50