Set image as background for Button as default, hover and click

leorob88

Well-known member
Joined
Dec 31, 2020
Messages
78
Programming Experience
1-3
I have a file watcher which basically detects some edits on stuff. When that happens, this method is called. This basically hides for a brief moment the form (and the button that has its same size) so it can capture the screen beneath and make the button's color as default, hover and click, taking the dominant color of the captured area. That, in practical terms, makes the button "almost transparent". More about the matter down below.


C#:
Expand Collapse Copy
        private void colorupdate()
        {
            this.Invoke((MethodInvoker)delegate
            {
                this.Visible = false;
            });
            Dictionary<Color, int[]> colori = new Dictionary<Color, int[]>();
            using (Bitmap pixel = new Bitmap(this.Width, this.Height))
            {
                using (Graphics captureGraphics = Graphics.FromImage(pixel))
                {
                    captureGraphics.CopyFromScreen(new Point(this.Location.X, this.Location.Y), Point.Empty, new Size(this.Width, this.Height));
                }
                for (int x = 0; x < this.Width; x++)
                {
                    for (int y = 0; y < this.Height; y++)
                    {
                        Color colore = pixel.GetPixel(x, y);
                        if (!colori.ContainsKey(colore)) { colori.Add(colore, new int[] { 1, x, y }); }
                        else { colori[colore][0]++; }
                    }
                }
            }
            int dss = colori.OrderByDescending(set => set.Value[0]).First().Value[0];
            int[] ds = colori.OrderByDescending(set => set.Value[0]).First().Value;
            Color color = colori.OrderByDescending(set => set.Value[0]).First().Key;
            this.Invoke((MethodInvoker)delegate
            {
                button1.BackColor = color;
                button1.FlatAppearance.MouseOverBackColor = color;
                button1.FlatAppearance.MouseDownBackColor = color;
                button1.FlatStyle = FlatStyle.Flat;
                button1.FlatAppearance.BorderSize = 0;
                this.Visible = true;
            });
        }

I wish to make the button actually transparent but I cannot because using Color.Transparent makes the button only clickable if the user clicks exactly where the click registers any color that is NOT transparent- i.e. if you click on a transparent area of the button, nothing happens. So, I wish to capture instead the area beneaht the button and the form and use it to set the button's "look" (lke, background image). In particular, i would need to set it as look even when clicked and hovered, because i don't want its default behavior change its appearance when interacting with the mouse (it's only supposed to be interacted with the mouse). Do you know perhaps a simple way to use the screenshot (in this code is already there, it's basically "pixel") as a perennial look and not get overwritten by mouse behavior?
 
If you don't want the button to act like a button anymore, why not just use a panel or a custom control? I believe that you can still get Click events from those controls. That way you can simply put a screenshot of what was under the panel into the panel's client area, and you don't need to contend any of the hover over, or mouse down, mouse up painting behaviors. Why fight with the default windows UI behavior?

<Rant> As an aside, as much as I appreciate you wanting a particular kind of behavior, this behavior that you are making doesn't comply with the standard Windows UI guidelines. This is why people complain that apps on Macs have a "unified" look and feel while Windows is "fragmented" -- nobody follows the Windows UI guidelines. </Rant>
 
If you don't want the button to act like a button anymore, why not just use a panel or a custom control? I believe that you can still get Click events from those controls. That way you can simply put a screenshot of what was under the panel into the panel's client area, and you don't need to contend any of the hover over, or mouse down, mouse up painting behaviors. Why fight with the default windows UI behavior?

<Rant> As an aside, as much as I appreciate you wanting a particular kind of behavior, this behavior that you are making doesn't comply with the standard Windows UI guidelines. This is why people complain that apps on Macs have a "unified" look and feel while Windows is "fragmented" -- nobody follows the Windows UI guidelines. </Rant>

don't worry, i appreciate your advice and opinion.

i went for button because it was the most obvious to interact with. i also tried to use label but it won't react to the click event...

even so, i'm afraid to discuss this further (because off topic?)... because in the end i realized that truly my logic (that is, my app) has an issue and i'm wandering around the same basic problem trying to avoid it: i really actually need the control NOT to take an image BUT to be transparent.

there's no way to avoid that, because my app stay in foreground, it's meant to be. so, whenever i move something beneath its surface it may generate a weird look. take example: white desktop, so white button if i copy the background "image", but i have also a random app window being dark/blackish, if my app stays front and i move the blackish app beneath it, what i get is the button is actually not transparent but instead a white box covering the black window. so yeah, i actually really need it to be transparent if i want its look to be consistent, not colored or imaged. but still, i need to interact when i click it.

so basically, yes, color.transparent could be an idea, but the real issue i tried to avoid is the fact the control MUST interact with the mouse even on the transparent areas. but i don't know if i'm looking for an impossible behavior or not.... i know sometimes windows UI has some limits and it may be tedious or complex in some case to find a workaround....
 
This seems to work form me. No taking a snapshot of the background. Just pure transparency used for the form and the panel.

