Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / WinForm General / February 2005

Tip: Looking for answers? Try searching our database.

Mouse capture for custom context or drop-down menus

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Fardreamer - 12 Feb 2005 15:25 GMT
Hello,

I'm implementing an undo/redo drop-down menu similar to the undo/redo
menus in VS.NET 2003. The idea is that when the drop-down arrow is
pressed, a floating ListBox control appears under the pressed button,
and disappears when the user chooses an action to undo or clicks
anywhere else in the form - meaning that when the ListBox is open, I
need to handle every MouseDown event on the form.

I tried attaching an event handler to the MouseDown event of the
ListBox and then setting ListBox.Capture = true, but unfortunately that
didn't do the trick (why I couldn't figure out). Mouse events were
raised for every control on the form that handles them, and my
ListBox.MouseDown handler was only called when the mouse was over the
list box - i.e., capture wasn't working. Next I tried P/invoking
SetCapture and ReleaseCapture, but with the same result (unless the
mouse button was still pressed, in which case ALL system mouse events
were captured - far from ideal).

After lots of searching I found a message on this group saying that
only in a MouseLeave event handler will Control.Capture = true actually
make any difference - that turned out to be true and I finally got
capture to work. The only problem was that since the ListBox first
appears when the cursor is still on the drop-down button, capture
wasn't set until the cursor was moved over and out of the ListBox.

I finally solved it by some ugly cursor position fiddling. I
(programmatically) move the cursor position over the ListBox and then
move it back. I'm sharing this here in case someone gets stuck on a
similar problem. Also, if anyone has a better solution I'd love to see
it.

// Called from the toolbar's ButtonDropDown event handler
private void OpenHistoryList(ToolBarButton button)
{
    // Get the entry list associated with this history button
    HistoryEntryList entryList = (HistoryEntryList) button.Tag;

    // Add the list to the form
    ListBox listbox = new ListBox();
    this.Controls.Add(listbox);

    // Retrieve sizes from the configuration file
    int width =
int.Parse(ConfigurationSettings.AppSettings["History.Width"]);
    int height =
int.Parse(ConfigurationSettings.AppSettings["History.Height"]);

    // Set the list coordinates
    listbox.Location = new Point(button.Rectangle.X, button.Rectangle.Y +
button.Rectangle.Height);
    listbox.Width = width;
    listbox.Height = height;

    // Add the action descriptions to the list
    foreach(HistoryEntry entry in entryList)
    {
        listbox.Items.Add(entry.Description);
    }

    listbox.BringToFront();
    listbox.Focus();
    listbox.MouseDown += new MouseEventHandler(CloseHistoryList);
    listbox.MouseLeave += new EventHandler(CaptureHistoryList);

    // Fiddle with mouse position to generate a MouseLeave event
automatically
    Point currentPos = Cursor.Position;
    Rectangle listCoords =
listbox.RectangleToScreen(listbox.ClientRectangle);
    Cursor.Position = new Point(listCoords.Left+1, listCoords.Top+1);
    Application.DoEvents();
    Cursor.Position = currentPos;
}

private void CloseHistoryList(object sender, MouseEventArgs e)
{
    // Get the listbox's coordinates
    ListBox listbox = (ListBox) sender;
    Rectangle listCoords =
listbox.RectangleToScreen(listbox.ClientRectangle);

    // Check whether the click was within the listbox's bounds
    if (
        e.X < listCoords.X || e.X > listCoords.X + listCoords.Width ||
        e.Y< listCoords.Y || e.Y > listCoords.Y + listCoords.Height
    )
    {
        // Remove the listbox from the form, release mouse capture and
dispose of the listbox
        this.Controls.Remove(listbox);
        listbox.Capture = false;
        listbox.Dispose();
    }

}

private void CaptureHistoryList(object sender, EventArgs e)
{
    ListBox listbox = (ListBox) sender;
    listbox.Capture = true;
}

Hope that's useful

-Doron
Matt Garven - 14 Feb 2005 00:41 GMT
Hi,

I'm curious why a regular ComboBox wasn't up to scratch in this particular
situation?

You could consider using a mouse hook if you're not put off by a bit of
p/invoking. That has worked well for us in similar circumstances. I can put
together a small code sample if you're interested.

Regards,
Matt Garven

> Hello,
>
[quoted text clipped - 102 lines]
>
> -Doron
Fardreamer - 14 Feb 2005 06:46 GMT
Hi Matt, thanks for your reply.

I'm not aware of any functionality that allows you to invoke only the
drop-down part of a ComboBox... did I miss something? Also, I have no
idea what a mouse hook is, but I'd be interested to see a code sample.

BTW, the condition in CloseHistoryList is incorrect. The conversion to
screen coordinates is unnecessary. The correct expression is

e.X < 0 || e.X > listbox.Width ||  e.Y< 0 || e.Y > listbox.Height
Matt Garven - 14 Feb 2005 07:41 GMT
Right, I didn't realise you wanted just the drop down part.

Information on Windows Hooks are available here (specifically on Mouse Hooks
which is what you want):
http://support.microsoft.com/default.aspx?scid=kb;EN-US;318804

You can intercept all mouse events before they're passed to your
application. What the article doesn't tell you is that returning 1 from the
MouseHookProc method in the sample will prevent the message being passed on
to your application.

Let me know if I can clarify anything further or if you need assistance
adapting the sample to your application. Hope this helps.

Regards,
Matt Garven

> Hi Matt, thanks for your reply.
>
[quoted text clipped - 6 lines]
>
> e.X < 0 || e.X > listbox.Width ||  e.Y< 0 || e.Y > listbox.Height
Fardreamer - 14 Feb 2005 13:18 GMT
Thanks Matt, that looks like a perfect solution.I'm surprised .NET
doesn't supply this basic functionality out-of-the-box, considering it
has such robust user control abilities. I haven't tried it yet though,
but judging by the example it seems pretty straightforward. I'll post
here if it works out for me (or if if I have any more questions).

Thanks again.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.