WPF DragLeave event coordinates not accurate - c#

I want to show a drag control (DataView) when the DragEnter event occurs and hide it when the DragLeave event fires. Since I also have children in the control, the leave event also fires when entering the child control. So I decided to only hide the control when the control receives a DragLeave event and the mouse is not within the complete drag control:
private void AView_DragLeave(object sender, DragEventArgs e)
{
var aPosition = e.GetPosition(DataView);
bool IsInside = (aPosition.X >= 5) && (aPosition.X < DataView.ActualWidth - 5) && (aPosition.Y >= 5) &&
(aPosition.Y < DataView.ActualHeight - 5);
DataView.Opacity = IsInside ? 1 : 0;
}
The problem now is that when DragLeave fires, the mouse is still inside the Data and I never come to the point where I have correct information to hide the control. Is there another solution for this problem. Getting the current mouse position does not work using Mouse.GetPosition() since the mouse location is not really tracked while drag&drop operations.

I think instead of handling the DragLeave event you should handle the PreviewDragLeave event of your AView and set e.handled = true;.
That way the children shouldn't fire the DragLeave event.

Related

UWP ScrollViewer - Differentiate between user scrolling and programmatic scrolling

I have an application where I need to take a certain action when the user gets to a certain place in a ScrollViewer. This action sometimes includes scrolling the ScrollViewer to a different location programmatically.
In order to moniter the user's scrolling action, I am listening for the ViewChanged event of the ScrollViewer. The issue is that when I scroll progrmatically from within the ViewChanged event handler, that same event handler ends up getting called again, causing undesired additional scrolling to happen.
I have tried creating a custom method to remove the event handler before calling ScrollViewer.ChangeView(), but this seems to have no effect.
Can anyone come up with a way around this issue, or a way to differentiate the user's scrolling action from my programmatic one?
private void MyScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (conditionals)
{
ScrollTo(location);
}
}
private void ScrollTo(double offset)
{
MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
MyScrollViewer.ChangeView(offset, null, null);
MyScrollViewer.ViewChanged += MyScrollViewer_ViewChanged;
}
It is, unfortunately, not possible to determine what triggered a ViewChanged event. It is however possible to solve this problem.
The issue is that ChangeView() is asynchronous, so re-adding the event handler immediately after calling ChangeView is too soon. ChangeView will raise a bunch of ViewChanged events with a final one where e.IsIntermediate == false; only once that happens should you re-hook the event handler. The best way to handle this might be to use a temporary event handler that waits for that e.IsIntermediate == false and then re-hooks the original handler.
To prevent the user from interacting with the ScrollViewer during the execution of ChangeView, the scroll and zoom modes can be temporarily disabled.
Finally, if the user is manipulating the ScrollViewer when the conditionals are met, that manipulation needs to be canceled before calling ScrollTo().
EDIT: In my implementation, an issue arose where because of the number of times these handlers were called, event handlers were added more than once. To combat this, I've taken the simple strategy from here.
private void MyScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!conditionals) return;
if (e.IsIntermediate)
{
var uiElement = MyScrollViewer.Content as UIElement;
uiElement?.CancelDirectManipulations();
}
ScrollTo(location);
}
private void Temporary_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (e.IsIntermediate) return;
MyScrollViewer.ViewChanged -= Temporary_ViewChanged;
MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
MyScrollViewer.ViewChanged += MyScrollViewer_ViewChanged;
MyScrollViewer.HorizontalScrollMode = ScrollMode.Enabled;
MyScrollViewer.VerticalScrollMode = ScrollMode.Enabled;
MyScrollViewer.ZoomMode = ZoomMode.Enabled;
}
private void ScrollTo(double offset)
{
MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
MyScrollViewer.ViewChanged -= Temporary_ViewChanged;
MyScrollViewer.ViewChanged += Temporary_ViewChanged;
MyScrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
MyScrollViewer.VerticalScrollMode = ScrollMode.Disabled;
MyScrollViewer.ZoomMode = ZoomMode.Disabled;
MyScrollViewer.ChangeView(offset, null, null);
}

