WPF ContextMenu Closed Event, how to differentiate closing method - c#

Inside my Canvas I have a ContextMenu with the expected functionality of closing when you select an option in the menu, or when you click outside of it. However I want my program to respond differently based on which of those methods was used to close the ContextMenu. From what I can tell the Closed Event on the ContextMenu fires in both scenarios. Is there any way to make that distinction?

Could you please explain more about problem?
I think it's obvious that you have to scenario. 1: click on ContexMenu options you created which will invoke an event fore sure and clicking outside it which will cancel the context Menu. by the way I made an answer base on my understanding of you problem. If I got it wrong please tell me.
The bellow code is Just a demo code , The logic Behind it is important.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Windows Load event
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
// Very Important Because by default it's null
this.ContextMenu = new ContextMenu();
// Making 3 sample Menu Item for ContextMenu
MenuItem firstMenuItem = new MenuItem()
{
Header = "FirsMenu"
};
//1st of three way to give event to Controls.
// Giving click Event to firstMenuItem to seprate it's click behavior from Other Menu Items
firstMenuItem.Click += (s, e) =>
{
// if Tag be Null on context Menu closing it's get that non of item selected so the click must be out side or lost focused
this.ContextMenu.Tag = 1;
MessageBox.Show("First Menu Clicked");
};
MenuItem secondMenuItem = new MenuItem()
{
Header = "SecondMenu"
};
//2nd of three way to give event to Controls.
// Giving click Event to secondMenuItem to seprate it's click behavior from Other Menu Items
secondMenuItem.Click += delegate
{
// if Tag be Null on context Menu closing it's get that non of item selected so the click must be out side or lost focused
this.ContextMenu.Tag = 1;
MessageBox.Show("Second Menu Clicked");
};
MenuItem thirdMenuItem = new MenuItem()
{
Header = "ThirdMenu"
};
//3rd of three way to give event to Controls.
// Giving click Event to thirdMenuItem to seprate it's click behavior from Other Menu Items
thirdMenuItem.Click += ThirdMenuOnClick;
this.ContextMenu.Items.Add(firstMenuItem);
this.ContextMenu.Items.Add(secondMenuItem);
this.ContextMenu.Items.Add(thirdMenuItem);
this.ContextMenu.Closed += ContextMenuOnClosed;
}
private void ThirdMenuOnClick(object sender, RoutedEventArgs e)
{
// if Tag be Null on context Menu closing it's get that non of item selected so the click must be out side or lost focused
this.ContextMenu.Tag = 1;
MessageBox.Show("Third Menu Clicked");
}
// Event for opening contextmenu on right mouse button click
private void MainWindow_OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
this.ContextMenu.IsOpen = true;
}
private void ContextMenuOnClosed(object sender, RoutedEventArgs e)
{
// if null means click must be out side or lost focused
if (((ContextMenu)sender).Tag == null)
{
MessageBox.Show("You Clicked OutSide");
}
// very Importnt code , because it will reset the context menu tag logically
((ContextMenu)sender).Tag = null;
}
}
Wish You bests, Heydar.

Related

Pop-up window in a WPF application

I've a C# WPF application which show uses Grid control in the xaml(P screen).For every row in the grid, I've a column called Details.Clicking on item in this column shows a pop-up windows which also has a grid in the xaml(C screen).
My item click event in the P's viewmodel has the following code:
var myChildWindow = new MyGridView();
myChildWindow.Show();
If the user clicks on the item multiple times, I just want to highlight the existing C pop-up window.If there's no existing windows open, then only I want to open a new windows.
I've worked on a similar requirement for Winforms applicaiton.How do I go about this for a WPF application please?
Thanks.
First you'd need to declare myChildWindow outside of the click event so that it is accessible from multiple events. So,
MyGridView myChildWindow;
goes outside the click event, probably as a private variable.
Then, in your click event see if it's null, and if it is, create it.
if (myChildWindow == null)
{
myChildWindow = new MyGridView();
myChildWindow.Show();
}
You could keep a reference to the window and get rid of this when the window is closed:
MyGridView myChildWindow;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (myChildWindow == null)
{
myChildWindow = new MyGridView();
myChildWindow.Closed += MyChildWindow_Closed;
myChildWindow.Show();
}
else
{
myChildWindow.Activate();
}
}
private void MyChildWindow_Closed(object sender, EventArgs e)
{
myChildWindow.Closed -= MyChildWindow_Closed;
myChildWindow = null;
}

