I just want to know what is the difference between "Public function" and "Function"
if anyone can help , and that will be highly appreciated..
thanks
In addition to the answer of Ekkehard.Horner, in QTP it is also possible to load Qtp Function Libraries (QFL) as .qfl or .vbs files.
A function, const or variable in a QFL that is private, can not be used in another QFL, Module or Action, while a public one can.
Functions, Constants and Variables are by default public:
' All public:
Dim MyVariable
Public MyOtherVariable
Const PI = 3.1415
Function GetHello
GetHello = "Hello"
End Function
Sub SayHello
MsgBox GetHello
End Sub
' All private:
Private myPrivates
Private Const HELLO = "HELLO!"
Private Function getHelloToo
getHelloToo = HELLO
End Function
Private Sub sayHelloToo
MsgBox getHelloToo
End Sub
Class Dog
Public Function Bark
Print "Bark! Bark! Bark!"
End Function
End Class
Yes, classes are always private in a module. You have to return it from a function to make them public available:
' Placed in the same module as Class Dog
Public Function GiveMeADog
Set GiveMeADog = new Dog
End Function
The concept of Public vs. Private accessability is best explained by using a class:
Option Explicit
Class cPubPrivDemo
Public m_n1
Dim m_n2
Private m_n3
Private Sub Class_Initialize()
m_n1 = 1
m_n2 = 2
m_n3 = 3
End Sub
Sub s1()
WScript.Echo "s1 (Public by default) called"
End Sub
Public Sub s2()
WScript.Echo "s2 (Public by keyword) called"
End Sub
Private Sub s3()
WScript.Echo "s3 (Private by keyword) called"
End Sub
Public Sub s4()
WScript.Echo "(public) s4 can call (private) s3 from inside the class"
s3
End Sub
End Class
Dim oPPD : Set oPPD = New cPubPrivDemo
WScript.Echo "Can access public member variables of oPPD:", oPPD.m_n1, oPPD.m_n2
WScript.Echo "No access to oPPD's private parts:"
Dim n3
On Error Resume Next
n_3 = oPPD.m_n3
WScript.Echo Err.Description
On Error GoTo 0
WScript.Echo "Can call public subs:"
oPPD.s1
oPPD.s2
WScript.Echo "Can't call private sub .s3:"
On Error Resume Next
oPPD.s3
WScript.Echo Err.Description
On Error GoTo 0
WScript.Echo "private sub s3 can be called from inside the class:"
oPPD.s4
From the script's output:
Can access public member variables of oPPD: 1 2
No access to oPPD's private parts:
Object doesn't support this property or method
Can call public subs:
s1 (Public by default) called
s2 (Public by keyword) called
Can't call private sub .s3:
Object doesn't support this property or method
private sub s3 can be called from inside the class:
(public) s4 can call (private) s3 from inside the class
s3 (Private by keyword) called
you can see:
A private component (variable, sub; the same holds for functions and properties) can be accessed from the inside of the component (here: class)
A public component can be accessed from the outside (not shown but probably plausible: publics can be used from the inside too)
Not specifying an access right/mode (?) explicitly (m_n2, s1) defaults to "Public"
Short answer to your question: None - because of (3)
The VBScript Docs for "Public statement" say
Declares public variables and allocates storage space. Declares, in a
Class block, a public variable.
and
Public statement variables are available to all procedures in all
scripts.
So one could research/test whether and how the accessability rules apply to (combined) scripts (source code files). As I don't know anything about QTP's handling of multiple source files, I can't help you there.
The issue of public and private would only matter when used within a class. Inside a VBScript class, the functions are public by default, therefore there would be no difference between the two. Using Private makes the function inaccessible from outside of the class.
Related
I don't really know what I'm going. Trying to gather small pieces of code from the web
I ended up with this:
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json.Linq
Module Module1
Sub Main()
LoadData()
End Sub
Private Async Sub LoadData()
Dim client As New Net.Http.HttpClient()
Dim url = "my url which return json"
Dim uri As New Uri(url)
Dim json As String = Await client.GetStringAsync(uri)
Dim j = JObject.Parse(json)("TIME SERIES INTRADAY")
Dim openPrice = j("1. open").Value(Of Double)
Diagnostics.Debug.WriteLine(openPrice)
Console.ReadLine()
End Sub
End Module
There is no issue when I build but when I run, I get this erroer message when I step on row
Dim json As String = Await client.GetStringAsync(uri)
error code:
The program '[4032] AVT.exe' has exited with code 0 (0x0).
Do you know why this line is returning this error?
Framework 4.5
You won't be able to do top-level Await in a console program. You can still make it work (preserving the Async on LoadData) with the following changes:
Change the signature of LoadData to Private Async Function LoadData() As Task
Change the call in Main to `LoadData.GetAwaiter().GetResult()
This will block in Main which is fine because there is no message loop to support a non-blocking wait. This is only appropriate in a console program, if you were doing this in any other setting your original implementation would have been correct (except that you should always use Async Function ... As Task in preference to Async Sub in any context aside from event handlers).
Yes, you can do this even in a console application.
I suggest you to use an ApplicationContext in order to keep all alive.
Doing as you shows, so, calling your LoadData()
your Main Sub going tasks are:
Enter
(and immediately after)
Exit
.
Take a look on code below to figure out the mechanism that you need to do what you want to do.
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json.Linq
Module Module1
Public Class MyApplicationContext
Inherits ApplicationContext
Private Async Sub LoadData()
Dim client As New Net.Http.HttpClient()
Dim url = "https://stackoverflow.com/questions/59324373/how-to-use-getstringasync-in-vb-net"
Dim uri As New Uri(url)
Dim json As String = Await client.GetStringAsync(uri)
Console.WriteLine(json)
Diagnostics.Debug.WriteLine(json)
'Dim j = JObject.Parse(json)("TIME SERIES INTRADAY")
'Dim openPrice = j("1. open").Value(Of Double)
'Diagnostics.Debug.WriteLine(openPrice)
Console.ReadLine() 'Here is stopped/waiting for user
'Here your application can be stopped after her work
'Application.Exit()
End Sub
Public Sub New()
MyBase.New()
AddHandler Application.ApplicationExit, AddressOf OnApplicationExit
LoadData()
End Sub
Private Sub OnApplicationExit(ByVal sender As Object, ByVal e As EventArgs)
Try
' delete your data here
Catch
End Try
End Sub
End Class
Public Sub Main()
Dim context As MyApplicationContext = New MyApplicationContext()
Application.Run(context)
End Sub
End Module
Expressions:
Forms![Frm_MainForm]![Frm_SubForm_1].Requery
Forms![Frm_MainForm]![Frm_SubForm_2].Requery
I use multiple database forms.
Can I put these expressions in a separate module and call the procedure for updating forms in different parts of the database?
For example in the module "md_Requery":
Public UpdateForm()
Forms![Frm_MainForm]![Frm_SubForm_1].Requery
Forms![Frm_MainForm]![Frm_SubForm_2].Requery
End sub
Public UpdateForm() is valid in the (Delarations) section of a module: it declares a public Variant array named UpdateForm.
That makes the body of the procedure illegal though, because [thing].Requery is an executable statement, that's illegal outside of a procedure's scope.
Change the declaration to read Public Sub UpdateForm(), and you'll notice the top-right dropdown will change from (Declarations) to UpdateForm, indicating that you're inside a procedure scope.
You can use this public procedure in the module:
Public Sub RequeryForm(frmName As String)
Dim frm As Form
If IsLoaded(frmName) then
Set frm = Forms(frmName)
frm.Requery
End If
End Sub
Public Function IsLoaded(frmName as String)
On Error GoTo Error_Handler
'to call this function, you must put the form in quotes: IsLoaded("frmEntries_EntrySelection")
IsLoaded = (SysCmd(SYSCMD_GETOBJECTSTATE, A_FORM, FormName) <> 0)
Exit_Procedure:
Exit Function
Error_Handler:
Select Case Err.Number
Case Else
MsgBox Err.Number & ", " & Err.Description
Resume Exit_Procedure
Resume
End Select
End Function
Call it like this:
RequeryForm "Frm_SubForm_1"
I have the following class module BlSecurity in my Access database:
Option Compare Database
Option Explicit
Private tiendaRepo As ITiendaRepository
Public Sub Init(ptiendaRepo As ITiendaRepository)
tiendaRepo = ptiendaRepo
End Sub
Public Sub Login(code As String)
If tiendaRepo.CheckIfCodeExists(code) = "" Then
Err.Raise Number:=CustomErrors.CenterCodeNotExisting
End If
Exit Sub
End Sub
Private Sub Class_Terminate()
Set tiendaRepo = Nothing
End Sub
This is TiendaRepository code:
Option Compare Database
Option Explicit
Implements ITiendaRepository
Public Function ITiendaRepository_CheckIfCodeExists(ByVal code As String) As String
Err.Raise vbObjectError, "CheckCode", "Not implemented"
Exit Function
End Function
And this is the "interface" ITiendaRepository I'm implementing:
Option Compare Database
Option Explicit
Public Function CheckIfCodeExists(ByVal code As String) As String
End Function
Then, inside a button handler:
Private Sub btnLogin_Click()
Dim bl As blSecurity
Set bl = New blSecurity
bl.Init (New TiendaRepository)
bl.Login (txtUsuario.Value)
End Sub
But when I click the button, I receive message:
Object doesn't support this property or method
in line bl.Init (New TiendaRepository)
. What's wrong with it?
It runs (i.e., raises the "Not implemented" message) on my test system with these two changes:
In your button-click module, remove the parentheses around New TiendaRepository.
Private Sub btnLogin_Click()
Dim bl As BlSecurity
Set bl = New BlSecurity
bl.Init New TiendaRepository ' <=== here
bl.Login txtUsuario.Value
End Sub
This is because VBA doesn't use parentheses when calling subroutines and not collecting a return value. If you add the parentheses, they actually cause evaluation of the default property. Therefore, instead of passing the New TiendaRepository object to bl.Init, you are passing whatever VBA thinks the default value is.
Note that the VBA editor put a space before the opening parenthesis in your code. That's a visual clue that it's not doing what you might expect coming from languages that always use parens on calls.
In BlSecurity.Init, add a Set:
Public Sub Init(ptiendaRepo As ITiendaRepository)
Set tiendaRepo = ptiendaRepo
End Sub
That is because you always (as far as I know) need Set when you are assigning objects (internally, references to objects).
If you want to use parentheses around method calls (not function calls) in VBA, you use the Call keyword. That is,
Call bl.Init(New TiendaRepository)
is the same as
bl.Init New TiendaRepository
This is true regardless of the number of parameters — Call foo(a,b,c) is the same as foo a, b, c.
Maybe
1) Note of set keyword
2) Removal of () in call
3) BlSecurity must also implement ITiendaRepository_CheckIfCodeExists
BlSecurity
Option Compare Database
Option Explicit
Implements iTiendaRepository
Private tiendaRepo As iTiendaRepository
Public Sub Init(ptiendaRepo As iTiendaRepository)
Set tiendaRepo = ptiendaRepo '*1
End Sub
Public Sub Login(code As String)
If tiendaRepo.CheckIfCodeExists(code) = "" Then
Err.Raise Number:=CustomErrors.CenterCodeNotExisting
End If
Exit Sub
End Sub
Private Sub Class_Terminate()
Set tiendaRepo = Nothing
End Sub
Public Function ITiendaRepository_CheckIfCodeExists(ByVal code As String) As String '*3
Err.Raise vbObjectError, "CheckCode", "Not implemented"
Exit Function
End Function
Module
Option Compare Database
Option Explicit
Private Sub btnLogin_Click()
Dim bl As BlSecurity
Set bl = New BlSecurity
bl.Init New TiendaRepository '*2
bl.Login txtUsuario.Value '<=== Not sure where declare and should remove ()
End Sub
Though I am unsure where you have declared txtUsuario
Perhaps someone can give me an advice how i could solve the following problem.
Is there simple solution to "bind" an instance attribut of an object to an element of my form. Of course it could be also solved by triggering the checkbox_Click() callback, but at the moment i'm not very happy to with this solution.
For example:
Form Load - Object Init:
Dim handlecontact As ClsHandleContact
Private Sub Form_Load()
''' init new model handler '''
Set handlecontact = New ClsHandleContact
''' bind attribute of instance to element of form '''
Me!CheckBox.Bind(handlecontact.boolean_attribut)
End Sub
Class ClsHandleContact:
Public boolean_attribut As Boolean
Private Sub Class_Initialize()
''' False by init '''
boolean_attribut = False
End Sub
If an user checks in, i would expect an update of my underlying object instance. Is there an official and supported way to realize this kind of binding ?
Thanks for any advice!
I would suggest using WithEvents to set this up. Here's a quick tutorial to get you started:
Create a form with a checkbox named Check0. Set Check0's After Update property to [Event Procedure]. In the form's code module:
Dim handlecontact As clsHandleContact
Private Sub Form_Load()
Set handlecontact = New clsHandleContact
Set handlecontact.MyCheckBox = Me.Check0
End Sub
And in the clsHandleContact class module:
Public WithEvents MyCheckBox As CheckBox
Private Sub MyCheckBox_AfterUpdate()
MsgBox "The value of the checkbox is now: " & MyCheckBox.Value
End Sub
I have an IEnumerable(of Employee) with a ParentID/ChildID relationship with itself that I can databind to a TreeView and it populates the hierarchy perfectly. However, I want to be able to manually loop through all the records and create all the nodes programmatically so that I can change the attributes for each node based on the data for that given item/none.
Is there a tutorial out there that explains how to do this? I've seen many that use datasets and datatables but none that show how to do it in Linq to SQL (IEnumerable)
UPDATE:
Here's how I used to do it with a DataSet - I just can't seem to find how to do the same with IEnumerable.
Private Sub GenerateTreeView()
Dim ds As New DataSet()
Dim tasktree As New Task(_taskID)
Dim dt As DataTable = tasktree.GetTaskTree()
ds.Tables.Add(dt)
ds.Relations.Add("NodeRelation", dt.Columns("TaskID"), dt.Columns("ParentID"))
Dim dbRow As DataRow
For Each dbRow In dt.Rows
If dbRow("TaskID") = _taskID Then
Dim node As RadTreeNode = CreateNode(dbRow("Subject").ToString(), False, dbRow("TaskID").ToString())
RadTree1.Nodes.Add(node)
RecursivelyPopulate(dbRow, node)
End If
Next dbRow
End Sub
Private Sub RecursivelyPopulate(ByVal dbRow As DataRow, ByVal node As RadTreeNode)
Dim childRow As DataRow
Dim StrikeThrough As String = ""
Dim ExpandNode As Boolean = True
For Each childRow In dbRow.GetChildRows("NodeRelation")
Select Case childRow("StatusTypeID")
Case 2
StrikeThrough = "ActiveTask"
Case 3
StrikeThrough = "CompletedTask"
ExpandNode = False
Case 4, 5
StrikeThrough = "ClosedTask"
ExpandNode = False
Case Else
StrikeThrough = "InactiveTask"
ExpandNode = False
End Select
Dim childNode As RadTreeNode = CreateNode("<span class=""" & StrikeThrough & """>" & childRow("Subject").ToString() & "</span>", ExpandNode, childRow("TaskID").ToString())
node.Nodes.Add(childNode)
RecursivelyPopulate(childRow, childNode)
ExpandNode = True
Next childRow
End Sub
Private Function CreateNode(ByVal [text] As String, ByVal expanded As Boolean, ByVal id As String) As RadTreeNode
Dim node As New RadTreeNode([text])
node.Expanded = expanded
Return node
End Function
If you just need a way of enumerating the tree you can implement this as a generator, it might look strange, you're probably better of with a user defined enumerator but it's essentially the same thing.
public interface IGetChildItems<TEntity>
{
IEnumerable<TEntity> GetChildItems();
}
public static IEnumerable<TEntity> Flatten<TEntity>(TEntity root)
where TEntity : IGetChildItems<TEntity>
{
var stack = new Stack<TEntity>();
stack.Push(root);
while (stack.Count > 0)
{
var item = stack.Pop();
foreach (var child in item.GetChildItems())
{
stack.Push(child);
}
yield return item;
}
}
The type constraint where TEntity : IGetChildItems is just to signify that you need to abstract how to descend the hierarchy. Without the above code would not compile.
This will enumerate the tree in a breadth first fashion, it will yield the parent element first then it's children, and then the children of those children. You can easily customize the above code to achieve a different behavior.
Edit:
The yield return stuff tells the compiler that it should return a value then continue. yield is a context keyword and it's only allowed inside an iterative statement. A generator is a simple way of writing a IEnumerable data source. The compiler will build a state machine from this code and create an enumerable anonymous class. Apparently the yield keyword does not exist in VB.NET. But you can still write a class which does this.
Imports System
Imports System.Collections
Imports System.Collections.Generic
Public Class HierarchyEnumerator(Of TEntity As IGetChildItems(Of TEntity))
Implements IEnumerator(Of TEntity), IDisposable, IEnumerator
Public Sub New(ByVal root As TEntity)
Me.stack = New Stack(Of TEntity)
Me.stack.Push(root)
End Sub
Public Sub Dispose()
End Sub
Public Function MoveNext() As Boolean
Do While (Me.stack.Count > 0)
Dim item As TEntity = Me.stack.Pop
Dim child As TEntity
For Each child In item.GetChildItems
Me.stack.Push(child)
Next
Me.current = item
Return True
Loop
Return False
End Function
Public Sub Reset()
Throw New NotSupportedException
End Sub
Public ReadOnly Property Current() As TEntity
Get
Return Me.current
End Get
End Property
Private ReadOnly Property System.Collections.IEnumerator.Current As Object
Get
Return Me.Current
End Get
End Property
Private current As TEntity
Private stack As Stack(Of TEntity)
End Class