WPF - How to not do DragDrop on MouseDoubleClick? - wpf

I've got a control with rectangles in a grid. I can drag rectangles from one cell to another within the grid. But I also need to double-click on a rectangle to bring up an edit dialog. The issue here is that just pressing the left mouse button on the rectangle will initiate a MouseMove event and cause the DragDrop to initiate when it's not wanted.
Here's the MouseMove and MouseDoubleClick event handlers:
Dim IsDragging As Boolean = False
Private Sub SchedItem_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If IsDragging Then Exit Sub
If e.LeftButton = MouseButtonState.Pressed Then
Dispatcher.InvokeAsync(Sub()
IsDragging = True
DragDrop.DoDragDrop(Me, Me, DragDropEffects.Move)
IsDragging = False
End Sub)
End If
End Sub
Private Sub SchedItem_PreviewMouseDoubleClick(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseDoubleClick
itmEditTask_Click(Nothing, Nothing)
e.Handled = True
End Sub
I tried not initiating the MouseMove by attempting to determine the distance the mouse had traveled since the MouseLeftButtonDown event. But the x & y numbers are all over the place and I can't reliably set a threshold value. Here's that code:
Private mPos As Point
Private Sub SchedItem_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
mPos = Mouse.GetPosition(Nothing)
End Sub
Private Function IsReallyMouseMove() As Boolean
Dim pPos As Point = Mouse.GetPosition(Nothing)
Debug.WriteLine(Math.Abs(mPos.X - pPos.X) & ", " & Math.Abs(mPos.Y - pPos.Y))
IsReallyMouseMove = Math.Abs(mPos.X - pPos.X) > 10 OrElse Math.Abs(mPos.Y - pPos.Y) > 10
End Function
So I'm hoping someone has done this and can provide some direction.

best way - redesign.
Make you item draggable by some object in item element. (Maybe little dot with different drag cursor)
Other area will handle double click.

Related

Get coordinates of a corner of a wpf button

This seems really simple but really having an issue! I have a button, that when I click on it, I want the x,y coordinates of one corner so I can pop up a window in a certain location from the button. I know the height and width of the button, and I can get the coordinates of where I have clicked with the mouse, but really struggling to get ccordinates of a corner. This is what I have so far:
Private Sub HandleClick(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim clickLocationPos = Mouse.GetPosition(Window.GetWindow(Me))
Dim xPos = clickLocationPos.X
Dim yPos = clickLocationPos.Y
End If
End Sub
This should give the clicked Button's top-left coordinate relative to the parent window:
Private Sub HandleClick(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim button = CType(sender, Button)
Dim topLeftCorner = button.TransformToAncestor(Me).Transform(New Point(0, 0))
Dim xPos = topLeftCorner.X
Dim yPos = topLeftCorner.Y
End Sub

Custom resizing to mouse position different then cursor position

I want to resize a custom window, so windowstyle=none.
For that I do not want to use some Open-Sourelib.
So I found this article via google.
After I changed the code a bit, since i want to use a button instead of a rectangle for resizing the code looks like this:
Private bottomResize As Boolean = False
Private initBtmY As Double
Private Sub BottomResizeRect_MouseEnter _
(ByVal sender As Object, ByVal e As _
System.Windows.Input.MouseEventArgs) _
Handles btResizeAndFold.MouseEnter
bottomResize = False
'Console.WriteLine("Mouse Enter called")
End Sub
Dim boing As Boolean = False
Private Sub BottomResizeRect_MouseLeftButtonDown _
(ByVal sender As Object, ByVal e As _
System.Windows.Input.MouseButtonEventArgs) _
Handles btResizeAndFold.PreviewMouseLeftButtonDown
bottomResize = True
boing = True
'Console.WriteLine("Mouse left down called")
'Get the initial Y coordinate
'cursor location on our window
initBtmY = e.GetPosition(Me).Y
End Sub
Private Sub BottomResizeRect_MouseLeftButtonUp _
(ByVal sender As Object,
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles btResizeAndFold.PreviewMouseLeftButtonUp
'Console.WriteLine("Mouse left up called")
bottomResize = False
btResizeAndFold.ReleaseMouseCapture()
End Sub
Private Sub BottomResizeRect_MouseMove _
(ByVal sender As Object, ByVal e As _
System.Windows.Input.MouseEventArgs) _
Handles btResizeAndFold.PreviewMouseMove
'Get the new Y coordinate cursor location
Dim newBtmY As Double = e.GetPosition(Me).Y
'Get the change between the initial and
'new cursor location
Dim diff As Double = initBtmY - newBtmY
'Minimum window height
Dim minHeight As Integer = 200
Dim differnceConstant = 5
If bottomResize = True And (diff > differnceConstant Or diff < (differnceConstant * -1)) Then
'Let our rectangle capture the mouse
btResizeAndFold.CaptureMouse()
Dim newHeight = e.GetPosition(Me).Y - diff
If newHeight > minHeight Then
Me.Height = newHeight
End If
End If
End Sub
The problem now is, that if i try to resize my window by pressing the left mouse button and then drag the mouse, the increase/decrease of the height of my window is not synchonosly to the movement of my mouse cursor, so the question is: how to make the movement of the mouse sychronos to growth of the window
Instead of Button you could use Thumb, which provide with DragDelta Event that suits your requirement:
https://wpf.2000things.com/2012/12/19/715-using-the-thumb-control-to-drag-objects-on-a-canvas/
That way you dont need to capture the mouse(you shouldnt've put the CaptureMouse in the PreivewMouseMove anyway, it will do that a lot).
And just set the height as the current height + the delta Y.

UserControl/ElementHost Transparent Background

I need a method that sets the background of the ElementHost completely transparent or so that it does not even render in the first place.
Current Structure
In the background I have a PictureBox.
Over that there is my UserControl (Which you can Download below).
Both the PictureBox and the UserControl have a Width of 150.
As you can see in the Picture above, the UserControl is 100% Invisible.
In the UserControl is an ElementHost with a Width of 120, within that there is a WPF-Content with a Width of 100.
Everything is Transparent, except the ElementHost1.
My Code
UserControl:
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = &H20
Return cp
End Get
End Property
Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
End Sub
Public Overrides Sub Refresh()
Parent.Invalidate(New Rectangle(Me.Location, Me.Size), True)
End Sub
Public Sub New()
InitializeComponent()
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = System.Drawing.Color.Transparent
ElementHost1.BackColor = System.Drawing.Color.Transparent
ElementHost1.BackColorTransparent = True
End Sub
I´ve also tried to create a Custom ElementHost:
Public Class TransElementHost
Inherits ElementHost
Public Sub TransElementHost()
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColorTransparent = True
'Me.BackColor = System.Drawing.Color.FromArgb(0, 0, 0, 0)
End Sub
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = &H20
Return cp
End Get
End Property
Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
End Sub
Public Overrides Sub Refresh()
Parent.Invalidate(New Rectangle(Me.Location, Me.Size), True)
End Sub
End Class
My SVGTest-UserControl
Does anybody have an idea?
I know it's been really long since this was active, but if you still need it I've (finally) got a code that will draw controls' backgrounds fully transparent.
Just put this code in the control's code. It overrides the OnPaintBackground event:
Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaintBackground(e)
If Parent IsNot Nothing Then
Dim index As Integer = Parent.Controls.GetChildIndex(Me)
For i As Integer = Parent.Controls.Count - 1 To index + 1 Step -1
Dim c As Control = Parent.Controls(i)
If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible = True Then
Dim bmp As New Bitmap(c.Width, c.Height, e.Graphics)
c.DrawToBitmap(bmp, c.ClientRectangle)
e.Graphics.TranslateTransform(c.Left - Left, c.Top - Top)
e.Graphics.DrawImageUnscaled(bmp, Point.Empty)
e.Graphics.TranslateTransform(Left - c.Left, Top - c.Top)
bmp.Dispose()
End If
Next
End If
End Sub
It's probably not the best fix, but you could try using the ButtonRenderer class. Put this code either in OnPaintBackground or OnPaint.
If Me.BackColor = Color.Transparent Then
Windows.Forms.ButtonRenderer.DrawParentBackground(e.Graphics, Me.ClientRectangle, Me)
End If
The ButtonRenderer class is used to draw the regular buttons; their border, background, text, image etc.
I used the above code myself to create a custom control with transparent background. (Though I see now that my control inherits from ButtonBase... But the above code is still worth a try).

Event not firing after item updated, even after re-adding the handler

In my Program Cyrus-Beck algorithm, I have to clip the points. The points inside the clipping window have to be red. The outside is still black.
I represent point in program as rectangle and I give the event handler for delete the point. when I wanted to delete points, points outside the window is working, but points that inside polygon didn't work, event the right-click event not call.
Here is my Point class.
Ignore the Component class, because it just nothing than handler to canvas in the main window
Public Class Points
Inherits Component
Public items As List(Of Point) = New List(Of Point)()
Public rects As List(Of Rectangle) = New List(Of Rectangle)
'add element
Public Overrides Sub Canvas1_MouseMove(sender As Object, e As MouseEventArgs)
canvas.Cursor = Cursors.Pen
End Sub
Public Overrides Sub Canvas1_MouseDown(sender As Object, e As MouseButtonEventArgs)
canvas.Cursor = Cursors.Pen
If e.LeftButton = MouseButtonState.Pressed Then
items.Add(e.GetPosition(canvas))
Dim p As Rectangle = New Rectangle()
p.Width = 3
p.Height = 3
canvas.SetLeft(p, e.GetPosition(canvas).X)
canvas.SetTop(p, e.GetPosition(canvas).Y)
p.Stroke = Brushes.Black
p.Fill = Brushes.Black
rects.Add(p)
End If
End Sub
'deleting element
'this method not calling when the element has updated
Sub point_rightClick(sender As Object, e As MouseButtonEventArgs)
Dim temp As Rectangle = New Rectangle()
temp = DirectCast(sender, Rectangle)
Dim pt As Point = New Point(Controls.Canvas.GetLeft(temp), Controls.Canvas.GetTop(temp))
canvas.Children.Remove(temp)
items.Remove(pt)
rects.Remove(temp)
End Sub
Public Shared Function samePoint(one As Rectangle, two As Rectangle) As Boolean
Return ((Controls.Canvas.GetLeft(one) = Controls.Canvas.GetLeft(two)) And (Controls.Canvas.GetTop(one) = Controls.Canvas.GetTop(two)))
End Function
Public Shared Function samePoint(one As Point, two As Rectangle) As Boolean
Return (one.X = Controls.Canvas.GetLeft(two)) And (one.Y = Controls.Canvas.GetTop(two))
End Function
End Class
To able to delete, I have deleting that represents the status, if deleting is true and currentState = MouseState.Pointer. the code will add handler to all the points (rectangles).
Private Sub chge_pointer_Click(sender As Object, e As RoutedEventArgs) Handles chge_pointer.Click
currentState = MouseState.Pointer
deleting = True
chooseHandler()
End Sub
Private Sub chooseHandler()
If deleting Then
For Each r As Rectangle In points.rects
AddHandler r.MouseRightButtonDown, AddressOf points.point_rightClick
Next
Else
For Each r As Rectangle In points.rects
RemoveHandler r.MouseRightButtonDown, AddressOf points.point_rightClick
Next
End If
End Sub
Before I modified the points to be red, I have to have a loop to check if the position is same, if the position same so edit the points
Dim z As CyrusBeck = New CyrusBeck(clippingWindow)
z.Clip(points.items)
'CyrusBeck has a properties 'points'
'CyrusBeck.points saves the all the point that inside the clipping window
For i As Integer = 0 To points.rects.Count - 1
For Each p As Point In z.points
If CGAProject.Points.samePoint(p, points.rects.Item(i)) Then
Dim temp_r As Rectangle = New Rectangle()
temp_r = points.rects.Item(i)
temp_r.Stroke = Brushes.Red
temp_r.Fill = Brushes.Red
points.rects.Add(temp_r)
points.rects.RemoveAt(i)
End If
Next
Next
when the program reaches this, currentState would Never to be MouseState.Pointer. so if user want to delete a point, user have to be press button chge_pointer so my chge_pointer_Click is called and all my handler would be re-setted again.
Why Rectangle.MouseRightButtonDown is not call when the rectangle has updated?
the rectangles that haven't update call the Rectangle.MouseRightButtonDown event, so I have re-set the handler.
What's worng?
Is there any alternative way so user can delete rectangle inside and outside the window?
Note :
I have resetted the handler just after I assigned back my Rectangle to the list, but it still didn't work.
I also create AddHandler temp_r.MouseRightButtonDown, AddressOf points.point_rightClick just after I assigned back my Rectangle to the list, but it didn't work either.
At the end of your events for clicking, like:
Sub point_rightClick(sender As Object, e As MouseButtonEventArgs)
I think you should have a "handles".
E.g.
Sub point_rightClick(sender As Object, e As MouseButtonEventArgs) Handles point.MouseClick
Without this, the sub will never be called (as it seems is happening).
This method also has no "handles"
Public Overrides Sub Canvas1_MouseDown(sender As Object, e As MouseButtonEventArgs)
and it could be:
Public Overrides Sub Canvas1_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Canvas1.MouseDown

WPF double animation completed event firing twice

I can't understand why the double animation event is firing twice. The rich text box gets updated on the completed method. When it is faded out, I change the text from queue. Then animation begins again. When queue is empty, it replenishes with new data. But because the event is firing twice, it skips some of the texts.
Private Sub CreateNewsAnimation()
fadingNewsAnimation.From = 0
fadingNewsAnimation.To = 1
fadingNewsAnimation.AutoReverse = True
fadingNewsAnimation.Duration = New Duration(TimeSpan.FromSeconds(newsDuration))
AddHandler fadingNewsAnimation.Completed, AddressOf fadingNewsAnimation_Completed
RTBoxNewsBottom.BeginAnimation(UIElement.OpacityProperty, fadingNewsAnimation)
End Sub
Private Sub fadingNewsAnimation_Completed(ByVal sender As Object, ByVal e As EventArgs)
If enableEvent = True Then
If m_NewsQueue.Count > 0 Then
Using newsMemStream As New IO.MemoryStream(System.Text.ASCIIEncoding.[Default].GetBytes(m_NewsQueue.Dequeue()))
RTBoxNewsBottom.Selection.Load(newsMemStream, DataFormats.Rtf)
RTBoxNewsBottom.BeginAnimation(UIElement.OpacityProperty, fadingNewsAnimation)
End Using
Else
PopulateNewsQueue()
End If
End If
End Sub

Resources