I have two ContextMenuStrip(s) in a Windows Form Application, one of them has 3 items and the other one has none.
Let's suppose this:
ContextMenuStrip c1 = new ContextMenuStrip();
ContextMenuStrip c2;
c1 has 3 ToolStripMenuItems, c2 is the ContextMenuStrip destination where c1 items should be duplicated.
I tried to write this:
c2 = new ContextMenuStrip(c1.Container);
but it gives me an ArgumentNullException because c1.Container is Equal to null.
I cant figure out how to solve this, can you help me?
Ps.
I would new ToolStripMenuItem(s), no references
and
while or foreach loops solutions are not the best way to do this.
Thank you :)
Then, have a function that creates the ContextMenuStrip and call it each time a new menu is needed
Func<ContextMenuStrip> newContextMenuStrip = () => {
var c = new ContextMenuStrip();
c.Items.Add("item 1");
c.Items.Add("item 2");
c.Items.Add("item 3");
return c;
};
var c1 = newContextMenuStrip();
var c2 = newContextMenuStrip();
Late to the party, but I have had the same issue in the past and found a reasonably simple solution.
Say your toolStripMenuItem is declared as 'TSMI_open' in a context menu,
you can effectively hot-swap it between context menus as they open.
Something like this:
void Context1_Opening(object sender, CancelEventArgs e)
{
Context1.Items.Insert(0, TSMI_open);
}
void Context2_Opening(object sender, CancelEventArgs e)
{
Context2.Items.Insert(0, TSMI_open);
}
The menu item will appear on both menus when seamlessly, and will cause no errors if the same menu is opened twice consecutively.
You need to create a new ContextMenuStrip and add the Items (not the Container of c1 to the new menu:
c2 = new ContextMenuStrip();
c2.Items.AddRange(c1.Items);
But note that this does not duplicate the items. The same item instances are now in both menus.
If you want to clone them, this is rather complicated as you have to take care of the specific types of the items, the properties you want to clone and especially the event handlers.
A simple example could be:
c2.Items.AddRange(c1.Items.OfType<ToolStripItem>()
.Select(item => new ToolStripMenuItem(item.Text))
.OfType<ToolStripItem>().ToArray());
The second OfType is necessary to avoid a co-variant array conversion from ToolStripMenuItem[] to ToolStripItem[] which is expected by AddRange().
And a side note: Container is the component that contains the menu (thatswhy it's null when the menu is not shown) and not the thing the menu keeps its items in.
Related
This is what I have so far and working correctly:
A RangeBar Chart with two Series, that I fill with Points at runtime, based on the results of an Oracle query. Each Point represents a data object with a number of properties.
What I would like to add to this is the following:
For each visible Point on my chart, I want the user to be able to right click the Point in order to open a Menu with a number of options. These options should invoke certain functioncalls, with a property of the selected Point used as a parameter in that functioncall. In this function then, a new window is to be opened that would display some information based on the data object represented by the Point and the menu item that was clicked.
What have I tried so far? I started fooling around with a ContextMenu based on information in this topic: Adding a right click menu to an item:
ContextMenu cm = new System.Windows.Forms.ContextMenu();
cm.MenuItems.Add("Item 1", new EventHandler(Item1_Click));
But much to my disappointment, I discover that a ContextMenu can only be assigned to my Chart object, while I wished to assign it to an individual Point in my Series' Points collection:
chart.ContextMenu = cm; // This works
serie1.Points[DataObject.pointIndex].ContextMenu = cm; // This does not work unfortunately
Looking at the documentation of the Points collection assigned to a Chart Series, I find no mention of any sort of Menu whatsoever. Does this mean that what I'm looking for simply isn't feasible in a technical sense or am I overseeing something? And if so, would there be any other technical implementation that would deliver the functionality I'm looking for? (right now I'm thinking a ToolTip could cover some of my needs, but this wouldn't be nearly as elegant nor extensive as displaying the information in a new window)
Points are not Controls. But you can easily use just one Context menu on the chart, open it it at the right spot and and feed in the relevanty point data. All you need is doing a HitTest on the Chart and opening the ContextMenu at the mouse position:
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Right))
{
var hitt = chart1.HitTest(e.X, e.Y);
DataPoint dp = null;
if (hitt.PointIndex >= 0)
{
dp = hitt.Series.Points[hitt.PointIndex];
}
ContextMenu cm = new System.Windows.Forms.ContextMenu(); // either reuse or dispose!
cm.MenuItems.Add("Item 1 X:" + dp.XValue, new EventHandler(Item1_Click));
cm.Tag = dp;
cm.Show(chart1, e.Location);
}
}
private void Item1_Click(object sender, EventArgs e)
{
Console.WriteLine("Item1_Click");
DataPoint dp = (sender as MenuItem).Parent.Tag as DataPoint;
if (dp != null) Console.WriteLine("Y:" + dp.YValues[0]);
}
If feed the DataPoint into the Tag property of the ContextMenu. Feel free to do your own processing..
I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}
I am adding Context Items for a Context Menu and showing the required Items where necessary for the user based on user selection. I would like to show these context items for the user NEW and CLOSE..
I did some thing like
ContextMenu.Add(NEW)
ContextMenu.Add(CLOSE)
But I am getting this in sorted order like CLOSE first and NEW last . But I need to display NEW first and CLOSE last. Is it possible to do.
This (very basic code) should do it. Place following code in the constructor of your Form:
var contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(new MenuItem("New"));
contextMenu.MenuItems.Add(new MenuItem("Close"));
this.ContextMenu = contextMenu;
Note: you still have to add the events yourself... :)
Update:
To add events to the items you'll have to declare them in a variable instead of passing them directly in the Add() method for the MenuItems. So the previous code will look like this:
var contextMenu = new ContextMenu();
var itemOne = new MenuItem("New");
itemOne.Click += ContextMenuItemClick;
contextMenu.MenuItems.Add(itemOne);
var itemTwo = new MenuItem("Close");
itemTwo.Click += ContextMenuItemClick;
contextMenu.MenuItems.Add(itemTwo);
ContextMenu = contextMenu;
As you can see, bot items have the same eventhandler for the Click-event. In that event you check which item was clicked. That code looks like this:
private void ContextMenuItemClick(object sender, EventArgs e)
{
var selectedItem = (MenuItem)sender;
switch(selectedItem.Text)
{
case "New" : //do some new stuff
break;
case "Close": //do some closing stuff
break;
}
}
Note that you could also set a separate eventhandler for each item in the menu, but then you end up with lots of methods for basically the same stuff... :) Hope this helps!
Update2:
With all the help I gave, you normally should have been able to achieve this by yourself, not? :) Anyway, for a separate handler the code will look like this:
itemOne.Click += itemOne_Click;
itemTwo.Click += itemTwo_Click;
private void itemOne_Click(object sender, EventArgs e)
{
//do the new stuff
}
private void itemTwo_Click(object sender, EventArgs e)
{
//do the closing stuff
}
Update3:
If you gave proper names to the menuitems you already added, you can change the order using the Index-property. Say I have following menuitems added to a ContextMenu:
var itemOne = new MenuItem("New") { Name = "NewItem" };
var itemTwo = new MenuItem("Close") { Name = "CloseItem" };
The 'NewItem' will be the first item and the 'CloseItem' will be the second. Now if I want to change the order without touching previous code you can do this:
contextMenu.MenuItems["NewItem"].Index = 1;
This will set the 'CloseItem' as the first and the 'NewItem' as the second. If you have more than 2 items, you better set the Index-property for each item individually.
I want my items list in a listbox but when i try
listbox1.Items.Add("Item1");
nothing is being added but
if i place the code in the forms load metod it works but when I call it from the
separate code module it does not. I think this is because another instance
of the form is being updated.
but how do i get the active form and then add the items.
I got a Abstract class and tree subclasses and i want my subclasses in a list and then showed on the form.
this was my first attempt but this dosent work.
private void button1_Click(object sender, EventArgs e)
{
Subclass o = new Subclass();
List<BaseClass> l = new List<BaseClass>();
l.Add(o);
Form1 f = new Form1();
f.AddObjectToListbox(l);
}
And then in my From1 i got
public void AddObjectToListbox(List<BaseClass> l)
{
foreach (Subclass objectname in l.OfType<Subclass>())
{
l.Items.Insert(0, "text" + O.getMetod);
}
}
but like it is now it just add to another instance of form1.
thanks for alle the help.
Your question is a litle bit vague...where are you calling listbox1.Items.Add("Item1"); from
e.g.
From a method in the Forms class file
From code in another file,
From code in a different assembly,
However, you can get the Active Form via the Form class static method:
System.Windows.Forms.Form.ActiveForm
May this helps
One problem might be the use of "Object" as both a class and variable name in the AddObjectToListBox method.
Also if you are casting obj to Subclass, but then assign it to a variable of type Object, that is not making much sense. You could solve both of these at once, like this.
foreach (Subclass obj in l.OfType<Subclass>())
{
lbVehicles.Items.Insert(0, "text" + obj.getMetod);
}
As a third, it looks like you're creating a new form every time the button is clicked, is that intentional?
suddenly you find what you was looking for yourself.
this did it.
Form currentForm = Form.ActiveForm;
ListBox lb = (ListBox)currentForm.Controls.Find("ListboxName", true)[0];
but still thanks for looking at my question.
First try to clear items and then try to add
I have a form. I've added the strip down button using drag and drop in the form. How can I (in the program) create and fill the toolStripMenu Item? My menu could contain different element...with different names.
If you want to add items programmatically to a ToolStripDropDownButton just do:
var item1 = new ToolStripButton("my button");
toolStripDropDownButton1.DropDownItems.Add(item1);
var item2 = new ToolStripComboBox("my combo");
toolStripDropDownButton1.DropDownItems.Add(item2);
// etc ...
Instead, if you need to add other ToolStripDropDownButton or other elements directly to you menu (ToolStrip), just do:
var item1 = new ToolStripDropDownButton("my dropdown button");
toolStrip1.Items.Add(item1);
var item2 = new ToolStripProgressBar("my progress bar");
toolStrip1.Items.Add(item2);
// etc ...
EDIT:
You must do it after InitializeComponent() otherwise you won't be able to access to design-time added components, e.g.:
InitializeComponent();
// we're after InitializeComponent...
// let's add 10 buttons under "toolStripDropDownButton1" ...
for (int i = 0; i < 10; i++)
{
var item = new ToolStripButton("Button_"+i);
toolStripDropDownButton1.DropDownItems.Add(item);
}
For the DropDown property you need a ContextMenuStrip. The easiest way to find out how to fill it up is to drag&drop one from the toolbox onto your form, fill it up, select it in the DropDown property and afterwards take a look into the Designer.cs file to see how all the stuff is glued together.
The drawback of using the DropDownItems property is that you can't alter some properties like ShowImageMargin.