From: Paulo on
Hi to all,

After searching the net for possible solutions and not finding anything
useful I came for your advice. I have a collection of objects with the
properties X and Y, which are the coordinates of vertices (points). I need
to add an Index property to those objects. I know that the actual index of
each vertex in the vertices collection may change after adding to or
removing elements from the collection. Is it possible to retrieve the actual
index of each vertex in the collection and assign it to a read-only
property, an Index property?

I tried the code below. Notice that "Friend Property Let Index" is declared
as Friend. Unfortunately it is not possible working with this because the
following error is raised:

Run-time error '451':
Property let procedure not defined and property get procedure did not return
an object.

It works fine if I declare "Public Property Let Index", but I want the
property to be read-only to prevent the programmer (me!) from changing the
Index value.

Any advice, including a completely different approach?

TIA,
Paulo

-----------------------------------------------------
Code for the Vertex Class (Global Multiuse)
Class name is: Vertex
Filename is: cls_Vertex.cls
-----------------------------------------------------
Option Explicit
Private mlng_Index As Long 'local copy
Private mdbl_X As Double 'local copy
Private mdbl_Y As Double 'local copy

Public Property Let Y(ByVal Value As Double)
mdbl_Y# = Value#
End Property

Public Property Get Y() As Double
Y# = mdbl_Y#
End Property

Public Property Let X(ByVal Value As Double)
mdbl_X# = Value#
End Property

Public Property Get X() As Double
X# = mdbl_X#
End Property

Friend Property Let Index(ByVal Value As Long)
mlng_Index& = Value&
End Property

Public Property Get Index() As Long
Index& = mlng_Index&
End Property

-----------------------------------------------------
Code for the collection Vertices Class (Global Multiuse)
Class name is: Vertices
Filename is: cls_Vertices.cls
-----------------------------------------------------
Option Explicit
Private mCol As Collection

Public Function Add(X As Double, Y As Double) As Vertex
Dim obj_Vertex As Vertex
Dim lng_Counter As Long
Set obj_Vertex = New Vertex
With obj_Vertex
.X# = X#
.Y# = Y#
End With
mCol.Add obj_Vertex
For lng_Counter& = 1& To mCol.Count&
mCol.Item(lng_Counter&).Index& = lng_Counter&
Next lng_Counter&
obj_Vertex.Index& = lng_Counter& - 1&
Set Add = obj_Vertex
Set obj_Vertex = Nothing
End Function

Public Property Get Item(Index As Long) As Vertex
Set Item = mCol(Index&)
End Property

Public Property Get Count() As Long
Count& = mCol.Count&
End Property

Public Sub Remove(Index As Long)
Dim lng_Counter As Long
mCol.Remove Index&
If mCol.Count& > 0& Then
For lng_Counter& = 1& To mCol.Count&
mCol.Item(lng_Counter&).Index& = lng_Counter&
Next lng_Counter&
End If
End Sub

Public Property Get NewEnum() As IUnknown
Set NewEnum = mCol.[_NewEnum]
End Property

Private Sub Class_Initialize()
Set mCol = New Collection
End Sub

Private Sub Class_Terminate()
Set mCol = Nothing
End Sub

-----------------------------------------------------
Code for testing: Paste the code to the declarations section of a form
with a command button Command1, and two list boxes, List1 and List2.
-----------------------------------------------------
Option Explicit

Private Sub Command1_Click()

Dim obj_Enum As Vertex
Dim obj_Item As Vertex
Dim col_Items As Vertices
Dim str_Info As String
Dim lng_Counter As Long

Set col_Items = New Vertices

For lng_Counter& = 1& To 5&
Set obj_Item = New Vertex
With obj_Item
.X = CDbl(lng_Counter&)
.Y = CDbl(lng_Counter&)
col_Items.Add .X, .Y
End With
Next lng_Counter&

If col_Items.Count > 0 Then
For Each obj_Enum In col_Items
With obj_Enum
str_Info$ = Format$(.Index&, "000") & " - ( " & .X & " , " & .Y & " )"
List1.AddItem str_Info$
End With
Next obj_Enum
End If

col_Items.Remove 2

Set obj_Item = New Vertex
With obj_Item
.X = CDbl(100)
.Y = CDbl(100)
col_Items.Add .X, .Y
End With

If col_Items.Count > 0 Then
For Each obj_Enum In col_Items
With obj_Enum
str_Info$ = Format$(.Index&, "000") & " - ( " & .X & " , " & .Y & " )"
List2.AddItem str_Info$
End With
Next obj_Enum
End If

End Sub

-----------------------------------------------------


From: Ivar on
I've not tried your code just had a read thru it.

If you want to make a property Read Only then declare the Let as private

This looks strange:
For lng_Counter& = 1& To 5&
Set obj_Item = New Vertex
With obj_Item
.X = CDbl(lng_Counter&)
.Y = CDbl(lng_Counter&)
col_Items.Add .X, .Y
End With
Next lng_Counter&

Shouldn't it be just
For lng_Counter& = 1& To 5&
Set obj_Item = New Vertex
col_Items.Add CDbl(lng_Counter&),CDbl(lng_Counter&)
Next

Maybe I'm missing something

Ivar
From: Larry Serflaten on

"Paulo" <nospam.pmpcosta(a)netcabo.pt> wrote in message

> I tried the code below. Notice that "Friend Property Let Index" is declared
> as Friend. Unfortunately it is not possible working with this because the
> following error is raised:
>
> Run-time error '451':
> Property let procedure not defined and property get procedure did not return
> an object.
>

I tried a project with just the X and Y and Index properties and had no
trouble setting the Let side to Friend (using VB5).

However, one way to give your component special access to your
objects (other classes) is to define a private interface and have your
object(s) implement that interface. Then when you need access you
route your calls through the interface. In your case your Vertex class
would implement a second interface (IVertex) and contain only the Get
side of the Index property. The interface would expose the Let side:

Public Function Add(X As Double, Y As Double) As Vertex
Dim obj_Vertex As Vertex
Dim obj_Setter As IVertex
Dim lng_Counter As Long
Set obj_Vertex = New Vertex
With obj_Vertex
.X# = X#
.Y# = Y#
End With
mCol.Add obj_Vertex
Set Add = obj_Vertex
' Refactor the re-indexing ???
For lng_Counter& = 1& To mCol.Count&
Set obj_Setter = mCol.Item(lng_Counter&)
obj.Setter.Index = lng_Counter&
Next lng_Counter&
Set obj_Setter = Nothing
Set obj_Vertex = Nothing
End Function


LFS


From: Paulo on
"Ivar" <ivar.ekstromer000(a)ntlworld.com> wrote:

> Shouldn't it be just
> For lng_Counter& = 1& To 5&
> Set obj_Item = New Vertex
> col_Items.Add CDbl(lng_Counter&),CDbl(lng_Counter&)
> Next
>
> Maybe I'm missing something

Thanks, you're correct. I missed that part...
Paulo


From: Paulo on
Hi Larry,

It works very good with one remark: the IVertex interface class may not to
be Private, because of the compile error:

�Private object modules cannot be used in public object modules
as parameters or return types for public procedures, as public
data members, or as fields of public user defined types.�

But no problema with interface beeing visible. The Index property will be
very usefull for my project.

Thanks a lot for your advice, Larry.

Paulo