Hiding the maximize button is not possible without you painting your own window frame.
Having it disabled tells the user that he can't maximize the form which is good UX. Hiding it doesn't help because double clicking the title bar will still maximize the window (if you haven't disabled Maximize).
You can set FormBorderStyle
set to the FixedToolWindow
or SizableToolWindow
, but then the form will not be displayed in the Windows task bar or in the ALT+TAB window. See update below.
You can hide the entire ControlBox
which will also remove Minimize
and Close
as well as the context menu.
Pick your poison!
Update (12/24/15)
I decided to revisit the landscape with various options and it seems that:
- contrary to what documentation says, setting
FormBorderStyle
to FixedToolWindow/SizableToolWindow
no longer hides the app in task bar or ALT+TAB window in Windows 7 and up. ShowInTaskbar
exclusively decides Show/Hide effect in this case (thanks to @pinowthebird for nudging me to take a re-look).
- Setting
FormBorderStyle
to FixedDialog
also hides the maximize/minimize buttons and shows up in task bar, although the default icon is now lost (not sure why).
- Setting
MaximizeBox = False
does NOT hide the buttons, again contrary to the documentation. It simply disables it (and maximize functionality via toolbar double click).
- Setting both
MaximizeBox = False
and MinimizeBox = False
hides them, irrespective of FormBorderStyle
.
Here are some screenshots:
Conclusion:
Based on your requirements, you can either opt for 1, 2 or 3. Hope this helps future visitors.
Disclaimer: These tests were done in VS 2015, .Net 4.6 and a brand new WinForm app. The documentation says that these properties were available since .Net 1.1. However, as you can see in the screenshots - take the documentation with a grain of salt! Also the OS plays a vital role in the outcome.
Try the following approach:
AutoClosingMessageBox.Show("Text", "Caption", 1000);
Where the AutoClosingMessageBox
class implemented as following:
public class AutoClosingMessageBox {
System.Threading.Timer _timeoutTimer;
string _caption;
AutoClosingMessageBox(string text, string caption, int timeout) {
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
using(_timeoutTimer)
MessageBox.Show(text, caption);
}
public static void Show(string text, string caption, int timeout) {
new AutoClosingMessageBox(text, caption, timeout);
}
void OnTimerElapsed(object state) {
IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox
if(mbWnd != IntPtr.Zero)
SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
_timeoutTimer.Dispose();
}
const int WM_CLOSE = 0x0010;
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
Update:
If you want to get the return value of the underlying MessageBox when user selects something before the timeout you can use the following version of this code:
var userResult = AutoClosingMessageBox.Show("Yes or No?", "Caption", 1000, MessageBoxButtons.YesNo);
if(userResult == System.Windows.Forms.DialogResult.Yes) {
// do something
}
...
public class AutoClosingMessageBox {
System.Threading.Timer _timeoutTimer;
string _caption;
DialogResult _result;
DialogResult _timerResult;
AutoClosingMessageBox(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) {
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
_timerResult = timerResult;
using(_timeoutTimer)
_result = MessageBox.Show(text, caption, buttons);
}
public static DialogResult Show(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) {
return new AutoClosingMessageBox(text, caption, timeout, buttons, timerResult)._result;
}
void OnTimerElapsed(object state) {
IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox
if(mbWnd != IntPtr.Zero)
SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
_timeoutTimer.Dispose();
_result = _timerResult;
}
const int WM_CLOSE = 0x0010;
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
Yet another Update
I have checked the @Jack's case with YesNo
buttons and discovered that the approach with sending the WM_CLOSE
message does not work at all.
I will provide a fix in the context of the separate AutoclosingMessageBox library. This library contains redesigned approach and, I believe, can be useful to someone.
It also available via NuGet package:
Install-Package AutoClosingMessageBox
Release Notes (v1.0.0.2):
- New Show(IWin32Owner) API to support most popular scenarios (in the
context of #1 );
- New Factory() API to provide full control on MessageBox showing;
Best Answer
You can skip the else part of your application. If your form is main form of application, it will exit anyway. Application.Exit() causes all windows to close. Your "first" close is still pending, so form is not yet closed and Application.Exit() tries to close your form for the second time.
You can try this: