How can I specify a printer other than the default printer? - vb.net

I'd like to be able to specify two different printers for two different jobs. I'm using the following class to handle printing these, but regardless of what I do, the default printer is always the one that's printed to.
Public Class Receipt : Inherits Printing.PrintDocument
Private _font As Font = New Font("Courier", 8)
Private _text As String = ""
Public Property Text() As String
Get
Return _text
End Get
Set(ByVal Value As String)
_text = Value.Trim
End Set
End Property
Public Sub New(ByVal str As String, ByVal settings As Printing.PrinterSettings)
MyBase.New()
_text = str
Me.PrinterSettings = settings
End Sub
Protected Overrides Sub OnPrintPage(ByVal e As Printing.PrintPageEventArgs)
Dim printHeight As Integer
Dim printWidth As Integer
Dim leftMargin As Integer
Dim rightMargin As Integer
With Me.DefaultPageSettings
.PaperSize = New System.Drawing.Printing.PaperSize("Custom", 300, 1200)
.Margins.Left = 25
.Margins.Right = 25
printHeight = .PaperSize.Height - .Margins.Top - .Margins.Bottom
printWidth = .PaperSize.Width - .Margins.Left - .Margins.Right
leftMargin = .Margins.Left
rightMargin = .Margins.Top
End With
Dim printArea As New RectangleF(leftMargin, rightMargin, printWidth, printHeight)
Dim format As New StringFormat(StringFormatFlags.LineLimit)
Try
e.Graphics.DrawString(_text, _font, Brushes.Black, printArea, format)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
End Class
If I inspect my PrinterSettings attribute immediately before the call to DrawString, the PrinterName attribute is still correctly set to the printer I specify, but it's still the default printer that kicks out the job. I'm sure I'm missing something obvious, but would certainly appreciate if someone could point out what it is. :)
Thanks

I just created a test app with the class code you posted and it works fine. It uses whatever printer I select. So I must conclude that wherever you are using this class you're accidentally altering the PrintSettings object after you initialize the object but before you call Print.
Or perhaps the printer name you specify isn't valid and the default is used as a backup. You can check this using PrinterSettings.IsValid after setting the PrinterName property.

The PrinterSettings.PrinterName property is actually what you should be using.
You can get a list of installed printers using PrinterSettings.InstalledPrinters (System.Drawing.Printing namespace). Perhaps your provider printer name is slightly different from what it should be, because I can confirm this actually works.

Related

How to properly render an embedded Font?

