Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using ListBox in unison with ContextMenuStrip

Status
Not open for further replies.

mcox05

Programmer
Apr 25, 2006
25
US
I am still new to the Event driven GUI programming paradigm of C# so I am just running with an idea of how to implement the desired effect: Imagine a chat room

A ListBox that has a list of names. The Users can highlight and select a user (that is easy to implement) then when 2 conditions are met, a ContextMenuStrip will appear.

Conditions to be met: 1. A name must be selected
2. the mouse click must have occured while hovering over the selected name.

I am using inherintance / Encapsulation to solve this delima.

UListBox contains all normal logic + the logic for using the ContexMenuStrip

Now so far I have been able to create a ListBox with names... but since they are nothing more than strings inside of the ListBox, there is no association to the ContextMenuStrip, thus right clicks do nothing.
Now, I could attach the ContextMenuStrip to the ListBox as so: ListBox.ContextMenu = menu; ... but this means that Condition #1 & 2 may not be met since the user would see the menu no matter what the clicked on within the bounds of the ListBox.

I do want to attach the ContextMenuStrip to The UListBox BUT it needs to implement the logic necessary to include the conditions required for it to be displayed.

I have been working on an implementation method to solve this problem but I'm stuck because I need the coordinates of the name that was clicked. If someone could help me complete this solution or give me some guidence that would be great. If a better solution exists then I'm also all ears for it.

Please refrain from being a coding style critic ... unless something is blatently inefficient then just save us both the time and don't mention it. I'm not a coding expert but I don't need people showing off there views of coding style. Thanks :)

Code:
using System;
using System.Text;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
namespace chatTesting.GUI
{
    public class UListBox : ListBox
    {
        private ContextMenuStrip menu;
        private System.Drawing.Point mousePosition;

        public UListBox():
            base()
        {
            InitializeListBox();
            InitializeDelegates();
            InitializeContextMenu();
        }

        /// INITIALIZATIONS
        /// ////////////////////////////////

        private void InitializeListBox()
        {
            this.FormattingEnabled = true;
            this.ScrollAlwaysVisible = true;
            this.Dock = DockStyle.Fill;
            this.Location = new System.Drawing.Point(0, 0);
            this.Name = "Channel Users";
            this.Size = new System.Drawing.Size(160, 200);
            this.TabIndex = 1;
        }

        private void InitializeDelegates()
        {
            this.MouseClick += new MouseEventHandler(UListBox_MouseMove);
        }

        private void InitializeContextMenu()
        {
            menu = new ContextMenuStrip();
            menu.DefaultDropDownDirection = ToolStripDropDownDirection.AboveRight;
            menu.Visible = true;

            menu.Opening += new System.ComponentModel.CancelEventHandler(menu_Opening);

            ToolStripMenuItem whisperItem = new ToolStripMenuItem("Whisper");
            whisperItem.Size = new System.Drawing.Size(117, 22);
            //whisperItem.Click += new EventHandler(whisperItem_Clicked);

            ToolStripMenuItem silenceItem = new ToolStripMenuItem("Silence");
            silenceItem.Size = new System.Drawing.Size(117, 22);
            //silenceItem.Click += new EventHandler(silenceItem_Clicked);

            ToolStripMenuItem whoIsItem = new ToolStripMenuItem("WhoIs");
            whoIsItem.Size = new System.Drawing.Size(117, 22);
            //whoIsItem.Click += new EventHandler(whoIsItem_Clicked);

            menu.Items.AddRange(new ToolStripItem[] { whisperItem, whoIsItem, silenceItem });
            this.ContextMenuStrip = menu;
        }

        /// CALLBACK METHODS
        /// ////////////////////////////////


        private void menu_Opening(object sender, CancelEventArgs arg)
        {
            //need to implement logic here... for checking to see if menu should display

            //|| (i want to check to see if the mouse click is within the bounds 
            //of the correct name[using mousePosition & unknown source ])
        }

        private void UListBox_MouseClick(object sender, MouseEventArgs arg)
        {
            if (arg.Button == MouseButtons.Right)
            {
                mousePosition.X = arg.X;
                mousePosition.Y = arg.Y;
            }
        }

        /// HELPER METHODS
        /// ////////////////////////////////


        public void PopulateUsersList(string[] users)
        {
            //Singleton InsertionList that is not necessary for this thread to be displayed
            users = InsertionSort.sort(users);
            this.Items.AddRange(users);
        }
    }
}
 
I believe you are using the context menu incorrectly.

You attach the context menu to the listviewitems as you add them to the listview.

ListViewItem item = lstNames.Items.Add("Jimmy");
item.ContextMenu = contextMenuStrip1; //Might be ContextMenuStrip property


then capture the Popup event on the context menu and do any work on the menuitems that you need.
 
a instead of using a ListBox I should be using a ListView in order to achieve the affect? that would make this whole thing much easier to do.
 
well I just checked the API for LIstViewItem ... it does not contain a ContextMenu property so the code example you supplied will not work.
 
Well I scoured the API of ListBox in more detail and discovered a method called int GetItemRectangle(int index).
This method is perfect form completing my implementation of a ListBox with a condition-defined ContextMenuStrip.

Here is the solution code for those who are interested.
Code:
[COLOR=blue]
using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;

