I am trying to mimic the behavior of, for example, Windows Explorer and how the Favorites items can launch a context menu.
I currently am using:
contextMenu.Show((sender as ToolStripMenuItem).GetCurrentParent().PointToScreen(e.Location));
This occurs in the MouseDown event of the ToolStripMenuItem. The problem is that the menu closes immediately after right-click, and I don't know any way to suspend it while the context menu is open.
I've tried deriving from ToolStripMenuItem and overriding the MouseDown/MouseUp but I can't figure out how to keep it open on click.
Is there a good way of doing this?
This is what I've had luck with, it's a bit more direct:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void MenuItemContext(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) return;
ToolStripMenuItem mID = (ToolStripMenuItem)sender;
ContextMenu tsmiContext = new ContextMenu();
MenuItem Item1 = new MenuItem();
MenuItem Item2 = new MenuItem();
Item1.Text = "Item1";
Item2.Text = "Item2";
tsmiContext.MenuItems.Add(Item1);
tsmiContext.MenuItems.Add(Item2);
Item1.Click += new EventHandler(Item1_Click);
Item2.Click += new EventHandler(Item2_Click);
hndPass = mID.Text;
tsmiContext.Show(menuStrip1, menuStrip1.PointToClient(new Point(Cursor.Position.X, Cursor.Position.Y)));
}
private String hndPass;
void Item1_Click(object sender, EventArgs e)
{
MenuItem mID = (MenuItem)sender;
MessageBox.Show("You clicked " + mID.Text + " in the context menu of " + hndPass);
}
void Item2_Click(object sender, EventArgs e)
{
MenuItem mID = (MenuItem)sender;
MessageBox.Show("You clicked " + mID.Text + " in the context menu of " + hndPass); ;
}
}
One way that you could accomplish this is by using the ToolStripDropDown control to host a ListBox inside of the ToolStripDropDown.
This may require some tweaking regarding the AutoClose behavior, but it should get you started:
First in your main form, add the following line to your ToolStripDropDropDown item
toolStripDropDownButton1.DropDown = new CustomListDropDown();
Then create a custom drop down class as follows:
public class CustomListDropDown : ToolStripDropDown
{
private ContextMenuStrip contextMenuStrip1;
private ToolStripMenuItem toolStripMenuItem1;
private ToolStripMenuItem toolStripMenuItem2;
private ToolStripMenuItem toolStripMenuItem3;
private System.ComponentModel.IContainer components;
public ListBox ListBox { get; private set; }
public CustomListDropDown()
{
InitializeComponent();
this.ListBox = new ListBox() { Width = 200, Height = 600 };
this.Items.Add(new ToolStripControlHost(this.ListBox));
this.ListBox.ContextMenuStrip = contextMenuStrip1;
this.ListBox.MouseDown += new MouseEventHandler(ListBox_MouseDown);
contextMenuStrip1.Closing += new ToolStripDropDownClosingEventHandler(contextMenuStrip1_Closing);
//add sample items
this.ListBox.Items.Add("Item1");
this.ListBox.Items.Add("Item2");
this.ListBox.Items.Add("Item3");
this.ListBox.Items.Add("Item4");
}
void contextMenuStrip1_Closing(object sender, ToolStripDropDownClosingEventArgs e)
{
this.Close();
this.AutoClose = true;
}
void ListBox_MouseDown(object sender, MouseEventArgs e)
{
this.AutoClose = false;
this.ListBox.SelectedIndex = this.ListBox.IndexFromPoint(e.Location);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenuStrip1.SuspendLayout();
this.SuspendLayout();
//
// contextMenuStrip1
//
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem1,
this.toolStripMenuItem2,
this.toolStripMenuItem3});
this.contextMenuStrip1.Name = "contextMenuStrip1";
//
// contextMenuStrip1.ContextMenuStrip
//
this.contextMenuStrip1.Size = new System.Drawing.Size(181, 48);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(180, 22);
this.toolStripMenuItem1.Text = "toolStripMenuItem1";
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(180, 22);
this.toolStripMenuItem2.Text = "toolStripMenuItem2";
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(180, 22);
this.toolStripMenuItem3.Text = "toolStripMenuItem3";
//
// CustomListDropDown
//
this.Size = new System.Drawing.Size(2, 4);
this.contextMenuStrip1.ResumeLayout(false);
this.ResumeLayout(false);
}
}
In my tests this worked reasonably well. Let me know how it goes.
As ContextMenuStrip is derived from ToolStripDropDown, you could do this:
private ContextMenuStrip CopyToContextMenu(ToolStripMenuItem mnuItemSource)
{
var mnuContextDestination = new ContextMenuStrip();
//Move itens from ToolStripMenuItem to ContextMenuStrip
while (mnuItemSource.DropDown.Items.Count > 0)
{
mnuContextDestination.Items.Add(mnuItemSource.DropDown.Items[0]);
}
//Put ContextMenuStrip in ToolStripMenuItem using DropDown property
mnuItemSource.DropDown = mnuContextDestination;
return mnuContextDestination;
}
Related
We want to create a usercontrol as container for maintain button status and arrange button location called ucSwitchButtonList.
This usercontrol added only one FlowLayoutControl.
After compiling, you can add ucSwitchButtonList on the form and successfully add buttons from CollectionEditor.
We can modify button objects added in ucSwitchButtonList and set properties at design time.
But, when we drag any button at design time, Visual Studio throws a NullReferenceException exception.
How to prevent user from dragging buttons in ucSwitchButtonList at design time? Or how to handle drag event at design time?
I want to achieve the effect of TabPage in TabControl, which can be selected but not dragged at design time.
ucSwitchButtonList.Designer.cs:
partial class ucSwitchButtonList
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.flpBtnContainer = new System.Windows.Forms.FlowLayoutPanel();
this.SuspendLayout();
//
// flpBtnContainer
//
this.flpBtnContainer.AutoScroll = true;
this.flpBtnContainer.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.flpBtnContainer.Location = new System.Drawing.Point(1, 1);
this.flpBtnContainer.Margin = new System.Windows.Forms.Padding(0);
this.flpBtnContainer.Name = "flpBtnContainer";
this.flpBtnContainer.Size = new System.Drawing.Size(116, 91);
this.flpBtnContainer.TabIndex = 0;
//
// ucSwitchButtonList
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.Controls.Add(this.flpBtnContainer);
this.DoubleBuffered = true;
this.Font = new System.Drawing.Font("新細明體", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
this.Name = "ucSwitchButtonList";
this.Padding = new System.Windows.Forms.Padding(1);
this.Size = new System.Drawing.Size(209, 202);
this.Load += new System.EventHandler(this.ucSwitchButtonList_Load);
this.ResumeLayout(false);
}
private System.Windows.Forms.Panel flpBtnContainer;
}
ucSwitchButtonList.cs:
public partial class ucSwitchButtonList : UserControl
{
ObservableCollection<Button> _ButtonList = new System.Collections.ObjectModel.ObservableCollection<Button>();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always),
Editor(typeof(CollectionEditor), typeof(UITypeEditor))
]
public ObservableCollection<Button> ButtonList
{
get => _ButtonList;
}
public ucSwitchButtonList()
{
InitializeComponent();
flpBtnContainer.Padding = new Padding(0);
flpBtnContainer.Size = this.Size;
flpBtnContainer.Dock = DockStyle.Fill;
_ButtonList.CollectionChanged += ButtonList_CollectionChanged;
Size = new Size(80, 160);
}
private void ucSwitchButtonList_Load(object sender, EventArgs e)
{
}
private void ButtonList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
flpBtnContainer.SuspendLayout();
flpBtnContainer.Controls.Clear();
flpBtnContainer.Controls.AddRange(_ButtonList.ToArray());
flpBtnContainer.ResumeLayout();
foreach (Control ctl in flpBtnContainer.Controls)
{
if (ctl is Button btn)
{
btn.Click += Btn_Click;
if (e.NewItems.Contains(btn))
{
btn.BackColor = Color.White;
}
}
}
Invalidate(true);
}
private void Btn_Click(object sender, EventArgs e)
{
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (DesignMode && BorderStyle == BorderStyle.None)
{
var rect = Rectangle.Inflate(ClientRectangle, 0, 0);
rect.Width--;
rect.Height--;
Color lc = ControlPaint.Dark(Parent.BackColor);
if (lc == Parent.BackColor) lc = ControlPaint.LightLight(Parent.BackColor);
using (Pen lp = new Pen(lc))
{
lp.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
lp.Alignment = System.Drawing.Drawing2D.PenAlignment.Center;
e.Graphics.DrawRectangle(lp, rect);
}
}
}
}
I create a TabPage and add on the TabPage a user control.
private void AddScriptTab(TabControl tab_contr, string tab_name, int idx)
{
TabPage tab_page = new TabPage();
UserControlScript user_contr = new UserControlScript(serial_port, ref tabs);
tab_contr.TabPages.Insert(idx, tab_page);
tab_page.Text = tab_name;
tab_page.Tag = "script";
user_contr.Dock = DockStyle.Fill;
user_contr.Tag = idx.ToString();
tab_page.Controls.Add(user_contr);
}
Now in the created TabPage I click on the button in my user control and check the tag I assigned
user_contr.Tag = idx.ToString();
private void buttonParamsLoad_Click(object sender, EventArgs e)
{
foreach (TabParams tab in tablist)
{
if (tab.Tag.ToString() == Tag.ToString())
tab.File = file_path;
}
}
But I getting an exception Tag is null.
Why?
OMG! I found the problem. I should add components in the right order.
private void AddScriptTab(TabControl tab_contr, string tab_name, int idx)
{
TabPage tab_page = new TabPage();
UserControlScript user_contr = new UserControlScript(serial_port);
tab_page.Text = tab_name;
tab_page.Tag = "script";
user_contr.Dock = DockStyle.Fill;
user_contr.Tag = idx.ToString();
tab_page.Controls.Add(user_contr);
tab_contr.TabPages.Insert(idx, tab_page);
}
I have a tabControl and a flowLayoutPanel inside each tab.. When I drag and drop a file onto a tab it creates a button with the icon of the file dropped. But i have the option to create more tabs and I want to be able to drag files into the selected tab.. but the problem is the flowLayoutPanel when adding the button..
My code so far:
public Process myProcess = new Process();
FlowLayoutPanel fl_panel = new FlowLayoutPanel();
string path_app;
public Form1()
{
InitializeComponent();
//add the flowLayoutPanel on the first tab
fl_panel.Dock = DockStyle.Fill;
fl_panel.BringToFront();
tabPage1.Controls.Add(fl_panel);
this.DragEnter += new DragEventHandler(Form1_DragEnter);
this.DragDrop += new DragEventHandler(Form1_DragDrop);
}
void Form1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
e.Effect = DragDropEffects.All;
}
void Form1_DragDrop(object sender, DragEventArgs e)
{
string[] fileList = e.Data.GetData(DataFormats.FileDrop) as string[];
foreach (string s in fileList)
{
Button button = new Button();
button.Click += new EventHandler(this.button_Click);
fl_panel.Controls.Add(button);
path_app = String.Format("{0}", s);
button.Tag = path_app;
string filename = path_app;
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(filename);
Bitmap bmp = icon.ToBitmap();
button.BackgroundImage = bmp;
button.Width = 60;
button.Height = 75;
button.FlatStyle = FlatStyle.Flat;
button.BackgroundImageLayout = ImageLayout.Stretch;
}
}
private void button_Click(object sender, System.EventArgs e)
{
String path_app = ((sender as Button).Tag as String);
myProcess.StartInfo.FileName = path_app;
myProcess.Start();
}
private void add_tab_btn_Click(object sender, EventArgs e)
{
//Create new tab with FLP inside
string title = Convert.ToString(textBox1.Text);
TabPage new_TabPage = new TabPage(title);
fl_panel.Dock = DockStyle.Fill;
fl_panel.BringToFront();
new_TabPage.Controls.Add(fl_panel);
tabControl1.TabPages.Add(new_TabPage);
}
}
If I use fl_panel.Controls.Add(button); it adds the buttons fine, on the first tab, but if I create a new tab I don't know how to use tabControl.SelectedTab with the fl_panel.Controls.Add(button) to add the buttons correctly on the selected tab.
You have to create a new FlowLayoutPanel for every tab:
FlowLayoutPanel fl_panel = new FlowLayoutPanel();
...
new_TabPage.Controls.Add(fl_panel);
And then you can cast the first element of the TabPage to the FlowLayoutPanel and access the Controls from there:
FlowLayoutPanel selectedFLP = (FlowLayoutPanel)tabControl.SelectedTab.Controls[0];
...
in an app where it is possible to create a serie of buttons programmatically, how can I retrieve the name of one of these buttons once I click its context menu?
This is the piece of code:
private void addButton_Click(object sender, System.EventArgs e)
{
int y = (buttonIndex * 80) + 5;
btn.Name = "btn" + buttonIndex.ToString();
btn.Content = "button " + buttonIndex.ToString();
btn.Width = 440;
btn.Height = 100;
Thickness margin = new Thickness(0, y, 0, 0);
btn.Margin = margin;
// .. all other properties..
pivot1Grid.Children.Add(btn);
buttonIndex++;
AddContextMenuItems(btn);
}
private void AddContextMenuWithMenuItems(Button btn)
{
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem1 = new MenuItem() { Header = "Edit", Tag = "Edit" };
MenuItem menuItem2 = new MenuItem() { Header = "Remove", Tag = "Remove" };
menuItem1.Click += new RoutedEventHandler(menuItem1_Click);
menuItem2.Click += new RoutedEventHandler(menuItem2_Click);
contextMenu.Items.Add(menuItem1);
contextMenu.Items.Add(menuItem2);
ContextMenuService.SetContextMenu(btn, contextMenu);
}
Now in the click event I should be able to get the name of the button clicked (it works in case the context menu is created directly in XAML):
void menuItem1_Click(object sender, RoutedEventArgs e)
{
string btnName = ((sender as MenuItem).Parent as ContextMenu).Name;
..
}
but it gives always an empty string.
Could you please tell me where is the mistake?
Thanks
In your code, you're casting sender.Parent to ContextMenu. So you know you are manipulating the ContextMenu object. Then why are you expecting to magically get the name of the button by querying the name of the context menu?
The easiest way in your case is to store your information in the Tag property of the context menu:
private void AddContextMenuWithMenuItems(Button btn)
{
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem1 = new MenuItem() { Header = "Edit", Tag = "Edit" };
MenuItem menuItem2 = new MenuItem() { Header = "Remove", Tag = "Remove" };
menuItem1.Click += new RoutedEventHandler(menuItem1_Click);
menuItem2.Click += new RoutedEventHandler(menuItem2_Click);
contextMenu.Items.Add(menuItem1);
contextMenu.Items.Add(menuItem2);
// Store the name of the button in the Tag property of the context menu
contextMenu.Tag = btn.Name;
ContextMenuService.SetContextMenu(btn, contextMenu);
}
Then, in the event handler, you just have to retrieve the value you've set before:
void menuItem1_Click(object sender, RoutedEventArgs e)
{
var contextMenu = (ContextMenu)((MenuItem)sender).Parent;
string btnName = (string)contextMenu.Tag;
..
}
I am trying to combine several ContextMenu items into one. Currently, I am using separate MenuItem for it to work. Is there a way to combine all these MenuItems into one WHILE being able to control each of these event triggers when the users click on the different MenuItems?
NotifyIcon notifyIcon = new NotifyIcon();
System.Windows.Forms.ContextMenu contextMenu = new System.Windows.Forms.ContextMenu();
System.Windows.Forms.MenuItem ChangeDetailsMenu = new System.Windows.Forms.MenuItem();
ChangeDetailsMenu.Text = "Change Contact Details";
ChangeDetailsMenu.Click += ChangeContactDetails;
System.Windows.Forms.MenuItem ChangeKinectAngleMenu = new System.Windows.Forms.MenuItem();
ChangeKinectAngleMenu.Text = "Change Kinect Angle";
ChangeKinectAngleMenu.Click += ChangeKinectAngle;
System.Windows.Forms.MenuItem exitMenu = new System.Windows.Forms.MenuItem();
exitMenu.Text = "Exit";
exitMenu.Click += ExitHandler;
contextMenu.MenuItems.Add(exitMenu);
contextMenu.MenuItems.Add(ChangeDetailsMenu);
contextMenu.MenuItems.Add(ChangeKinectAngleMenu);
Icon icon = new Icon("kse.ico");
notifyIcon.ContextMenu = contextMenu;
notifyIcon.Icon = icon;
notifyIcon.Visible = true;
private void ExitHandler(object sender, EventArgs e)
{
notifyIcon.Visible = false;
System.Windows.Application.Current.Shutdown();
}
private void ChangeContactDetails(object sender, EventArgs e)
{
}
private void ChangeKinectAngle(object sender, EventArgs e)
{
}