2

I have a simple windows form. When a user clicks a button it does an intense computation that takes 10-20 seconds. During this time I want to cover the UI elements with a semi-transparent black to indicate to the user that they cannot be clicked.

I tried putting a panel over the UI with a back color like so

mypanel.BackColor = Color.FromArgb(100, 0, 20, 0);

But the panel is solid and the UI is not visible behind it.

I want my UI to be shaded black and not clickable. Is this possible? I'm used to web development where this kind of thing is standard and trivial so I'm a bit baffled that its not obvious.

Thanks!

Arin Ghazarian
  • 5,105
  • 3
  • 23
  • 21
asutherland
  • 2,849
  • 4
  • 32
  • 50
  • what about changing the `A` value (`100`) to something like `20`? – Federico Berasategui Jan 07 '14 at 21:03
  • @HighCore that makes the panel lighter but not transparent (that is the controls behind it are not visible), the alpha is on a 0-256 scale so even 100 as I have it should be mostly see through. – asutherland Jan 07 '14 at 21:05
  • "The BackColor property does not support transparent colors unless the SupportsTransparentBackColor value of System.Windows.Forms.ControlStyles is set to true." - http://msdn.microsoft.com/en-us/library/system.windows.forms.control.backcolor(v=vs.110).aspx – mao47 Jan 07 '14 at 21:08
  • 2
    `I'm used to web development` - You would do better with WPF. winforms doesn't really support transparency, and the XAML paradigm is much closer to the Web paradigm than the traditional, too-much-code-for-anything winforms approach. Besides, winforms is a really old technology that is not recommended for any new projects, only to maintain legacy applications. – Federico Berasategui Jan 07 '14 at 21:09
  • @mao47 I am trying to set that property on my form like this.SetStyle(System.Windows.Forms.ControlStyles.SupportsTransparentBackColor, true); Is this the wrong way to set it, its not working. – asutherland Jan 07 '14 at 21:16
  • I am not a winforms guy... That looks right to me according to the documentation. I can't find any other information about what could be causing your error--most SO questions say one of those solutions should work. – mao47 Jan 07 '14 at 21:27

2 Answers2

3

What you can do is set the enabled property of the Form to false, this will grey out all controls in the Form.

For example:

private void button2_Click(object sender, EventArgs e)
    {
        this.Enabled = false;
    }

All this does is lock the form, the user will not be able to do anything to it. When the operation is finished (or use a timer) you will need to do the opposite and set the enabled property back to true.

User
  • 46
  • 2
1

Had to deal with winforms transparency the other day in a similar scenario, too and the first thing i found out was: it doesn't support semi-transparency (in an easy way).
I ended up digging deep into window styles (the most important one being WS_EX_LAYERED) and rolled my own control.

Most of this stuff i found by massive googling and try and error...

Let's go for it:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public partial class ShaderControl : UserControl
{

    //we will need all these imports, see their documentation on what they do exactly
    [DllImport("user32")]
    private static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
    [DllImport("user32")]
    private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
    [DllImport("user32", EntryPoint = "SetWindowLong", SetLastError = true)]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
    [DllImport("user32", EntryPoint = "SetLayeredWindowAttributes")]
    private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    [DllImport("user32", EntryPoint = "GetWindowLong", SetLastError = true)]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    //we're going to give the window these constans as parameters
    private const int WS_EX_TOOLWINDOW = 0x80; //make it a toolwindow
    private const int WS_EX_NOACTIVATE = 0x8000000; //make it non-activating
    private const int WS_EX_TOPMOST = 0x8; //make it the topmost window

    //and we need these ones later on, too to achieve semi-transparency
    private const int GWL_EXSTYLE = -20;
    private const int WS_EX_LAYERED = 0x80000;
    private const int LWA_ALPHA = 0x2;

    private double opacity = 0.8; //between 0 and 1

    public ShaderControl()
    {
        InitializeComponent();
    }

    protected override CreateParams CreateParams
    {
        //here we create the window parameters
        //this will be called once when the window is created
        get
        {
            CreateParams p = base.CreateParams;
            p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
            return p;
        }
    }

    public new void Show()
    {
        //here we make the window a child of the desktop
        if (this.Handle == IntPtr.Zero)
        {
            base.CreateControl();
        }
        SetParent(base.Handle, IntPtr.Zero);
        ShowWindow(base.Handle, 1);
    }

    protected sealed override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);

        if (this.Visible)
        {
            //every time the window gets shown we have to update the window attributes
            //the important thing here is the WS_EX_LAYERED attribute, this makes it possible to achieve semi-transparency
            int wl = GetWindowLong(this.Handle, GWL_EXSTYLE);
            wl = wl | WS_EX_LAYERED;
            SetWindowLong(this.Handle, GWL_EXSTYLE, wl);
            SetLayeredWindowAttributes(this.Handle, 0, (byte)(opacity * 255), LWA_ALPHA);
        }
    }

    public double Opacity
    {
        get
        {
            return opacity;
        }
        set
        {
            //when the opacity changes we have to renew the window attributes
            opacity = value > 0d ? Math.Min(1d, value) : Math.Max(0d, value);
            SetLayeredWindowAttributes(this.Handle, 0, (byte)(opacity * 255), LWA_ALPHA);
        }
    }
}

Now all that you're left with is setting ShaderControl.Opacity to a value between 0 and 1 that suits your needs and - the most important - give this control the right size and position!

Further reading:
vbaccelerator.com - Floating Control
codeproject.com - Microsoft WORD 2007 Style Semi transparent Minibar
Topmost form, clicking "through" possible?

Community
  • 1
  • 1
KeyNone
  • 8,745
  • 4
  • 34
  • 51
  • Also, assuming i can get this to work, do you mind if I more or less use this as is? Could it be MIT license? – asutherland Jan 07 '14 at 21:56
  • If i didn't want you (and others) to use it i would'nt have posted it here ;) – KeyNone Jan 07 '14 at 21:57
  • Also, i don't know if this will work out of the box. The "real" code i use is spread between two classes and i just copied all the relevant parts together for this scenario. If it doesn't work out of the box and you're really eager to get this shader then at least you know where to start searching now. :) – KeyNone Jan 07 '14 at 21:59