Click here to Skip to main content
15,307,395 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I have created a class called Songs. It contains some variables called: "name","fullname" and "directory".

The idea on the Form is for a user to use OpenFileDialog to select a .wav file to add the song to a list in the a listbox. Everytime the user enters a file, it should create a new Songs (the class) thing.
So the song they input will have 3 informations attached because it's a class Songs (Sorry If I'm not making sense, correct me if I'm wrong!).

Each item on the list will have:
name - The name shown on the list extracted from the FileName e.g. song.wav
fullname - The full name including directory e.g. C:\Users\Joel\Music\song.wav
directory - Just the file name directory e.g. C:\Users\Joel\Music

Ok, how do I do this?

I have the class set up but I don't know how to create a new version of the Songs class for each item input to the list for each song!
The user will see the listBox like a list of the "name" property for each one without the directory attached.
like song1.wav song2.wav song3.wav ...
but when they double click it I want the program to read the directory to find the file.
So when you double click the song on the listbox it will read the property "fullname" to find the file.

The problem i have is:
I have used classes before but I always had to declare the class thing before at the start of the program like "Static Monster example" (Monster is class name)
but I always had to declare these at the start. But for this new program I can't do that because I need it to create a new one for each song the user chooses.

Any ideas?
I'm sorry for such a crap post and badly explained things and you might not understand any of it at all. But I really need help with this - I'll rephrase if you want, thanks
Posted
Comments
R. Giskard Reventlov 27-Jan-12 20:04pm
   
Smells suspiciously like homework.
Joel Whatley- 27-Jan-12 20:15pm
   
Lol no =) I am far infront of my group in college, I get bored because the other stuff is too easy, and I am just searching Windows just trying to re-create simple applications in a basic way. So far I have done a calculator, word processor and now a music player (this post) im doing. : )
LanFanNinja 27-Jan-12 20:42pm
   
Check solutions below.
LanFanNinja 27-Jan-12 22:22pm
   
If you are reading this comment after checking my solution please recheck it as there was a mistake I fixed.

changed
Song selectedSong = songs[listBox1.SelectedValue];
to
Song selectedSong = songs[listBox1.SelectedItem.ToString()];
BillWoodruff 27-Jan-12 23:19pm
   
LanFanNinja's solution below (note his revision in a later comment) is a fine example for you to study.

The simplest way, I think to "get a grip" on what you are struggling with here is to think of a Class (that is not "static") as being a special type of Object which is a template for the making of Instances of the Object-template. It's a "blue-print:" a "specification," an "abstraction."

And, there's nothing wrong with having as many properties, or fields, as you like in your Object definition, and calculating them and setting them (for those fields which need to be calculated) in the Constructor of the Object !

If those fields (calculated) are, in the future, frequently accessed: it makes sense to calculate them once, in the Constructor.

You can define your entire Class as Public if you wish.

What you define as Public within the class (fields, properties, methods, even other Classes, or structs), should be, imho, choices made based on future needs to "expose" those "class members" outside the Class to instantiators/consumers of the Class (possibly even "inheritors from" the Class).

And, the need, at times, to expose different internal parts of a Class, to different "consumers" or "inheritors," can be handled by the skilfull use of Interfaces, but that's an advanced topic, and, I think, not relevant here.
LanFanNinja 27-Jan-12 23:25pm
   
Very well said sir.
LanFanNinja 28-Jan-12 20:11pm
   
Check my new solution for you (solution 4). Hopefully it will help you on your quest for audio playback nirvana. :)

Joel,

I feel you have a mess about types and objects (instances), and references. The class Song would be a type and not an object.the It should not be plural, according to the naming conventions. The class under this name is only one, but many instances can be created. You don't create "a new Songs (a class) thing". An example if the instance is song in the code by LanFanNinja. Now, this is a variable name, which is actually the reference to the actual object, and the type is the reference type. It means that if you did
C#
Song song1 = new Song("first");
Song song2 = song1;
you would have two references referencing only one object created using the constructor.

If you copied, say, integer object in its natural (not boxed) form, you would have two different objects. If you change one of them, another one would remain the same. With reference objects, if you changed song2 you would observe this change in song1 as well, because song2 and song1 work like the same object; you consider these two variables as the names of the same thing.

—SA
   
Comments
LanFanNinja 27-Jan-12 22:45pm
   
+5 Correct. This certainly needed to be pointed out.
Sergey Alexandrovich Kryukov 27-Jan-12 22:49pm
   
Thank you very much.
--SA
BillWoodruff 27-Jan-12 23:10pm
   