namespace chatTesting.GUI[/color]
{
    public class UListBox : ListBox
    {
        private ContextMenuStrip menu;
        private System.Drawing.Point mousePosition;

        public UListBox():
            base()
        {
            InitializeListBox();
            InitializeDelegates();
            InitializeContextMenu();
        }

        [COLOR=green]/// INITIALIZATIONS
        /// ////////////////////////////////[/color]

        private void InitializeListBox()
        {        
            this.FormattingEnabled = true;
            this.ScrollAlwaysVisible = true;
            this.Dock = DockStyle.Fill;
            this.Location = new System.Drawing.Point(0, 0);
            this.Name = "Channel Users";
            this.Size = new System.Drawing.Size(160, 200);
            this.TabIndex = 1;
        }

        private void InitializeDelegates()
        {
            this.MouseMove += new MouseEventHandler(UListBox_MouseMove);
        }

        private void InitializeContextMenu()
        {
            menu = new ContextMenuStrip();
            menu.DefaultDropDownDirection = ToolStripDropDownDirection.AboveRight;
            menu.Visible = true;
            [COLOR=green]//This event is fired before the actual showing process of the ContextMenuStrip, therefore allowing the developer to cancel the opening action if abitrary conditions aren't met[/color]
            [COLOR=red]menu.Opening += new System.ComponentModel.CancelEventHandler(menu_Opening);[/color]

            ToolStripMenuItem whisperItem = new ToolStripMenuItem("Whisper");
            whisperItem.Size = new System.Drawing.Size(117, 22);
            //whisperItem.Click += new EventHandler(whisperItem_Clicked);

            ToolStripMenuItem silenceItem = new ToolStripMenuItem("Silence");
            silenceItem.Size = new System.Drawing.Size(117, 22);
            //silenceItem.Click += new EventHandler(silenceItem_Clicked);

            ToolStripMenuItem whoIsItem = new ToolStripMenuItem("WhoIs");
            whoIsItem.Size = new System.Drawing.Size(117, 22);
            //whoIsItem.Click += new EventHandler(whoIsItem_Clicked);

            menu.Items.AddRange(new ToolStripItem[] { whisperItem, whoIsItem, silenceItem });
            this.ContextMenuStrip = menu;
        }

        [COLOR=green]/// CALLBACK METHODS
        /// ////////////////////////////////[/color]

        [COLOR=red]private void menu_Opening(object sender, CancelEventArgs arg)
        {
            int index = this.SelectedIndex; [COLOR=green]//returns -1 if no index selected[/color]
            if (index > -1)
            {
                Rectangle bounds = this.GetItemRectangle(index);
                int x = mousePosition.X;
                int y = mousePosition.Y;
                [COLOR=green]//if it is true, do nothing. it's easy to check for true than 
                //Not truth in this situation.[/color]
                if (((x >= bounds.X) && (y >= bounds.Y)) &&
                     ((x <= bounds.X + bounds.Width) && (y <= bounds.Y + bounds.Height))) ;
                else
                    arg.Cancel = true;
            }
        }[/color]

        [COLOR=red]private void UListBox_MouseMove(object sender, MouseEventArgs arg)
        {
            mousePosition.X = arg.X;
            mousePosition.Y = arg.Y;
        }[/color]

        [COLOR=green]/// HELPER METHODS
        /// ////////////////////////////////[/color]

        public void PopulateUsersList(string[] users)
        {
            users = InsertionSort.sort(users);
            this.Items.AddRange(users);
        }
    }
}
 
Just as a performance note - instead of using mouseMove you might want to capture the MouseDown event and set your mouse position at this point.

Remember too that you can always get the mouse position at any time using Cursor.Position


Thanks for your solution though!
 
Those are great suggestions and I wholy agree that constantly tracking the mouse movement is a performance hit BUT unfortunately the order in which the CALLBACK methods are executed rules out the ability to use MouseDown event.

The menu_Opening() event will execute first which means that the MouseDown event will not record the position first. This obviously presents a problem because the menu_Opening() method will then check the position ... it will not be recorded as it should have been therfore the ContextMenuStrip will not appear.

As for the idea of using MousePosition .. that tracks the Mouse's position on the entire application using absolute corrdinates whereas the corrdinates supplied by the GetItemRectangle(int index) method will supply a Rectangle with relative corrdinates (relative to the UListBox itself) this pretty much means that the ContextMenuStrip will not show at all except in bazaar situations.

Idea #2 does have promise though ... I suppose there might be a mathmatical way to covert those absolute corrdinates supplied by MousePosition into relative coordinates to UListBox.


Therefore, I was left with the simplest approach which was to record mouse movements that where within the UListBox control. This action gives me relative corrdinates that correlate correctly with the coordinates of the items in UListBox

I'm going to attempt Idea #2 for kicks to see if I can improve performance. I'll repost code if I figure out the solution.
 
Ha my mistake. Thanks for the easy fix JurkMonkey
The menu_Opening() event will execute first which means that the MouseDown event will not record the position first. This obviously presents a problem because the menu_Opening() method will then check the position ... it will not be recorded as it should have been therfore the ContextMenuStrip will not appear.

---Myself

seems that with Click, MouseClick ... menu_Opening() will execute first but MouseDown will execute before menu_Opening() which makes a performance gain over tracking the mous movements constantly.

Glad you made the suggustion so I would double check it. :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top