WinForms ContextMenu stays open when MenuItem is clicked

So I have a form DataGridView and when I right click on the grid I want to display a context menu that has one menu item in it. The menu item will open a second form that will provide some configuration options for the DataGridView.
Now all of this works absolutely fine, the context menu displays correctly and the second form opens correctly and all of the functionality on that form works correctly.
The only issue is that the context menu will only close if I click anywhere other than the menu item. No matter how many times I click on the menu item the context menu does not close.
I have tried looking for work arounds but as far as I can tell there is no way to programatically close the context menu.
Any help would be greatly appreciated. Below are copies of the click events for opening the context menu and for the menu item click event.
private void DataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
DataGridView dgv = (DataGridView)sender;
if (dgv.CurrentCell == null)
{
return;
}
else
{
Rectangle r = dgv.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
Point p = new Point(r.X + e.X, r.Y + e.Y);
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add(new MenuItem("Item", Item_Click));
cm.Show(dgv, p);
}
}
}
private void Item_Click(object sender, EventArgs e)
{
new SecondForm().Show();
}
UPDATE:
I solved the issue by replacing the ContextMenu class with the ContextMenuStrip class, removing the MouseClick event handler and assigning the ContextMenuStrip object to DataGridView.ContextMenuStrip. It appears as though the ContextMenuStrip class deals with showing the menu when it's relevant control is right clicked, so if you add a click event handler to deal with opening the menu it will repeatedly try to render the menu making it flicker several times before it is eventually rendered
in your class add a private variable
private bool CloseMenu = true;
on mouse down of your Context Menu
private void Item_Click(object sender, EventArgs e)
{
CloseMenu = false;
new SecondForm().Show();
}
add context menu closing event
private void contextMenuStripMy_Closing(object sender, ToolStripDropDownClosingEventArgs e)
{
e.Cancel = !CloseMenu;
CloseMenu = true;
}
I found some workdaround on this problem after many trials.
Firstly, this is about ContextMenu and not ContextMenuStrip.
This is not perfect solution but it works good if you do not have other alternative solution.
Here is how.
Just Set Visiable = false for all MenuItems under the Context Menu.
Only one problem with this is that it shows small square around the mouse.
I think this small square is the empty context menu box.
However, this small square will be removed quickly if user clicks anywhere and if the context menu lose focus.
It is really small and not bothering.
In code, this will look something like this in case you have only one menu item.
if (cm != null)
{
if (cm.MenuItems.Count > 0) cm.MenuItems[0].Visible = false;
}
If you have multiple of items, then just loop through all menu items.
if (cm != null)
{
for(int i = 0; i < cm.MenuItems.Count ; i++)
cm.MenuItems[i].Visible = false;
}
Hope this helps.
It worked for my case.
So I did not have to swtich to ContextMenuStrip from ContextMenu.

Add button click event with the menu item instead of navigate url

I'm trying to create dynamic menuitem driven from the database in ASP.NET. After creating the menuitem, I want to bind them with the button click event for each of the menuitem passing a parameter instead of NavigateUrl option.
MenuItem menuItem = new MenuItem
{
Value = row["MenuId"].ToString(),
Text = row["Title"].ToString(),
//NavigateUrl = row["Url"].ToString(),
//some logic to bind to onclick event
};
You can't add the click event to each menuItem you have to add it to the Menu and in the click event you verify which menuItem was clicked.
So make sure you add the MenuItemClick to your menu like this :
menu.MenuItemClick += Menu_MenuItemClick;
Then you can have a function that will create the MenuItems and in the event handler you can have something like this to verify which one was selected :
private void Menu_MenuItemClick(object sender, MenuEventArgs e)
{
if(e.Item.Text == "text1")
{
//Do what you need
}
else if(e.Item.Text == "text2")
{
//Do what you need
}
}
I resolved the issue. I had to add OnMenuItemClick event to Menu such as OnMenuItemClick="Menu_OnClick".Based on the click, I checked
protected void Menu_OnClick(object sender, MenuEventArgs e)
{
string value = e.Item.Value.ToString();
//OR
// string value = e.Item.Text.ToString();
//some logic
}

Is there a way to stop a Menu from closing when you click on it?

