I have a simple .Net 5.0 WinForm form which is automatically hidden after the application started:
private void form_Shown (object sender, EventArgs e)
{
Hide ();
}
It only creates a notify icon. This notify icon has a context menu strip that is shown by a left mouse click:
private void notifyIcon_Click (object sender, EventArgs e)
{
contextMenuStrip.Show (MousePosition);
}
This works fine, but as long as the context menu strip is visible, a dummy taskbar icon appears:
This doesn't happen if the form is not hidden or the context menu strip is shown by a right mouse click (via ContextMenuStrip property).
How can I prevent this icon?
I finally found a solution myself:
[DllImport ("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern bool SetForegroundWindow (HandleRef hWnd);
private void notifyIcon_Click (object sender, EventArgs e)
{
SetForegroundWindow (new HandleRef (notifyIcon.ContextMenuStrip, notifyIcon.ContextMenuStrip.Handle));
notifyIcon.ContextMenuStrip.Show (MousePosition);
}
Try Me.Hide()
lets see the outcome
Try assign your context menu to the notifyicons .ContextMenu property like and use the notifyicons .Visible property to show it instead of using the .Show method.
Related
In a Winforms application, I use a ContextMenuStrip (shown when right clicking on notification icon).
I would like to detect if a key has been pressed when context menu item is open (eg : by registering to an event).
There is no KeyDown event on ContextMenuStrip, unlike most controls.
However, there is a PreviewKeyDown event.
I have registered to that event, however it does not work as expected.
Here is my context menu :
Item1
Item2
SubMenu > SubItem1
SubItem2
If I press a key while Item1 is higlighted (mouse is on it), event is triggered. However, if I press a key when SubItem1 is highlighted, nothing happen.
Same behavior happen if no item is highlighted :
If only context menu is opened (no item highlighted), event is triggered.
If sub context menu is opened (no item highlighted), event is not triggered.
Here is some code sample as requested :
//MainForm.cs
void ContextMenuStrip1PreviewKeyDown(object sender, EventArgs e)
{
MessageBox.Show("OK"); //not always called, as explained above
}
//MainForm.Designer.cs (generated automatically by form designer)
this.contextMenuStrip1.PreviewKeyDown +=
new System.Windows.Forms.PreviewKeyDownEventHandler(this.ContextMenuStrip1PreviewKeyDown);
To detect the Shift key state when a context menu strip is open, As an option you can handle Application.Idle event and will check the key state using GetKeyState function:
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern short GetKeyState(int keyCode);
public const int KEY_PRESSED = 0x8000;
public static bool IsKeyDown(Keys key)
{
return Convert.ToBoolean(GetKeyState((int)key) & KEY_PRESSED);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Application.Idle += Application_Idle;
}
void Application_Idle(object sender, EventArgs e)
{
if (!contextMenuStrip1.Visible)
return;
if (IsKeyDown(Keys.ShiftKey))
someMenuItem.Text = "Shift is Down";
else
someMenuItem.Text = "Shift is Up";
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Application.Idle -= Application_Idle;
base.OnFormClosed(e);
}
I want to have it such that left clicking on the NotifyIcon also causes the context menu (set with the ContextMenuStrip property) to open as well. How would I achieve this? Do I have to handle Click and figure out the positioning myself?
Edit: showing the menu with trayIcon.ContextMenuStrip.Show() results is a few undesirable behaviors:
The menu is not shown at the same location as if right click the NotifyIcon (it appears that you can't set the x and y coords to where the taskbar is, at least on Windows 7 which is what I'm running). It will appear above the task bar (not that big of a deal, but consistency would be nice).
While the menu is shown, there is an extra icon added to the task bar.
Clicking somewhere other than the menu does not close it (whereas if you right click to bring up the context menu clicking else where automatically closes the context menu).
Is it at all possible to just invoke the menu however the built in right click handler is doing it?
You would normally handle the MouseClick event to detect the click and call the ContextMenuStrip.Show() method:
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) {
contextMenuStrip1.Show(Control.MousePosition);
}
But that doesn't actually work properly, the CMS won't close when you click outside of it. Underlying issue is a Windows quirk (aka "bug") that is described in this KB article.
Invoking this workaround in your own code is pretty painful, the pinvoke is unpleasant. The NotifyIcon class has this workaround in its ShowContextMenu() method, they just made it difficult to get to since it is a private method. Reflection can bypass that restriction. I discovered this hack 5 years ago and nobody reported a problem with it yet. Set the NFI's ContextMenuStrip property and implement the MouseUp event like this:
using System.Reflection;
...
private void notifyIcon1_MouseUp(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(notifyIcon1, null);
}
}
If you handle MouseUp rather than Click, you will be able to tell which button was clicked, as well as the location of the click. You can use this location as the location to show the ContextMenu
notifyIcon.MouseUp += new MouseEventHandler(delegate(object sender, MouseEventArgs e) { contextMenu.Show(e.Location); });
You can wire in a onClick event for notify icon then call show in the on click
private void wire()
{
notifyIcon1.Click += new EventHandler(notifyIcon1_Click);
}
void notifyIcon1_Click(object sender, EventArgs e)
{
contextMenuStrip1.Show(Cursor.Position);
}
use the following code to show context menu on both right and left click on notifyicon, if you find any issue then text me at arshad_mcs786#hotmail.com (arshad from Islamabd)
//System.Runtime.InteropServices use thi as reference
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool SetForegroundWindow(HandleRef hWnd);
private void notifyIcon1_Click(object sender, EventArgs e)
{
SetForegroundWindow(new HandleRef(this, this.Handle));
int x = Control.MousePosition.X;
int y = Control.MousePosition.Y;
x = x - 10;
y = y - 40;
this.contextMenuStrip1.Show(x,y );
//this.PointToClient(Cursor.Position)
}
I'm used to reference the current control in access vba, how to do so in C# winform ?
You can also use the form's ActiveControl property.
I took codekaizen's code and dropped it into a form along with a timer and several controls (a DataGridView, Panel, and a Button and CheckBox in the Panel). Added this code in the timer's Tick event:
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = ActiveControl.Name;
label2.Text = GetFocusedControl().Name;
}
and they reported the same active control as I clicked from one control to another.
I'm not sure if there is a better method, but P\Invoke solves this for me:
private static Control GetFocusedControl()
{
Control focused = null;
var handle = GetFocusedControlHandle();
if (handle != IntPtr.Zero)
{
focused = Control.FromHandle(handle);
}
return focused;
}
// ...
[DllImport("user32.dll")]
private static extern IntPtr GetFocusedControlHandle();
I want to have it such that left clicking on the NotifyIcon also causes the context menu (set with the ContextMenuStrip property) to open as well. How would I achieve this? Do I have to handle Click and figure out the positioning myself?
Edit: showing the menu with trayIcon.ContextMenuStrip.Show() results is a few undesirable behaviors:
The menu is not shown at the same location as if right click the NotifyIcon (it appears that you can't set the x and y coords to where the taskbar is, at least on Windows 7 which is what I'm running). It will appear above the task bar (not that big of a deal, but consistency would be nice).
While the menu is shown, there is an extra icon added to the task bar.
Clicking somewhere other than the menu does not close it (whereas if you right click to bring up the context menu clicking else where automatically closes the context menu).
Is it at all possible to just invoke the menu however the built in right click handler is doing it?
You would normally handle the MouseClick event to detect the click and call the ContextMenuStrip.Show() method:
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) {
contextMenuStrip1.Show(Control.MousePosition);
}
But that doesn't actually work properly, the CMS won't close when you click outside of it. Underlying issue is a Windows quirk (aka "bug") that is described in this KB article.
Invoking this workaround in your own code is pretty painful, the pinvoke is unpleasant. The NotifyIcon class has this workaround in its ShowContextMenu() method, they just made it difficult to get to since it is a private method. Reflection can bypass that restriction. I discovered this hack 5 years ago and nobody reported a problem with it yet. Set the NFI's ContextMenuStrip property and implement the MouseUp event like this:
using System.Reflection;
...
private void notifyIcon1_MouseUp(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(notifyIcon1, null);
}
}
If you handle MouseUp rather than Click, you will be able to tell which button was clicked, as well as the location of the click. You can use this location as the location to show the ContextMenu
notifyIcon.MouseUp += new MouseEventHandler(delegate(object sender, MouseEventArgs e) { contextMenu.Show(e.Location); });
You can wire in a onClick event for notify icon then call show in the on click
private void wire()
{
notifyIcon1.Click += new EventHandler(notifyIcon1_Click);
}
void notifyIcon1_Click(object sender, EventArgs e)
{
contextMenuStrip1.Show(Cursor.Position);
}
use the following code to show context menu on both right and left click on notifyicon, if you find any issue then text me at arshad_mcs786#hotmail.com (arshad from Islamabd)
//System.Runtime.InteropServices use thi as reference
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool SetForegroundWindow(HandleRef hWnd);
private void notifyIcon1_Click(object sender, EventArgs e)
{
SetForegroundWindow(new HandleRef(this, this.Handle));
int x = Control.MousePosition.X;
int y = Control.MousePosition.Y;
x = x - 10;
y = y - 40;
this.contextMenuStrip1.Show(x,y );
//this.PointToClient(Cursor.Position)
}
Is there a way to disable the exit button on a windows form without having to import the some external .dll's? I disable the exit button by importing dll's using the following code but I don't like it. Is there a simpler (built-in) way?
public Form1()
{
InitializeComponent();
hMenu = GetSystemMenu(this.Handle, false);
}
private const uint SC_CLOSE = 0xf060;
private const uint MF_GRAYED = 0x01;
private IntPtr hMenu;
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern int EnableMenuItem(IntPtr hMenu, uint wIDEnableItem, uint wEnable);
// handle the form's Paint and Resize events
private void Form1_Paint(object sender, PaintEventArgs e)
{
EnableMenuItem(hMenu, SC_CLOSE, MF_GRAYED);
}
private void Form1_Resize(object sender, EventArgs e)
{
EnableMenuItem(hMenu, SC_CLOSE, MF_GRAYED);
}
You can't easily disable the exit button (the one in the top right that closes the form).
You can, however, hide the button completely, by setting the ControlBox property to false.
ControlBox can be turned on and off at any time, so you can use this if you want to dynamically allow closing at some times, and not at others.
Alternatively, you can handle the FormClosing event and cancel the close if you choose.
Here's a demo.
Create a new Windows Forms project.
Drop a CheckBox control on the form, with Text "ControlBox". Hook up its Click event to this:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
ControlBox = checkBox1.Checked;
}
Then, drop a second CheckBox control on the form with Text "Cancel Close". Hook up the FormClosing event of the form to this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = checkBox2.Checked;
}
Run the application and start playing around with the checkboxes. You'll soon see how things work.
To disable the close button on the form just write the below code on the form closing event.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
A little poking around found this handy helper class:
Disable Close Button and Prevent Form Being Moved (C# version)
It actually does more than what you're looking for, but essentially does it very nearly the same way you do in your sample code. The helper class hooks into the load/resize events for you so you don't have to remember to do it yourself.
Yes.
Setting the form.ControlBox = false will hide the close button. Although, it will also hide the minimize and maximize button.
You can also set form.FormBorderStyle = FormBorderStyle.None, which will hide the whole title bar.
If you want to show the X button but just stop the form from closing, override OnClosing and set the e.Cancel property to true.
Catch the FormClosing event and cancel it in the arguments.
Using visual studio select the form go to the properties and set the ControlBox property to false or try this.ControlBox = false; or frmMainForm.ControlBox = false;
Disabling the button is possible without importing dlls. ControlBox = false causes minimise and maximise buttons and the border to disappear as well and it does not disable the close 'X' button - it hides. This solution disables it:
private const int CP_NOCLOSE_BUTTON = 0x200;
protected override CreateParams CreateParams
{
get
{
CreateParams myCp = base.CreateParams;
myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON ;
return myCp;
}
}
Source: https://www.codeproject.com/kb/cs/disableclose.aspx