I have a ContextMenuStrip attached to a list view, and it's working great, but what I'm interested in knowing is how I can have it show up only when one or more items are selected in the listview.
Thanks!
You could use the Opening event. The event args has a Cancel property so that you can examine the state of your application and decide whether to have the menu show (by doing nothing) or prevent it from showing (by setting e.Cancel = true). However, like #Grzenio mentions, I would find it more intuitive if the item that i right-clicked on became selected automatically.
Another option would be to use the Opening event to populate the context menu with only one disabled item, with a text like (no item is selected) or so; this would inform the user about why the command is not available.
For other people reading this thread, a nice way is to gray out the options in the menu (in Opening event) when no items are selected instead of not displaying the menu at all
if (List.SelectedItems.Count == 0)
{
// e.Cancel=true;
List.Enabled = false;
}
else
{
List.Enabled = true;
}
For me its intuitive that if you have no items selected (or you right-click on a non-selected item), the item would get automatically selected just before you show the context menu.
If the first solution is not acceptable, I think I would try to attach the ContextMenuStrip when items get selected and detach it when they are unselected.
Private Sub ListView1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseUp
If e.Button = MouseButtons.Right And ListView1.SelectedItems.Count > 0 Then
Dim cn As New ContextMenuStrip()
cn.Items.Add("Apple")
Me.ListView1.ContextMenuStrip = cn
cn.Show(Control.MousePosition.X, Control.MousePosition.Y)
End If
End Sub
Related
I want to implement visual studio style Add or Remove Ruttons toolstrip like following
in my winforms application, how can I achieve this?
I haven't tried anything on this as I am not sure how to start and I don't have much working experience on winforms.
Please suggest.
At first glance it doesn't look all that difficult.
Just add a ToolStripDropDownButton to your ToolStrip with no image or text. That will make the appearance seem more or less similar.
Add to this drop down button one ToolStripMenuItem with a "Add or Remove Buttons" caption. We'll call it AddRemoveMenuItem.
Now populate AddRemoveMenuItem's child menu items with menu items representing all your configurable ToolStripItems. You can link menu item and configurable tool strip item through the menu item's Tag property (you could also subclass ToolStripMenuItem adding a ToolStripItem LinkedToolStripItem { get; set; } property but I don't think its really worth it).
All these "linked" menu items will have to handle their Click events where they will switch their linked tool strip item's Visible property and synchronize their Checked state accordingly.
I'd do that the following way:
linkedMenuItem.Click += (sender, e) => linkedMenuItem.Checked = !linkedMenuItem.Checked;
linkedMenuItem.CheckedChanged +=
(sender, e) =>
{
var linkedToolStripItem = linkedMenuItem.Tag as ToolStripItem;
if (linkedToolStripItem != null)
{
linkedToolStripItem.Visible = linkedMenuItem.Checked;
}
};
When starting up your application set the linked menu items Checked state accordingly to your app's default settings, user settings, etc. and you are done.
I have like 10 items in a ContextMenuStrip. I have an Opening Event handler which decides which of the 10 items will be visible. Based on application logic I may hide many of them by setting Visible = false and Enabled = false;
What happens is in the case that 6/10 are displayed. Their will be an area 4 menu items height of blank space the first time I click the strip. If I right click to make it appear the second time it shows up where it should be. So, the strip basically has the position calculated for a 10 item strip when in reality it's only a 6 item strip.
How do I force it to recalculate?
Here are some of the things I've tried:
contextMenuStrip1.Refresh();
contextMenuStrip1.Update();
contextMenuStrip1.PerformLayout();
contextMenuStrip1.AutoSize = true;
ToolStripItem tempItem = contextMenuStrip1.Items.Add("temp");
contextMenuStrip1.Items.Remove(tempItem);
contextMenuStrip1.Refresh();
contextMenuStrip1.Update();
contextMenuStrip1.Invalidate();
I've found that
AutoSize = false;
AutoSize = true;
after all item manipulations works. Haven't found any other solution.
In my case, I add items on opening (dynamic list of windows), and sometimes item caption was longer than the menu.
Have you tried using Invalidate()?
From the MSDN:
Invalidates the entire surface of the control and causes the control
to be redrawn. (Inherited from Control.)
Perhaps you need to use then WM_PAINT message, I have found that sometimes you need to use SendMessage (Interop) with Winforms.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd145213(v=vs.85).aspx
I have a ContextMenu with some sub-menus that have items (MenuItem) that can be selected. When the ContextMenu is opened, I want to recursively open the currently selected item. So, I have the following code:
protected override void OnOpened( RoutedEventArgs e ) {
base.OnOpened( e );
OpenCurrentSubMenu( Items );
}
private static bool OpenCurrentSubMenu( ItemCollection itemCollection ) {
foreach (MenuItem item in itemCollection) {
if (item.IsChecked) {
return true;
}
else if( OpenCurrentSubMenu( item.Items ) ) {
item.IsSubmenuOpen = true;
return true;
}
}
return false;
}
I also have some other code that ensures that only one item is checked.
This seems to work great the first time I select an item in a sub-menu. When I re-open the ContextMenu, the open sub-menus cascade open to the selected item:
However, when I leave the context menu, and re-open it a second time, the selected menu does NOT open:
Does anyone know why and how to fix it?
Three things to try:
When the context menu is opened, recurse over the entire hierarchy and set IsSubmenuOpen = false before you try to open any submenus. It may be that the previously open submenu is remembered and thus you're trying to tell it to have two open submenus at the same level.
Recurse to find the submenus that need to be opened and store them in a list. Then iterate through the list and set them so that the topmost menu is set open before its child submenu is set open. (It may be that trying to open the child when its parent is not yet open won't always work reliably).
Nasty brute force approach: Delete and recreate the context menu each time it is opened. It's not nice, but if you're opening a context menu you're likely to be worried about the performance implications. And it appears that it works the first time, so make every time the first time.
It might help to set the value to false before setting it back to true. I can't confirm this though.
item.IsSubmenuOpen = false;
item.IsSubmenuOpen = true;
I think the logic should work well ... when the menu item is checked, but not when sub menu item is checked.
In my opinion try two loops .... one for menu and another one for sub menu items.
None of the solutions works/clear to me and none of them looks elegant.
I found something that works.
Try this:
public partial class WindowWithContextMenu : Window
{
// Add this Show method
public new void Show()
{
base.Show();
base.Activate();
// Do this for any context menu!
MyContextMenu.IsSubmenuOpen = true;
MyContextMenu.IsSubmenuOpen = false;
MyContextMenu2.IsSubmenuOpen = true;
MyContextMenu2.IsSubmenuOpen = false;
// And so on
}
...
...
}
This way you modify a bit the original Show() method of the window.
Now when you call the Show() method, it will run this small hack.
Then at some point, you may click on some button that opens the menus.
The thing is that this time it will always work and not only on the first time!
MyContextMenu and MyContextMenu2 are defined in the XAML file and they are not opened by default in the XAML file.
I have no idea why this works but it did. It is ugly but the hack is simple and a copy-paste solution...
I also found that in order to reproduce the issue, you need to lock the account and log in again. It reproduced by 100% each time I did it.
I have a user interface with a TabControl that initially displays a start page. Other items can be added to it by double-clicking on content in, for example, a DataGrid. New tabs should be selected when they are created. If the document corresponding to the item in the grid is already open, then the existing tab for that document should be opened rather than creating a new one.
I know that I should be able to programmatically select a tab by setting the TabControl's SelectedItem or SelectedIndex properties. However, the desired tab never actually activates. If I set one and then inspect the TabControl's state in the debugger, then both fields seem to update properly. However, after I continue execution, I see that the selected tab remains unchanged in the UI, and if I pause and inspect the TabControl's state again I see that the SelectedItem and SelectedIndex have returned to their previous values. Selecting a tab by clicking on it in the UI, on the other hand, works just fine.
Here's the declaration for the TabControl:
<TabControl x:Name="Tabs" >
<TabItem x:Name="StartPageTab" Header="Start Page" DataContext="{Binding Path=StartPageViewModel}">
...
</TabItem>
</TabControl>
And the code for adding and selecting tabs:
private void _SelectTab(MyViewModel model)
{
TabItem tab;
if (_TryFindTab(model, out tab)) Tabs.SelectedItem = tab;
}
private bool _TryFindTab(MyViewModel target, out TabItem tab)
{
foreach (TabItem item in Tabs.Items)
{
MyViewModel model = item.DataContext as MyViewModel;
if (model != null && model.Equals(target))
{
tab = item;
return true;
}
}
tab = null;
return false;
}
private void _AddTab(MyViewModel model)
{
TabItem tab = new TabItem { DataContext = model, Content = new MyView() };
Binding bind = new Binding { Source = model, Path = new PropertyPath("Name") };
tab.SetBinding(TabItem.HeaderProperty, bind);
Tabs.Items.Add(tab);
Tabs.SelectedItem = tab;
}
It turned out to be related to something I conveniently omitted from the original problem description:
The DataGrid in question was in the content for StartPageTab. I was handling double-clicks on that DataGrid by capturing its MouseDoubleClick event, searching the visual tree to find what DataGridRow was double-clicked (if any), and then raising an event that would eventually be captured by the main window, which would respond by calling either _SelectTab or _AddTab, depending on whether the document in question was already open.
At which point, the call stack would unroll and get back to that MouseDoubleClick event handler. In that handler, I forgot to set the MouseButtonEventArgs's Handled property to true. So WPF kept searching for someone else to handle that click event - and the element that it eventually found would respond by asking for focus, which in turn meant that the original tab needed to get focus back.
Adding e.Handled = true; stopped that whole mess in its tracks, so the new tab could stay selected.
You could try using tab.focus()
I have tabs in my application and this is a quick way to make your selected tab visible.
Have you tried binding to TabItem.IsSelected and updating that in you view model?
In an older C# app I had, using page controls, I was able to force the page active by telling the tab control object to select the tab...
MyTabControlWithMultiplePages.SelectTab(PageIWantShown);
In my windows application i have a context menu with a grid the problem is that I want to disable the ToolStripMenuItem in context menu according to the user previlages.How can i do that. i have done like this but it is not working
private void contextMenuStrip_Machine_Opening(object sender, CancelEventArgs e)
{
toolStripAuthorize.Enabled = INFOpermission.accessAuthorize;
}
but it is not working
You need to set toolStripAuthorize.Enabled to either true or false.
I have no idea what INFOpermission.accessAuthorize is because you didn't show the code that defines that (enum?), but if it's anything other than false, this isn't going to work out like you expect.
I can guarantee that setting the Enabled property of the ToolStripMenuItem that you want to disable to false in the Opening event handler will work. If it's not working for you, you're doing something else wrong, and you need to give us some more information to go on.
If you're stuck, see the sample code here: How to: Handle the ContextMenuStrip Opening Event
EDIT: Armed with new information provided in the comments, I've now isolated the source of the problem. You've assigned the ContextMenuStrip to the RowTemplate of a DataGridView control, and are therefore not able to modify items contained in that context menu in its Opening event handler method.
It turns out that this is a known bug that someone decided was "by design". You can see the original bug report here on Microsoft Connect. The explanation given is that whenever a new row is created based on the RowTemplate (which is how the RowTemplate works), the ContextMenuStrip that you've assigned gets cloned as well. That means the same context menu instance is not used for each row, and whatever properties that you try to set on the original menu items have no effect.
Fortunately, it also gives us a workaround. Like all events, the Opening event passes the actual instance of the ContextMenuStrip that is about to be opened as its sender parameter. This is the context menu whose items you need to modify in order for your alterations to be visible.
So what's the code? It looks like this:
private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
{
ContextMenuStrip cmnu = (ContextMenuStrip)sender;
cmnu.Items[1].Enabled = false;
}
Notice, though, that you'll have to reference the individual menu item that you want to modify by its index. This is just the zero-based position of the item in the menu that you want to modify. You can't use the toolStripAuthorize object like you were trying to do before because a new instance of it has been cloned for each new context menu instance.