I'm trying to come up with a solution to create dynamic context menus that can be generated at run time. I have implemented a IGuiCommand interface that implements something similar to the normal Command pattern.
interface IGuiCommand
{
Execute();
Undo();
bool CanUndo {get;set;}
Redo();
string CommandName {get;set;}
string CommandDescription {get;set;}
}
The idea is to allow the control that is Right Clicked to submit it's own list of commands to display in a given context menu.
While I could have each control build a context menu, I would prefer to use a single context menu and dynamicly generate the menu to allow easier management during run time. When a control state or application state changes, I would like the context menu to reflect the change. For example if I right click a checkbox, then the checkbox would submit to the context menu an Enable or Disable command to display depending on the checkbox's current Checked value.
I think I could easily implement this if I had some way to know which control was "Right Clicked" in order to bring up the context menu for that specific control.
It seems surprising the ContextMenu events doesn't supply an EventArg that indicates the control that was right clicked (or whatever command that would cause the context menu to Popup)
You just need to override the ContextMenuStrip_Opening event. The sender object is a ContextMenuStrip, which contains a SourceControl element. When you apply the proper cast, you will have access to everything you need.
private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) {
var contextMenu = (sender as ContextMenuStrip);
if (contextMenu != null) {
var sourceControl = contextMenu.SourceControl;
contextMenuStrip1.Items.Clear();
//contextMenuStrip1.Items.Add(...);
}
}
Related
I am working on a custom context menu in an open-source text editor project I found on the internet. The custom context menu is made out of a Form. The problem is when I open the custom context menu, the text editor(main form) lost focus(becomes gray). I want it to look like this when the context menu opens, but not this. I have tried some solutions but they didn't work:
Solution 1: Caused the custom context menu to be on the back of the text editor once opened. It then stopped the custom context menu from working properly.
Solution 2: Caused my program to be crashed so it automatically closed.
Solution 3: I tried using either Control.Focus() or Control.Activate() to have focus on the text editor when invoking the custom context menu, but still didn't work.
Are there any other solutions I could try or am I missing something here?
Link(Head to: Branch -> v2.01) to the custom context menu project that I am working on. Please note that I have made it so that Control Key(CTRL) + Right mouse button(RMB) to invoke the custom context menu.
I am not sure whether this is conventionally the correct way to do it, but it works for me.
However, another problem it poses is that the context menu item functions cannot be executed. It can be clicked but then the nothing happens.
//activated event handler for the context menu
private void ContextMenu_Activated(object sender, EventArgs e)
{
//solution A
this.Activate(); //gives the main form the focus
//solution B
//this.Focus(); //gives the main form the focus
contextMenuObj.Visible = true; //forces the context menu to be displayed
//to maintain highlighted text in textbox in main form
richTextBox1.HideSelection = false;
//reset default flag once custom context menu opens
ctrlIsDown = false;
rmbIsUp = false;
}
This way, the main form is not greyed out when the context menu is displayed.
I am looking to have a user definable menu bar that contains a list of "favorite" actions that they can perform. I have a predefined menu of buttons which will perform different actions (open a new tab with content, update a record, etc.). I want the user to be able to select one of this controls, and choose to add it to their "favorite actions" which is a toolbar that will sit at the top of the page (this is currently done with a context menu). Ideally, the user will also be able to reorder this list of favorite actions.
Thus far, I've tried to use an ObservableCollection and List which is bound to a list view to tackle the first part of the problem. on the click method of the context menu, I have the following:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mnu = sender as MenuItem;
Button MyButton = null;
if (mnu != null)
{
ContextMenu MyContextMenu = (ContextMenu)mnu.Parent;
MyButton = MyContextMenu.PlacementTarget as Button;
}
dc.menuitems.Add(MyButton);
}
The list works when I add an object, however it did have issues firing INPC. When using an observable collection, I get the following error:
System.ArgumentException: 'Must disconnect specified child from current parent Visual before attaching to new parent Visual.'
I suspect this may be due to the fact that I'm somehow not creating a copy of the element, but rather reassigning it.
Is my approach the best approach? If so, how do I go about resolving the error that I see? What would be the best way to handle reordering of the items? I haven't been able to find much helpful information on creating such a control.
Once I've resolved this issue, I intend to use either a JSON or XML serializer to store this collection in the user settings to have their favorites stay persistent across application launches. Is this the best way to store this information?
I have a custom RichTextBox control that inherits from WPF's RichTextBox. I want to add the option "Add to dictionary" to the default context menu.
I've scoured the net for tips on accessing the Default context menu of WPF's RichTextBox but can't find anything. I plan on using the solution Adding menu item to default context menu but I would like to reuse as much of .NET's code and logic before creating my own context menu.
So far, my attempts have only resulted with the default context menu being replaced. The closest I've gotten to adjusting the context menu is when I overrided OnContextMenuOpening method. However, it consistently states the ContextMenu is null.
How do I get access to the default context menu and add my desired option?
RichTextBox.ContextMenu is null whenever I examine it, even though the default context menu shows up. If I set the ContextMenu with a new ContextMenu, it replaces the existing context menu with a blank menu.
public class CustomRTB : RichTextBox
{
protected override void OnContextMenuOpening(ContextMenuEventArgs e)
{
this.ContextMenu = new ContextMenu();
}
}
I have a custom Windows Forms Control to which I want to add a context menu. The custom control has an active selection that can be manipulated by keyboard or mouse. I set the custom control's ContextMenu property to the context menu I wish to show. When the user right-clicks, the context menu appears right where it is expected.
However, when the user presses the Menu key (or equivalently, Shift-F10) the context menu appears in the dead center of my control. I would like to be able to control the location of where the context menu appears depending on where the selection is in the control. How is this done? And does it also work for ContextMenuStrip?
EDIT
I'm writing up the worked solution given the answer below. First, set the ContextMenu property (or ContextMenusStrip property, as appropriate). Then, override the OnKeyDown method to intercept Shift-F10, and do an explicit Show() in that case.
this.ContextMenu = <my context menu>;
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyData == (Keys.Shift|Keys.F10))
{
ContextMenu.Show(this, new Point(<x and y coordinates as appropriate>));
e.Handled = true;
}
else
{
base.OnKeyDown(e);
}
}
This way, you don't have to muck with the logic that shows the context menu on right-click.
The Show() method of the ContextMenuStrip allows you to provide a control on which it is shows as well as the location.
For instance in example below:
this.contextMenuStrip1.Show(button1, new Point(50, 50));
It will display the menu 50 pixels to the right and below the button1. And when specifying only the location:
this.contextMenuStrip1.Show(new Point(50, 50));
The menu is displayed in the position relative to the main form.
I have a form. This form has a user control. This user control has a panel and a context menu. The context menu is not attached to the panel. There are other controls that are dynamically created and added to this panel. One of those controls is a button. When you click this button, I set the contextmenustrip property to my context menu.
My problem is that I need to read the items in that context menu prior to there being the opportunity to attach the context menu to the button.
Each time a form is loaded, I iterate though all the child controls of the form. If a control has children, I iterate through those, and so on... I can't seem to get at the context menu that is unassigned so to speak. It has not been attached to any control so it does not appear to be a child control of any controls on the form.
myConectMenu is never added to the user conrol like this.Controls.Add(myConectMenu). How can that context menu not be nested in the forms control collection? How can I get at that context menu?
Here is the designer code:
private System.Windows.Forms.ContextMenuStrip myContextMenu;
void InitializeComponent()
{
this.myContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.myContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.myToolStripMenuItem1,
this.myToolStripMenuItem2});
this.myContextMenu.Name = "myContextMenu";
this.myContextMenu.Size = new System.Drawing.Size(158, 92);
}
Update
The control iteration happens in a base class from which all forms in my application derive.
There is a private components object that the myContextMenu is added to. I imagine this is there so you can see the context menu in design view when it's not attached to a control. Perhaps I could leverage this?
private System.ComponentModel.IContainer components = null;
this.myContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
As you correctly observed, myContextMenu is not added to the Controls connection. Control has ContextMenuStrip property which you should check.
public void FindContextMenuStrip(Control input)
{
foreach(Control control in input.Controls)
{
if(control.ContextMenuStrip != null)
DoSomethingWithContextMenuStrip(control.ContextMenuStrip)
if(control.Controls.Count > 0)
FindContextMenuStrip(control);
}
}
Put relevant code in DoSomethingWithContextMenuStrip method.
EDIT:
I saw your comment where you specified what you wanted to do with ContextMenuStrip.
How about creating a method in Base class which takes user details and creates a context menu strip?
public ContextMenuStrip GetContextMenuStripForUser(User user)
{
//code to create context menu strip, with only those items enabled for which user has access.
}
In your final form, use this method to get ContextMenuStrip.
Create a custom contextmenu (SecureContextMenu in my case) that derives from contextmenu. Implement the open event and iterate through the items collection disabling the items that are not authorized.
Be sure to create a HasBeenOpened property and set it to true the first time the open event fires so that you don't have to keep checking the same controls every time the context menu is opened.
Use the SecureContextMenu everywhere you want context menu items checked against the list of authorized items.
It's a component and not a control attached to the form. Compare it to another form: you can manually .Show() a form from another form, but neither of them will show in in each other's .Control collection. Well, maybe that analogy wasn't the best... :s