c# object type conversion - c#

I'm trying to create a context menu which will be able to add extra menu items, with an attached child menu, as required. I've been trying to do it so I have separate classes building up each part so it can be written nicely with objects.
The problem I am having is that the AddRange method for ContextMenuStrip doesn't have a constructor to deal with my object. I've tried converting it to ToolStripMenuItem type with operators which has not worked, as I suspected it wouldn't.
I am sure this can be achieved so I assume I've thought something through wrong. Is there a way to get around this or I banning my head against a wall with my current structuring?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Context
{
class TestMenu
{
public TestMenu()
{
ContextMenuStrip filesToUploadContext = new System.Windows.Forms.ContextMenuStrip();
// Hot Folder Header
ToolStripMenuItem hotHead = new System.Windows.Forms.ToolStripMenuItem();
// Holder for files in Hot Folder
ParentItem hotFile; // foreach
// Dropped Files Header
ToolStripMenuItem dropHead = new System.Windows.Forms.ToolStripMenuItem();
// Holder for files that have been dragged and dropped in
ParentItem dropFile; // foreach
ToolStripSeparator toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
ToolStripSeparator toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
filesToUploadContext.Items.AddRange(new ToolStripItem[] {
hotHead,
toolStripSeparator1,
hotFile, // Not a toolStrip item
dropHead,
toolStripSeparator2,
dropFile // also not a toolStrip item
});
//// Testing stuff vv
//// Hot Folder
//hotFile.DropDownItems.AddRange(new ToolStripItem[]
// {
// viewHot,
// deleteHotFile
// });
//// Dropped Items Folder
//dropFile.DropDownItems.AddRange(new ToolStripItem[]
// {
// viewDrop,
// removeDropFile
// });
//// Hot Folder Section Heading
//hotHead.Name = "hotHead";
//hotHead.Text = "Hot Folder Files";
//hotHead.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
//// Drop Folder Section Heading
//dropHead.Name = "dropHead";
//dropHead.Text = "Dropped Files";
//dropHead.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
}
class ParentItem
{
// MenuItem to be used for found files
// Options will contain child items
public ToolStripMenuItem name = new ToolStripMenuItem();
public ChildMenu options { get; set; }
public ParentItem();
}
class ChildMenu
{
// Options available for specific files at end of menu tree
public ToolStripMenuItem view = new ToolStripMenuItem("View File");
public ToolStripMenuItem delete = new ToolStripMenuItem("Delete File");
public ToolStripMenuItem remove = new ToolStripMenuItem("Remove File");
public ChildMenu();
}
}
}

You can't add anything else than a ToolStripMenuItem as child of a ToolStripMenuItem.
Don't spend time trying to inherit your ChildMenu and ParentItem classes from ToolStripMenuItem. Just add ToolStripMenuItems and drop these classes.
EDIT:
I would do something like the following (actually compiles):
public void PopulateMenu(string fileName, ContextMenuStrip parent)
{
ToolStripMenuItem newMenu = new ToolStripMenuItem(fileName);
newMenu.DropDownItems.Add(new ToolStripMenuItem("View File"));
newMenu.DropDownItems.Add(new ToolStripMenuItem("Delete File"));
newMenu.DropDownItems.Add(new ToolStripMenuItem("Remove File"));
parent.Items.Add(newMenu);
}
Then for each of your file:
this.PopulateMenu(the_file_name, the_parent_menu);
Also don't forget to attach handlers to the Click event of your menus, or they'll be useless ;-)

In order to convert your object to a different type, like SystemObject newObject = (SystemObject)myObject those two types will need to have some relation to one another. Depending on the circumstance they can either have a common Interface or have some ancestry (if your type inherits from the other type, for example).
In your specific example your object would need to inherit from the ToolStripMenuItem object in order to be converted to one.
I'm sorry it's not better news, I hope that might help you a little though :)
On rereading my answer
I realise I might be teaching you to suck eggs and you're actually looking for a way to use AddRange and an object without a relation to ToolStripMenuItem, so please accept my apologies if that is the case :)

Related

Debugger Visualizer Winform ToolStripDropDownMenu ComboBox only shows items when first shown