The class "Song" is both a Type, and Object: specifically "Song" is a Type of user-defined class, a sub-type of reference Types.

MSDN: "All types derive from the System.Object base type." http://msdn.microsoft.com/en-us/library/2hf02550(VS.71).aspx

Instances of the class "Song" are, of course, Objects.
   
Wrong. Song is Object, of course, but Object is not an object (not an instance), but a type. Isn't that obvious?
So, Song is System.Object but not an object (not an instance).
Is is clear now?

Now, in Delphi, a major predecessor of .NET and C#, one can work with a class as an object, that is, a class is a first-class object called "meta-class". .NET is less advanced. The role of meta-class is modeled by the instances of the class System.Type, but this type is only one for all types, which is less powerful than meta-classes.

--SA
BillWoodruff 28-Jan-12 5:05am
   
Wrong. You have simply further obfuscated a very simple fact about .NET. Read the MSDN documents referred to above.

What you are doing is confusing the fact that on the macro-level Classes in .NET are Types, in fact, technically "first-class Types," in terms of "formal Computer Science; however, on the micro-level, in C# code: Classes themselves cannot be arguments to such methods as GetType(), etc.

From the C# "ground-level" perspective it does not make sense to consider the actual Type of a Class, even though a Class is a Type, of a special "flavor," as I mentioned: a sub-type of Reference Types, which a sub-type called a "User defined Class."

The equivalency of any instance of a Class to both 'Object and 'System.Object is a reflection of an inheritance relationship.

// assume an instance of Song has been instantiated
// named 'instanceOfSong
Console.WriteLine(instanceOfSong is Object);
Console.WriteLine(instanceOfSong is System.Object);
Console.WriteLine(instanceOfSong is Type);
Console.WriteLine(instanceOfSong is System.Type);

The output of these three statements will be:

True
True
False
False

Of course, calling instanceOfSong.GetType() will return, as expected: "Song."

Also, go into .NET, and type System.Type somewhere and observe what IntelliSense tells you in the pop-up.

A further experiment that may open your eyes: set a breakpoint at a place where you have access to some Public Class you have created: open the Visual Studio "Command Window" and try these:

? Song

? typeof(Song)

Then contrast the output of the 'typeof result above with:

? Type.GetType("Song")

Which will be "null."

Bringing in Delphi here is a "red-herring:" a distraction that has nothing to do with the topic at hand.

Hopefully, between the two of us, we have now completely befuddled the original poster :)
Sergey Alexandrovich Kryukov 28-Jan-12 12:50pm
   
Sorry, you are blurring things and trying to bend definitions to save your statement. You can call a class an object because everything is a matter of definition, but it does not justify your original statement.

So, you says that Song is object because Song is Object. In don't know was that manipulation with the fact that 'object' and 'Object' are spelled almost the same or not, but this is how it looks.

Do you argue that object is Type or not? My original motivation was that OP messes up types and instances (object). We first classify things into objects and types. "Classification" means they do not intercept. In this sense, Song is type and not object. In the same sense, Object is type, and not object. Would you argue?

Now, let's talk about classes as first-class objects. Here, even if we call them objects, they are objects of a different class, and, by the way, as soon as we don't have meta-classes, they are not first class objects.

I tried to explain it about the type System.Type. The problem is: the objects of System.Type are NOT types. Surprise? And the return of the function is GetType is not a type. This is the object (instance) of the type Systen.GetType. These object carry information of the type, they can be used to create instances pretty much like types, but they cannot be used as real type. They mimic types and model type's functionality.

I feel that you know how to use those things, etc., the argument is kind of "philosophical".

--SA
Joel Whatley- 28-Jan-12 1:22am
   
Yes sorry I am very uninformed about classes, objects, etc. My college group has just moved off simple Console programming and into Windows form. So far we have learned about a few items like textboxes, buttons, listboxes, dialogs, etc. etc.
Never learned information about classes,objects, types, references.
I have been trying to learn more ahead of college group by using the internet.
Would it help to paste the whole code for my program (it's small and basic) for someone to help me sort this out
LanFanNinja 28-Jan-12 1:37am
   
"Yes sorry I am very uninformed about classes, objects, etc."
That is very understandable.

"Would it help to paste the whole code for my program (it's small and basic) for someone to help me sort this out"
How small are we talking here?? 50 lines or less?
What are you still having problems with? Post your code and lets have a look I am sure myself or someone else on here will be able to help you out.
Joel Whatley- 28-Jan-12 16:40pm
   
There is like 100 lines or less but I can leave some out which is like other buttons which aren't included in my problem
Joel Whatley- 28-Jan-12 16:52pm
   
http://pastebin.com/y21A87xn
Here is my latest solution for you hopefully it is more what you are looking for.

The songsListBox holds the songs you add using the addSongsButton theses songs can then be selected and sent to the playListBox using the addToPlaylistButton.

I am sure you can figure out the rest of the changes but if not just ask and I will help you.

NOTE: Again this code was not heavily tested so if you encounter any strange problems let me know and myself or someone else on here will try to help you.

Let me know how it works out for you. :)

