I'm trying to figure out if there's an elegant solution to the problem I've been faced with.
So basically, I designed a borderless loading splash screen which is completely movable via dragging. I find that this happens if the splash screen gets hidden via Hide(), then displays a window via ShowDialog() with the owner set to the splash screen. Things get extremely buggy, but only if you're in mid-drag (with left mouse button down). You become unable to click or move anything, even Visual Studio becomes unresponsive unless you explicitly alt-tab out of the application.
Considering I know when I'm going to spawn the window, I was thinking maybe there'd be a way to cancel the DragMove operation, but I'm having no luck. What I've figured out is that DragMove is synchronous, so I'd guess it'd have to be cancelled from a different thread or in an event callback.
Edit:
public partial class Window_Movable : Window
{
public Window_Movable()
{
InitializeComponent();
}
public Boolean CanMove { get; set; } = true;
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (CanMove == false)
return;
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(1000);
Dispatcher.Invoke(() => {
Hide();
new Window_Movable() {
Title = "MOVABLE 2",
CanMove = false,
Owner = this
}.ShowDialog();
});
});
DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("DING");
}
}
I had the same problem and found out that DragMove() method is a problem.
https://groups.google.com/forum/#!topic/wpf-disciples/7OcuXrf2whc
To solve I decided to refuse from using it and implement moving logic.
I've combined some solutions from
https://www.codeproject.com/Questions/284995/DragMove-problem-help-pls
and
C# WPF Move the window
private bool _inDrag;
private Point _anchorPoint;
private bool _iscaptured;
private void AppWindowWindowOnMouseMove(object sender, MouseEventArgs e)
{
if (!_inDrag)
return;
if (!_iscaptured)
{
CaptureMouse();
_iscaptured = true;
}
var mousePosition = e.GetPosition(this);
var mousePositionAbs = new Point
{
X = Convert.ToInt16(_appWindowWindow.Left) + mousePosition.X,
Y = Convert.ToInt16(_appWindowWindow.Top) + mousePosition.Y
};
_appWindowWindow.Left = _appWindowWindow.Left + (mousePositionAbs.X - _anchorPoint.X);
_appWindowWindow.Top = _appWindowWindow.Top + (mousePositionAbs.Y - _anchorPoint.Y);
_anchorPoint = mousePositionAbs;
}
private void AppWindowWindowOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_inDrag)
{
_inDrag = false;
_iscaptured = false;
ReleaseMouseCapture();
}
}
private void AppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_anchorPoint = e.GetPosition(this);
_anchorPoint.Y = Convert.ToInt16(_appWindowWindow.Top) + _anchorPoint.Y;
_anchorPoint.X = Convert.ToInt16(_appWindowWindow.Left) + _anchorPoint.X;
_inDrag = true;
}
I've spend all yesterday evening and find a more hacky but more functional solution. Which support Maximized state and not require manual coordinate calculation.
private bool _mRestoreForDragMove;
private void OnAppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
if (_appWindowWindow.ResizeMode != ResizeMode.CanResize &&
_appWindowWindow.ResizeMode != ResizeMode.CanResizeWithGrip)
{
return;
}
_appWindowWindow.WindowState = _appWindowWindow.WindowState == WindowState.Maximized
? WindowState.Normal
: WindowState.Maximized;
}
else
{
_mRestoreForDragMove = _appWindowWindow.WindowState == WindowState.Maximized;
SafeDragMoveCall(e);
}
}
private void SafeDragMoveCall(MouseEventArgs e)
{
Task.Delay(100).ContinueWith(_ =>
{
Dispatcher.BeginInvoke((Action)
delegate
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
_appWindowWindow.DragMove();
RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent
});
}
});
});
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (_mRestoreForDragMove)
{
_mRestoreForDragMove = false;
var point = PointToScreen(e.MouseDevice.GetPosition(this));
_appWindowWindow.Left = point.X - (_appWindowWindow.RestoreBounds.Width * 0.5);
_appWindowWindow.Top = point.Y;
_appWindowWindow.WindowState = WindowState.Normal;
_appWindowWindow.DragMove();
SafeDragMoveCall(e);
}
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_mRestoreForDragMove = false;
}
The thing is to send LeftMouseButtonUp event after a short delay to avoid blocking from DragMove()
Sources for last solve:
DragMove() and Maximize
C# WPF - DragMove and click
And one more completely different solution. To make a window movable you can use CaptionHeight of WindowChrome.
i.e.
var windowChrome =
WindowChrome.GetWindowChrome(appWindow.Window);
windowChrome.CaptionHeight = MainWindowToolbar.Height;
WindowChrome.SetWindowChrome(appWindow.Window, windowChrome);
but you should also set attached property WindowChrome.IsHitTestVisibleInChrome="True" for all controls in the window.
Related
I'm trying to understand what is executing before the MouseWheel event.
What I've done:
I have a form which has AutoScroll property set to true. There is a control (ZEDGRAPH) at the top and the bottom of this form.
To overcome the issue of scrolling and zooming at the same time I captured the mousewheel += new MouseEvenHandler(mymethod) for the form.Then using a bool variable I keep track of when the control (ZEDGRAPH) has focus and when it does not.
When it has focus I make verticalscroll.value = (int)mydesiredposition;
This works in accomplishing what I wanted which is to ignore the mousewheel event in the form and focus on the control.
What I am struggling with is the fact that when I scroll the form flickers every time and scrolls down before coming to the set scrollbar value.
So what I am wondering is what is getting triggered before this mouseeventhandler that causes it to flicker and is there a relatively simple workaround this?
My code snapshot:
public Form(Form1 f)
{
InitializeComponent();
this.MouseWheel += new MouseEventHandler(mousewheel);
}//end of constructor
//
//
bool mousehoverZedGraph1 = false;
bool mousehoverZedGraph2 = false;
//
//
private void zedGraphControl1_MouseHover(object sender, EventArgs e)
{
mousehoverZedGraph1 = true;
return;
}
private void mousewheel(object sender, MouseEventArgs e)
{
if (mousehoverZedGraph1 == true)
{
VerticalScroll.Enabled = false;
VerticalScroll.Value = 0;
return;
}
else if (mousehoverZedGraph2 == true)
{
VerticalScroll.Value = 429;
VerticalScroll.Enabled = false;
}
else
{
//VerticalScroll.Value += e.Delta;
}
}
private void Form_MouseEnter(object sender, EventArgs e)
{
mousehoverZedGraph1 = mousehoverZedGraph2 = false;
VerticalScroll.Enabled = true;
}
A small video highlighting the flicker:
I have two WPF controls. One is a TreeView and the other is a graph control.
I have dragging and dropping working between them. When I drag from the TreeView control to the graph control and drop something it works as I want it to. The mouse cursor has the dragging drop look to it during this. However I want to change the mouse cursor (to something that points up) if the user points the mouse to the top half of the graph control. If the user goes to the bottom of the graph control then I want the cursor to go back to the original dragging drop look.
I thought I could use the GiveFeedback event with the 1st control but that doesn't return the graph object to me.
I can provide code if needed but I don't think it would be helpful. I do have a method called MouseNearTop(Graph g, DragEventArgs e) that returns a bool true if the mouse is in the top half of the grid ad false if on the bottom half.
UPDATE:
I tried using the Mouse.OverrideCursor property but that seems to change the mouse after you release the button. I tried again using the static class DragDrop but that throws exceptions and still doesn't work.
This is for the code for my second attempt:
namespace WpfApplication11
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private bool pointingUp = true;
private void Rectangle_DragOver(object sender, DragEventArgs e)
{
currentPoint = e.GetPosition(MyRectangle);
if ((currentPoint.X > 0) && (currentPoint.X < MyRectangle.ActualWidth) && (currentPoint.Y > 0) && (currentPoint.Y < (MyRectangle.ActualHeight / 2)))
{
if (!pointingUp)
{
//Mouse.OverrideCursor = Cursors.UpArrow;'
try
{
DragDrop.DoDragDrop(MyRectangle1, MyRectangle1, DragDropEffects.Copy);
}
catch
{
}
pointingUp = true;
}
}
else
{
if (pointingUp)
{
//Mouse.OverrideCursor = null;
try
{
DragDrop.DoDragDrop(MyRectangle1, MyRectangle1, DragDropEffects.Move);
}
catch
{
}
pointingUp = false;
}
}
}
Point currentPoint = new Point();
private void MyRectangle1_MouseMove(object sender, MouseEventArgs e)
{
System.Media.SystemSounds.Beep.Play();
if (e.LeftButton == MouseButtonState.Pressed)
{
//if (FileTree.SelectedItem == null)
//{
// return;
//}
//var node = FileTree.SelectedItem as TreeViewNode;
// && (node.TableName.Equals("Ttmp", StringComparison.InvariantCultureIgnoreCase))
//if ((node.Items.Count == 0) && !(node.TableName == "Temp" || node.NodeDisplayName.EqualsAtLeastOne(StringComparison.InvariantCultureIgnoreCase, "DiFR", "DSFR")))
//{
var mousePos = e.GetPosition(null);
var diff = _startPoint - mousePos;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
DragDrop.DoDragDrop(MyRectangle1, MyRectangle1, DragDropEffects.Move);
}
//}
}
}
private Point _startPoint;
private void MyRectangle1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
private void MyRectangle_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
//Point p = e.GetPosition(MyRectangle);
}
private void MyRectangle_Drop(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.None;
}
}
}
I've used a Rectangle for demo purposes, but I think this should work OK with whatever graph you're using.
private void MyRectangle_DragOver(object sender, DragEventArgs e)
{
Point p = e.GetPosition(MyRectangle);
if ((p.X > 0) && (p.X < MyRectangle.ActualWidth) && (p.Y > 0) && (p.Y < (MyRectangle.ActualHeight / 2)))
{
Mouse.OverrideCursor = Cursors.UpArrow;
}
else
{
Mouse.OverrideCursor = null;
}
}
I figured a solution.
In the object I am dragging the data into I have this method:
private void ObjectDraggingInto_DragOver(object sender, DragEventArgs e)
{
if (ObjectDraggingFrom.DragDroppingOn)
{
ObjectDraggingFrom.MoveUpCursor = MouseNearTop(sender, e)
? true
: false;
}
}
Then here is the code from the object that I started dragging from:
public static bool MoveUpCursor = false;
public static bool DragDroppingOn = false;
private void ObjectDraggingFrom_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (MoveUpCursor)
{
e.UseDefaultCursors = false;
Mouse.SetCursor(Cursors.UpArrow);
}
else
{
e.UseDefaultCursors = true;
}
e.Handled = true;
}
private void ObjectDragging_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
((DataExploreViewModel)DataContext).ImportDraggedAndDroppedFiles((string[])e.Data.GetData(DataFormats.FileDrop));
}
DragDroppingOn = false;
}
private void ObjectDraggingFrom_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (myData.SelectedItem == null)
{
return;
}
var mousePos = e.GetPosition(null);
var diff = _startPoint - mousePos;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
DragDroppingOn = true;
DragDrop.DoDragDrop(FileTree, data, DragDropEffects.Move | DragDropEffects.Copy);
}
}
}
So this will change the mouse drag cursor to a up arrow when you are in the upper half of the object you are dragging to. Otherwise the cursor will look like the normal drag cursor.
A bit lost here with first time accessing cursor's properties for movement.
I want a pictureBox(player) to move slowly towards the mouse cursor after fst_click(bool) is enabled. I have tried a multitude of methods but have had no success. I have tried cases but my return methods are inoperable any help on this?
I though maybe a mouse listener of some sort, to update the location of the mouse and depending on timer speed move towards the location but paths are definitely not my strong point.
private int _x;
private int _y;
private bool MRight = false;
private bool MLeft = false;
private bool MUp = false;
private bool MDown = false;
bool fst_click = false;
private Position _objPosition;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clicking = true;
}
public void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{ clicking = true; }
}
I removed the incorrect case statement and added a different bool. This statement did switch between 4 cases W, A, S, D but the player location couldn't be updated smoothly?
I also added a timer to attempt to insert a boolean that would create smooth movement but again I couldn't relate to the players' location?
public void tmrMoving_Tick(object sender, EventArgs e)
{
if (clicking == true)
{
player.Location = Cursor.Position;
clicking = false;
}
}
private void Form1_MouseMoveHandler(object sender, MouseEventArgs e)
{
//Point mousePoint = new Point(player.Location.X, player.Location.Y);
}
Update
I have added the new coding below, if there are any ideas on how to repair this as I get no functionality from it. The cursor needs to be moving towards the clicked position at a certain speed, the commented out code is a failed attempt and I am looking to replace it.
namespace games1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
tmrMoving.Enabled = true;
Invalidate();
}
private void tmrMoving_Tick(object sender, EventArgs e)
{
var xdiff = Cursor.Position.X - player.Location.X;
var ydiff = Cursor.Position.Y - player.Location.Y;
var diff = Math.Sqrt(xdiff + ydiff);
// diff = ydiff / 10;
// diff = xdiff / 10;
// xdiff = player.Location.X;
// ydiff = player.Location.Y;
}
}
}
Situation: WinRT application, canvas on a main page. The canvas has a number of children. When user taps on canvas and moves pointer, I’m trying to scroll them. All works fine, but I don’t know how to emulate inertial scrolling.
The code:
private GestureRecognizer gr = new GestureRecognizer();
public MainPage()
{
this.InitializeComponent();
gr.GestureSettings = GestureSettings.ManipulationTranslateInertia;
gr.AutoProcessInertia = true;
}
I’ve subscribed to some canvas events:
//Pressed
private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
var _ps = e.GetIntermediatePoints(cnvMain);
if (_ps != null && _ps.Count > 0)
{
gr.ProcessDownEvent(_ps[0]);
e.Handled = true;
Debug.WriteLine("Pressed");
}
initialPoint = e.GetCurrentPoint(cnvMain).Position.X;
}
}
//Released
private void Canvas_PointerReleased(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
var _ps = e.GetIntermediatePoints(cnvMain);
if (_ps != null && _ps.Count > 0)
{
gr.ProcessUpEvent(_ps[0]);
e.Handled = true;
Debug.WriteLine("Released");
}
}
// Moved
private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (gr.IsActive || gr.IsInertial)
{
gr.ProcessMoveEvents(e.GetIntermediatePoints(null));
// Here is my code for translation of children
e.Handled = true;
}
}
So, I can translate canvas children, but there is no inertia. How can I enable it?
Unfortunately, I can't use something like GridView or ListView in this app because of specific data.
You should use the GestureRecognizer with ManipulationInertiaStarting. This should give you enough information to implement inertial scrolling.
I can catch a single-click on a TextBlock like this:
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("you single-clicked");
}
I can catch a double-click on a TextBlock like this:
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (e.ClickCount == 2)
{
MessageBox.Show("you double-clicked");
}
}
}
But how do I catch them both on a single TextBlock and differentiate between the two?
You need to fire the event after the click sequence is over... when is that? I suggest using a timer. The MouseDown event would reset it and increase the click count. When timer interval elapses it makes the call to evaluate the click count.
private System.Timers.Timer ClickTimer;
private int ClickCounter;
public MyView()
{
ClickTimer = new Timer(300);
ClickTimer.Elapsed += new ElapsedEventHandler(EvaluateClicks);
InitializeComponent();
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
ClickTimer.Stop();
ClickCounter++;
ClickTimer.Start();
}
private void EvaluateClicks(object source, ElapsedEventArgs e)
{
ClickTimer.Stop();
// Evaluate ClickCounter here
ClickCounter = 0;
}
Cheers!
If you need to detect the difference, I suggest you use a control such as Label that does the work for you:
label.MouseDown += delegate(object sender, MouseEventArgs e)
{
if (e.ClickCount == 1)
{
// single click
}
};
label.MouseDoubleClick += delegate
{
// double click
};
EDIT: My advice was following from documentation on MSDN:
The Control class defines the
PreviewMouseDoubleClick and
MouseDoubleClick events, but not
corresponding single-click events. To
see if the user has clicked the
control once, handle the MouseDown
event (or one of its counterparts) and
check whether the ClickCount property
value is 1.
However, doing so will give you a single click notification even if the user single clicks.
You must use a timer to differentiate between the two. Add a timer to your form in the GUI (easiest that way - it will automatically handle disposing etc...). In my example, the timer is called clickTimer.
private bool mSingleClick;
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (e.ClickCount < 2)
{
mSingleClick = true;
clickTimer.Interval = System.Windows.Forms.SystemInformation.DoubleClickTime;
clickTimer.Start();
}
else if (e.ClickCount == 2)
{
clickTimer.Stop();
mSingleClick = false;
MessageBox.Show("you double-clicked");
}
}
}
private void clickTimer_Tick(object sender, EventArgs e)
{
if (mSingleClick)
{
clickTimer.Stop();
mSingleClick = false;
MessageBox.Show("you single-clicked");
}
}
I did it this Way and it works perfectly
If e.Clicks = 2 Then
doubleClickTimer.Stop()
ElseIf e.Clicks = 1 Then
doubleClickTimer.Enabled = True
doubleClickTimer.Interval = 1000
doubleClickTimer.Start()
End If
Private Sub doubleClickTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles doubleClickTimer.Tick
OpenWebPage("abc")
doubleClickTimer.Stop()
End Sub
You are simply can use MouseDown event and count click number, like this:
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
// your code here
}
My suggestion, implemented in a UserControl by simply using a Task:
private int _clickCount = 0;
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
_clickCount = e.ClickCount;
}
protected override async void OnPreviewMouseUp(MouseButtonEventArgs e)
{
if (_clickCount > 1)
{
//apparently a second mouse down event has fired => this must be the second mouse up event
//no need to start another task
//the first mouse up event will be handled after the task below
return;
}
await Task.Delay(500);
if (_clickCount == 1)
{
//single click
}
else
{
//double (or more) click
}
}
The drawback of all these solutions is, of course, that there will be a delay before actually responding to the user's action.
You could do it on MouseUp instead of MouseDown. That way you can ask the ClickCount property for the total number of clicks, and decide what to do from that point.
It's my working solution :)
#region message label click --------------------------------------------------------------------------
private Timer messageLabelClickTimer = null;
private void messageLabel_MouseUp(object sender, MouseButtonEventArgs e)
{
Debug.Print(e.ChangedButton.ToString() + " / Left:" + e.LeftButton.ToString() + " Right:" + e.RightButton.ToString() + " click: " + e.ClickCount.ToString());
// in MouseUp (e.ClickCount == 2) don't work!! Always 1 comes.
// in MouseDown is set e.ClickCount succesfully (but I don't know should I fire one clicked event or wait second click)
if (e.ChangedButton == MouseButton.Left)
{
if (messageLabelClickTimer == null)
{
messageLabelClickTimer = new Timer();
messageLabelClickTimer.Interval = 300;
messageLabelClickTimer.Elapsed += new ElapsedEventHandler(messageLabelClickTimer_Tick);
}
if (! messageLabelClickTimer.Enabled)
{ // Equal: (e.ClickCount == 1)
messageLabelClickTimer.Start();
}
else
{ // Equal: (e.ClickCount == 2)
messageLabelClickTimer.Stop();
var player = new SoundPlayer(ExtraResource.bip_3short); // Double clicked signal
player.Play();
}
}
}
private void messageLabelClickTimer_Tick(object sender, EventArgs e)
{ // single-clicked
messageLabelClickTimer.Stop();
var player = new SoundPlayer(ExtraResource.bip_1short); // Single clicked signal
player.Play();
}
#endregion
My issue was with single/double-clicking rows in a DataGrid in WPF. For some reason the ButtonDown events weren't firing, only the OnMouseLeftButtonUp event was. Anyway, I wanted to handle the single-click differently from the double-click. It looks me a little time (I'm sure the solution isn't perfect, but it appears to work) to distill the problem down until I got it down to the below. I created a Task which calls an Action and that Action's target can be updated by a second click. Hope this helps someone!
private Action _clickAction;
private int _clickCount;
private void Grid_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Button Click Occurred");
_clickCount++;
if (_clickCount == 1)
{
_clickAction = SingleClick;
}
if (_clickCount > 1)
{
_clickAction = DoubleClick;
}
if (_clickCount == 1)
{
Task.Delay(200)
.ContinueWith(t => _clickAction(), TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(t => { _clickCount = 0; });
}
}
private void DoubleGridClick()
{
Debug.WriteLine("Double Click");
}
private void SingleGridClick()
{
Debug.WriteLine("Single Click");
}