I have a Visual Studio debugger visualizer project, and when I add a ToolStripComboBox to a ToolStripDropDownMenu, the combobox's items only appear the first time the form is shown.
Like this:
The most basic version of the winform code showing the issue is this:
public class MyVisualizerDialog : Form
{
public MyVisualizerDialog()
{
var toolStripComboBox = new ToolStripComboBox
{
Items = { "One", "Two", "Three" }
};
var toolStripDownDown = new ToolStripDropDownMenu
{
Items = { toolStripComboBox }
};
var toolStrip = new ToolStrip
{
Items =
{
new ToolStripMenuItem("Options")
{
DropDown = toolStripDownDown
}
}
};
Controls.Add(toolStrip);
}
}
Then the visualizer code is simply:
public class MyVisualizer : DialogDebuggerVisualizer
{
protected override void Show(
IDialogVisualizerService windowService,
IVisualizerObjectProvider objectProvider)
{
windowService.ShowDialog(
new MyVisualizerDialog());
}
}
Some extra details:
If I add the ToolStripComboBox to ToolStripMenuItem.DropDownItems, it works fine - it seems to specifically be an issue with having a ToolStripComboBox in a ToolStripDropDown.
If I create and open multiple instances of the same form class in a console app, it works fine.
Once the issue occurs, it keeps occurring - even when I revert the code to the version without the ToolStripDropDown
If I restart Visual Studio, it works the first time the form is shown, then not afterwards.
Any ideas?! Anyone know some wrinkle in the way the IDialogVisualizerService disposes controls or something?!
Thanks for reading :)
It appears that, when the debugger visualizer is closed - which is handled in the debugger side, not in the debuggee side - the DropDown is destroyed but the ToolStripManager doesn't know about it and it finds itself with an invalid handle that it doesn't know how to manage.
Since the ToolStripManager is also active in design mode, this propagates the problem throughout the designer interface: you may find that some DropDown items still work after the debugger visualizer has been closed, but you may not be able to add other ToolStripComboBox items anywhere.
If you insist, also those that appeared to be working may not work anymore.
Note that this misbehavior can translate to ComboBox objects; not directly, but when you try to access their Items collection through the interface.
It may also prevent the Project from compiling.
Explicitly disposing of the Form object created in the debugger visualizer side, can partially solve the problem on the debuggee side, but not, as it turns out, on the debugger visualizer side.
A simple solution is to avoid setting the DropDown object of a ToolStripMenuItem and use a MenuStrip instead, adding Items to a ToolStripDownDown.
Create custom data visualizers
Visualizer Security Considerations
Sample debugger visualizer (simple Image visualizer) to test the good and bad behavior.
▶ Create a Class Library Project, Target Framework set to .Net Framework, AnyCPU profile.
▶ Add a reference to [Visual Studio install Path]\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.DebuggerVisualizers.dll and System.Windows.Forms.
▶ Compile the .dll as Release.
▶ Copy the .dll to the \Common7\Packages\Debugger\Visualizers directory of your current Visual Studio installation path.
▶ Start a debug session, add a breakpoint where an Image/Bitmap property is set/loaded and use the magnifier tool to open a preview.
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.DebuggerVisualizers;
[assembly: DebuggerVisualizer(
typeof(ImageVisualizer.DebuggerSide),
typeof(VisualizerObjectSource), Target = typeof(Image), Description = "Test Visualizer")]
namespace TestVisualizer
{
public class DebuggerSide : DialogDebuggerVisualizer
{
override protected void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
var image = (Image)objectProvider.GetObject();
var form = new Form();
form.ClientSize = new Size(image.Width, image.Height);
form.FormBorderStyle = FormBorderStyle.FixedSingle;
form.SuspendLayout();
// ------- WORKING CODE ---------------
var menuStrip = new MenuStrip() { };
var tsComboBox = new ToolStripComboBox { Items = { "One", "Two", "Three" } };
var toolStripDownDown = new ToolStripMenuItem() { Text = "Options" };
toolStripDownDown.DropDownItems.AddRange(new ToolStripItem[] { tsComboBox });
menuStrip.Items.AddRange(new ToolStripItem[] { toolStripDownDown });
// ------- WORKING CODE ---------------
// ------- BAD CODE ---------------
//var toolStripComboBox = new ToolStripComboBox { Items = { "One", "Two", "Three" } };
//var toolStripDownDown = new ToolStripDropDownMenu { Items = { toolStripComboBox } };
//var toolStrip = new ToolStrip {
// Items = { new ToolStripMenuItem("Options") { DropDown = toolStripDownDown } }
//};
// ------- BAD CODE ---------------
var pBox = new PictureBox() { Image = image, Dock = DockStyle.Fill };
//form.Controls.Add(toolStrip);
form.Controls.Add(menuStrip);
form.Controls.Add(pBox);
form.MainMenuStrip = menuStrip;
form.ResumeLayout(false);
form.PerformLayout();
windowService.ShowDialog(form);
form?.Dispose();
}
}
}

