This might be a dumb question, but I just turned on Option Strict for the first time, and I'm not sure what the best approach is here.
I have a bunch of dynamically created PictureBox controls, and I'm adding event handlers when they're created to handle their paint events. In the paint event, I need to access the ClientRectangle of the PictureBox. Notice that I've changed sender As Object to sender As PictureBox:
Public Sub Example(sender As PictureBox, e As PaintEventArgs)
AlreadyExistingRectangle = sender.ClientRectangle
AlreadyExistingRectangle.Inflate(-2, -2)
' Draw stuff in AlreadyExistingRectangle
End Sub
I need the AlreadyExistingRectangle for various reasons (though I suspect there are better solutions). The reason I'm using sender as PictureBox is because my paint events are kind of slow, and I thought it might speed it up, as sender.ClientRectangle would cause late binding otherwise. But now, narrowing occurs, because the delegate sub uses sender as Object.
So, is there a simple solution, or should I just allow late binding or narrowing? And if so, which is faster?
I think it'd be best if you created a variable in the Paint event handler where you cast sender to a PictureBox. Then you can wrap the whole thing in a Try/Catch block to catch the cast exceptions that DirectCast throws if sender is not a PictureBox.
Try
Dim senderPictureBox As PictureBox = DirectCast(sender, PictureBox)
'Do your stuff...
Catch ex As InvalidCastException
'Either do something here or just ignore the error.
End Try
Alternatively, since throwing exceptions is costly for your already, as you state it, slow code, you could use TryCast which instead of throwing an exception just returns Nothing if the cast fails (this is much faster performance-wise).
Dim senderPictureBox As PictureBox = TryCast(sender, PictureBox)
If senderPictureBox IsNot Nothing Then
'Do your stuff...
End If
Related
Consider a simple .NET form with a couple of radio buttons and a checkbox.
Each of the radio buttons has a CheckedChanged handler setup that performs some action based on the state of the checkbox.
My problem is, when I initialize on the default radiobutton to be checked (from the designer properties window) the CheckedChanged event is fired for that radio button, but the Checkbox hasn't been initialized yet so I either get a null pointer exception or the wrong value is used in the handler. Either way, I don't want that handler code to be run unless the user picks a radio button after the form has been loaded.
I currently get around this by not initializing the radio button, but I need to set that default eventually and the best place is from the designer. I also can add a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack.
What can I do to prevent that handler from running its code?
To make it feel slightly less dirty, if you initialize the controls in the constructor of the form you might be able to use the forms IsHandleCreated property rather than your own bool to check if it should actually validate or not.
I would think that normally you wouldn't want to validate anything before it's been shown for the first time and handle isn't created until it is.
Code Example:
Private Sub myRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles myRadioButton.CheckedChanged
If myRadioButton.Checked AndAlso myRadioButton.IsHandleCreated Then
'Do Work
End If
End Sub
"I also can put a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack."
It's also the easist and best way to do it!
Lets say .NET provides a neat way to turn an and off all the event handlers until the form is loaded. Even just the ones YOU are handling. It would still not be sufficiently flexible to disable what you wanted to enable but disable what you didn't. Often form setups happen and you want the events to fire. Also the form won't build right if no events fire.
The easy solution is to declare an initializing variable:
Private Initializing as boolean = True
Private Sub rb_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbNuevos.CheckedChanged, RbDesaparecidos.CheckedChanged, RbModificados.CheckedChanged, RbNoDesap.CheckedChanged, RbDesHoy.CheckedChanged, RbChT.CheckedChanged
if Initializing then return
'Your Code
End Sub
Public Sub New()
' Llamada necesaria para el Diseñador de Windows Forms.
InitializeComponent()
' Agregue cualquier inicialización después de la llamada a InitializeComponent().
initializing = false
end sub
Most sophisticated: Remove the "handles" from the method, and use AddHandler on the new method.
Public Sub New()
' Llamada necesaria para el Diseñador de Windows Forms.
InitializeComponent()
' Agregue cualquier inicialización después de la llamada a InitializeComponent().
AddHandler RbChT.CheckedChanged, AddressOf rb_CheckedChanged
end sub
For radiobutton see Hans Olsson answer
For numeric up down, do it like this
Private Sub myNumeric_ValueChanged(sender As Object, e As EventArgs) Handles myNumeric.ValueChanged
If myNumeric.Value >= 0 AndAlso myNumeric.IsHandleCreated Then
'Do the work
End If
End Sub
The keyword is myNumeric.Value and IsHandleCreated
Yet another way:
Private Sub dgvGroups_CellValueChanged(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvGroups.CellValueChanged
If Me.Visible = False Then Exit Sub ' Sub gets called on form load which causes problems
wksGroups.Cells(e.RowIndex + 1, 1) = dgvGroups.Item(e.ColumnIndex, e.RowIndex).Value
wksGroups.Cells(1, 5) = dgvGroups.RowCount
One thing I've found that works is adding the events manually after you load the form.
To do this you can simply go into the generated form code found in the designer file of that form, and pull out the lines that add the event. It would look something like this:
this.controlName.CheckedChanged += new System.EventHandler(this.controlName_CheckedChanged);
Then put all of these calls into a method that you call after the InitializeComponent call in your form's constructor.
Just in case anyone is still searching for this the event is fired upon initializing the form BUT the form is not yet visible, Also Say that you have a foreign key relationship upon which you have a default value needed issue that gets fired every row update too. So the following code worked for me....
if (Visible && !(e.ColumnIndex == 0))
{
phoneEdited = true;
MessageBox.Show("A Phone entry has been entered");
}
Don't set checked on a control that really does much in designer.
The global flag and conditional exits where needed.
Try..Catch the sore spots to ignore a meaningless exception.
(Using VS 2017) It appears to me that it is an annoyance but not a bug. It is consistent with the model in use. The event is fired by normal operation of code, but code I did not write (but can access where fools fear to tread) and where there appears to be no (decent) place earlier in the normal flow to anticipate it.
The cleanest answer seems to be not to check radio button or checkbox controls in the designer at all if they trigger any significant code. Instead these controls should be changed by code (e.g. checked = true) in the Load event (for example) AFTER all the initialization is done.
There is no loss of flexibility here since both are fixed before the build, only in different places. The event handlers will handle it exactly as if a user had clicked the control in the natural flow of a well designed GUI application. (This reminds me of the ancient RPG proverb "Don't buck the cycle". (Anyone here remember RPG? I, not part of IBM-oriented team, never used it but had interesting discussions with some who did. ) Pre-checking controls hits the wrong part of the VS cycle.)
If for any reason that will not work, the next best thing is the kludge suggested elsewhere of a single status boolean initialized false and set true at the appropriate time with conditional exits in the necessary places to prevent them from crashing until then. It will get the job done, but it's ugly. Better than failure.
Another thing I tried before I decided that designer level pre-set checks were the problem and there was a very acceptable alternative was to put the danger spots in a Try..Catch to be able to ignore the exception. Also a kludge.
For the cleanest code, reverse the True/False approach used in some other examples. Focus on 'ready' rather than 'busy'. Here's an example for a Windows Form:
At the Class level, add Private app_ready As Boolean (it will be False by default).
At the end of the Form.Shown event handler, add app_ready = True.
In each control event handler where it's needed, add:
If app_ready Then
' code
End If
Starting a routine with something like If initialising Then Exit Sub just doesn't feel right!
Maybe for some functionality you can use the click event instead of the check changed event.
I put a public variable in the Module1 file
Dim Public bolForm_LoadingTF as Boolean = True
In each formLoad event I put
bolForm_LoadingTF = True
In each control with an OnSelectedIndexChanged
event I put if bolForm_LoadingTF = True then Exit Sub
At the end of the form load event I put
bolForm_LoadingTF = False
I am probably breaking a bunch of rules but this works
for me.
I need to be able to pass the thread name into a subroutine in order to abort it when I need to.
So I have this so far:
Dim BrowserSpawn As New System.Threading.Thread(AddressOf BrowserSub)
BrowserSpawn.Start()
Private Async Sub BrowserSub(BrowserSpawn)
...
End Sub
Because the subroutine creates a browser within Form1 groups I needed to invoke access to these controls within the sub.
Note: This works fine when I'm not passing in the thread name.
If Me.GroupNamehere.InvokeRequired Then
Me.GroupNamehere.Invoke(New MethodInvoker(AddressOf BrowserSub))
Else
'Do nothing
End If
When I'm passing in the thread name these become a problem when trying to compile:
Method does not have a signature compatible with delegate 'Delegate Sub MethodInvoker()'.
I'm hoping this is just a syntax thing but I can't seem to get it to work. Is there any way I'm able to pass in this thread name without breaking my invokerequired check?
If I try and change it to the obvious:
Me.GroupNamehere.Invoke(New MethodInvoker(AddressOf BrowserSub(BrowserSpawn)))
It tells me Addressof operand must be the name of a method (without parentheses). Although without the parentheses it's not happy either so I don't know where to go from here.
/edit:
Stumbled across How can I create a new thread AddressOf a function with parameters in VB?
Which seems to confirm what I was trying passing something like:
Private Sub test2(Threadname As Thread)
' Do something
End Sub
And the sub seems happy with that. But I'm not sure how to do that without breaking the invoker part.
Me.GroupNameHere.Invoke(New MethodInvoker(AddressOf SubNameHere))
Works normally. If SubNameHere() becomes SubNameHere(threadname as thread) then that seems happy enough but the invoke line breaks and doesn't want more than the address of.
Two slight syntax changes sorted it:
Private Async Sub SubName(ThreadNameAs Thread)
and
GroupName.Invoke(New MethodInvoker(Sub() Me.SubName(ThreadName)))
I used the following code:
Sub AppStartup()
AddHandler AppDomain.CurrentDomain.UnhandledException,
Sub(sender As Object, args As UnhandledExceptionEventArgs)
Dim e = CType(args.ExceptionObject, Exception)
ShowMessage("Oops...", HandleErr(e), MessageBoxImage.Error)
End Sub
End Sub
and I don't get the property handled Although it appears this link is supposed to be his
Your link points at XAML article, your question is probably about WinForms.
Are you starting this inside VS? Please note that exception will first be handled by VS, and then by this handler. When you run as a standalone executable, it should work as expected. You don't need to set any e.Handled to anything.
While your question doesn't really make sense, I'd say its safe to say you are trying to implement an unhandled exception handler.
This article should have all the information you need.
http://msdn.microsoft.com/en-gb/library/system.appdomain.unhandledexception.aspx
I am new to .net and am doing my best to understand the Nuances (or Nuisance). I am trying to print text on a form. I did find many examples of the Graphics class, DrawString() etc. Unfortunately, I cannot get any of them to work. The stumbling block for me seems to be the error “NullReferenceException” and/or the warning “variable usage before it has been assigned”. One example found at "http://msdn.microsoft.com/en-US/library/76c5db29(v=vs.80).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1". vs2005 displays the warning "Variable 'myE' is used before it has been assigned a value. A null referene excpetion could result at runtime and sure enough when I run a NullReferenceException is thrown at "e.Graphics.DrawString(drawString, drawFont, drawBrush, drawPoint)" How do I fix this?
Public Class Form1
Public Sub DrawStringPointF(ByVal e As PaintEventArgs)
Dim drawString As [String] = "Sample Text"
Dim drawFont As New Font("Arial", 16)
Dim drawBrush As New SolidBrush(Color.Black)
Dim drawPoint As New PointF(150.0F, 150.0F)
e.Graphics.DrawString(drawString, drawFont,drawBrush, drawPoint)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim myE As PaintEventArgs
DrawStringPointF(myE)
End Sub
End Class
You can't do that.
Instead, handle the Paint event and draw everything you need to.
To trigger a redraw, call Invalidate().
Your question is broader than your problem.
You're asking what a null reference exception is; this question has been answered many times elsewhere. A NRE (NullReferenceException) is referring to a variable that isn't defined at all
We can think about memory as being a vast, open parking lot. When you define a variable, using a statement like this,
Dim myObject as Integer
... you're claiming one or more of the parking spaces in the parking lot. However, all you've done is declare that this spot is yours, but not what goes in it. To give it a value, simply use the equals operator.
myObject = 100
Simple objects, known as primitives, like strings, numbers and characters, have an implied value when they're not defined. For example, integers are assumed to be zero if they have no value.
In your program, when you get a NRE, mouse over each argument in the function and the object that the function is being called from. If any of them claim to have a value of Nothing, there's your problem. You've reserved space for your object, but not yet given it a value.
As SLaks said, your specific problem is related to how you're calling the method, but without a strong understanding of what Nothing means, it may not correctly address the underlying question>
Finally, let's look at code that's specifically yours. You call
Dim myE as PaintEventArgs.
In this statement, you have reserved space in the memory- that is to say, claimed a parking space we talked about earlier. However, it does NOT have a value! It's empty. The program cannot function properly because you're telling it to perform an action on something that doesn't exist. To fix it, use the New operator. When making new objects, first reserve them in memory (Dim actually stands for "define in memory") and then give them a value with the New operator. You'd see something like this:
Dim myObj As Object
myObj = New Object()
You can also do it inline:
Dim myObj As Object = New Object()
When you try to do that with your PaintEventArgs, you'll see that that's quite impossible. PaintEventArgs requires information that's taken from a Windows Form control. That method you've written needs to handle an event. This post is too long as it is, so hopefully someone else can explain events and how they're related to methods. Basically, each control (textbox, image, or even the picture itself) raises an event every time it "paints" to the screen. You can "handle" that event; or do actions when it occurs, by doing this:
Public Sub OnPaint(sender as Object, e as PaintEventArgs) Handles myControl.OnPaint
You are declaring that whenever myControl raises an event for OnPaint, it will call this method, with arguments provided from the control.
You cannot simply substitute the required arguments from elsewhere. In order for it to work properly, the event arguments must be passed from the control that you wish to paint to.
I hope this helps. Please let me know if I've been unclear.
I'm looking to call a pre-existing event handler subroutine from the form_Load event handler.
But the below doesn't work because control doesn't come back and I want to do more.
UPDATE:
I'm new to this so I don't know what the proper protocol is but...
The reason for the non-return was that a statement like the below ended the subroutines execution.
If aLabel.Tag = 1...
the fix was adding New to the declaration to create an instance of it, ie..
changing....
Dim aLabel As Label
... to ...
Dim aLabel As New Label
I'm surprised I didn't get a warning but instead they just abruptly stopped execution of the sub. That wasn't very helpful :)
Thanks again for your time guys...
(Maybe this question should be deleted now that it has served its purpose)
#konrad #karl
END OF UPDATE
What doesn't work is....
Private Sub Form1_Load...
button1_Click(sender, e) 'But Control doesn't come back.
end sub
Do I change the sender to something?
Thanks in advance
Dave
Invoking event handlers like this is a bad idea, because you are trying to simulate the event context by making sender and/or EventArgs be something else.
Instead, put the logic that you want to invoke into a Subroutine or Function and have your Form1_Load method call that; likewise if you really do have a real click event handler, then that handler code can call the method too, like this:
Private Sub Form1_Load()
DoSomeWork()
End Sub
Protected Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
DoSomeWork()
End Sub
Private Sub DoSomeWork()
' Put logic here that you want to do from form load and a button click
End Sub
This has the benefit of making the code cleaner, clearer and easier to maintain as you only need to change the logic in one place should you need to change the logic.
Note: Obviously, you can pass parameters to the DoSomeWork method, if need be, and change it to a Function if you need it to return something.