• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1302
  • Last Modified:

ToolStripItemClickedEventHandler

I am trying to add an event handler to my dropdownitems of my toolstrip. currently when i add the event handler, each time the event is triggered, the dropdownitems are duplicated.

my toolstrip menu items are projects, and each project can have multiple tasks. when a user clicks on the task, the event handler is triggered, which has a method that starts to track time. this also creates a new menu item to stop the timer.

how can i configure my code to prevent the duplicate items?

Cheers,
Brendan
private void SetupMenu()
        {
            ctxMainMenu.Items.Clear();
            
            //ctxMainMenu.Refresh();
            _workItemValid = false;

            ToolStripMenuItem newMenu = null;

            if (_currentWorkItemID > 0)
            {
                newMenu = new ToolStripMenuItem("Stop Working On WorkItem:" + _currentWorkItemID);
                ctxMainMenu.Items.Add(newMenu);
                ctxMainMenu.Refresh();
            }

            try
            {
                //ctxMainMenu.Refresh();
                foreach (string projectName in checkedProjects.Items)
                {
                    
                    newMenu = new ToolStripMenuItem(projectName);
                    //clear the menu before building a new one
                    newMenu.DropDown.Items.Clear();
                    ctxMainMenu.Refresh();
                    newMenu.DropDownItems.AddRange(GetWorkItemMenuesForProject(projectName));
                    //newMenu.DropDownItemClicked += new ToolStripItemClickedEventHandler(newMenuItem_DropDownItemClicked);
                    ctxMainMenu.Items.Insert(0,newMenu);
                    
                }
            }
            catch { }



****************

        private void newMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                if (e.ClickedItem.Text.Contains("Stop Working"))
                {
                    StopWork();
                }
                else
                {
                    if (_currentWorkItemID > 0)
                    {
                        SwitchToWorkItemTo(int.Parse(e.ClickedItem.Text.Split(':')[0].ToString()));
                    }
                    else
                    {
                        _currentWorkItemID = int.Parse(e.ClickedItem.Text.Split(':')[0].ToString());
                        StartWork(_currentWorkItemID);
                    }
                }
                ctxMainMenu.Refresh();
            }
            catch { }
        }

Open in new window

0
brendanlefavre
Asked:
brendanlefavre
  • 11
  • 11
1 Solution
 
Bob LearnedCommented:
Here is what I see:

1) You are creating a new instance of a ToolStripMenuItem, and then clearing the DropDown.Items, which is not needed, since you just creating a new instance.

newMenu = new ToolStripMenuItem(projectName);
newMenu.DropDown.Items.Clear();

2) I see where you are adding items to the main ContentMenuStrip, but not removing them, so that appears to be where they are getting duplicated.
0
 
brendanlefavreAuthor Commented:
how can i go about removing the duplicates?
0
 
Bob LearnedCommented:
If you describe your overall process (30000 meter view), I might be able to find a way, but right now I am not sure...

I would think that you could define all the menu items one time, and then toggle the visibility, but that doesn't sound quite right to me...
0
Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

 
brendanlefavreAuthor Commented:
I have a WorkItemManager class that has two methods, one for getting projects, and another for getting work items.

the form code behind is where all of the logic is located

My overall goal is to populate the main menu with project items, and then submenu items for each project which are tasks.

there is an event handler attached to the task items that starts and stops a timer, which writes the time back to the data base via a method in the WorkItemManager class.


I have attached both classes to give an idea of where I started.

Cheers,
Brendan

 WorkItemManager.cs
frmMainConfiguration.cs
0
 
Bob LearnedCommented:
In frmMainConfiguration, I see the SetupMenu (that you showed above), which appears to set up the context menu.  It looks like it is only called once.  I don't see where duplicates could be added.

  private void SetupMenu()
        {
            ctxMainMenu.Items.Clear();

Open in new window

0
 
brendanlefavreAuthor Commented:
the duplicates appear after clicking on a task. if you click the task more than once, the duplicates will keep adding up. almost like its appending task items to the menu
0
 
Bob LearnedCommented:
There is a lot of code in frmMainConfiguration, so I don't know which control is associated with the "clicking on a task" action...
0
 
brendanlefavreAuthor Commented:
This is the code associated with the click event, it starts at line 186.

      private void newMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                if (e.ClickedItem.Text.Contains("Stop Working"))
                {
                    StopWork();
                }
                else
                {
                    if (_currentWorkItemID > 0)
                    {
                        SwitchToWorkItemTo(int.Parse(e.ClickedItem.Text.Split(':')[0].ToString()));
                    }
                    else
                    {
                        _currentWorkItemID = int.Parse(e.ClickedItem.Text.Split(':')[0].ToString());
                        StartWork(_currentWorkItemID);
                    }
                }
                ctxMainMenu.Refresh();
            }
            catch { }
        }

Open in new window


Inside the SetupMenu method, and the in the foreach loop. I think this is the line where the duplicates are being generated.

newMenu.DropDownItems.AddRange(GetWorkItemMenuesForProject(projectName));

Open in new window

0
 
Bob LearnedCommented:
Where are all the places where SetupMenu is called?  I don't see a call in newMenuItem_DropDownItemClicked...
0
 
brendanlefavreAuthor Commented:
the strange thing is SetupMenu is only called once, and that is in the LoadSettings method.

Is there a better approach for building the menu?
0
 
Bob LearnedCommented:
Can you attach a .png screen shot of what the menu looks like, please?
0
 
