I am writing my first code to handle a Drag and Drop Event in WPF / VB.Net.
To act as a learning exercise, I am trying to initiate a DoDragDrop when the left button is pressed over a Button Control. I thought this would be achieved as follows:
Private Sub ButtonYield_MouseMove(sender As Object, e As MouseEventArgs) Handles ButtonYield.MouseMove
If e.LeftButton = MouseButtonState.Pressed Then
'Drag Drop Code Here
MsgBox("Event Called")
End If
End Sub
In practice, though, this event is only called if the left button is pressed prior to the mouse moving over the Button Control.
Is there something fundamental that I am missing here?
Thanks in advance!
This is behaving as expected, your code is checking for the mouse moving when the left button is clicked. You need to keep track of when the mouse if clicked on your button and only then do the drag drop if the mouse moves. Something along these lines (untested):
Private _mouseDownOverButton As Boolean = False
Private Sub ButtonYield_MouseLeave(sender As Object, e As MouseEventArgs) Handles ButtonYield.MouseLeave
_mouseDownOverButton = False
End Sub
Private Sub ButtonYield_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles ButtonYield.MouseLeftButtonDown
_mouseDownOverButton = True
End Sub
Private Sub ButtonYield_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles ButtonYield.MouseLeftButtonUp
_mouseDownOverButton = False
End Sub
Private Sub ButtonYield_MouseMove(sender As Object, e As MouseEventArgs) Handles ButtonYield.MouseMove
If _mouseDownOverButton Then
'drag drop code here
End If
End Sub
Related
I need to use "Show()" method for dialog winform using Ironpython
and i need to get result of button pressing and use it in another part of program
how can i wait for pressing OK in modal "Show()" dialog?
mform = Form3()
mform.Show()
# How to wait for button OK
Thx
Use ShowDialog() instead of Show().
Then check "if (mform.DialogResult == DialogResult.OK)"
Suppose you have a main form called Form1 and a secondary form called AddForm which presents some result when the [OK] button is pressed.
The main form then can subscribe to the FormClosed event of the secondary form and pull the result if the user clicked on the [OK] button.
This can all happen in the subroutine where the secondary form is shown
Private Sub Button1.Click(sender as Object, e as EventArgs) Handles Button1.Click
Dim dlg as New AddForm
AddHandler dlg.FormClosed, _
Sub(e,ev) TextBox1.Text = If(dlg.DialogResult = DialogResult.OK, dlg.Result, String.Empty)
dlg.Show()
End Sub
And in the secondary form make sure there is a property called Result which contains the results
The [OK] button has a handler like so
Private Sub Button1_Click(sender as Object, e as EventArgs) Handles Button1.Click
Result = ...
DialogResult = DialogResult.OK
Me.Close()
End Sub
and the [Cancel] button has a handler as so
Private Sub Button2_Click(sender as Object, e as EventArgs) Handles Button2.Click
DialogResult = DialogResult.Cancel
Me.Close()
End Sub
Im using Visual Basic 2008
I have 2 forms
Main, EditCustomerInfo
Main form contains the following
Public Class Main
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
EditCustomerInfo.ShowDialog()
End Sub
EditCustomerInfo contains a text box and the following
Public Class EditCustomerInfo
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If Not CustomerIDTextBox.Text = "" Then
Me.Close()
Else : Me.Hide()
End If
End Sub
WHAT IT DOES:
So with this code alone when i debug the program it takes me to the main form and allows me to click a button to open the editcustomerinfo form
When im on the editcustomerinfo form i have a textbox and a button. If something is typed in the textbox and the button is clicked then the form hides, if nothing is typed in the textbox when the button is clicked then the form closes.
WHAT I WOULD LIKE IT TO DO:
If something is typed in the textbox i would like the button on the editcustomerinfoform to hide the editcustomerinfoform and also create a button on main form that allows the user to bring the editcustomerinfo form back up with what was typed in the text box.
Suggestions?
Automatic behavior, such as this, always worries me. How do you know when a user has completed their input without a lost focus event. If there are no other controls on the screen, then users won't readily know how to trigger the event. That being said, you can use a timer to delay the screen closing from a KeyPress event.
Public Class EditCustomerInfo
Private WithEvents userInputDelay As Timer = New Timer() With {.Interval = 1000} REM 1 second delay for next user input
Public ReadOnly Property CustomerId As String
Get
Return CustomerIDTextBox.Text
End Get
End Property
Private Sub CustomerIDTextBox_KeyPress(sender As Object, e As KeyPressEventArgs) Handles CustomerIDTextBox.KeyPress
userInputDelay.Enabled = False
REM Reset the timer
userInputDelay.Enabled = True
End Sub
Private Sub userInputDelay_Tick(sender As Object, e As KeyPressEventArgs) Handles userInputDelay.Tick
If Not CustomerIDTextBox.Text = "" Then
Me.Close()
Else : Me.Hide()
End If
End Sub
End Class
Add a button (Button2) to your Main. The code below will hide Button1 when the EditCustomerInfo.Textbox1.Text value is null/blank/white space. Button2's visibility is always the inverse of Button1.
Private EditCustomerInfoInstance As New EditCustomerInfo
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
EditCustomerInfoInstance.ShowDialog()
Button1.Visible = String.IsNullOrWhiteSpace(EditCustomerInfoInstance.CustomerId)
End Sub
Private Sub Button1_VisibleChanged(sender As Object, e As EventArgs) Handles Button1.VisibleChanged
Button2.Visible = Not Button1.Visible
End Sub
Consider this example:
Private Sub Button_Click(
sender As Button, e As RoutedEventArgs) Handles btn.Click
sender.IsEnabled = False
Thread.Sleep(5000)
sender.IsEnabled = True
End Sub
In my scenario the Button_Click is a command delegate in the VM, and the Thread.Sleep is some long-running process (about 2-10 seconds).
I want, that when the user calls the command, it should immediately update the UI disabling the button so the user cannot execute it while it's running, then execute that operation, then, when operation completed, unblock the button.
I tried wrapping the middle line like the following:
Dispatcher.BeginInvoke(Sub() Thread.Sleep(5000))
But it didn't do the job.
What's the best way to do it?
The button click event is handled by the UI thread, hence when you invoke thread.sleep you make the UI thread sleep, and you see no changes until the method ends.
Therefore you need to run the process on a new thread, and when the process ends, make the UI changes using the dispatcher.
For example:
Private event TaskEnded()
Private Sub Button_Click(sender As Button, e As RoutedEventArgs) Handles btn.Click
btn.IsEnabled = False
dim l as new Thread(sub()
Thread.Sleep(5000)
RaiseEvent TaskEnded
End Sub)
l.start()
End Sub
Private Sub bla() Handles Me.TaskEnded
dispatcher.BeginInvoke(sub()
btn.IsEnabled = True
end sub)
End Sub
The MVVM way you'll bind your button IsEnabled property to a boolean property in your viewModel, and update the VM propety instead on the button directly.
Instead of creating a thread of your own you can also use the BackgroundWorker Control.
By calling the Method "RunWorkerAsync" the DoWork Event get's called in another Thread.
By Calling the Method "CancelAsync" form your UI thread you can set the Backgroundworker to "Cancellation Pending" (Property of the Control "CancellationPending" is then true). In your long running background thread you can check for that property (e.g. if you have a loop: exit the loop as soon as CancellationPending is true). This is a quite nice feature to safely abort the thread.
In addition with the Backgroundworker you can also report the progress of the thread (e.g. for use in a ProgressBar)
Example:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
'** Set to true if you want the ReportProgress Event
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.WorkerSupportsCancellation = True
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim i As Integer
Dim n As Integer = 100
Dim iLastPerc As Integer
While Not BackgroundWorker1.CancellationPending AndAlso i < n
'** Do your time consuming actions here
Threading.Thread.Sleep(500)
If Math.Floor((i / n) * 100) > iLastPerc Then
'** If the Progress has changed. Report
iLastPerc = CInt(Math.Floor((i / n) * 100))
BackgroundWorker1.ReportProgress(iLastPerc)
End If
i += 1
End While
End Sub
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
'** Run the Backgroundworker
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'** Update the ProgressBar
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'** Worker is done. Check for Exceptions or evaluate the Result Object if you like
End Sub
Private Sub btnCancel_Click(sender As System.Object, e As System.EventArgs) Handles btnCancel.Click
'** Cancel the worker
BackgroundWorker1.CancelAsync()
MsgBox("Finished!")
End Sub
End Class
In reference to your question the code should be:
Private Sub btn_Click(sender As Button, e As RoutedEventArgs) Handles btn.Click
sender.IsEnabled = False
Using bw As New BackgroundWorker()
AddHandler bw.DoWork, Sub(s, ea) Thread.Sleep(5000)
AddHandler bw.RunWorkerCompleted, Sub(s, ea) sender.IsEnabled = True
bw.RunWorkerAsync()
End Using
End Sub
Bind the button enabled property to a property in your VM (say ProcessComplete).
Use the button onclick event to trigger a method in your VM that starts up your long winded process. Keep the ProcessComplete False whilst the process is running and then set it True when it completes.
I am attempting to hide the mouse pointer when there has been a few seconds of inactivity and then re-show the pointer again when the user moves the mouse. I have been able to get the mouse pointer to hide and re-show as I require it, however when I execute grid.Children.Clear() and grid.Children.Add() the mouse pointer re-appears (but again hides after a few seconds of inactivity).
My code is as below:
Private Sub Window1_MouseMoved(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseMove
'MsgBox("Mouse Has Moved", MsgBoxStyle.Critical, "Mouse Moved")
LastMouseMove = DateTime.Now
If IsHidden Then
Cursor = Cursors.Arrow
IsHidden = False
End If
End Sub
Private Sub MouseHide_Tick(ByVal sender As Object, ByVal e As EventArgs)
Dim elaped As TimeSpan = DateTime.Now - LastMouseMove
If elaped >= TimeoutToHide AndAlso Not IsHidden Then
Cursor = Cursors.None
IsHidden = True
'System.Console.SetCursorPosition(0, 0)
End If
End Sub
Private Sub setupMouseHide()
Try
'Dim timer As New System.Timers.Timer(1000)
Dim dispatcherTimer As DispatcherTimer = New System.Windows.Threading.DispatcherTimer()
AddHandler dispatcherTimer.Tick, AddressOf MouseHide_Tick
dispatcherTimer.Interval = New TimeSpan(0, 0, 3)
dispatcherTimer.Start()
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "Setup Display Message: error encountered")
End Try
End Sub
I was wondering if this is a known issue or is there a better way of achieving what I am seeking to do?
Thanks,
Matt
It might be a bug, but its not uncommon for layout changes to cause a mouse move event to be raised.
I'd say your best bet might be to check and store the actual coordinates of the mouse in that mouse move event. That way you can ignore the errant mouse move events.
Not ideal, but I think it would work.
I need only to show a custom control (a clock with rotating hands) and with this to replace the mouse cursor, if I use a file .cur or .ani to replace the mouse cursor
Me.CUrsor = New Cursor("absolute path of the .ani file")
there is no problem: I can change the cursor during a procedure: but the quality of the animation is very bad, and, also for other reasons, I'd prefer to use my little user-control. The problem is that if I write:
Me.gridScreen.Visibility = Visibility.Visible
' some operations that takes about 1 second
Me.gridScreen.Visibility = Visibility.Hidden
(gridScreen is the grid that contains the user-control)
Obviously I can see nothing, because the update of the UI happens at the end of the procedure. I have tried Me.UpdateLayout(), but it doesn't work.
I have tryed to use the dispacker in many way but none that works :-(
This is my lost attempt:
(uCurClock is the usercontrol, gridScreen a Grid placed at the top-level in the window, with trasparent background, that contains the usercontrol)
Private Sub showClock()G
Dim thread = New System.Threading.Thread(AddressOf showClockIntermediate)
thread.Start()
End Sub
Private Sub hideClock()
Dim thread = New System.Threading.Thread(AddressOf hideClockIntermediate)
thread.Start()
End Sub
Private Sub showClockIntermediate()
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf showClockFinale))
End Sub
Private Sub hideClockIntermediate()
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf hideClockFinale))
End Sub
Private Sub showClockFinale()
Dim pt As Point = Mouse.GetPosition(Nothing)
Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)
Me.gridScreen.Visibility = Visibility.Visible
Me.Cursor = Cursors.None
Me.UpdateLayout()
End Sub
Private Sub hideClockFinale()
Me.gridScreen.Visibility = Visibility.Hidden
Me.Cursor = Cursors.Arrow
Me.UpdateLayout()
End Sub
Private Sub u_MouseMove(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseMove
Dim pt As Point = e.GetPosition(Nothing)
Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)
e.Handled = True
End Sub
Private Sub u_MouseEnter(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseEnter
Me.uCurClock.Visibility = Visibility.Visible
e.Handled = True
End Sub
Private Sub u_MouseLeave(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseLeave
Me.uCurClock.Visibility = Visibility.Hidden
e.Handled = True
End Sub
PIleggi
While the following code will do what you ask for, I suspect it won't actually help you, since you've mentioned animation. You're going to need to use multiple threads. However, just to demonstrate why that is, here's something that answers the question you've asked:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
uc1.Visibility = Visibility.Visible
Cursor = Cursors.Wait
' Push visibility changes now.
' (Sort of like DoEvents - and a horrible idea for exactly the same
' reasons that DoEvents was a total train wreck. Never actually do
' this - use a background thread instead.)
Dim df As New DispatcherFrame(True)
Dispatcher.BeginInvoke(Sub() df.Continue = False, DispatcherPriority.ContextIdle)
Dispatcher.PushFrame(df)
Thread.Sleep(1000)
ClearValue(CursorProperty)
uc1.Visibility = Visibility.Hidden
End Sub
Assuming you have some usercontrol called uc1 on the page, this will force it to be visible while your slow procedure runs.
But no animations will run. The problem is, if you're doing something slow on the UI thread, the UI thread can't do anything else - it can't run animations, it can't respond to user input. Basically the UI is frozen out. The only reason the code shown here even makes the user control visible is that it basically says "do any outstanding UI thread work now", which has the side effect of processing your change to the Visible property.
But animations happen on the UI thread too.
If you want to do this properly, you need to do the work on a background thread, possibly by using the BackgroundWorker, or by writing your own threading code.
reference DispatcherFrame Class Reference
good ole DoEvents for WPF!!!
Public Sub DoEvents()
Dim frame As New DispatcherFrame()
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
Dispatcher.PushFrame(frame)
End Sub
Public Function ExitFrame(ByVal f As Object) As Object
CType(f, DispatcherFrame).Continue = False
Return Nothing
End Function