vb.net can't remove handler - wpf

I got this piece for moving an element around on a canvas
Private p As Point
Private Sub moveHandler() Handles Me.MouseDown
p = Mouse.GetPosition(Me)
AddHandler canvasRef.MouseMove, AddressOf moveLoop
End Sub
Private Sub moveLoop()
If Mouse.LeftButton = MouseButtonState.Pressed Then
Dim c As Point = Mouse.GetPosition(canvasRef)
Canvas.SetLeft(Me, c.X - p.X)
Canvas.SetTop(Me, c.Y - p.Y)
Else
RemoveHandler canvasRef.MouseMove, AddressOf moveLoop
End If
End Sub
It underlines the removehandler and says something like "The addressof expression has no effect because it requires a relaxed something something, make delegate and remove that instead!"
Makes no sense to me.

That's because the signature of the moveLoop method doesn't match the signature of the MouseEventHandler delegate. Because VB.NET is so lax, it allows you to add it as a handler for the event, by internally creating an anonymous method with the required parameters that calls your handler. But when you try to remove the handler, it doesn't work because the anonymous method created before is no longer accessible...
The easiest fix is to change the signature of your method so that it matches the signature of the delegate:
Private Sub moveLoop(ByVal sender As Object, ByVal e As MouseEventArgs)
Another option is to store a reference to the handler as shown in keyboardP's answer.

Create a new EventHandler and then add and remove like so
Dim moveLoopHandler As New EventHandler(AddressOf moveLoop)
AddHandler canvasRef.MouseMove, moveLoopHandler
RemoveHandler canvasRef.MouseMove, moveLoopHandler

Related

How do I declare arry of objects outside a soubroutine in VB.net

I wrote a code to demonstrate the issue:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
doSomething()
End Sub
Dim controlArr() As Object = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
Private Sub doSomething()
Dim testStr As String = ""
For Each control In controlArr
Select Case control.GetType
Case GetType(NumericUpDown)
control.value = 1
Case GetType(CheckBox)
control.checked = True
End Select
Next
End Sub
End Class
When I run the code I receive Null Referenece Exception "Object reference not set to an instance of an object", the error disapears when I declare the controlArr array inside DoSomething subroutine. Anyway I would prefer having it declared outside since I am using it in many functions. I would like to understand it better so if you provided me with a topic I could read up on I would be very grateful. Thank you very much for your help.
The issue is that declarations are processed before the constructor. That means that this line:
Dim controlArr() As Object = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
is executed before the code that creates all the controls on the form and assigns them to those fields. As such, all the fields are Nothing at the time that code is executed and so your array contains a whole lotta nothin'. There's no issue creating objects to initialise fields like that and that code does successfully create an array. It's just that you are implicitly setting every element of that array to Nothing so that's what you get to use later on.
If you want to reference any control then you have to wait until after the form's controls are created. That means, at the earliest, after the call to InitializeComponent in the constructor. More generally, you should do it in the Load event handler, e.g.
Dim controlArr As Object()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Form1.Load
controlArr = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
End Sub

Calling method from button click does not have compatible signature

I'm getting a "...does not have a signature compatible with delegate..." message when trying to pass a parameter to a method. Can someone advise me on what I'm doing wrong?
Private Sub btnSubmit_Click(sender As Object, e As RoutedEventArgs) Handles btnSubmit.Click
Step6Click("btnSubmit")
End Sub
Private Sub Step6Click(whereFrom As String)
Initially, no error is flagged but when the application is built, that's when the error occurs.
I changed ("btnSubmit") to ("xxx") thinking there was some conflict with passing "btnSubmit" when there was a button named the same. Still get the error.
If I take out the parameter all together, then no error occurs, but I need to know in Step6Click where the call came from.
I can bypass the error by creating a property or variable called "whereFrom". However I'd like to understand why this is an error.
Complete error message:
Error 33 Method 'Private Sub Step6Click(whereFrom As String)' does not have a signature compatible with delegate
'Delegate Sub RoutedEventHandler(sender As Object, e As System.Windows.RoutedEventArgs)'.
Thanks.
If Step6Click is an event handler that you are trying to hook up to the Click event of a Button in your XAML markup it should have the following signature or no parameters at all:
Private Sub Step6Click(sender As Object, e As RoutedEventArgs)
End Sub
You could create another method that you call from both your event handlers, i.e. just create another method with a different name, e.g.:
Private Sub btnSubmit_Click(sender As Object, e As RoutedEventArgs) Handles btnSubmit.Click
Step6ClickMethod("btnSubmit")
End Sub
Private Sub Step6Click(sender As Object, e As RoutedEventArgs)
Step6ClickMethod("btnSubmit")
End Sub
Private Sub Step6ClickMethod(ByVal whereFrom As String)
End Sub

What is the VB syntax to add a handler using a delegate?

In a WPF project:
I believe this C# line of code is simply adding a handler to the User Interface object and forcing the parameter to 'false' when the handler is invoked.
item.MouseDoubleClick += delegate { otherMethod1(false); };
In adding VB handlers without parameters am I correct this would be the proper syntax?
AddHandler item.MouseDoubleClick AddressOf otherMethod2
How do I express in VB a handler with parameters as in the first line above?
Try this:
AddHandler item.MouseDoubleClick, Sub(sender As Object, e As MouseButtonEventArgs)
otherMethod1(False)
End Sub