C#
public partial class Form1 : Form
{
    SoundPlayer player;
    Dictionary<string, Song> songs;
    Song selectedSong = null;

    public Form1()
    {
        InitializeComponent();

        songs = new Dictionary<string, Song>();

        // this allows you to select multiple songs from the 
        // songsListBox to add to the playListBox by holding
        // down the Ctrl or Shift key.
        songsListBox.SelectionMode = SelectionMode.MultiExtended;

        //allows multiple files to be selected
        // by holding down the Ctrl or Shift key.
        openSongDialog.Multiselect = true;
    }

    //add songs to the songsListBox
    private void addSongsButton_Click(object sender, EventArgs e)
    {
        if (openSongDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            for (int i = 0; i < openSongDialog.FileNames.Length; i++)
            {
                Song song = new Song(openSongDialog.FileNames[i]);
                songs.Add(song.Name, song);
                songsListBox.Items.Add(song.Name);
            }
        }
    }

    //removes selected songs from the songsListBox and also the playListBox
    private void removeSongsButton_Click(object sender, EventArgs e)
    {
        if (selectedSong != null && songsListBox.SelectedItems.Contains(selectedSong.Name))
        {
            StopPlayback();
            selectedSong = null;
        }

        for (int i = songsListBox.SelectedItems.Count; i > 0; i--)
        {
            string selectedSongName = songsListBox.SelectedItems[i - 1].ToString();

            songsListBox.Items.Remove(selectedSongName);
            playListBox.Items.Remove(selectedSongName);
            songs.Remove(selectedSongName);
        }
    }

    private void playListBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (playListBox.SelectedItem != null &&
            songs[playListBox.SelectedItem.ToString()] != selectedSong)
        {
            StopPlayback();
            selectedSong = songs[playListBox.SelectedItem.ToString()];
        }
    }

    private void playButton_Click(object sender, EventArgs e)
    {
        if (selectedSong != null)
        {
            player = new SoundPlayer(selectedSong.FullPath);
            player.Play();
        }
    }

    private void stopButton_Click(object sender, EventArgs e)
    {
        StopPlayback();
    }

    private void StopPlayback()
    {
        if (player != null)
        {
            player.Stop();
            player.Dispose();
        }
    }

    //adds songs selected in the songsListBox to the playListBox
    private void addToPlaylistButton_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < songsListBox.SelectedItems.Count; i++)
        {
            // the if statement checks if playListBox already
            // contains a song with this name before adding it.
            if (!playListBox.Items.Contains(songsListBox.SelectedItems[i]))
            {
                playListBox.Items.Add(songsListBox.SelectedItems[i]);
            }
        }
    }

    //removes selected song from the playListBox
    private void removeFromPlaylistButton_Click(object sender, EventArgs e)
    {
        if (selectedSong != null && selectedSong.Name == playListBox.SelectedItem.ToString())
        {
            StopPlayback();
            selectedSong = null;
        }

        playListBox.Items.Remove(playListBox.SelectedItem);
    }

    //removes all songs from the playListBox
    private void clearPlaylistButton_Click(object sender, EventArgs e)
    {
        if (selectedSong != null)
        {
            StopPlayback();
            selectedSong = null;
        }

        playListBox.Items.Clear();
    }
}

class Song
{
    internal string FullPath { get; private set; }
    internal string Directory { get; private set; }
    internal string Name { get; private set; }

    public Song(string path)
    {
        FullPath = path;
        Directory = System.IO.Path.GetDirectoryName(path);
        Name = System.IO.Path.GetFileNameWithoutExtension(path);
    }
}
   
v7
Comments
Joel Whatley- 28-Jan-12 21:21pm
   
I love you. I actually do!
It works! Finally!!
:happy:

Just 1 thing:)
In my original program there were 2 list boxes.
The first one was to enter songs into via the dialog.
The second was the proper music list you moved the songs you want from the first one.

For example:

listBox2.Items.Add(listBox1.Items[listBox1.SelectedIndex].ToString());
listBox1.Items.Remove(listBox1.Items[listBox1.SelectedIndex].ToString());