DragOver button not firing event c#

As the title suggests i am having trouble getting a DragOver event to function correctly. I have over 100 buttons on a form and i want their colour to change when a picturebox is dragged over them. I have set all buttons AllowDrop = true and have included the code below in the method.
private void ShipOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.None;
Button b = (Button)sender;
b.BackColor = Color.Green;
label22.Text = "";
}
I do not see why this will not work. I also have a DragLeave method which simply changes the colour to a different one.
One thing to note is that the item i am dragging over the button is larger than the button itself. Not sure whether this will have an effect.
You need to wire up the events to your method. If all of the buttons are in a single panel, you can do something like this in your form's constructor:
foreach (Button b in panel1.Controls.OfType<Button>()) {
b.DragOver += ShipOver;
}
Same principle applies to the DragLeave event.

Copy item from one listbox to another on double click. Doubleclick event not fired. Winform C#

I am quite new to Winform dev. I have two list boxes. When the user double clicks an item in the first listbox, I want this to be copied to the second list box. The problem is that my double click method never gets fired.
here's my code:
//here I register the event
this.fieldsArea.MouseDoubleClick += new MouseEventHandler(fieldsArea_MouseDoubleClick);
Then here is the double click method:
private void fieldsArea_MouseDoubleClick(object sender, MouseEventArgs e)
{
MessageBox.Show("from method");
int index = fieldsArea.IndexFromPoint(e.Location);
string s = fieldsArea.Items[index].ToString();
selectedFieldsArea.Items.Add(s);
}
So I want the element from fieldsArea to be copied to selectedFieldsArea... The messagebox never shows and in debug I see that I never enter this method...
Am I missing something here?
ps: I have drag drop implemented which works well.
UPDATE: The problem comes from the MouseDown event also being implemented. So here's my mousedown event.
private void fieldsArea_MouseDown(object sender, MouseEventArgs e)
{
if (fieldsArea.Items.Count == 0)
return;
int index = fieldsArea.IndexFromPoint(e.Location);
string s = fieldsArea.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s,
DragDropEffects.All);
}
ps: I have drag drop implemented which works well.
That means probably that you have registered a MouseDown event, which interfere with the MouseDoubleclick.
Just for testing purpose, try to delete the Drag&Drop implementation ( unregister the MouseDown event) and then the MouseDoubleclick should work.
Make sure you don't have other Mouse event like MouseClick MouseDown event registered, which could interfere with MouseDoubleclick event.
Update:
Add following code in your MouseDown event handler, you can check if it is a double-click first.
if(e.Clicks>1)
{
int index = fieldsArea.IndexFromPoint(e.Location);
string s = fieldsArea.Items[index].ToString();
selectedFieldsArea.Items.Add(s);
}
so here is your new handler:
private void fieldsArea_MouseDown(object sender, MouseEventArgs e)
{
if (fieldsArea.Items.Count == 0)
return;
int index = fieldsArea.IndexFromPoint(e.Location);
string s = fieldsArea.Items[index].ToString();
if(e.Clicks>1)
{
selectedFieldsArea.Items.Add(s);
}
else
{
DragDropEffects dde1 = DoDragDrop(s,
DragDropEffects.All);
}
}
I believe you may have either "MouseClick/MouseDown" event or "SelectedIndexChanged" event, these events resist to get fire of "MouseDoubleclick" event, so you need to handle them properly. Thanks

C# Show/hide element on MouseHover/MouseLeave of the parent