I download a True Type Font and I embedded it just as this page explains.
I had to set the UseCompatibleTextRendering property to be able to load it but it looks very weird, I don't know why it looks good in the browser but not in the application.
Just to be clear I added the font to my resources, set it as embedded resource, I used this module:
Imports System.IO
Imports System.Reflection
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Module ExternalFontType
Public Function GetFont(aAssembly As Assembly,
strFontName As String, intFontSize As Integer,
fsFontStyle As FontStyle) As Font
Using pcolFonts As New PrivateFontCollection
Dim bFont() As Byte = ExternalFontType.bRawFontData(aAssembly, strFontName)
Dim ptrMemFont As IntPtr =
Marshal.AllocCoTaskMem(bFont.Length)
Marshal.Copy(bFont, 0, ptrMemFont, bFont.Length)
pcolFonts.AddMemoryFont(ptrMemFont, bFont.Length)
Marshal.FreeCoTaskMem(ptrMemFont)
Return New Font(pcolFonts.Families(0),
intFontSize, fsFontStyle)
End Using
End Function
Private Function bRawFontData(aAssembly As Assembly, strFontName As String) As Byte()
Using stFont As Stream =
aAssembly.GetManifestResourceStream(strFontName)
If (stFont Is Nothing) Then Throw _
New Exception(String.Format("Cannot load _
font '{0}'", strFontName))
Dim bFontBuffer() As Byte = New _
Byte(CInt(stFont.Length - 1)) {}
stFont.Read(bFontBuffer, 0, CInt(stFont.Length))
Return bFontBuffer
End Using
End Function
End Module
and included it in this code
lbl.UseCompatibleTextRendering = True
lbl.Font = ExternalFontType.GetFont(Me.GetType.Assembly, "ProyectName.FontName.ttf", 15, FontStyle.Bold)
More than one problem with that code:
The PrivateFontCollection cannot be declared with a Using statement: this collection must be preserved as long as the Fonts it points to are needed. It's usually declared as a Field in the class (Form) that uses it or in a shared class (or Module, here), then disposed of when not needed anymore.
Marshal.FreeCoTaskMem() cannot be used here; it's a temptation to call it after Marshal.AllocCoTaskMem(), but not in this occasion. This can (will) compromise the Font data allocation. What you need to do is dispose of the PrivateFontcollection object. The Framework will take care of the COM affair (it will do it for you even if you forget to dispose of the PrivateFontcollection object. You should try not to forget, though).
The assembly reference is not required: the Font is added to the Project's Resources as a byte array, which is all that's needed. It can then be retrieved either by name, e.g., My.Resources.SomeFontName, or using the ResourceManager.GetObject() method, casting the returned object to Byte():
Dim fontData As Byte() = My.Resources.SomeFontName
Dim fontData As Byte() = DirectCast(My.Resources.ResourceManager.GetObject("SomeFontName"), Byte())
▶ You have already mentioned this but let's say it again: not all controls can use these Fonts. Only controls that can use Fonts drawn by GDI+ can actually use Fonts from the PrivateFontCollection, the Label and Button controls are among of these, in fact both expose a UseCompatibleTextRendering property. A RichTextBox, for example, cannot.
If the Font is created correctly, you can use Graphics.DrawString() to draw strings content using that Font, even when you cannot set it as the Font of a Control.
Private myFontCollection As PrivateFontCollection = New PrivateFontCollection()
In the Form's Constructor, add Font from the Project's Resources.
Here I'm using a helper class, FontManager, which exposes a public shared method AddFontsFromResource(): pass to this method the PrivateFontCollection and a list of resources names corresponding to the Font names.
This method fills the collection with Fonts that can be installed successfully and returns the number of Fonts installed.
Of course you use whatever other method you prefer to reference your Fonts.
Note. In the example, three Font resources are added to the collection:
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"}
but two belong to the same FontFamily, so the PrivateFontCollection will contain just two elements, not three.
Public Sub New()
Dim installedFontsCount = FontManager.AddFontsFromResources(myFontCollection,
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"})
' The Font can set here or anywhere else
someLabel.UseCompatibleTextRendering = True
someLabel.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Regular)
someButton.UseCompatibleTextRendering = True
someButton.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Italic)
End Sub
It's important to dispose of the PrivateFontCollection when it's not needed anymore: when the Form that initialized it closes or before the Application closes:
You could also use a shared object to reference a PrivateFontCollection that can be used anywhere in the Project. In this case the collection needs to be disposed of when the Application closes.
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
myFontCollection.Dispose()
End Sub
Helper class:
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Public Class FontManager
Public Shared Function AddFontsFromResources(fontCollection As PrivateFontCollection, fontNames As String()) As Integer
If fontNames.Length = 0 Then Return Nothing
Dim installedFontsCount = 0
For Each fontName As String In fontNames
Try
Dim fontData As Byte() = CType(My.Resources.ResourceManager.GetObject(fontName), Byte())
If fontData Is Nothing Then Throw New InvalidOperationException()
Dim data As IntPtr = Marshal.AllocCoTaskMem(fontData.Length)
Marshal.Copy(fontData, 0, data, fontData.Length)
fontCollection.AddMemoryFont(data, fontData.Length)
installedFontsCount += 1
Catch ex As Exception
' Placeholder: Notify User/Log/Whatever
Debug.Print($"Font installation failed for {fontName}")
End Try
Next
Return installedFontsCount
End Function
End Class
C# version:
using System.Drawing.Text;
using System.Runtime.InteropServices;
public static int AddFontsFromResources(PrivateFontCollection fontCollection, string[] fontNames)
{
int installedFontsCount = 0;
if (fontNames.Length == 0) return 0;
foreach (string fontName in fontNames) {
try {
byte[] fontData = (byte[])Properties.Resources.ResourceManager.GetObject(fontName);
var data = Marshal.AllocCoTaskMem(fontData.Length);
Marshal.Copy(fontData, 0, data, fontData.Length);
fontCollection.AddMemoryFont(data, fontData.Length);
installedFontsCount += 1;
}
catch (Exception) {
// Placeholder: Notify User/Log/Whatever
Console.WriteLine($"Font installation failed for {fontName}");
}
}
return installedFontsCount;
}

How do I save objects in Configuration of Winforms application?

I'm trying to implement functionality to allow the user to select his favorite forms. Favorite forms are forms he/she needs quick access to. To avoid browsing for too long through the ToolStripMenu's.
I try to save a reference to a form in the application configuration. But I'm getting the error
Value of type 'System.Windows.Forms.Form' cannot be converted to
'String'.
Public Sub SetSetting(ByVal pstrKey As String, ByVal frmFavorite As Form)
Dim keyExists As Boolean = False
For Each strKey As String In configuration.AppSettings.Settings.AllKeys
If strKey.Equals(pstrKey) Then
configuration.AppSettings.Settings.Item(pstrKey).Value = frmFavorite
keyExists = True
End If
Next
If Not keyExists Then
configuration.AppSettings.Settings.Add(pstrKey, frmFavorite)
End If
configuration.Save(ConfigurationSaveMode.Modified)
ConfigurationManager.RefreshSection("appSettings")
End Sub
You can only store string values in the application config file, no objects.
But just store the name of the Form in the config file.
When starting your application create the form via reflection like shown in this Object Factory example.
Public Class ObjectFactory
Public Shared Function CreateAnObject(ByVal ObjectName As String) As Object
Dim Assem = [Assembly].GetExecutingAssembly()
Dim myType As Type = Assem.GetType(ObjectName.Trim)
Dim o As Object = Nothing
Try
o = Activator.CreateInstance(myType)
Catch oEx As TargetInvocationException
MessageBox.Show(oEx.ToString)
End Try
Return o
End Function
End Class
...
Dim formName as String = configuration.AppSettings.Settings.Item(<YourSettingKey>)
Dim oForm As Form = _
ObjectFactory.CreateAnObject(formName)

Multi-Threading IP Address Pings Causing Application Crash

Boy, learning something new can be a real headache if you can't find a solid source. I have been designing applications in a linear fashion for some time now and want to step up into a more powerful approach. I have been reading up on threading, and perhaps have gone to an larger level than I should. However, one usually steps up when the application calls for it and no better time than the present to learn something new.
My program is designed to do something that seems rather simple, but has become extremely difficult to create in a smooth running manor. The original design created object of each device on the network it wished to ping, in my real world environment they are Kindles. The goal was to ensure they were still connected to the network by Pining them. I used a For Loop and Obj Array to do this set on a Timer. This had unexpected results causing the ListView to flicker and load slowly after the ListView1.Items.Clear. I evolved into updating the List Items rather than clearing them and the flicker remained.
I assumed this was due to the slow process of the array and pings so I started hunting for solutions and came across Multi-Threading. I have known about this for some time, but have yet to dive into the practice. My program seemed to need more speed and smoother operation so I took a stab at it. The below code in its complete form is the result, however it crashes and throws errors. Clearly I have not used Threading as it was intended. Using it in simpler functions works fine and I feel I have the grasp. That is if i want my program to pointlessly run counters.
I don't know what to do next in my steps for getting this task done, and figure I am combining several different methods into a mush of dead program. I could really use some help getting back on track with this. All comments welcome and thank you for checking out my code.
Form1 Code
Public Class Form1
'Obj Array
Public Shared objDevice As New List(Of kDevice)
'Thread Array for each Obj
Public Shared thread() As System.Threading.Thread
Private Sub ipRefresh(objID, itemPos)
Dim objDev As kDevice = objID
If My.Computer.Network.Ping(objDev.kIP) Then
objDev.kStatus = "Online"
objDev.kPings = 0
Else
objDev.kPings += 1
End If
If objDev.kPings >= 8 Then
objDev.kStatus = "Offline"
objDev.kPings = 0
ListView1.Items(itemPos).BackColor = Color.Red
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = objDev.kName
str(1) = objDev.kIP
str(2) = objDev.kStatus
str(3) = objDev.kPings
itm = New ListViewItem(str)
ListView1.Items(itemPos) = itm
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.CheckForIllegalCrossThreadCalls = False
' Adding ListView Columns
ListView1.Columns.Add("Device", 100, HorizontalAlignment.Left)
ListView1.Columns.Add("IP Address", 150, HorizontalAlignment.Left)
ListView1.Columns.Add("Status", 60, HorizontalAlignment.Left)
ListView1.Columns.Add("Pings", 60, HorizontalAlignment.Left)
Dim ipList As New List(Of String)
Dim nameList As New List(Of String)
Using MyReader As New Microsoft.VisualBasic.FileIO.TextFieldParser("kDevices.csv")
MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
MyReader.Delimiters = New String() {","}
Dim currentRow As String()
Dim rowP As Integer = 1
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
Dim cellP As Integer = 0
Dim nTemp As String = ""
For Each currentField As String In currentRow
Select Case cellP
Case 0
nameList.Add(currentField.Replace("""", ""))
Case 1
ipList.Add(currentField.Replace("""", ""))
End Select
cellP += 1
Next
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & " is invalid. Skipping")
End Try
rowP += 1
End While
End Using
Dim nameLAR As String() = nameList.ToArray
Dim ipLAR As String() = ipList.ToArray
ReDim Preserve thread(nameLAR.Length)
For i As Integer = 0 To nameLAR.Length - 1
Dim newDevice As New kDevice
Dim objNum = i
objDevice.Add(newDevice)
newDevice.kName = nameLAR(i)
newDevice.kIP = ipLAR(i)
If My.Computer.Network.Ping(newDevice.kIP) Then
newDevice.kStatus = "Online"
Else
newDevice.kStatus = "Loading"
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
thread(objNum) = New System.Threading.Thread(Sub() Me.ipRefresh(objDevice(objNum), objNum))
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
For i As Integer = 0 To objDevice.Count - 1
thread(i).Start()
Next
End Sub
End Class
kDevice Class
Public Class kDevice
Private strkName As String
Private strkIP As String
Private strkStatus As String
Private strkLastStatus As String
Private strkPings As Integer = 0
Public Property kName As String
Get
Return strkName
End Get
Set(value As String)
strkName = value
End Set
End Property
Public Property kIP As String
Get
Return strkIP
End Get
Set(value As String)
strkIP = value
End Set
End Property
Public Property kStatus As String
Get
Return strkStatus
End Get
Set(value As String)
strkStatus = value
End Set
End Property
Public Property kPings As Integer
Get
Return strkPings
End Get
Set(value As Integer)
strkPings = value
End Set
End Property
End Class
The Error / Crash on Line 32 of my code which is when it tries to pass the update to the ListView Item
An unhandled exception of type 'System.ArgumentException'
occurred in Microsoft.VisualBasic.dll
Additional information: InvalidArgument=Value of '18'
is not valid for 'index'.
or
An unhandled exception of type 'System.NullReferenceException'
occurred in Microsoft.VisualBasic.dll
Additional information: Object reference not set to an instance
of an object.
If my code does not make sense, or at lease the idea of what I was trying to make it do, please let me know and I will explain whichever parts are unclear. Again thank you for looking over my issue.
Just a possible issue I noticed:
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
In this bit here, you declare str(4) which would be 5 possible indexes (remember it starts at zero), where you should have 4 (str(3)) . I don't think this is the whole issue, but just a small bit you should probably fix. You also may want to look into other ways to update the listview without setting
Me.CheckForIllegalCrossThreadCalls = False
Here's an awesome guide that helped me when I did my first multi threaded application: http://checktechno.blogspot.com/2012/11/multi-thread-for-newbies.html

VB.net function that returns an object that is specified by the input

I am trying to write a function that returns a newly created object ( a form ) that is specified by the input. I'm having trouble with how to work out the concept of giving a type as an input then creating an object of that type in the body of the function. Here is an outline of what I'm working on.
Public Function MakeMyForm(ByVal frmType as Form) as Form
Dim NewObj as New frmType
Return NewObj
End Function
I'd like to be able to call the function in this way:
Dim myform as CustomFormType
myform = MakeMyForm(CustomFormType)
Can my concept be accomplished in VB.net?
Ok, if I understand you, you just want a generic method:
Public Function MakeMyForm(Of T As {New, Form})() As T
Return New T()
End Function
and call it like this:
Dim myform As CustomFormType = MakeMyForm(Of CustomFormType)()
of course, why wouldn't you just use:
Dim myform As New CustomFormType()
Well you can try this:
Dim frmnew() As Form
Dim createdforms As Integer = 0
Private Sub createform(wintext As String, height As Integer, width As Integer, backcolor As Color, topmost As Boolean, formborderstyle As FormBorderStyle, winstate As FormWindowState, opacity As Decimal, startposition As FormStartPosition, enabled As Boolean) 'add as many properties as you like
ReDim Preserve frmnew(createdforms)
frmnew(createdforms) = New Form
With frmnew(createdforms)
.Text = wintext
.Height = height
.Width = width
.BackColor = backcolor
.TopMost = topmost
.FormBorderStyle = formborderstyle
.WindowState = winstate
.Opacity = opacity
.StartPosition = startposition
.Enabled = enabled
End With
frmnew(createdforms).Show()
createdforms += 1
End Sub
and you can test it with the code below:
createform("Afnan Makhdoom", 500, 700, Color.Aqua, False, Windows.Forms.FormBorderStyle.Fixed3D, FormWindowState.Normal, 0.9, FormStartPosition.CenterScreen, True)
Public Function Makemyform(ByVal frmType As Form) As Form
Dim obj As Form
obj = newfunc(frmType)
Return obj
End Function
Public Function newfunc(ByVal mytype As Form) As Form
Return New Form
End Function
This is usually done using generics in a function such as:
Public Function GetItem(Of T)(key As String) As T
Usage:
myIntVar = myFoo.GetItem(Of Int32)(bar)
The purpose of which is for the code calling it to specify how it needs the return. In the above a whole bunch of data has been serialized and the original Type lost, so when fetching it back, the Of T helps convert it rather than using Object as the return.
For forms, it is more problematic:
Public Function MakeAForm(Of T)() As Form ' cant do As T
You'd have to add more code to cast Form to Form1 or frmCust to avoid tbName is not a member of System.Windows.Forms.Form errors. Even the correct way as shown by Mr Dokjnas present problems trying to do more with the form:
Public Function MakeAForm(Of T As {New, Form})() As T
Dim frm As New T
If frm.GetType Is frm8088.GetType Then
frm.textbox1.text = "ziggy" ' error
End If
Return frm
Here, it is 'TextBox is not a member of T`. If your forms were compiled to a ClassLib so the IDE could know more about the Types (forms) you could get it to work. But the first sign of futility is revealed in using it:
Dim frm As Form = MakeAForm(Of frm8100VI)()
frm.Show()
It takes more code to call the FormMaker than to just create an instance.

Get the name of the object passed in a byref parameter vb.net

How can I get the name of the object that was passed byref into a method?
Example:
Dim myobject as object
sub mymethod(byref o as object)
debug.print(o.[RealName!!!!])
end sub
sub main()
mymethod(myobject)
'outputs "myobject" NOT "o"
end sub
I'm using this for logging. I use one method multiple times and it would be nice to log the name of the variable that I passed to it. Since I'm passing it byref, I should be able to get this name, right?
For minitech who provided the answer:
This would give you the parameter name in the method and it's type, but not the name of the variable that was passed byref.
using system.reflection
Dim mb As MethodBase = MethodInfo.GetCurrentMethod()
For Each pi As ParameterInfo In mb.GetParameters()
Debug.Print("Parameter: Type={0}, Name={1}", pi.ParameterType, pi.Name)
Next
If you put that in "mymethod" above you'd get "o" and "Object".
That's impossible. Names of variables are not stored in IL, only names of class members or namespace classes. Passing it by reference makes absolutely zero difference. You wouldn't even be able to get it to print out "o".
Besides, why would you ever want to do that?
Alternatively you could get the 'Type' of the object using reflection.
Example: (Use LinqPad to execute)
Sub Main
Dim myDate As DateTime = DateTime.Now
MyMethod(myDate)
Dim something As New Something
MyMethod(something)
End Sub
Public Class Something
Public Sub New
Me.MyProperty = "Hello"
End Sub
Public Property MyProperty As String
End Class
Sub MyMethod(Byref o As Object)
o.GetType().Name.Dump()
End Sub
Sorry to say, but this is your solution. I left (ByVal o As Object) in the method signature in case you're doing more with it.
Sub MyMethod(ByVal o As Object, ByVal name As String)
Debug.Print(name)
End Sub
Sub Main()
MyMethod(MyObject, "MyObject")
End Sub
Alternatively you could create an interface, but this would only allow you to use MyMethod with classes you design. You can probably do more to improve it, but as this code stands you can only set the RealName at creation.
Interface INamedObject
Public ReadOnly Property RealName As String
End Interface
Class MyClass
Implements INamedObject
Public Sub New(ByVal RealName As String)
_RealName = RealName
End Sub
Private ReadOnly Property RealName As String Implements INamedObject.RealName
Get
Return _RealName
End Get
End Property
Private _RealName As String
End Class
Module Main
Sub MyMethod(ByVal o As INamedObject)
Debug.Print(o.RealName)
End Sub
Sub Main()
Dim MyObject As New MyClass("MyObject")
MyMethod(MyObject)
End Sub
End Module
If your program is still in the same place relative to the code that made it, this may work:
' First get the Stack Trace, depth is how far up the calling tree you want to go
Dim stackTrace As String = Environment.StackTrace
Dim depth As Integer = 4
' Next parse out the location of the code
Dim delim As Char() = {vbCr, vbLf}
Dim traceLine As String() = stackTrace.Split(delim, StringSplitOptions.RemoveEmptyEntries)
Dim filePath As String = Regex.Replace(traceLine(depth), "^[^)]+\) in ", "")
filePath = Regex.Replace(filePath, ":line [0-9]+$", "")
Dim lineNumber As String = Regex.Replace(traceLine(depth), "^.*:line ", "")
' Now read the file
Dim program As String = __.GetStringFromFile(filePath, "")
' Next parse out the line from the class file
Dim codeLine As String() = program.Split(delim)
Dim originLine As String = codeLine(lineNumber * 2 - 2)
' Now get the name of the method doing the calling, it will be one level shallower
Dim methodLine As String = Regex.Replace(traceLine(depth - 1), "^ at ", "")
Dim methodName = Regex.Replace(methodLine, "\(.*\).*$", "")
methodName = Regex.Replace(methodName, "^.*\.", "")
' And parse out the variables from the method
Dim variables As String = Regex.Replace(originLine, "^.*" & methodName & "\(", "")
variables = Regex.Replace(variables, "\).*$", "")
You control the depth that this digs into the stack trace with the depth parameter. 4 works for my needs. You might need to use a 1 2 or 3.
This is the apparently how Visual Basic controls handle the problem.
They have a base control class that in addition to any other common properties these controls may have has a name property.
For Example:
Public MustInherit Class NamedBase
Public name As String
End Class
Public Class MyNamedType
Inherits NamedBase
public Value1 as string
public Value2 as Integer
End Class
dim x as New MyNamedType
x.name = "x"
x.Value1 = "Hello, This variable is name 'x'."
x.Value2 = 75
MySubroutine(x)
public sub MySubroutine(y as MyNamedType)
debug.print("My variable's name is: " & y.name)
end sub
The output in the intermediate window should be:
My variable's name is: x