Is there a way to do this ? :)
LanFanNinja 28-Jan-12 21:33pm
   
"Is there a way to do this ?"
Yes.

Give me a little bit and check back.
Joel Whatley- 28-Jan-12 21:43pm
   
You're the best!!
LanFanNinja 28-Jan-12 22:38pm
   
OK recheck my solution for the latest revision.
Joel Whatley- 28-Jan-12 23:12pm
   
Alright. Thank you so much for your help, you made my week!
You are ridiculously helpful.
<3:)
LanFanNinja 28-Jan-12 23:36pm
   
You're welcome.

I guess I can assume I understood what you where asking for then?

"You are ridiculously helpful."
Well I guess being ridiculously helpful is better than being my normal plain old ridiculous! :)
LanFanNinja 29-Jan-12 1:48am
   
Just to let you know I made one small change to the if statement in clearPlaylistButton_Click event handler. I don't think it matters either way but this just seemed more ... right.

changed from
if (selectedSong != null &&
selectedSong.Name == playListBox.SelectedItem.ToString())

to
if (selectedSong != null)
Something like this should work for you. If you have any questions just ask and I will try to help you.

NOTE: I did not try to run this code so it could contain an error or two. Let me know if this is the case and I will help you figure it out.

C#
class Song
{
    internal string Name { get; private set; }
    internal string FullPath { get; private set; }
    internal string Directory { get; private set; }

    public Song(string path)
    {
        FullPath = path;
        Name = Path.GetFileNameWithoutExtension(path);
        Directory = Path.GetDirectoryName(path);
    } 
}

// * NOTE: openSongDialog is of the type OpenFileDialog *
class Form1 : Form
{
    List<Song> songs = new List<Song>();

    if (openSongDialog.ShowDialog() == DialogResult.OK)
    {
        Song song = new Song(openSongDialog.FileName);
       
        songs.Add(song);
    }
}


EDIT:
After thinking about this some more I realized you would probably be better off to use a Dictionary instead.

Using a Dictionary would allow you to index songs using the songs name from the ListBox.

Here is an example:

C#
class Form1 : Form
{
    Dictionary<string, Song> songs = new Dictionary<string, Song>();

    if (openSongDialog.ShowDialog() == DialogResult.OK)
    {
        Song song = new Song(openSongDialog.FileName);
       
        songs.Add(song.Name, song);
    }
}


Then you could do something like this to get the song selected in the ListBox.
C#
Song selectedSong = songs[songListBox.SelectedItem.ToString()];
   
v8
Comments
Sergey Alexandrovich Kryukov 27-Jan-12 22:22pm
   
Good enough, but:
1) using public is not justified; public is only needed if the types and members are to be accessible from some referencing assembly;

2) Names like Form1, listBox1 are not acceptable from the style and maintenance point of view; whatever Designer auto-generate violate (good) Microsoft naming conventions, should always be renamed to give some semantic names.

3) Three string fields are totally redundant. You could either store name and path (and compose fill name when needed), or only store full name and extract whatever needed on the fly. You could use three string properties though, but getter should be implemented based on just one fields (or two fields).

4) Private set is redundant -- you never use it, should cause warning. I was convinced that this usage is just fine, please see our comments below -- thank you.

I voted 4.

Cheers,
--SA
LanFanNinja 27-Jan-12 22:28pm
   
Thanks for the vote.

"using public is not justified"
Should I have used protected instead?

"Names like Form1, listBox1 are not acceptable"
This was only an example however I agree I should have used more meaningful names.
Sergey Alexandrovich Kryukov 27-Jan-12 22:45pm
   
Of course not protected (it is the access for ancestors only). You should use "internal". This is the same as "public" but limit access to the assembly where the types/members are declared. Do yourself a pretty good fair and review all 4 access types again. Then you will add to your arsenal "internal" and "internal protected" (instead of protected).

You should not give more access then it is really required.

The names for example? Right, you can do it, but the problem is: inquirers will not understand that it was just for example, they will likely follow your lead as the expert and might think this is acceptable. It's always better not to set a wrong model for the students. Moreover, giving good names is not harder than bad once. (After all, this is the one of the main uses of the Refactoring Engine of VS.)
Call them "MyForm", "MainForm", "listBoxSongs"; and everyone will understand it.

Thank you for attention.
--SA
LanFanNinja 27-Jan-12 22:49pm
   
Sorry my bad I meant to write internal and not protected. I have been on hiatus for three weeks and I think I must have misplaced my mind somewhere! :)

Thank you for the info and suggestions.
LanFanNinja 27-Jan-12 23:22pm
   