In C#, we have the following:
A UserControl containing a PictureBox and an invisible FlowPanel.
What I want to achieve:
When the UserControl is hovered
(MouseHover), the invisible
FlowPanel will be set to visible =
true. When the mouse leaves the
UserControl or FlowPanel, the
FlowPanel should be set visible =
false.
Using MouseLeave on UserControl doesn't do the job, because this event is triggered when the mouse enters FlowPanel. Hiding the FlowPanel when the mouse leaves FlowPanel does it, but is buggy (sometimes MouseLeave is triggered, sometimes not).
What's the best way to fix this?
i did somthing similar on one of my forms
do a if(contorl.Opacity = 1.0) inside your first event
private void Form1_MouseLeave(object sender, EventArgs e)
{
if (this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))
{
this.Opacity = 1.0;
}
else
{
int loopctr = 0;
for (loopctr = 100; loopctr >= 5; loopctr -= 10)
{
this.Opacity = loopctr / 99.0;
this.Refresh();
Thread.Sleep(100);
}
}
}
In the case when FlowPanel.MouseLeave isn't triggered, isn't UserControl.MouseLeave triggered? I suppose that hiding on both events may do the trick.
This is a common UI problem. Mouse events come up as samples so it's possible that some pixel positions are missed and a control doesn't get the mouse up event.
A not so nice way that works is setting up some form of Timer when MouseHover is detected inside the Control and poll for the cursor in regular intervals (such as 342ms).

Preventing double events in DataGridView

I have a problem that I am not sure how to solve. I have a DataGridView (EditMode = EditProgrammatically). This grid has one checkbox column and one or more textbox columns. The functionality is as following (or should be at least):
When you click on a checkbox, the checkbox should be toggled
When a row (or many rows) are selected, and you press space, the checkboxes should be toggled.
I have these two event handlers:
private void grid_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.ColumnIndex == useColumn.Index)
{
if (ModifierKeys != Keys.Shift && ModifierKeys != Keys.Control)
{
ToggleRows(grid.SelectedRows);
}
}
}
private void RowSelectorForm_KeyDown(object sender, KeyEventArgs e)
{
if (grid.Focused && e.KeyCode == Keys.Space)
{
ToggleRows(grid.SelectedRows);
e.Handled = true; // Not sure if this is needed or even does anything
e.SuppressKeyPress = true; // Or this for that matter...
}
}
This almost works. The problem is when you press space and a checkbox cell is active. When a textbox cell is active, it works like it should. The problem is that when you press space and a checkbox cell is active, both events gets fired. Which means it first selects and then deselects (or the reverse). So the checkboxes end up being like they was. How can I prevent this?
I have thought about using a flag, but not sure where I can put it, since I can't really know if it was a double event or if it was just the user using space and then clicking with the mouse. So that can't really be used I think. Is there a different event I should use? Is there a way to see if the cell was clicked by mouse or by space? Is there a way to disable the automatic checkbox toggling when space is pressed? What can I do?
Note: Reason for RowSelectorForm_KeyDown and not just grid_KeyDown was that I was trying to use KeyPreview and then suppress the keypress if it was space and the grid was focused. But that SuppressKeyPress doesn't really seem to do anything at all =/ Maybe I've just misunderstood it...
Well, I didn't want to do it, but I have now fixed it with a timer... but if anyone knows how to do it properly, please let me know!!
Current solution:
private DateTime lastClick = DateTime.MinValue;
and in both events:
if (DateTime.Now - lastClick > TimeSpan.FromMilliseconds(400))
{
lastClick = DateTime.Now;
ToggleRows(grid.SelectedRows);
}
It has been a time since I worked with C#, but I assume that you can disconnect the grid_CellClick event handler before calling ToggleGrid in RowSelectorForm_KeyDown.
After the call, you can reconnect the event handler.
Also, there might be some way to supress the event from being fired in the first place. In some API's certain methods are specially provided that don't trigger any events.
The frustrating problems are:
.Handled doesn't prevent the check box from changing
.SuppressKeyPress doesn't prevent the checkbox from changing
If you DoubleClick on the checkbox, the first click fires the Click() event (toggling the checkbox) and the second click fires the DoubleClick() event (toggling the checkbox yet again).
However, the KeyDown event fires before any of the grid events. Perhaps setting a flag to indicate the spacebar was pressed or if the row was already selected, reset the value of the checkbox.
I used the following and it seemed to work well:
private Keys _ClickSource = 0;
private void dgv_CellClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (_ClickSource == 0 || _ClickSource != Keys.Space)
{
dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = ! (System.Convert.ToBoolean(dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value));
}
_ClickSource = null;
}
private void dgv_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
_ClickSource = e.KeyCode;
}

Categories