I am writing a Combobox custom control and cannot use the Format() function. The editor marks the format line as an error.
Public Class TestCombo
Inherits ComboBox
Protected Overrides Sub OnDrawItem(ByVal e As DrawItemEventArgs)
Dim MyStr = Format(5459.4, "##,##0.00") ' The error is here.
End Sub
End Class
After a little exploring, I found that Combobox uses ListControl as an event.
How can I tell the editor to address the function and not the event?
As you noticed, the ComboBox class has an event called Format (which is inherited from ListControl). So, when you try to call the Format() function inside ComboBox, the compiler thinks that you are trying to use the event because it has the narrowest scope, hence, the error.
To get around this, you may explicitly call the module name (i.e., Strings) where the Format() function is declared:
Dim MyStr = Strings.Format(5459.4, "##,##0.00")
Alternatively, you may use String.Format() or ToString() (which is the standard way in .NET):
Dim MyStr2 = String.Format("{0:##,##0.00}", 5459.4)
Dim MyStr3 = 5459.4.ToString("##,##0.00")
Related
I have written a very simple Ceaser Cipher encryption algorithm on VBA. It takes a string value and applies x shift.
Quite happy with it. However, would like to take it to the next step, but I am not sure if it is doable.
I would like to call this encryption function in another module and pass a string value to encrypt.
I.e. What I am trying to do is something like this
Private Sub Encryption()
'Encryption method of string Var
End Sub
---- and in another sub ----
Private Sub function()
Dim Text as String
Text = "Hello"
Encryption(Text)
End Sub
So in this example, I have defined a method to encrypt any string variable. In the other function, I defined a string and initialised it as "Hello". Then called Encryption function on it... Not sure if this is doable in VBA?
I am not sure how I could pass in a variable when I am calling a function within a function. Any advise please?
You would need to change the Sub Routine to a Function. Also, get rid of the Private keyword as this will not allow you to call it across different modules.
So Change this:
Private Sub Encryption()
'Encryption method of string Var
End Sub
to this:
Function Encryption(ByVal inputString as String) As String
'Encryption method of string Var
End Sub
Now, inside your function you need to change whatever variable holds the first string (the one to be encrypted) to the newly created variable, inputString. It's hard to help you in this area as you did not provide the full code for Sub Encryption().
Also, ensure that you set the Function name itself to the newly encrypted text within the function. So your function would essentially look as follows:
Function Encryption(ByVal inputString as String) As String
'Encryption method of string Var
Encryption = 'the value to return from this function
End Sub
Now, your second sub would look like this:
Private Sub test()
Dim Text as String, encryptedText as String
Text = "Hello"
encryptedText = Encryption(Text)
End Sub
You will just make the new variable encryptedText equal to the new function, Encryption.
So, the major difference between a Sub and a Function is that a Function will return values. They both essentially process code the same way.
METHOD 2 (Edit)
After rethinking your question, I believe that you were using your Sub to obtain your encrypted text from passing the variable as ByRef, and you may just be having difficulties with calling the Sub because of the Private keyword.
If this what was going on, then you can simply remove the Private keyword and it should work as intended.
So your Sub should look like:
Sub Encryption() '<-- Notice, no Private scope
'Encryption method of string Var
End Sub
or
Public Sub Encryption()
'Encryption method of string Var
End Sub
You should take a look at this for additional reading regarding scope.
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"))
I'm used to programming in C# so I have no idea how to approach delegates and passing methods in VB
The error that I am getting is: Argument not specified for parameter 'message' of 'Public Sub ReceiveMessage(message As String)'
Here is the constructor of the class that I am trying to pass to:
Delegate Sub ReceiveDelegate(message As String)
Public ReceiveMethod As ReceiveDelegate
Sub New(ByRef receive As ReceiveDelegate)
ReceiveMethod = receive
End Sub
This is the method that I am trying to pass to that constructor:
Public Sub ReceiveMessage(message As String)
MessageBox.Show(message)
End Sub
I'm using it as such:
Dim newClass As New Class(ReceiveMessage)
The purpose of this, is that once the class receives data from a network device, it can call the corresponding method on the Form asynchronously.
You need to create the delegate object and use the AddressOf operator, like this:
Dim newClass As New Class(New ReceiveDelegate(ReceiveMessage))
However, if you don't explicitly create the delegate object, VB.NET will automatically determine the right type, based on the signature, and create it for you, so you can just do it like this:
Dim newClass As New Class(AddressOf ReceiveMessage)
The latter is obviously less typing, but the former is more explicit. So, take your pick. Both ways are perfectly acceptable and common.
I have a little problem with an interface. A bunch of my classes implement the ILayoutObject interface. A method declares a variable as ILayoutObject (defaulting it as Nothing) and then runs some code which decides which object it should be. The problem is, the evaluation code runs in a method which receives the variable as a parameter and assigns an object to it. With objects, this would be no problem. The object would be affected by the changes in the method and everything would be OK. Howeverm, when using an interface, the variable in the calling code remains Nothing and behaves like a normal variable. Does anyone have any ideas on how to circumvent that? Alas, due to code structure I am unable to use ByRef or functions :(
Here is some code:
Protected LayoutHandler As Dictionary(Of String, Action(Of Constants.OptionsEntryStructure, ILayoutElements)) = New Dictionary(Of String, Action(Of Constants.OptionsEntryStructure, ILayoutElements)) From
{
{Constants.KeyLayoutType, AddressOf KeyLayoutType}
}
Sub MakeLayOuts
Dim LayoutElement As ILayoutElements = Nothing
Dim Value = "SomeValues"
Dim key = "Key"
LayoutHandler(key)(Value, LayoutElement)
' LayoutElement remains nothing.....
End Sub
Protected Sub KeyLayoutType(elem As Constants.OptionsEntryStructure, Layout As ILayoutElements)
Layout = New LayoutObject 'which would implement the interface
End Sub
You need to declare the parameter as ByRef if you want to alter the object to which the variable in the calling code points to:
Protected Sub KeyLayoutType(elem As Constants.OptionsEntryStructure, ByRef Layout As ILayoutElements)
Layout = New LayoutObject 'which would implement the interface
End Sub
This is true with any reference type (classes). The fact that they are referenced with an interface makes no difference.
If you can't use ByRef, and you can't use a function to return the new object, then your only other real option would be to request a type of object which has the layout object as a property. For instance:
Public Interface ILayoutElementContainer
Public Property LayoutElement As ILayoutElements
End Interface
Protected Sub KeyLayoutType(elem As Constants.OptionsEntryStructure, Container As ILayoutElementContainer)
Container.LayoutElement = New LayoutObject 'which would implement the interface
End Sub
I try to understand the VBA scope type, it's impossible to make this such of thing in VBA, but it's possible in other language (java,scala,etc):
public sub try()
dim myVar as String
myvar = "hello world"
Call displayVar()
end sub
public sub displayVar()
msgbox (myvar)
end sub
Can you give me some information about this type of limited scoping ? It's dynamical or lexical, i don't really understand the difference :/
Franck Leveque has provided a clear and simple demonstratation of the difference between local and global declarations.
However, like most languages, VBA allows you to pass parameters to subroutines. Sometimes a global variable is the only choice or the only sensible choice. But normally it it is better to declare myVar within try and pass it as a parameter to displayVar. This prevents displayVar from accidentally changing myVar because, by default, parameters are passed as values. If you want a subroutine to change the value of a parameter you must explicitly pass the parameter as a reference. This is true of most modern programming languages.
Note also that Public means these subroutines are visible to subroutines in other modules. If Public was omitted or replaced by Private, try and displayVar would only be visible within their module.
In the code below I have passed the value of myVar as a parameter to displayVar.
Public Sub try()
Dim myVar As String
myvar = "hello world"
Call displayVar(myVar)
End Sub
Public Sub displayVar(Stg As String)
Call Msgbox(Stg, VBOKOnly)
End Sub
Variable myVar is declared in the function try()
As such, you can only use it in the try() function scope.
By try() function scope, I mean all the instruction written between public sub try() and end sub.
You try to call your variable from another function (displayVar). Which define it's own scope and is not inside the try function scope.
If you want this, you have to declare your variable in the global scope (outside any function)
For example :
dim myVar as String
public sub try()
myvar = "hello world"
Call displayVar()
end sub
public sub displayVar()
msgbox (myvar)
end sub