I have changed the properties access modifiers from public to internal as per your suggestion.

EDIT:
Also changed my variable names to be a little more meaningful as well.
LanFanNinja 27-Jan-12 22:36pm
   
#3 I agree with. I was only doing this based on the variable names the OP provided to make it easier for him.

#4 "Private set is redundant -- you never use it, should cause warning"
When declaring properties this way (Auto-Implemented) I have no choice but to add the the setter else I cannot assign to it inside of the class it is declared in and not marking it as private would allow it to be set outside of the class which is not what I wanted. So IMO there is no other way.
Sergey Alexandrovich Kryukov 27-Jan-12 22:48pm
   
Sorry, not true.

You have a choice: you can always write "string Value { get { return value; } }". This is one of the most common and practically important cases -- read-only properties. After all, I hope you agree that you should always release the code without any warnings. Failure to do so would effectively make a very useful mechanism of warnings defunct.

--SA
LanFanNinja 27-Jan-12 22:57pm
   
Certainly if I declared my properties like this

string name;
public string Name { get { return name; } }

that would be true however when using auto-implemented properties this this is not the case.

for example

public string Name { get; }
Name = "some name";

I would not be able to assign to Name as there is no setter!
EDIT: And yes as you (SAKryukov) have pointed out it would not even compile.

I learned how to implement properties this way from a text book when I learned about auto-implemented properties and I have seen many others write code the same way.

I get absolutely no warning declaring properties in this way.

I have even seen Microsoft programmers declare properties this way.
   
Please, could you check up what you say with a compiler before you post the comment?

Compare:

static string SomeProperty { get; set; } // will compile; auto-implelented property;

static string SomeProperty { get; } //will not compile!

Isn't that obvious why? With this syntax, a compiler has no chance to auto-implement it, because there is no a way to get a value, in contrast to the previous sample.

Did you finally get it?
--SA

LanFanNinja 28-Jan-12 1:23am
   
That is actually part of the point I was try to make! Auto-implemented properties must define both get and set accessors.

So when using auto-implemented properties the only way to make them immutable is to define the set accessor as private.

There is no option to simply leave the set accessor out it must be defined! Therefore I have no other choice than to set its access to private so that the property cannot be set outside of the class it is defined in.

Did you finally get it?
   
Yes, now I finally got it, sorry for the confusion and thanks for this new comment.

After your explanation I tried this code:

class PropertUser {
internal string StringProperty { get; private set; }
}

and found that it does not cause compiler warning. After some thinking, I realized that this code is functionally equivalent to

class PropertUser {
internal string StringProperty { get { return value; } }
private string value;
}

So, everything is fine with this point of your solution.

As a result of this discussion, I was convinced that I should remove this point of my criticism from my notes. (The other remains valid.)

Now, I also understand the use of this case better. Thank you very much!
--SA
LanFanNinja 28-Jan-12 2:06am
   
I understand completely and have felt the whole time that it was simply a misunderstanding. Absolutely no harm done.

I have also took your advice and made some other modifications to my code that you suggested such as renaming openFileDialog1 and listBox1 to something more meaningful and changed the access modifiers for the properties.

Please understand I have the utmost respect for you and would not argue a point unless I truly thought I was correct.

Have a good day!
Sergey Alexandrovich Kryukov 28-Jan-12 12:35pm
   
Oh, sure, needless to mention that was a decent discussion.
No matter what, truth is the first priority.

Respect,
--SA
LanFanNinja 27-Jan-12 23:03pm
   
Please have a look at this code

MSDN Code Example .

As you can see from the example code these are perfectly legal read-only auto-implemented properties.
Sergey Alexandrovich Kryukov 27-Jan-12 22:38pm
   
Also, I think OP is really confused about types and objects. I tried to clarify, please see.
--SA
   
Above, I've scratched out the my last one of my critical notes as I was convinced that it's fine.

Again, thank you very much for this useful discussion.
--SA
LanFanNinja 28-Jan-12 2:11am
   
You're welcome and thank you.
Although you create each Song class 'on the fly' (note singular, Song) you should have a collection intowichnyou add each newly created Song object - I'd call that class Songs.

C#

Public Class Songs : List<song>

So now you have a reference to the objects you are creating...

Also,your three properties should probably only be 2 - the file name and the path - with perhaps the fulfills pathname property concatenation the other properties - rather than actually storing duplicate data internally. (if it is homework y'd lose marks for that. ;)

Excuse the typing and formatting, I'm on the iPad and the popup for formatting is stuck open so I can't see what I am typing
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900