F
Fardreamer
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
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