I've another question for you whereas nobody could help me.
First of all, I'd like to know how you would do to pass a param of a property of one class to another class. I'll explain better:
I've two class modules (clsGame and clsSound). In clsGame I have this code:
Code:
Dim m_Sound As New clsSound
Public Property Get Sounds(ByVal Item As String) As clsSound
Set Sounds = m_Sound
End Property
while in clsSound
Code:
Public Sub Play()
'Play a sound here
End Sub
This code should play the sound which name is passed as param in Sounds property from clsGame... obviously it doesn't work because I cannot pass 'Item' from Sound property to 'Play' method, unless I've to move the param from the property into the sub, or keep 'Item' as public var somewhere else or save it into registry and delete it when it isn't more in use.
There's a work-around to use a param of a property of a class into another class without creating public vars or other?
Then, I'd to know how to create default properties (such as the Text property for Textboxes or Caption for Labels, etc...). Most programmers said me it's not possible, but I don't believe that. In fact, reading from MSDN I discovered that it would be possible (even if there isn't how to do it)... So I hope you know how.
Wait... hold on a sec... first you wouldn't pass the parm of a property to something else... you should be using the property... but then looking at your code... what's Item? You're passing it to the property, but then totally discard it...so what's the Point? Besides it's the Get method of the property, so you shouldn't be passing into it... unless the property is an array or part of a dictionary... but then you're not using it anyways, so it doesn't matter if you're passing in something or not...
If clsSound has a Play method, then that's what you should be calling... the Play method of the instance of the class... in this case m_Sound.Play ... since m_Sound is private inside of clsGame, you might need a method in clsGame that invokes the Play method of m_Sound... but it depends on where and how you do want to invoke it.
Lastly... it IS possible to create default properties in VB6 ... but it's not intuitive... I forget exactly how (it's been a while since I've needed to do this and I no longer have VB6 installed) ... but you use the Class Editor tool and set the Property Index to 0 on the property you want as the default... it's not something that can be done from the normal code editor...
You're right: I was just a bit confused with my explaination. I'll try to be cleaner, even though I don't know how to explain...
I wish that, when I call the Play method in this way,
Code:
Game.Sounds("C:\WINDOWS\Chimes.WAV").Play
the method read the param Item of Sounds property and sounds it.
But as well as it isn't possible, I have to keep Item into an upper level (e.g. into a bas module) so that clsSound can read it. This solution forces me to have a module only for this variable, and I don't like.
I thought to save Item into registry and read from it when I had to play it, but also this solution doesn't satisfy me.
However now I've found a middle way, that's something like this (I'm not sure I'm writing right 'cause at the moment I don't have my project):
Code:
'clsGame
Dim m_Sounds As New clsSound
Public Property Get Sounds(ByVal Item As String) As clsSound
Dim tmp As String: tmp = m_Sounds.Sound(Item)
Set Sounds = m_Sounds
End Property
'clsSound
Dim OK As Boolean
Public Property Get Sound(Optional ByVal Item As Variant) As String
Static m_Item As String
If Not IsMissing(Item) Then
m_Item = Item
OK = True
Else
Sound = m_Item
End If
End Property
Public Sub Play()
If OK Then
Debug.Print Me.Sound
OK = False
End If
End Sub
'Somewhere else in your code
Dim Game As New clsGame
Call Game.Sounds("C:\WINDOWS\CHIMES.WAV").Play
It should work... In the morning I'll look my project and I'll correct the post if it is not correct...
Anyway, I'd be glad if you could be more accurate into describing me the process about how to create default properties.
I think you'd do better to create a Public Sub or Function within the class module. Something like this perhaps:
Code:
Public Function Play(Filename As String) As Boolean
'Play the file
'If successful, set Play to True (for error handling if desired, otherwise use a Sub)
End Function
As for creating default properties, go to Procedure Attributes under Tools, click Advanced, then select Default for the Procedure ID.
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
I think you'd do better to create a Public Sub or Function within the class module.
Oh, I know this is the easier way to do what I want, but I discarded this possibility because it won't reproduce the scheme I have in my mind. I wish to build up something like the property Drives of the FSO object:
Code:
Dim FSO As New FileSystemObject
FSO.Drives("C:\").FreeSpace
As well as I must play one sound at time, it's easier for me write a code similar to this:
Code:
With Game.Sound("C:\WINDOWS\CHIMES.WAV")
.Play
Do While .IsStillPlaying()
DoEvents
Loop
.Stop
'etc..
End With
instead of passing to each procedure the file name to which I'm referring to. With the code above I would pass only once the param I need. And when I change that param the code works with the newer one (i.e. it plays the new file and more...)
I thought that I could pass the param from Sound property to clsSound if I would wrote a Friend property in that class. But this forces me to have both a LET and a GET property, that is also visible in my 'main' project and not only among the classes. This means that when I write my code, into the Intellisense I'll see within other properties that one that needs to exchange the param. There would not be great problems, if it wouldn't be that I have in my project lots and lots of lines of code among classes' properties and other... so, I wish that Intellisense hides the 'un-necessary things' I never use in my main project.
And for now the solution I found I believe that can satisfy at best my requests. But if you know for sure how the FSO.Drives property works, if you tell me I can copy the algorithm to create my own properties
NOTE: When I talk about 'Main project' I'm refer to the game itself, and not to all the classes I use to build it!
Originally Posted by WizBang
As for creating default properties, go to Procedure Attributes under Tools, click Advanced, then select Default for the Procedure ID.
Thank you very much, you're always so kind. With these info I believe I can improve my codes since now.
Last edited by Cereal Killer; December 14th, 2012 at 04:28 AM.
There are a few ways to do what you want with classes and properties. One is by using an Enum. Another is by using a class as a parameter. Yet another is by using a class as a Public property. A UDT (User Defined Type) can also be used to achieve some advantageous capabilities as well.
One use of an Enum is that you could pass multiple commands into a procedure or property, so that different functions can be performed without the use of separate Public procedures. In addition, VB Intellisense will provide easy access to all members of the Enum.
To do something like FSO.Drives, you could use a class as a property. However, this may not be the best solution for your particular needs. A lighter weight approach might be to use an Enum, or UDT.
Anyway, my suggestion of a Public Sub or Function still stands. A Public Function could actually be more useful than a property. It simply depends on what exactly you need to do.
I will try to put together a small example project for you later, demonstrating some of the aforementioned approaches.
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
I explain you more in details how my project is structured:
I have several classes (clsGame, clsSound, clsPlayers, etc...). I declare as public Game as a new instance of clsGame.
Game has several properties and methods. Some properties are grouped into clsSound and others into clsPlayers. So Game shows a Sounds property (that returns an instance of clsSound within related properties and methods) and a Players property (that returns an instance of clsPlayers within related properties and methods). We MUST put the focus on SOUNDS property.
Sounds property accepts a param (that is an Enum of the possible wav sounds my program can play) and gets back a clsSound instance. The clsSound has a Play method, that has to play the wav sound which name is passed to Sounds property.
It's something like if I have to return a collection of clsSound: in basis of the param, I return the correct instance of clsSound. The return clsSound has to execute the method I invoke when I call Sound property.
So the code resulting should be like Game.Sounds(<Enum>).Play
The only way I found to make it work was that one I posted before. And things are going to be more complicated when I write new code and I add functionality. UDTs would help me if I've only to show properties and not also methods and functions from a property. It is possible in VB.Net, not in VB6
Anyway I look forward to your sample project as soon as you'll post it. Maybe we're saying the same things but we don't understand each other - you must know I come from Italy and my skills in English suffer of some limits -
If there is only one instance of the sound class, then you need not use a class for it. A standard module would provide all the functionality you seek, while consuming fewer resources. For example, in a module you might have:
Code:
Option Explicit
Public Enum Sounds
Sound1
Sound2
Sound3
Sound4
End Enum
Public Sub Play(Sound As Sounds)
'Play the sound
End Sub
Then, from anywhere in your application, a sound could be played like:
Code:
Module1.Play Sound3
You can also use a unique name for the Public Sub, in which case there'd be no need to specify the module name.
If you use a class for the sound, then you'd use it this way:
Code:
Dim S As clsSound
Set S = New clsSound
S.Play Sound3
Set S = Nothing
If you want a sound class to be within a game class, then it could work like this:
Code:
Dim G As clsGame
Set G = New clsGame
G.Sound.Play Sound3
Yet another way to do it, the syntax would more closely resemble what you've been asking:
In your sound class:
Code:
Public Sub Play()
'Play the sound
End Sub
In your game class:
Code:
Public Enum Sounds
Sound1
Sound2
Sound3
Sound4
End Enum
Public Function Sound(S As Sounds) As clsSound
Set Sound = New clsSound
End Function
Then you'd use it like:
Code:
Dim G As clsGame
Set G = New clsGame
G.Sound(Sound3).Play
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
If there is only one instance of the sound class, then you need not use a class for it. A standard module would provide all the functionality you seek, while consuming fewer resources.
Bad news: I've more than one clsSound.
Then I wanted to avoid to pass directly as param the sound I was going to play, because Play method has several params and adding new ones it would be difficult to read the code and to write it, too. Obviously this is my personal opinion
However, I studied hard and I finally found a good solution, that lets me to do all want. I post here
Code:
'In clsGame
Dim m_Sound As New clsSound
Public Property Get Sound(ByVal Index As Integer) As clsSounds
Set Sound = m_Sound 'Return the instance of clsSounds
On Error Resume Next
Err.Raise 999 'Raise this err to point out that Item property can be written
Sound.Item = Index 'Pass the param
End Property
'In clsSounds
Dim m_Item As String
Public Property Let Item(ByVal n_Item As String)
If Err = 999 Then
m_Item = n_Item
Err.Clear
ElseIf Err = 0 Then
Err.Raise 383
End If
End Property
Public Property Get Item() As String
Item = m_Item
End Property
Public Sub Play()
Debug.Print "I'm going to play the item : " & Me.Item
End Sub
'In Form1
Private Sub Form_Load()
Dim Game As New clsGame
Call Game.Sound(<Index>).Play
Game.Sound(<Index>).Item = <something> 'Returns the err 383 - Readonly property
End Sub
This code works exactly as I want: even though it uses both a LET and a GET property, the LET property works only if it is called inside of Sound property. De facto, it's like if I have only the GET property, who returns the item currently set.
Anyway, thank you for all the time you spent to help me...
Bye bye,
Perhaps you missed the last of the solutions I gave you, as it accomplishes what you asked, more intuitively than what you're now doing. Didn't you want the Intellisense to show the sound choices Enum?
Incidentally, for multiple parameters, you can use a UDT to group them, making it far easier to work with.
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
I looked to your last solution and even though I didn't try it, I don't think it works, because there's one point you've not considered: how does the Play method (that is written into clsSound) play the file which name (or Enum) is passed to Sound property as its own param (and for this, it's like if it was declared as Private) from another class?
It is necessary to have something else that keeps the Enum so that Play can read the value and then to play it. The possible solutions are three:
the first one is to add a module and declare a var into it as Public that keeps the Sound value so that Play can access to it, but this forces to have one module to do this only.
the second one is to save into registry the value when Sound property is executed so that when Play method is invoked it read from registry the value and plays the sound. But also this way is not perfect, cause you get registry dirty and you're obliged to save and delete the keys you wrote each time the properties are used. Another disadvantage is that you cannot use the With...End With staments 'cause the operations saving/deleting don't let you to use it.
the last option you have is to use a LET/GET property, without using any 'strange' code to control how property is written. This approach is the easier but you can easly make errors when you write your code.
With my code, you have more control on what you're doing. The first advantage is the capabality to use With...End With structure to manage the Sound property within all its sub-properties and methods. Then you can access to each function without writing each time the param that does it work. At least you can know at any time what is the sound you're playing simply reading the Item property exposed by Sound property... you don't ever need to worry about some mistakes to assign in writing a new value to Item that could raise an error because as my property is structured if you try do this, you return an run-time error of read-only property.
I think this is the best property I have ever written because it meets EXACTLY my needs.
However, thanks again, but I took my ultimate decision.
Ooops...my mistake, in haste I omitted some code. My apologies.
However, the code you posted will not work exactly right. It cannot return the actual Item value, because a new value is assigned when attempting to read it. But perhaps worst of all, intentionally raising an error just to make the code work is poor programming practice.
Attached is an example project which functions more desirably. It will not allow the sound value to be altered except when invoking the Play function. The value passed when merely reading the sound value is essentially a dummy argument, having no effect. No errors need be raised to do this. It also provides the Intellisense for the sound value, which you indicated that you wanted. Plus I added an extra goodie or two just for fun.
There are of course many different ways to accomplish this. It just depends on what you need and/or want. For instance, you mentioned needing more than one copy of the sound class. In such a case, you could use an array, and that opens up a few other possibilities.
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
Private Sub Form_Load()
Dim G As clsGame
Set G = New clsGame
With G
Debug.Print .Sound(None).SoundName & " (" & .Sound(None).SoundID & ")"
.Sound(Ding).Play
.Sound(2).SoundID = 4 'cannot change property - no effect
Debug.Print .Sound(None).SoundName & " (" & .Sound(None).SoundID & ")"
.Sound(Dong).Play
End With
Set G = Nothing
End Sub
The line marked is a non-sense line: if I cannot assign a new value, why don't I receive any error?
I can forget this and try to change the value later. And when the program doens't work as I think, I might be crazy before I remember the reasoning at the basis of the property, don't you?
I believe it's better to get back an error if I cannot do something (e.g. the MultiLine property of a TextBox does it: in run-time you receive an error if you try to change its value).
You noted me the overwriting fault... How do you receive this?
If you call the Item property, you get back exactly the value passed as Index to Sound property... If there's something I missed, please tell me. I did lots of tests, and I didn't note anything wrong.
At the end, I would reply to one thing only:
intentionally raising an error just to make the code work is poor programming practice.
That's not true. There are a lots of different reasons to do that, and it is often the only way to do them. Some examples:
- this is the most well-known one: creating a DebugMode function. You may do it using the Debug.Assert statement and writing lots of lines of code. But a shortcut exists: you surely know that Debug.Print ONLY works in debug mode and not also in compiled EXEs. You can write the DebugMode function taking advantages of this and writing 3 lines of code:
Code:
Function DebugMode () As Boolean
On Error Resume Next
Debug.Print 1 / 0 'whereas this statement is run only at debug time, the error is raised in debug mode only
DebugMode = (Err = 11)
Err.Clear
End Function
- another example could be that to verify if key exists in Registry, like this:
Code:
Function KeyExists(AppName As String, Section As String, KEY As String) As Boolean
On Error Resume Next
Call GetSetting(AppName, Section, KEY, Nothing)
KeyExists = (Err = 0)
End Function
- another common one is this:
Code:
Function FileExists (FileName As String) As Boolean
On Error Resume Next
FileExists = ((GetAttr(FileName) And vbDirectory) = vbNormal)
End Function
As you can see intentionally raising errors it's a way to do things rapidly (and in most cases also well) using VB code only. Think: VB.NET offers you this capability by the Try...Catch...Finally statements.
I believe that an error, if it is managed correctly, it's a value like another, neither more nor less of a byte variable you can declare...
I repeat again: these are all my personal opinions, I took after few years of programming, and they might be wrong or right, but it doens't matter: the important thing is that my program works fine indipendently from the way I used to develop it, don't you agree?
Last edited by Cereal Killer; December 17th, 2012 at 04:28 AM.
...if I cannot assign a new value, why don't I receive any error?
I can forget this and try to change the value later. And when the program doens't work as I think, I might be crazy before I remember the reasoning at the basis of the property, don't you?
You could raise an error when attempting to change the value if you wish. Just as you could raise an error when attempting to assign an invalid value. It is entirely up to you. The example is merely to demonstrate how the value can be read without changing it. It is your project, so it wouldn't be proper for me to write the whole thing for you. However, it would make sense that a value which can be set at runtime should also be readable at runtime.
You noted me the overwriting fault... How do you receive this?
If you call the Item property, you get back exactly the value passed as Index to Sound property... If there's something I missed, please tell me. I did lots of tests, and I didn't note anything wrong.
Simply reading the value requires passing a value to the Sound procedure. That value is then assigned, so you cannot actually read the value which is already assigned. This is one main reason why I suggested some other ways to go about it, other than passing a value to the Sound procedure, even when you don't want to assign a value.
As you can see intentionally raising errors it's a way to do things rapidly (and in most cases also well) using VB code only. Think: VB.NET offers you this capability by the Try...Catch...Finally statements.
...
I believe that an error, if it is managed correctly, it's a value like another, neither more nor less of a byte variable you can declare...
There is a large difference between handling errors which are beyond the ability of your program to prevent, and intentionally causing an error to make code work. This is certainly different than raising an error in response to invalid input or other inappropriate action on the part of the user. You see, your code creates an error even when the function is being used correctly.
I repeat again: these are all my personal opinions, I took after few years of programming, and they might be wrong or right, but it doens't matter: the important thing is that my program works fine indipendently from the way I used to develop it, don't you agree?
I do not agree because it is impossible to read the value currently set. If you do not want to read the value, perhaps you should raise an error, otherwise, (to use your own argument) you might forget this and try to read it later. But again, a value which can be set at runtime should be readable at runtime.
I've attached an example of a way to do it which is more intuitive, and avoids some of the problems we've been discussing.
Please remember to rate the posts and threads that you find useful.
How can something be both new and improved at the same time?
I don't say you are wrong, I only say that you cannot fully understand the 'philosophy' of my program, and how and why it is structured in that way, and try to explain you it is really difficult, neither do I.
I analyzed the project as best I could and after some months of work there are some aspects missed me yet. Things aren't so easy as they seem.
Yesterday, I had to change again my code, and now I have something like this:
Code:
With Game.Sounds.Item(<Index>)
.Open_ <Alias>
.Play <Sync|Async>
.Close_
MsgBox .Name
MsgBox .Alias
End With
This new code lets me to do things I couldn't do before (e.g. playing more than one sound at time). The new structure also avoid the risk to overwrite the file name of the sound when you attempt to read it, because Item gets back the right instance of clsItem from a collection in basis of the Index you pass it.
The code is based on the old one (as concerning to the Name property, while the Alias property returns the argument of Open_ method or a null string if sound is not opened).
I can't understand what is the matter if I raise intentionally an error to check whether properties can be written or not... I think it is the easier way to do what I want.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.