How to overload a built-in vb.net function - vb.net

I want to overload the built in MsgBox() function in VB.Net.
I want to add a beep every time a MsgBox pops up, but would rather not make a new function, e.g., MsgBoxWithBeep(), and do a whole bunch of find/replace in my existing code.
I can do this just fine within a module:
Public Sub MsgBox(msg As String)
Beep()
MsgBox(msg)
End Sub
But of course this ends up being an endless recursive loop.
I can't do MyBase.MsgBox() because this is not a class.
Is there an assumed class that all built-in function use like so I can do something like VbNetBaseClass.MsgBox() or some other way to get back to the original function?.

A simple solution would be to just use MsgBox to call MessageBox.Show like this:
Public Sub MsgBox(msg As String)
Beep()
MessageBox.Show(msg, Me.Text, MessageBoxButtons.OK)
End Sub
Or better yet, use the exclamation/error icons that already make a beep:
Public Sub MsgBox(msg As String)
MessageBox.Show(msg, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Sub
_
Edit: good point from blackwood...you can make it into a function if you need the result:
Public Function MsgBox(msg As String, _
Optional title As String = "", _
Optional msgButtons As MessageBoxButtons = MessageBoxButtons.OK, _
Optional msgIcon As MessageBoxIcon = MessageBoxIcon.None) _
As System.Windows.Forms.DialogResult
Return MessageBox.Show(msg, title, msgButtons, msgIcon)
End Function

I don't believe that you'll be able to override or overload the MsgBox() method as it is static and a standard module (as opposed to a class) as seen here within the source :
[StandardModule]
public sealed class Interaction
{
/* Other methods omitted for brevity */
public static MsgBoxResult MsgBox(object Prompt, MsgBoxStyle Buttons = MsgBoxStyle.ApplicationModal, object Title = null);
}
Your best bet is going to be to write your own method or wrapper to handle calling it with your optional overloads, etc.

Related

How to set messagebox icon using a string variable in vb.net

I've been trying to make a program open a messagebox with the icon identified by a string variable, but I can't figure out how! I've tried using something like this
MessageBox.Show("Message here", _
"Message", _
MessageBoxButtons.YesNoCancel, _
MessageBoxIcon. + IconVariable)
But it gives me the error:
'MessageBoxIcon' is a type and cannot be used as an expression.
I created a Function from the link suggested by #GSerg in comments.
Private Function GetIconEnum(s As String) As MessageBoxIcon
Dim icon As MessageBoxIcon
Try
icon = DirectCast([Enum].Parse(GetType(MessageBoxIcon), s), MessageBoxIcon)
Catch ex As Exception
Return MessageBoxIcon.None
End Try
Return Icon
End Function
Private Sub OPCode()
Dim IconVariable As String = "Question"
MessageBox.Show("Message here", "Message", MessageBoxButtons.YesNoCancel, GetIconEnum(IconVariable))
End Sub

Add handler in runtime within a constructor VB.NET

REEDIT
I have reedited the question to be more understandable following the advices of #enigmativity in his comments.
This is an example of what I want to do in my program. I tried to make it as simple as I can, so I hope you can understand my question.
The problem is the following: I am trying to add handlers dynamically, because I have an object which needs to fire a handler when something happens on it, and I can create this objects dynamically in the program. Maybe following this example you could understand my problem.
If I have an object, for example, a car, with its own properties (doors, wheels, motor..), and something happen in the road, I want the handler BREAK fires, and in the BREAK function do whatever I need to make it break. Here is the example code of the class for this object:
Public Class Car
Public Property carName As String
Public Property wheels As Integer
Public Property motor As String
Public Property color As String
Public Property speed As Decimal
Private Event BreakCar()
Public Sub New()
End Sub
Public Sub New(ByVal name_ As String, ByVal wheels_ As Integer, motor As String, color As String, ByVal speed As Decimal)
carName = name_
wheels = wheels_
motor = motor_
color = color_
speed = speed_
End Sub
Public Sub Break()
Try
AddHandler BreakCar, AddressOf EmergencyBreak
RaiseEvent AutoMsg()
Catch ex As Exception
MsgBox("Error: " & ex.Message & ". Stacktrace: " & ex.StackTrace)
End Try
End Sub
Private Sub EmergencyBreak()
Try
MsgBox("Emergency Break")
Catch ex As Exception
MsgBox("Error: " & ex.Message & ". Stacktrace: " & ex.StackTrace)
End Try
End Sub
End Class
Also, in the main form I have something like this:
Public Class frmMain
...
Private Sub AddMe(ByVal name_ As String, ByVal wheels_ As Integer, ByVal motor_ As String, ByVal color_ As String, ByVal speed_ As Decimal, Byval initCoordinates_ as PointLatLng)
Try
unitsList.Add(New Car(name_, wheels_, motor_, color_, speed_))
Dim marker As GMapMarker
marker = New GMarkerGoogle(initCoordinates_, GMarkerGoogleType.orange_dot)
marker.ToolTipMode = MarkerTooltipMode.Always
marker.ToolTipText = name_
meOverlay.Markers.Add(marker)
Dim lvItem As ListViewItem
lvItem = lvUnits.Items.Add(name_)
lvItem.SubItems.Add(Math.Round(initCoordinates_.Lat, 4))
lvItem.SubItems.Add(Math.Round(initCoordinates_.Lng, 4))
Dim unit_ As Car = unitsList.FirstOrDefault(Function(x) x.carName = name_)
unit_.Break()
Dim tTemp_ As Thread = New Thread(Sub() CarMovement(unit_))
tTemp_.Name = name_
tTemp_.Priority = ThreadPriority.Normal
tTemp_.Start()
unitsThreadList.Add(tTemp_)
Catch ex As Exception
MsgBox("Error: " & ex.Message & ". Stacktrace: " & ex.StackTrace)
End Try
End Sub
...
End Class
With this, I am trying to add cars in the system, using a different thread to each one, and after adding the car to the system, start its handler to make it BREAK when some condition happens.
My problem is that the handler never fires, and I don't know the reasons because when something happens to the car X, the handler Break() must fires but it doesn't.
Am I doing something wrong? Maybe it is not possible to add handlers to each object created dynamically like this?
Thanks for any help!

Expression in With statement becomes Nothing in lambda expression in Task

I have discovered that referencing a member variable in a lambda expression executed in a Task throws a NullReferenceException when accessing it using the With statement.
For example I expect the following code to print two lines on the console. The first one accesses the SomeString member via obj.SomeString while the second one uses the With statement and accesses the member via .SomeString. I expected both options to be equivalent but the second one throws an exception.
Class Foo
Public SomeString As String
End Class
Module Module1
Sub Main()
Dim obj As New Foo With {.SomeString = "Hello World"}
With obj
Task.Factory.StartNew(
Sub()
Console.WriteLine("1:" + obj.SomeString) ' works
Console.WriteLine("2:" + .SomeString) ' NullReferenceException here
End Sub)
End With
Console.ReadKey()
End Sub
End Module
When I move the Console.ReadKey() statement into the With statement, the code works.
I fixed the actual code by not using the With statement but I still don't know what concept I'm missing here. Why can I access members of the obj variable in the lambda expression but not the members of the With expression? It has not been garbage collected because I can still see it in the debugger when the exception is thrown. The expression seems to go out of scope (or something like that) but why doesn't the compiler just do what I expect and treats it the same as obj?
It is because of the voodoo that the VB compiler does to support the With block and lambda expressions. If you look at your code through a decompiler like Redgate's Reflector, your code gets converted into something like the code below except that I renamed the variables to ones supported by VB; they can be quite long and include characters that are invalid for VB variable names
<STAThread> _
Public Shared Sub Main()
Dim var1 As New GeneratedClass1
Dim foo As New Foo With {.SomeString = "Hello World"}
var1.objVar = foo
Dim var2 As New GeneratedClass1.GeneratedClass2 With {.var2 = var1, .theWithVariable = var1.objVar}
Task.Factory.StartNew(New Action(AddressOf var2._Lambda___1))
var2.theWithVariable = Nothing
Console.ReadKey()
End Sub
<CompilerGenerated> _
Friend Class GeneratedClass1
' Methods
<DebuggerNonUserCode> _
Public Sub New()
End Sub
<DebuggerNonUserCode> _
Public Sub New(ByVal other As GeneratedClass1)
If (Not other Is Nothing) Then
Me.objVar = other.objVar
End If
End Sub
' Fields
Public objVar As Foo
' Nested Types
<CompilerGenerated> _
Friend Class GeneratedClass2
' Methods
<DebuggerNonUserCode> _
Public Sub New()
End Sub
<DebuggerNonUserCode> _
Public Sub New(ByVal other As GeneratedClass2)
If (Not other Is Nothing) Then
Me.theWithVariable = other.theWithVariable
End If
End Sub
<CompilerGenerated> _
Public Sub _Lambda___1()
Console.WriteLine(("1:" & Me.var2.objVar.SomeString))
Console.WriteLine(("2:" & Me.theWithVariable.SomeString))
End Sub
' Fields
Public theWithVariable As Foo
Public var2 As GeneratedClass1
End Class
End Class
You can see that the compiler creates a class that holds a reference to the With variable and the method of the lambda expression. As soon as the With variable is out of scope it is set to Nothing and hence the null reference expression when task executes.

How to call a public function from another form

Frm1 contains the code for validation of textbox:
Public Function AlphabeticalOnly(ByVal Str As String) As Boolean
Dim pattern As String = "^[a-zA-Z\s]+$"
Dim reg As New Regex(pattern)
If reg.IsMatch(Str) = False Then
MsgBox(Str & " is invalid! Please enter alphabetical characters only!", MsgBoxStyle.Critical, "Error")
End If
Return reg.IsMatch(Str)
End Function
Because there're quite an amount of validations, I don't want to repeat all the code again in the other forms.
Private Sub btnDone_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDone.Click
If AlphabeticalOnly(txtName.Text) = False Then
Exit Sub
End If
...
End Sub
I tried the code above in another form, but the error list shows that AlphabeticalOnly is not declared.
Is there anything that I need to add to my code?
First of all, don't put the function on a form. If it's common code shared by all forms, put it in its own class file.
Second, this common code shouldn't be prompting the user with a message box. This function should just perform the logic and nothing more. (This also makes the function easier to unit test.) Then allow the consuming code (in this case a form) to interact with the user. (Especially since the current implementation checks the match twice, which isn't necessary.)
Since this function doesn't rely on object state, you can make it Shared. Something like this:
Public Class CommonFunctions
Public Shared Function IsAlphabeticalOnly(ByVal Str As String) As Boolean
Dim pattern As String = "^[a-zA-Z\s]+$"
Dim reg As New Regex(pattern)
Return reg.IsMatch(Str)
End Function
End Class
Then on your forms you can invoke that function:
If CommonFunctions.IsAlphabeticalOnly(txtName.Text) = False Then
MsgBox(Str & " is invalid! Please enter alphabetical characters only!", MsgBoxStyle.Critical, "Error")
End If