Updating UI from another thread with VB in WPF

I am trying to use a timer to scan my Xbox 360 controller. But I cannot directly update my UI like the code I wrote below.
I would get a exception when I try to run this code.
An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code
Additional information: The calling thread cannot access this object because a different thread owns it.
XButton is a radiobutton on the GUI that I want to toggle.
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports System.Timers
Imports System.Windows.Threading
Public Class XboxControllerStatus
Friend WithEvents Timer1 As Timer
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Elapsed
Dim currentState As GamePadState = GamePad.GetState(PlayerIndex.One)
If currentState.IsConnected Then
If currentState.Buttons.X.Pressed Then
XButton.IsChecked = True
Else
XButton.IsChecked = False
End If
End If
End Sub
End Class
This works for me, all the time
Control.Invoke(sub()
'Put code here
End Sub)
First you need to set up a delegate
Delegate Sub SetCheckBoxCallback(ByVal value As Boolean)
Friend Sub SetCheckBox(ByVal value As Boolean)
XButton.IsChecked = value
End Sub
after that all you need to do is call the following code from within your timer to invoke it:
Dim DesiredValue as Boolean = True
Me.Dispatcher.Invoke(New SetCheckboxCallback(AddressOf SetCheckbox), New
Object() {DesiredValue})

asynchronous threads and anonymous delegates

Ok, now I can use 2 techniques to launch my threads: Dispatcher and BackgroundWorker.
Dispatcher:
' I launch the asynchronous process
Dim a As New Action(AddressOf operazioneLunga)
a.BeginInvoke(Nothing, Nothing)
' I update the GUI passing some parameters
Dim a As New Action(Of Integer, String)(AddressOf aggiorna_UI)
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, a, 5, "pippo")
BackgroundWorker:
Private bw As BackgroundWorker = Nothing
Private Sub initial()
bw = New BackgroundWorker
AddHandler bw.DoWork, AddressOf longOp
AddHandler bw.RunWorkerCompleted, AddressOf endBGW
bw.RunWorkerAsync ()
End Sub
Private Sub longOp(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim l As List(Of miaClasse2) = <Long Operation ...>
e.Result = l
End Sub
Private Sub endBGW(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
Dim l As List(Of miaClasse2) = e.Result
Dim be As BindingExpression = BindingOperations.GetBindingExpression(mioDatagrid, DataGrid.ItemsSourceProperty)
Dim m As miaClasse1 = DirectCast(be.DataItem, miaClasse1)
m.GetData (l)
mioDatagrid.UpdateLayout()
mioDatagrid.ScrollIntoView (mioDatagrid.Items(0))
RemoveHandler bw.DoWork, AddressOf massiccia
RemoveHandler bw.RunWorkerCompleted, AddressOf fineBGW
bw.Dispose()
End Sub
I don't know what is better, but I think I'll use BackgroundWorker, because I suppose there are other argouments about Dispatcher I have to know and I don't feel safe.
Pileggi
My previous post:
Hi everyone!
My application is in WPF / Vb framework 3.5 SP1. I need to execute some methods on asynchronous threads. I know this way:
Private Delegate Sub dMassiccia()
Private Delegate Sub dAggiornaUI()
Private Sub iniziale()
Dim massicciaTemp As New dMassiccia(AddressOf massiccia)
massicciaTemp.BeginInvoke(Nothing, Nothing)
End Sub
Private Sub massiccia()
'long operations...
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, _
New dAggiornaUI(AddressOf aggiornaUI))
End Sub
Private Sub aggiornaUI()
'update the UI...
End Sub
But in this way I have to declare a delegate for every mothod I want to launch on an asynchronous thread, and it's very uncomfortable. I have a lot of method to launch in this way. I know there are the anonymous delegates, but I don't know how to use them in this case.
Can you help me?
Pileggi
PS. Other information: in this moment I don't need to lookup the status of the process launched in the asynchronous thread. The long operations are some requests to a webservice that can take some seconds every time. There is no problem for the number of threads, because I limit the possibilities for the user to start new threads until one of them is finished. I need the asyncronous threads, among other reasons, because I don't wont to block the application, I want to replace the mouse cursor with a user-control, etc..
What is it that you're trying to do, that requires you to launch all of these threads? It looks like you're creating the secondary thread just to be able to do GUI updates.
First of all, if you have to create a lot of threads, then you run the risk of running out of available threads pretty quickly. I thought the max was only 64, but the documentation says 250 per process, and it's also settable via GetMaxThreads and SetMaxThreads. Regardless, you need to decide if using the ThreadPool threads (which is what's used when you use BeginInvoke/EndInvoke) is appropriate for you.
How long do your GUI updates take? Are they going to run the entire duration of your application? Can you use a regular thread instead? Look into using a BackgroundWorker for GUI updates if you just need to update status information periodically. In some cases, even DispatcherTimer might do the trick. It just depends on what you want to do.
You also don't show all of your code, but in what's posted, EndInvoke is not called. If you do this and end up throwing an exception, you won't be able to catch it and handle the error properly.

Resources