How to get GTKSharp TreeView widget to display expanders?

So I am creating a treeview selector with C#/GTKSharp. I have the basic tree view selector functionality working: The data is loaded into my model and I can click on a node to collapse/expand.
The part I can't work out is how to tell the cell renderer to display the collapse/expand toggle button. In the examples it appears as a triangle that points right or down depending on whether the node is opened or collapsed. I just have a blank space that works as expected as I click but shows nothing.
One possibility is that I have a white on white text issue but I doubt it as my labels show up fine and I have not done any formatting yet.
I tried adding code for ShowExpanders but that was already true.
TreeView = new Gtk.TreeView();
// We add the event handlers (i.e. the control part) to the tree
TreeView.RowActivated += SelectorActivated; //On double click
TreeView.Selection.Changed += SelectorSelected; // On select (single click)
// Raise a context menu here??
// Connect to the ButtonPressEvent
// Raise a popup button
// Create columns [View]
Gtk.TreeViewColumn TreeViewColumTitle = new Gtk.TreeViewColumn();
TreeViewColumTitle.Title = "Profile";
Gtk.CellRendererText NameCellTitle = new Gtk.CellRendererText();
TreeViewColumTitle.PackStart(NameCellTitle, true);
TreeViewColumTitle.SetCellDataFunc(NameCellTitle, new Gtk.TreeCellDataFunc(RenderTitle));
NameCellTitle.Mode = CellRendererMode.Activatable;
// Populate the model
// Note that we could dispense with this step if we generated an ITreeModel
// interface in the Object class.
BindModel(Model);
// Attach everything to the pane
TreeView.Model = GTKModel;
TreeView.AppendColumn(TreeViewColumTitle);
TreeView.ShowExpanders = true;
TreeView.ExpanderColumn.Visible = true;
...
private void BindModel(Model Model) {
GTKModel = new Gtk.TreeStore(typeof(Object));
foreach (Object Object in Model.Selector) {
var BindingData = new BindingDataGTK(this, Object);
BindingData.Iter = GTKModel.AppendValues(Object);
Object.BindingData = BindingData;
BindChildren(GTKModel, BindingData);
}
}
private void BindChildren(TreeStore TreeStore, BindingDataGTK ObjectBinding) {
foreach (var Child in ObjectBinding.Object) {
var BindingData = new BindingDataGTK(this, Child);
BindingData.Iter = TreeStore.AppendValues(ObjectBinding.Iter, Child);
Child.BindingData = BindingData;
BindChildren(TreeStore, BindingData);
}
}
private void RenderTitle(Gtk.TreeViewColumn Column, Gtk.CellRenderer Cell,
Gtk.ITreeModel GTKModel, Gtk.TreeIter Iter) {
Object Object = (Object)GTKModel.GetValue(Iter, 0);
(Cell as Gtk.CellRendererText).Text = Object.Title;
Console.WriteLine("Render {0}", Object.Title);
}
So far as I know this is pretty much an automatic feature, I don't think anything special is needed to make it happen (I've certainly never needed to). You might want to try using a TreeIter to construct your tree instead?
E.g. assuming you already have a TreeView on your form with 0 (zero) columns in it called "treeview" and a list of "MyObject"s called "myListOfObjects"...
treeview.AppendColumn ("Some Title", new CellRendererText(), "text", 0);
Gtk.TreeStore _ts = new TreeStore (typeof(string));
foreach (IMyObject _mo in myListOfObjects) {
Gtk.TreeIter _it = _ts.AppendValues (_mo.SomeText);
RecurseInto (_ts, _it, _mo);
}
treeview.Model = _ts;
...
void RescureInto(Gtk.TreeStore ts, Gtk.TreeIter it, IMyObject mo)
{
foreach (IMyObject _child_mo in mo.Children) {
Gtk.TreeIter _it = ts.AppendValues (it, _child_mo.SomeText);
RecurseInto (ts, _it, _child_mo);
}
}
In theory this should work fine.

Elegant way to remove a ToolStripMenuItem