Identify form the method is being called from

I have a global method (in a module) that multiple forms are calling. I can't figure out how to pass/identify the calling form so that it's controls are recognised when referenced in the method:
Public Sub SomeFunction(callingForm As Form)
callingForm.ErrorProvider.SetError(TextBox1, "Faux pas!")
End Sub
Public Sub SomeOtherFunction(callingForm As Form)
SomeFunction(Me)
End Sub
I the above example, I've attempted passing the form as a parameter but I'm being told:
ErrorProvider is not a member of System.Windows.Forms.Form.
This is pretty common, to want to treat all your forms the same, yet different. You will want to create another class that each form can implement. This allows you to do something specific... generically.
Create a class similar to this, call it and the function whatever you want:
Public Interface IErrorForm
Sub MyValidate
End Interface
Implement it in your forms:
Public Class Form1
Implements IErrorForm
Public Sub MyValidate() Implements IErrorForm.MyValidate
'...Your code here
'TextBox1.Text = "Faux pas!"
End Sub
Now, wherever you want to call the function, you could do something like this:
Public Sub SomeFunction(callingForm As Form)
If GetType(IErrorForm).IsAssignableFrom(xFrm.GetType) Then
CType(xFrm, IErrorForm).MyValidate()
End If
End Sub
Another approach with returning value from Validate function
As you mentioned in the comments
...one of the key purposes of my method is to avoid having to set the
errors outside of the method, to reduce duplicitous code
As I understand you want Validate function check, given control as parameter, for errors and show error message through ErrorProvider.
My suggestion will be shared Validate function return string value, which contains error message generated after validating control
If no error then function return Nothing or String.Empty
Public Function Validate(ctrl As Object, formats As String) As String
Dim errorMessage As String = Nothing 'or String.Empty
' Your validating code here
' If no error errorMessage = Nothing
Return errorMessage
End Function
Then in the form (possible in the Validated event handler)
'....
Me.MyErrorprovider.SetError(Me.MyTextBox,
MyGlobalFunctions.Validate(Me.MyTextBox, "formats"))