We have a form in our program that allows a user to determine what a user can see when setting privileges, by using a menu bar. When a user clicks on an item, that item then is "selected" (gets a tick next to it). However, this also closes the menu.
Is there a way to stop this menu from closing (without affecting any other menu's in the program) when a user clicks on it? So far I have not found anything in the settings, and any _click methods are not affecting it either.
Listen the ToolStripDropDownClosingEventHandler present in ToolStripDropDown. To do this just access the ToolStripMenuItem.DropDown Property, and so, add a listener that will handle the Closing event. We just need to check if the mouse pointer it's inside of toolStrip rectangle and this can be done with this piece of code:
private void ListenToolStripMenuItems(IEnumerable<ToolStripMenuItem> menuItems)
{
// listen all menuItems
foreach (ToolStrip menuItem in menuItems)
menuItem.DropDown.Closing += OnToolStripDropDownClosing;
}
private void OnToolStripDropDownClosing(object sender, ToolStripDropDownClosingEventArgs e)
{
var tsdd = (ToolStripDropDown)sender;
// checking if mouse cursor is inside
Point p = tsdd.PointToClient(Control.MousePosition);
if (tsdd.DisplayRectangle.Contains(p))
e.Cancel = true; // cancel closing
}
This way the AutoClose still working and the toolStrip will close properly.
I'm a hack, but I would do this for each item you can click:
sampleNameToolStripMenuItem.ShowDropDown();
That way whenever you click something, it will also drop the menu down again right after.
I had a similar issue. The Items to be checked was a Level down in my case.
I solved it by adding a MouseEnter and MouseLeave Event to the ToolStripMenuItems that can be checked/unchecked.
In the MouseEnter I set AutoClose of both menuItems to false - in the MouseLeave I set it back to true.
Visualization of the issue I had
private void AddEventToToolStripMenuItems(IEnumerable<ToolStripMenuItem> menuItems)
{
// listen all menuItems
foreach (ToolStripMenuItem menuItem in menuItems)
{
menuItem.MouseEnter += OnToolStripMouseEnter;
menuItem.MouseLeave += OnToolStripMouseLeave;
}
}
private void OnToolStripMouseEnter(object sender, EventArgs e)
{
firstLevelTooStipMenuItem.DropDown.AutoClose = false;
secondLevelTooStipMenuItem.DropDown.AutoClose = false;
}
private void OnToolStripMouseLeave(object sender, EventArgs e)
{
firstLevelTooStipMenuItem.DropDown.AutoClose = true;
secondLevelTooStipMenuItem.DropDown.AutoClose = true;
}
Get the ToolStripMenuItem
Get the DropDown from ToolStripMenuItem
Set AutoClose as false
Ref:
https://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripmenuitem(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripdropdown(v=vs.110).aspx

How do I detect a change of tab page in TabControl prior to SelectedIndexChanged event?

I currently determine what page of a tabcontrol was clicked on via the SelectedIndexChanged event.
I would like to detect before the selected index actually changes, for validation purposes. For example, a user clicks a tab page other than the one they are viewing. A dialog is presented if form data is unsaved and asks if it's ok to proceed. If the user clicks no, the user should remain on the current tab.
Currently I have to remember the previous tab page and switch back to it after an answer of 'no.'
I considered MouseDown (and the assorted calculation logic), but I doubt that's the best way.
Add such an event to the tabControl when form_load:
tabControl1.Selecting += new TabControlCancelEventHandler(tabControl1_Selecting);
void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
{
TabPage current = (sender as TabControl).SelectedTab;
// Validate the current page. To cancel the select, use:
e.Cancel = true;
}
I've actually tried all of the events including the suggestions here and none of the mentioned events occur at the right time to actually trap moving from the tab.
Even the tab page validation event fires when entering the tab rather than leaving it - either that or there's something peculiar going on with my machine or .NET 4. On the other hand, in .NET 4 there is the Deselecting event which fires at the right time for my purposes.
private void tab_Deselecting(object sender, TabControlCancelEventArgs e)
{
}
The TabControl has a collection of TabPages, each of which you can enforce validation on, e.g.:
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
foreach (var page in _tabControl.TabPages.Cast<TabPage>())
{
page.CausesValidation = true;
page.Validating += new CancelEventHandler(OnTabPageValidating);
}
}
void OnTabPageValidating(object sender, CancelEventArgs e)
{
TabPage page = sender as TabPage;
if (page == null)
return;
if (/* some validation fails */)
e.Cancel = true;
}
}

Categories

Resources