brendanlefavreAuthor Commented:
I have attached a couple of screenshots
startup.png
before-click-event.png
after-click-event.png
0
 
Bob LearnedCommented:
OK, this is what I see now:

1) StopWork calls LoadSettings, and LoadSettings calls SetupMenu.

private void StopWork(bool SuppressNag)
        {
            try
            {
                SaveBank();
                _currentWorkItemID = 0;
                _currentWorkItemMinuteBank = 1;
                LoadSettings(false);
                ctxMainMenu.Refresh();
                ctxMainMenu.Hide();
                if (!SuppressNag)
                    Nag();
            }
            catch { }
        }

Open in new window


2) You are checking to see if the project items already exist in the context menu:

                    
                     if (!ctxMainMenu.Items.Contains(GetWorkItemMenuesForProject(projectName)))
                    {
                        newMenu.DropDownItems.AddRange(GetWorkItemMenuesForProject(projectName));
                    }

Open in new window


3) You are clearing the context menu items in the SetupMenu method, so I don't think that is what is causing the duplicates.  You might want to check the return for GetWorkItemMenuesForProject.

4) One debugging step that I prefer, since you can hover over the variable and see the debugging information is to store the result in an intermediate value:

ToolStripMenuItem[] menuItems = GetWorkItemMenuesForProject(projectName));
newMenu.DropDownItems.AddRange(menuItems);

Open in new window


Put a break point on the newMenu.DropDownItems, check the length for the ToolStripMenuItem array.
0
 
brendanlefavreAuthor Commented:
after stepping through the code, it appears that the method
private ToolStripMenuItem[] GetWorkItemMenuesForProject(string projectName)

Open in new window

is the cause. Each time it is called, the task item is added, even if the item already exists. is it possible to check if an item exists in the array, and only add the item if it does not exist?
0
 
Bob LearnedCommented:
The problem that I can see is that you can't use the Contains method, since it compares object references, and you have different objects, so it won't find a match, and add again.  

What .NET IDE version are you working with?
0
 
brendanlefavreAuthor Commented:
I'm using Visual Studio 2010, but I also have 2008 installed. Most of my development work has been in sharepoint, so winforms are a bit new to me.

Cheers,
Brendan  
0
 
Bob LearnedCommented:
Here is a proposal, using a Lambda expression (since you have 3.5 or higher):

        private ToolStripMenuItem[] GetWorkItemMenuesForProject(string projectName, ToolStripMenuItem parentMenu)
        {
            List<MyWorkItem> workItemList = wim.GetMyWorkItemsForProject(projectName);

             var query = from work in workItemList
                               join menu in parentMenu.DropDownItems
                                    on menu.Text equals work.Name into leftJoin
                               from newMenu in leftJoin.DefaultIfEmpty()
                               where newMenu != null
                               select newMenu;
        ...

Open in new window


Reference:

How to: Perform Left Outer Joins (C# Programming Guide)
http://msdn.microsoft.com/en-us/library/bb397895.aspx
0
 
brendanlefavreAuthor Commented:
thank you for the code sample

couple of questions

-do I only need to add a closing curly brace?

-I'm getting an error "A query body must end with a select clause or a group clause"      
0
 
Bob LearnedCommented:
I didn't set up a compilable project, so that code was developed in the question comment box.  My goal was to only give you a push towards LINQ queries.  

Can you show me the code that you have now?  You may have grabbed the code, before I added the "select"...
0
 
brendanlefavreAuthor Commented:
this is what I have so far

would it help if i attach the entire project?


 
    private ToolStripMenuItem[] GetWorkItemMenuesForProject(string projectName, ToolStripMenuItem parentMenu)
        {
            List<MyWorkItem> workItemList = wim.GetMyWorkItemsForProject(projectName);

             var query = from work in workItemList
                                join menu in parentMenu.DropDownItems
                                    on menu.Text equals work.Name into leftJoin
                                from newMenu in leftJoin.DefaultIfEmpty()
                                select new {work.projectName};

             foreach (var v in query)
             {

             } 

Open in new window

0
 
Bob LearnedCommented:
Aha, this is a great opportunity for a LINQ lesson!!   This is like jumping right into the deep end of the pool, if you haven't worked with LINQ before!!

Scenario:
The development team has 3 developers.  Some of the developers wear two hats, and are part of the architect team.

Question:
Which developers are not architects also?

Example:

          
            List<string> developmentTeam = new List<string> { "John", "Bob", "Greg" };
            List<string> architectTeam = new List<string> { "John", "Jeff", "Alan" };

            var query = from dev in developmentTeam
                        join architect in architectTeam
                            on dev equals architect into merge
                        from result in merge.DefaultIfEmpty()
                        where result == null
                        select dev;

            List<string> differenceList = query.ToList();

Open in new window


Remarks:
To work with outer joins, you need to merge the results into another entity ("merge" in this case).  The DefaultIfEmpty will set the result value to null if a match isn't found.  The "where" condition tests for the null condition, which means only select a value if there isn't a match between development and architect names.
0
 
brendanlefavreAuthor Commented:
your guidance helped me work through the code, and I learned something new along the way. The project that I inherited was loaded with different types of issues, and the menu was just one of them. I have decided to take the approach of starting over.

Cheers,
Brendan
0

Featured Post

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

  • 11
  • 11
Tackle projects and never again get stuck behind a technical roadblock.
Join Now