I use a standard WPF ComboBox control. When popup is opened and user clicks somewhere outside, popup is closed. But if there is button on the window and user clicks on it (with popup still opened), button's click handler is not executed. Popup is closed, but user has to click one more time on the button to raise click event on it.
I know that is standard behavior for this control. Have you any ideas how to bypass this behavior? Thanks!
I fixed some bugs with #Eng. M.Hamdy very good approach and did it in C#, also applying it to all comboboxes application wide.
Application hook:
EventManager.RegisterClassHandler(typeof(ComboBox), UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(FixComboBoxOutClick));
Handler code:
private void FixComboBoxOutClick(object sender, MouseButtonEventArgs e) {
if (sender is ComboBox combo) {
Point comboRelativePoint = Mouse.GetPosition(combo);
if (comboRelativePoint.X < 0 || comboRelativePoint.Y < 0 || comboRelativePoint.X > combo.ActualWidth || comboRelativePoint.Y > combo.ActualHeight) {
UIElement popupContent = combo.FindChild<Popup>(null).Child;
Point popupRelativePoint = Mouse.GetPosition(popupContent);
if (popupRelativePoint.X < 0 || popupRelativePoint.Y < 0 || popupRelativePoint.X > popupContent.RenderSize.Width || popupRelativePoint.Y > popupContent.RenderSize.Height) {
combo.IsDropDownOpen = false;
}
}
}
}
You can look for FindChild<T>() implementations here.
You can create an event for ComboBox DropDownClosed and with the hittestfunction, find the other control that the user has clicked.
private void ComboBox_DropDownClosed(object sender, EventArgs e)
{
Point m = Mouse.GetPosition(this);
VisualTreeHelper.HitTest(this, this.FilterCallback, this.ResultCallback, new PointHitTestParameters(m));
}
private HitTestFilterBehavior FilterCallback(DependencyObject o)
{
var c = o as Control;
if ((c != null) && !(o is MainWindow))
{
if (c.Focusable)
{
if (c is ComboBox)
{
(c as ComboBox).IsDropDownOpen = true;
}
else
{
var mouseDevice = Mouse.PrimaryDevice;
var mouseButtonEventArgs = new MouseButtonEventArgs(mouseDevice, 0, MouseButton.Left)
{
RoutedEvent = Mouse.MouseDownEvent,
Source = c
};
c.RaiseEvent(mouseButtonEventArgs);
}
return HitTestFilterBehavior.Stop;
}
}
return HitTestFilterBehavior.Continue;
}
private HitTestResultBehavior ResultCallback(HitTestResult r)
{
return HitTestResultBehavior.Continue;
}
Then in the FilterCallback function after finding that control, raise the mouse down event on that control.
I found the raise event, does not work on comboboxes so for clicking that, I simply set the IsDropDownOpen to true.
I found the code in here and modified it a little.
I used an easy solution:
In the PreviewMouseLeftButtonDown event, if the mouse pos is outside the combobox, close the dropdown. This will allow other control to get the mouse click:
Dim p = Mouse.GetPosition(combo)
If p.X < 0 OrElse p.Y < 0 OrElse p.X > combo.Width OrElse p.Y > combo.Height Then
cmb.IsDropDownOpen = False
End If
You can try to release the mouse capture right after the ComboBox gets one:
In your's ComboBox properties in XAML:
GotMouseCapture="ComboBox_OnGotMouseCapture"
And in code-behind:
private void ComboBox_OnGotMouseCapture(object sender, MouseEventArgs e)
{
ComboBox.ReleaseMouseCapture();
}
Related
I have a datagridview in a .NET winform app. I would like to rightclick on a row and have a menu pop up. Then i would like to select things such as copy, validate, etc
How do i make A) a menu pop up B) find which row was right clicked. I know i could use selectedIndex but i should be able to right click without changing what is selected? right now i could use selected index but if there is a way to get the data without changing what is selected then that would be useful.
You can use the CellMouseEnter and CellMouseLeave to track the row number that the mouse is currently hovering over.
Then use a ContextMenu object to display you popup menu, customised for the current row.
Here's a quick and dirty example of what I mean...
private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu m = new ContextMenu();
m.MenuItems.Add(new MenuItem("Cut"));
m.MenuItems.Add(new MenuItem("Copy"));
m.MenuItems.Add(new MenuItem("Paste"));
int currentMouseOverRow = dataGridView1.HitTest(e.X,e.Y).RowIndex;
if (currentMouseOverRow >= 0)
{
m.MenuItems.Add(new MenuItem(string.Format("Do something to row {0}", currentMouseOverRow.ToString())));
}
m.Show(dataGridView1, new Point(e.X, e.Y));
}
}
While this question is old, the answers aren't proper. Context menus have their own events on DataGridView. There is an event for row context menu and cell context menu.
The reason for which these answers aren't proper is they do not account for different operation schemes. Accessibility options, remote connections, or Metro/Mono/Web/WPF porting might not work and keyboard shortcuts will down right fail (Shift+F10 or Context Menu key).
Cell selection on right mouse click has to be handled manually. Showing the context menu does not need to be handled as this is handled by the UI.
This completely mimics the approach used by Microsoft Excel. If a cell is part of a selected range, the cell selection doesn't change and neither does CurrentCell. If it isn't, the old range is cleared and the cell is selected and becomes CurrentCell.
If you are unclear on this, CurrentCell is where the keyboard has focus when you press the arrow keys. Selected is whether it is part of SelectedCells. The context menu will show on right click as handled by the UI.
private void dgvAccount_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex != -1 && e.RowIndex != -1 && e.Button == System.Windows.Forms.MouseButtons.Right)
{
DataGridViewCell c = (sender as DataGridView)[e.ColumnIndex, e.RowIndex];
if (!c.Selected)
{
c.DataGridView.ClearSelection();
c.DataGridView.CurrentCell = c;
c.Selected = true;
}
}
}
Keyboard shortcuts do not show the context menu by default, so we have to add them in.
private void dgvAccount_KeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyCode == Keys.F10 && e.Shift) || e.KeyCode == Keys.Apps)
{
e.SuppressKeyPress = true;
DataGridViewCell currentCell = (sender as DataGridView).CurrentCell;
if (currentCell != null)
{
ContextMenuStrip cms = currentCell.ContextMenuStrip;
if (cms != null)
{
Rectangle r = currentCell.DataGridView.GetCellDisplayRectangle(currentCell.ColumnIndex, currentCell.RowIndex, false);
Point p = new Point(r.X + r.Width, r.Y + r.Height);
cms.Show(currentCell.DataGridView, p);
}
}
}
}
I've reworked this code to work statically, so you can copy and paste them into any event.
The key is to use CellContextMenuStripNeeded since this will give you the context menu.
Here's an example using CellContextMenuStripNeeded where you can specify which context menu to show if you want to have different ones per row.
In this context MultiSelect is True and SelectionMode is FullRowSelect. This is just for the example and not a limitation.
private void dgvAccount_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (e.RowIndex == -1 || e.ColumnIndex == -1)
return;
bool isPayment = true;
bool isCharge = true;
foreach (DataGridViewRow row in dgv.SelectedRows)
{
if ((string)row.Cells["P/C"].Value == "C")
isPayment = false;
else if ((string)row.Cells["P/C"].Value == "P")
isCharge = false;
}
if (isPayment)
e.ContextMenuStrip = cmsAccountPayment;
else if (isCharge)
e.ContextMenuStrip = cmsAccountCharge;
}
private void cmsAccountPayment_Opening(object sender, CancelEventArgs e)
{
int itemCount = dgvAccount.SelectedRows.Count;
string voidPaymentText = "&Void Payment"; // to be localized
if (itemCount > 1)
voidPaymentText = "&Void Payments"; // to be localized
if (tsmiVoidPayment.Text != voidPaymentText) // avoid possible flicker
tsmiVoidPayment.Text = voidPaymentText;
}
private void cmsAccountCharge_Opening(object sender, CancelEventArgs e)
{
int itemCount = dgvAccount.SelectedRows.Count;
string deleteChargeText = "&Delete Charge"; //to be localized
if (itemCount > 1)
deleteChargeText = "&Delete Charge"; //to be localized
if (tsmiDeleteCharge.Text != deleteChargeText) // avoid possible flicker
tsmiDeleteCharge.Text = deleteChargeText;
}
private void tsmiVoidPayment_Click(object sender, EventArgs e)
{
int paymentCount = dgvAccount.SelectedRows.Count;
if (paymentCount == 0)
return;
bool voidPayments = false;
string confirmText = "Are you sure you would like to void this payment?"; // to be localized
if (paymentCount > 1)
confirmText = "Are you sure you would like to void these payments?"; // to be localized
voidPayments = (MessageBox.Show(
confirmText,
"Confirm", // to be localized
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2
) == DialogResult.Yes);
if (voidPayments)
{
// SQLTransaction Start
foreach (DataGridViewRow row in dgvAccount.SelectedRows)
{
//do Work
}
}
}
private void tsmiDeleteCharge_Click(object sender, EventArgs e)
{
int chargeCount = dgvAccount.SelectedRows.Count;
if (chargeCount == 0)
return;
bool deleteCharges = false;
string confirmText = "Are you sure you would like to delete this charge?"; // to be localized
if (chargeCount > 1)
confirmText = "Are you sure you would like to delete these charges?"; // to be localized
deleteCharges = (MessageBox.Show(
confirmText,
"Confirm", // to be localized
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2
) == DialogResult.Yes);
if (deleteCharges)
{
// SQLTransaction Start
foreach (DataGridViewRow row in dgvAccount.SelectedRows)
{
//do Work
}
}
}
Put a context menu on your form, name it, set captions etc. using the built-in editor
Link it to your grid using the grid property ContextMenuStrip
For your grid, create an event to handle CellContextMenuStripNeeded
The Event Args e has useful properties e.ColumnIndex, e.RowIndex.
I believe that e.RowIndex is what you are asking for.
Suggestion: when user causes your event CellContextMenuStripNeeded to fire, use e.RowIndex to get data from your grid, such as the ID. Store the ID as the menu event's tag item.
Now, when user actually clicks your menu item, use the Sender property to fetch the tag. Use the tag, containing your ID, to perform the action you need.
Use the CellMouseDown event on the DataGridView. From the event handler arguments you can determine which cell was clicked. Using the PointToClient() method on the DataGridView you can determine the relative position of the pointer to the DataGridView, so you can pop up the menu in the correct location.
(The DataGridViewCellMouseEvent parameter just gives you the X and Y relative to the cell you clicked, which isn't as easy to use to pop up the context menu.)
This is the code I used to get the mouse position, then adjust for the position of the DataGridView:
var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
The entire event handler looks like this:
private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
// Ignore if a column or row header is clicked
if (e.RowIndex != -1 && e.ColumnIndex != -1)
{
if (e.Button == MouseButtons.Right)
{
DataGridViewCell clickedCell = (sender as DataGridView).Rows[e.RowIndex].Cells[e.ColumnIndex];
// Here you can do whatever you want with the cell
this.DataGridView1.CurrentCell = clickedCell; // Select the clicked cell, for instance
// Get mouse position relative to the vehicles grid
var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
// Show the context menu
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
}
}
}
Follow the steps:
Create a context menu like:
User needs to right click on the row to get this menu. We need to handle the _MouseClick event and _CellMouseDown event.
selectedBiodataid is the variable that contains the selected row information.
Here is the code:
private void dgrdResults_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
contextMenuStrip1.Show(Cursor.Position.X, Cursor.Position.Y);
}
}
private void dgrdResults_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
//handle the row selection on right click
if (e.Button == MouseButtons.Right)
{
try
{
dgrdResults.CurrentCell = dgrdResults.Rows[e.RowIndex].Cells[e.ColumnIndex];
// Can leave these here - doesn't hurt
dgrdResults.Rows[e.RowIndex].Selected = true;
dgrdResults.Focus();
selectedBiodataId = Convert.ToInt32(dgrdResults.Rows[e.RowIndex].Cells[1].Value);
}
catch (Exception)
{
}
}
}
and the output would be:
Simply drag a ContextMenu or ContextMenuStrip component into your form and visually design it, then assign it to the ContextMenu or ContextMenuStrip property of your desired control.
For the position for the context menu, y found the problem that I needed a it to be relative to the DataGridView, and the event I needed to use gives the poistion relative to the cell clicked. I haven't found a better solution so I implemented this function in the commons class, so I call it from wherever I need.
It's quite tested and works well. I Hope you find it useful.
/// <summary>
/// When DataGridView_CellMouseClick ocurs, it gives the position relative to the cell clicked, but for context menus you need the position relative to the DataGridView
/// </summary>
/// <param name="dgv">DataGridView that produces the event</param>
/// <param name="e">Event arguments produced</param>
/// <returns>The Location of the click, relative to the DataGridView</returns>
public static Point PositionRelativeToDataGridViewFromDataGridViewCellMouseEventArgs(DataGridView dgv, DataGridViewCellMouseEventArgs e)
{
int x = e.X;
int y = e.Y;
if (dgv.RowHeadersVisible)
x += dgv.RowHeadersWidth;
if (dgv.ColumnHeadersVisible)
y += dgv.ColumnHeadersHeight;
for (int j = 0; j < e.ColumnIndex; j++)
if (dgv.Columns[j].Visible)
x += dgv.Columns[j].Width;
for (int i = 0; i < e.RowIndex; i++)
if (dgv.Rows[i].Visible)
y += dgv.Rows[i].Height;
return new Point(x, y);
}
There is simple answer to this topic and that is to use CellMouseDown
After designing your ContextMenu
// somewhere in your code
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add(new MenuItem("Option1"));
cm.MenuItems.Add(new MenuItem("Option2"));
Assign it to the DataGridView
myDataView.ContextMenu = cm;
Get Data from the CLICKED cell without changing the Selected one
private void myDataView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
string myData = myDataView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
// Now ADD the captured DATA to the ContextMenu
cm.MenuItems.Add(new MenuItem("myData"));
// OR if you are doing it by the designer and YOU ALREADY have an Item
// you can simply change the name of it in the designer page to your
// desired one and then just change the Text of it
MenuItem_BTN.Text = "$Properties of {myData}";
}
}
I'm using C# with WPF. I have a grid of buttons and I need to do the following: If the user presses one button, moves the cursor and releases it on another button, the content of the first button is moved to the other one, something like dragging.
I tried using The previewmousedown and the previewmouseup button events to know which button the mouse is pressed on and which button it is released on but the previewmouseup event is fired also on the button the mouse is pressed on (not on the one the mouse is released on).
Any ideas about how to implement this in other ways, please? Thanks a lot in advance.
The easiest way to do drag & drop is with the built-in DragDrop API. Here's a proof of concept for you, where buttons can be clicked normally *and* dragged to swap their content.
If you want to change the behavior so the content is copied or moved (instead of swapped), just change the lines under the comment in OnButtonDrop.
ButtonDragging.xaml:
<Window x:Class="WpfTest2.ButtonDragging"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel LastChildFill="True">
<Label x:Name="_statusLabel" DockPanel.Dock="Bottom" Content=" " />
<Grid x:Name="_grid" />
</DockPanel>
</Window>
ButtonDragging.xaml.cs:
public partial class ButtonDragging
{
private Button _mouseDownButton;
private Point _mouseDownLocation;
public ButtonDragging()
{
InitializeComponent();
BuildButtonGrid();
}
private void BuildButtonGrid()
{
const int rows = 5;
const int columns = 5;
var starLength = new GridLength(1d, GridUnitType.Star);
for (var i = 0; i < rows; i++)
_grid.RowDefinitions.Add(new RowDefinition { Height = starLength });
for (var i = 0; i < columns; i++)
_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = starLength });
for (var i = 0; i < rows; i++)
{
for (var j = 0; j < columns; j++)
{
var button = new Button { Content = $#"({i}, {j})", AllowDrop = true };
Grid.SetColumn(button, i);
Grid.SetRow(button, j);
button.PreviewMouseMove += OnButtonMouseMove;
button.PreviewMouseLeftButtonDown += OnButtonLeftButtonDown;
button.PreviewMouseLeftButtonUp += OnButtonLeftButtonUp;
button.Drop += OnButtonDrop;
button.Click += OnButtonClick;
button.LostMouseCapture += OnButtonLostMouseCapture;
_grid.Children.Add(button);
}
}
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
_statusLabel.Content = $#"You clicked {(sender as Button)?.Content}!";
}
private void ClearPendingDrag()
{
_mouseDownButton = null;
_mouseDownLocation = default(Point);
}
private void OnButtonDrop(object sender, DragEventArgs e)
{
ClearPendingDrag();
var source = e.Data.GetData(typeof(object)) as Button;
if (source == null)
return;
var target = (Button)sender;
if (target == source)
return;
var sourceContent = source.Content;
var targetContent = target.Content;
// As a proof of concept, this swaps the content of the source and target.
// Change as necessary to get the behavior you want.
target.Content = sourceContent;
source.Content = targetContent;
_statusLabel.Content = $#"You swapped {sourceContent} with {targetContent}!";
}
private void OnButtonLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var button = (Button)sender;
_mouseDownButton = button;
_mouseDownLocation = e.GetPosition(button);
if (!Mouse.Capture(button, CaptureMode.SubTree))
ClearPendingDrag();
}
private void OnButtonLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ClearPendingDrag();
}
private void OnButtonMouseMove(object sender, MouseEventArgs e)
{
if (_mouseDownButton == null)
return;
var position = e.GetPosition(_mouseDownButton);
var distance = position - _mouseDownLocation;
if (Math.Abs(distance.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(distance.Y) > SystemParameters.MinimumVerticalDragDistance)
{
var button = (Button)sender;
var data = new DataObject(typeof(object), button);
data.SetData("Source", sender);
DragDrop.DoDragDrop(button, data, DragDropEffects.Move);
ClearPendingDrag();
}
}
private void OnButtonLostMouseCapture(object sender, MouseEventArgs e)
{
ClearPendingDrag();
}
}
Note that there are probably some third-party (and open source) drag and drop solutions out there that are more MVVM-friendly. It's worth checking out, but this should get you a minimum viable deliverable.
Take a look at this blog post. This is the best drag and drop article I read online.
From the link below here are the series of events for a drag and drop operation:
Dragging is initiated by calling the DoDragDrop method for the source control.
The DoDragDrop method takes two parameters:
data, specifying the data to pass
allowedEffects, specifying which operations (copying and/or moving) are allowed
A new DataObject object is automatically created.
This in turn raises the GiveFeedback event. In most cases you do not need to worry about the GiveFeedback event, but if you wanted to display a custom mouse pointer during the drag, this is where you would add your code.
Any control with its AllowDrop property set to True is a potential drop target. The AllowDrop property can be set in the Properties window at design time, or programmatically in the Form_Load event.
As the mouse passes over each control, the DragEnter event for that control is raised. The GetDataPresent method is used to make sure that the format of the data is appropriate to the target control, and the Effect property is used to display the appropriate mouse pointer.
If the user releases the mouse button over a valid drop target, the DragDrop event is raised. Code in the DragDrop event handler extracts the data from the DataObject object and displays it in the target control.
To detect a mouse Drag operation on the source, source control needs to subscribe to MouseLeftButtonDown and MouseMove.
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove);
}
To prevent from starting a false drag & drop operation where the user accidentally drags, you can use SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance.
void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDrag(e);
}
}
}
void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
Now you detect a drag operation, all you need to know is dropping into.
A simple scenario without any effect could be done like this.
private void StartDrag(MouseEventArgs e)
{
IsDragging = true;
DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
DragDropEffects de = DragDrop.DoDragDrop(this.DragSource, data, DragDropEffects.Move);
IsDragging = false;
}
I think this can get you started. I really recommend to read the complete post from the link.
https://blogs.msdn.microsoft.com/jaimer/2007/07/12/drag-amp-drop-in-wpf-explained-end-to-end/
I have panel on main form in disabled state I want to enable it by mouse enter event. How can I?
private void pnlOne_MouseEnter(object sender, EventArgs e)
{
pnlOne.Enabled = true;
pnlOne.Visible = true;
}
I try above one but it not working...
If you have Disabled your control mouse event wont get fired. You cant do this.
Even if you enabled some other events,Check whether your panel is in the front . Use bring to front in the designer. Reason could be another container control is in the middle.
You can try a MouseMove event on Panel's parent control. In that you can check cursor position and if cursor is on Panel, you can enable Panel.
private void PANELS_PARENT_CONTROL_MouseMove(object sender, MouseEventArgs e)
{
if (e.Location.X > pnlOne.Location.X &&
e.Location.X < (pnlOne.Location.X + pnlOne.Width) &&
e.Location.Y > pnlOne.Location.Y &&
e.Location.Y < (pnlOne.Location.Y + pnlOne.Height))
{
pnlOne.Enabled = true;
pnlOne.Visible = true;
}
else
{
pnlOne.Enabled = false;
pnlOne.Visible = false;
}
}
I have a datagridview control on winforms with the following event to drag 'row' out of datagrid:
private void gridOperations_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
if (Math.Abs(e.X - mouseDownPos.X) >= SystemInformation.DoubleClickSize.Width || Math.Abs(e.Y - mouseDownPos.Y) >= SystemInformation.DoubleClickSize.Height)
{
string[] filesToDrag = { "tmp/generated.log" };
gridOperations.DoDragDrop(new DataObject(DataFormats.FileDrop, filesToDrag), DragDropEffects.Copy);
}
}
}
The problem is I also have click and double click events on datagridview, the double click event almost never gets executed at first, unless I don't move my mouse at all when clicking. how can I add a 'thershold' so that if I hold mouse down and drag cell for 3 pixels, then it will trigger gridOperations.DoDragDrop? Thanks!
try to use an integer counter.
Every time the Event is fired you increment the int and if it is reaches your treshold you execute the rest of the Code and reset it to 0.
like:
private int thCount = 0;
private void gridOperations_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left && thCount==5)
{
//...
thCount = 0;
}
else
{
thCount++;
}
}
hope i could help you
with a form like this:
I wrote this piece of code to take care of enable/disable logic for moveup/down buttons when they click on at item ( we don't care about Avaiable list on the left, we just care about Selected list on the right)
private void SelectedLV_SelectedIndexChanged(object sender, EventArgs e)
{
// what to do wth move up button
if (SelectedLV.SelectedIndices.Count == 1 && SelectedLV.SelectedItems[0].Index > 0)
{
MoveUpBtn.Enabled = true;
}
else
{
MoveUpBtn.Enabled = false;
}
//what to do with move down button
if (SelectedLV.SelectedIndices.Count == 1 && SelectedLV.SelectedItems[0].Index < SelectedLV.Items.Count - 1)
{
MoveDownBtn.Enabled = true;
}
else
{
MoveDownBtn.Enabled = false;
}
}
I think it works fine for that scenario but my question is what about when we click off of Selected Listview, What is good logic to handle that and Disable Both Moveup/Down buttons?
I don't want them be enabled when we are not inside SelectedListView...
Also if you notice any issue with the code I pasted please let me know.
Thanks
You are about to shoot your foot with the focus requirement. These kind of UI updates are best done with the Application.Idle event, it only runs when nothing important is happening. And can help to eliminate a lot of event handlers. Like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
Application.Idle += Application_Idle;
this.FormClosed += delegate { Application.Idle -= Application_Idle; };
}
void Application_Idle(object sender, EventArgs e) {
bool focusOk = this.ActiveControl == SelectedLV;
bool selectOk = SelectedLV.SelectedIndices.Count == 1;
int index = selectOk ? SelectedLV.SelectedIndices[0] : -1;
MoveUpBtn.Enabled = focusOk && selectOk && index > 0;
MoveDownBtn.Enabled = focusOk && selectOk && index < SelectedLV.Items.Count-1;
}
}
Don't forget to set the focus back in the buttons' Click event handler. And don't forget about the ListView.HideSelection property. Set it to False so that focus doesn't matter anymore.
The problem is once you click on the Move buttons, then you are outside of the SelectedListView control, so the logic should really be based on if you have a correct index value or not:
private void SelectedLV_SelectedIndexChanged(object sender, EventArgs e)
if (SelectedLV.SelectedIndicies.Count == 0) {
MoveUpBtn.Enabled = false;
MoveDownBtn.Enabled = false;
} else {
// normal processing
}