I am looking for an elegant way to remove a specific menu item called Annotate from the ContextMenu. This is how it is done, so I would appreciate your input on this.
public sealed class ContextMenuStripEx : ContextMenuStrip
{
private readonly ToolStripMenuItem _createAnnotationToolStripMenuItem = new ToolStripMenuItem();
...
public PlotContextMenuStripEx()
{
...
Items.AddRange(new ToolStripItem[]
{
...
_createAnnotationToolStripMenuItem,
...
});
//
// createAnnotationToolStripMenuItem
//
_createAnnotationToolStripMenuItem.Name = "createAnnotationToolStripMenuItem";
_createAnnotationToolStripMenuItem.Size = new Size(169, 22);
_createAnnotationToolStripMenuItem.Image = CommonRes.tsAnnotateM;
_createAnnotationToolStripMenuItem.Text = "Annotate";
}
}
Now imagine somewhere else in another class there is a call to get the ContextMenuStrip, something like:
ContextMenuStrip menuplot = myControl.GetPaneContextMenu();
I want to make the removal part more elegant, because I dont want to rely on the string comparison. Its very ugly:
foreach (var item in menuplot.Items)
{
var name = (item as ToolStripItem).Name;
if (string.Compare(name, "createAnnotationToolStripMenuItem") == 0)
{
// remove the item
}
}
Is there any better way to do this please? many thanks.
I suggest:
having the menu items references property accessors set to internal, or public.
On the other hand, I would hide menu items instead of removing them. This can be done in the Opening event. The reason is that removing items from the collection may be painful to handle later when the same instance of the context menu is reused.
Instead of a loop and removal, I use this way:
void menuplot_Opening(object sender, CancelEventArgs e)
{
...
// Accessible menu items are easier to handle
menuplot.createAnnotationToolStripMenuItem.Visible = false;
...
}
The Opening event is interesting. For example, it allows you to check and cancel the opening of the popup if your conditions are not met by setting e.Cancel = true;

How to read a stack.peek in order to put it in an if statement C#

I am trying to create a menu system and i am storing the menus in stacks once they have already been visited. Im trying to use Stack.Peek() to basically say: if menuName = menuStack.Peek, then continue.
menus have a drawRectangle, sprite, and Menuname enumeration associated with them, and all menus are child classes of the Menu class.
public static void GoToMenu(MenuName menuName)
{
Stack<Menu> menuStack = new Stack<Menu>();
Stack<Menu> tempStack = new Stack<Menu>();
if(menuStack.Peek() = MainMenu){
}
}
More or less, if menuStack.Peek returns a mainMenu object. How do i check that?
i just really dont know how to read menuStack.Peek(). I dont know how to apply it to an if statement to check if it equals a mainmenu object, a pausemenu object or whatever.
public static void GoToMenu(MenuName menuName)
{
Stack<Menu> menuStack = new Stack<Menu>();
Stack<Menu> tempStack = new Stack<Menu>();
if(menuStack.Peek().Name == menuName){
menuStack.Pop();
}
}
that is what i needed

VS2005 C# Currency Manager Issue with Position Not Changing

We have a custom collection of objects that we bind to a listbox control. When an item is added to the list the item appears in the listbox, however when one selects the item the currency manager position will not go to the position. Instead the currency manager position stays at the existing position. The listbox item is high lighted as long as the mouse is press however the cm never changes position.
If I copy one of the collection objects the listbox operates properly.
One additional note the collection also has collections within it, not sure if this would be an issue.
I found the issue, after spending way too much time....
This issue was related to one of the propertys of the item(custom class) in the collection which was bound to a date picker control. The constructor for the class never set the value to a default value.
This caused an issue with the currency manager not allowing the position to change as the specific property (bound to the date picker) was not valid.
Me bad! I know better!
You might need to post some code; the following (with two lists tied together only by the CM) shows that it works fine... so to find the bug we might need some code.
using System;
using System.ComponentModel;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
BindingList<Foo> foos = new BindingList<Foo>();
foos.Add(new Foo("abc"));
foos.Add(new Foo("def"));
ListBox lb1 = new ListBox(), lb2 = new ListBox();
lb1.DataSource = lb2.DataSource = foos;
lb1.DisplayMember = lb2.DisplayMember = "Bar";
lb1.Dock = DockStyle.Left;
lb2.Dock = DockStyle.Right;
Button b = new Button();
b.Text = "Add";
b.Dock = DockStyle.Top;
b.Click += delegate
{
foos.Add(new Foo("new item"));
};
Form form = new Form();
form.Controls.Add(lb1);
form.Controls.Add(lb2);
form.Controls.Add(b);
Application.Run(form);
}
}
class Foo
{
public Foo(string bar) {this.Bar = bar;}
private string bar;
public string Bar {
get {return bar;}
set {bar = value;}
}
}
Collections don't have a sense of "current item". Perhaps your custom collection does, but the ListBox is not using that. It has its own "current item" index into the collection. You need to handle SelectedIndexChanged events to keep them in sync.

Categories