Draw Semi Transparent Overlay Image All Over the Windows Form Having Some Controls

How to draw a semi-transparent rectangle on Panel containing some user controls in it?

Here is an example of using a 2nd Form to overlay the selection.

Sample Image

I am using a TabPage tabPage5 as my container, you should be able to adapt to any other Control or even the whole Form..

I use two class level variables and prepare the overlay Form somwhere at the start..:

Form overlay = new Form();
Point m_Down = Point.Empty;

void prepareOverlay()
{
overlay.BackColor = Color.Fuchsia; // your selection color
overlay.Opacity = 0.2f; // tranparency
overlay.MinimizeBox = false; // prepare..
overlay.MaximizeBox = false;
overlay.Text = "";
overlay.ShowIcon = false;
overlay.ControlBox = false;
overlay.FormBorderStyle = FormBorderStyle.None;
overlay.Size = Size.Empty;
overlay.TopMost = true;
}

These events are needed for the parent container the user will drag over:

private void tabPage5_MouseDown(object sender, MouseEventArgs e)
{
m_Down = e.Location;
overlay.Size = Size.Empty;
overlay.Show();
}

private void tabPage5_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
overlay.Location = tabPage5.PointToScreen(m_Down);
overlay.Size = new Size(e.X - m_Down.X, e.Y - m_Down.Y);
}
}

private void tabPage5_MouseUp(object sender, MouseEventArgs e)
{
// do your stuff and then hide overlay..
overlay.Hide();
}

I have only coded for the standard case of a user dragging from the top left to the bottom right.

For the other cases you will need to do a little juggling with Math.Abs& Math.Min, calculating the Size in absolutes and using the coordinate minima for the Location..

To collect all covered Controls you will have to revert the overlay.Bounds to the client coordinates using selectionRect = tabPage5.RectangleToClient (overlay.Bounds); To collect nested Controls the selection could use a method like this one:

List<Control> controlSelection = new List<Control>();

List<Control> getControls(Control container, Rectangle rect)
{
controlSelection = new List<Control>();
foreach (Control ctl in container.Controls)
if (rect.Contains(ctl.Bounds))
{
controlSelection.Add(ctl);
foreach (Control ct in ctl.Controls) controlSelection.Add(ct); ;
}
return controlSelection;
}

To call it use:

Rectangle grabRect = tabPage5.RectangleToClient (overlay.Bounds);
controlSelection = getControls(tabPage5, grabRect);
Console.WriteLine(controlSelection.Count + " Controls grabbed.");

For multiple levels of nesting you will need a recursive function! For this case it may be better to leave the selectionRectangle is screen coordinates and to transform the tested controls' bound to screen as well..

And, of course it is up to you to decide if a selection must just hit (Rectangle.IntersectsWith()) or cover the targets completely; I have coded the latter (Rectangle.Contains()).

C# winforms transparent form overlay issue

For some reason, the Color.DarkGray BackColor attribute for the transparent form was causing the issue. Changing the BackColor to Color.White fixed this.

Thanks to Patrice Gahide for helping me.

Form overlay showing behind form on move and resize

No matter how many times you call BringToFront() it will not move your form on top of the active one. BringToFront() does not make a control a top-level control, and it does not raise the Paint event.

There are too ways to go about fixing it. The best way IMO is to make your loading control a child of the main form instead of them being separate and you having to manually position the control after every move or resize.

The other method that could be done is to change the control window's z-order and activate it after every time a move, resize or maximize/minimize event is raised.

This can be achieved with a little bit of p-invokes:

public void FocusForm()
{
// force window to have focus
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(this.Handle);
ShowWindow(this.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(this.Handle);
ShowWindow(this.Handle, SW_SHOW);
}
this.Activate();
}

[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

I use this to set my main window to be top most when started so you might want to lose the Activate() call at the end.

I must say that even though this might work for you [I haven't fully tested it for move and resize], I suggest you revise your code and put the loading control form as a child to your main form.

EDIT:

If you need a transparent control then you can check the accepted answer to this SO question. I cannot post the code here as I do not want to make a false claim that it is my code.



Related Topics



Leave a reply



Submit