1759362972510.png


C#:
Expand Collapse Copy
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Configuration;

namespace WinFormsNetCore
{
    class MainForm : Form
    {
        Panel _panel;

        MainForm()
        {
            Text = "WinForms .NET Core";
            Width = 800;
            Height = 600;
            BackColor = Color.GreenYellow;
            TransparencyKey = Color.GreenYellow;

            _panel = new Panel
            {
                Dock = DockStyle.Fill,
                BackColor = Color.Transparent,
            };
            _panel.Click += (s, e) => MessageBox.Show("Panel clicked!");
            _panel.Paint += (s, e) =>
            {
                var g = e.Graphics;
                using var brush = new SolidBrush(Color.Blue);
                g.FillEllipse(brush, 100, 100, 200, 200);
            };
            Controls.Add(_panel);
        }

        [STAThread]
        static void Main()
        {
            var form = new MainForm();
            Application.Run(form);
        }
    }
}
 
*** removed unnecessary quote ***

Oh I see, i'll have to try this. I just tried a quick substitution in the code but some things aren't working as i need so i suppose i need first to understand for good what your code exactly does. it may be i need to adapt it to my app somehow. i have to go to bed now but the first doubt i have is why you color the panel blue if it needs to be transparent...
 
Last edited by a moderator:
I'm not coloring the panel blue. I'm just making a blue circle within the panel. It's to represent some other graphics -- likely the text -- that you would put in for the "button". You can click anywhere -- either in the blue circle, or outside the blue circle but still within the form -- e.g the transparent area. The click is detected regardless.
 
shortening your code and removing what i don't need... i don't need a panel, i need a control that can have text and a panel can't. so i tried instead a label, as i said.

set it transparent at form load
set a click event handler to react the click

but what i get is the label reacts the click only where the color is not transparent- i.e. only if i click over the text and so the color is not transparent in the click area.
 
Labels don't accept clicks as you've discovered earlier.

That's why I use a panel with will accept clicks anywhere. The reason I put in the paint code above was to show that you can draw anything you want in the panel: graphics, text, images, etc.
 
1759407858053.png


Here's the same code but changed to render text:
C#:
Expand Collapse Copy
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Configuration;

namespace WinFormsNetCore
{
    class MainForm : Form
    {
        readonly Panel _panel;

        MainForm()
        {
            Text = "WinForms .NET Core";
            Width = 800;
            Height = 600;
            BackColor = Color.GreenYellow;
            TransparencyKey = Color.GreenYellow;

            _panel = new Panel
            {
                Dock = DockStyle.Fill,
                BackColor = Color.Transparent,
                ForeColor = Color.Fuchsia,
                Text = "Click Me",
                Font = new Font("Arial", 76, FontStyle.Italic),
            };
            _panel.Click += (s, e) => MessageBox.Show("Panel clicked!");
            _panel.Paint += (s, e) =>
            {
                var panel = (Panel)s!;
                var g = e.Graphics;
                var textSize = g.MeasureString(panel.Text, panel.Font).ToSize();
                var center = (panel.Size - textSize) / 2;
                using var brush = new SolidBrush(panel.ForeColor);
                g.DrawString(panel.Text, panel.Font, brush, center.Width, center.Height);
            };
            Controls.Add(_panel);
        }

        [STAThread]
        static void Main()
        {
            var form = new MainForm();
            Application.Run(form);
        }
    }
}
 
And in case, you think it's the DockStyle.Fill which is magically making it work and don't want a panel to fill the entire form, replace that line with these lines to see that the the clicks are only accepted within the panel, but not outside of it. (The border is so that you can visualize where the panel starts and ends.)
C#:
Expand Collapse Copy
Size = new Size(400, 200),
Location = new Point(100, 100),
BorderStyle = BorderStyle.FixedSingle,
 
can confirm doesn't work for me. transparent color always goes for no reaction.
currently i can consider 2 causes: my full code somehow prevents the effect or i made some mistake implementing your code.

consider i work with visual studio 2019 which does not follow your code structure. so first of all, the last lines are not needed for me as they are in a separate file which launches the app and the form. since it wasn't all needed, i excluded your Form properties. i already have a transparent form like that so basically it's always the same. i basically needed only the panel's so i copied it inside the form's constructor and just above declared the panel. excluding that i cannot use (Panel)s! because i'm on c# 7.3, and some fixing for the code (i cant divide the size by 2 because it's a forbidden operation, so i did it in the drawing line of code), the rest is pretty much the same. i hid the button to be sure it doesn't stand in the way. and that's that. if you think there's something wrong so far, tell me so i can know. but this aside, i suppose there might be just something in my code preventing the correct behavior of the panel...
 
The code above was meant to be the minimal code that could just be copied and pasted into a WinForms app using .NET 9 in Visual Studio 2022. I'll see if I can downgrade the code to work with C# 7.3.

Post the minimal code that is not working so that we can copy and paste to try it out and debug.
 
Back
Top Bottom