I have setup a custom control that acts as a loading overlay, the overlay is a form with a picturebox on it to show the image.
When the overlay shows it goes into position and is infront of the main form calling it, this looks great. However when the user moves the form or resizes it the form then goes behind to main form.
The overlay form moves and resizes without problems when the main form is moving or resizing, however after it has finished resizing or moving the form goes behind the main form. How can I bring the overlay form back to the top without using TopMost?
I call the below code from the main form using
LoadingControl p = new LoadingControl(dataGridView1, this);
p.Show();
And the overlay form:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class LoadingControl : Form
{
private Color BackgroundColour = Color.Black;
private double BackgroundOpacity = 0.50;
private Image LoadingImage = APPNAME.Properties.Resources.loading_120x128;
private Form Mainform;
private Control MainControl;
public LoadingControl(Control parent, Form frm)
{
MainControl = parent;
Mainform = frm;
SetupForm();
Size = parent.ClientSize;
Location = parent.PointToScreen(Point.Empty);
Mainform.Move += AdjustPosition;
MainControl.SizeChanged += AdjustPosition;
}
private void SetupForm()
{
FormBorderStyle = FormBorderStyle.None;
BackColor = BackgroundColour;
Opacity = BackgroundOpacity;//0.50;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
PictureBox pbox = new PictureBox {Image = LoadingImage, Parent = this};
pbox.Width = pbox.Image.Width;
pbox.Height = pbox.Image.Height;
pbox.Left = (Width/2) - (pbox.Width/2);
pbox.Top = (Height/2) - (pbox.Height/2)-10;
pbox.Anchor = AnchorStyles.None;
Controls.Add(pbox);
}
public void SetLoadingImage(Image img)
{
LoadingImage = img;
}
public void SetBackgroundColour(Color col)
{
BackgroundColour = col;
}
public void SetOpacity(double Opa)
{
BackgroundOpacity = Opa;
}
private void AdjustPosition(object sender, EventArgs e)
{
//TopMost = true;
BringToFront();
ClientSize = MainControl.ClientSize;
Location = MainControl.PointToScreen(Point.Empty);
//Mainform.Activate();
//TopMost = false;
BringToFront();
Focus();
//MakeTopMost(this);
}
}
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.