0

I am currently working on my own MP3 player and want to add drag & drop functionality to be able to drag & drop your music either a file at a time or a whole directory at a time. I have the View of my ListView set to details, and am using the following code:

void Playlist_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(DataFormats.FileDrop))
        e.Effect = DragDropEffects.Copy;  
}

void Playlist_DragDrop(object sender, DragEventArgs e)
{
    Playlist.Items.Clear();
    string[] songs = (string[])e.Data.GetData(DataFormats.FileDrop, false);

    Parallel.ForEach(songs, s =>
    {
        if (File.Exists(s))
        {
            if (string.Compare(Path.GetExtension(s), ".mp3", true) == 0)
            {
                MessageBox.Show(s);
                AddFileToListview(s);
            }
        }
        else if (Directory.Exists(s))
        {
            DirectoryInfo di = new DirectoryInfo(s);
            FileInfo[] files = di.GetFiles("*.mp3");
            foreach (FileInfo file in files)
            {
                AddFileToListview(file.FullName);
                MessageBox.Show(file.FullName);
            }
        }
    });
}
private void AddFileToListview(string fullFilePath)
{
    if (!File.Exists(fullFilePath))
        return;
    string song = Path.GetFileName(fullFilePath);
    string directory = Path.GetDirectoryName(fullFilePath);

    if (directory.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)))
        directory = directory.Substring(0, directory.Length - 1); //hack off the trailing \

    ListViewItem itm = Playlist.Items.Add(song);
    itm.SubItems.Add(directory); //second column = path
}

I have the MessageBox in there to make sure my code is being hit and tit always shows me the right data but nothing shows in the ListView. Any ideas what I'm doing wrong?

@ClearLogic: You were right I forgot to define columns in the ListView, thanks. Now I have another problem, I can drag multiple directories into the ListView with no problems, but when I try to add multiple single MP3's I get a cross-thread exception on the line

 ListViewItem itm = Playlist.Items.Add(song);
PsychoCoder
  • 10,570
  • 12
  • 44
  • 60

1 Answers1

0

Thanks to @ClearLogic for all their help in solving this issue, I thought I'd share my code in case someone else is having some issues as well.

private void Playlist_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(DataFormats.FileDrop))
        e.Effect = DragDropEffects.Copy;  
}

private void Playlist_DragDrop(object sender, DragEventArgs e)
{
    //get the file names
    string[] songs = (string[])e.Data.GetData(DataFormats.FileDrop, false);

    //we're using a Parallel.ForEach loop because if a directory is selected it can contain n number of items, this is to help prevent a bottleneck.
    Parallel.ForEach(songs, song =>
    {
        //make sure the file exists
        if (File.Exists(song))
        {
            //if it's an mp3 file then call AddFileToListview
            if (string.Compare(Path.GetExtension(song), ".mp3", true) == 0)
            {
                AddFileToListview(song);
            }
        }
        //A HA! It's a directory not a single file
        else if (Directory.Exists(song))
        {
            //get the directory information
            DirectoryInfo di = new DirectoryInfo(song);

            //get all the mp3 files (will add WMA in the future)
            FileInfo[] files = di.GetFiles("*.mp3");

            //here we use a parallel loop to loop through every mp3 in the
            //directory provided
            Parallel.ForEach(files, file =>
            {
                AddFileToListview(file.FullName);
            });
        }
    });
}

private void AddFileToListview(string fullFilePath)
{
    double nanoseconds;
    string totalTime = string.Empty;

    //First things first, does the file even exist, if not then exit
    if (!File.Exists(fullFilePath))
        return;

    //get the song name
    string song = Path.GetFileName(fullFilePath);

    //get the directory
    string directory = Path.GetDirectoryName(fullFilePath);

    //hack off the trailing \
    if (directory.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)))
        directory = directory.Substring(0, directory.Length - 1); 

    //now we use the WindowsAPICodePack.Shell to start calculating the songs time
    ShellFile shell = ShellFile.FromFilePath(fullFilePath);

    //get the length is nanoseconds
    double.TryParse(shell.Properties.System.Media.Duration.Value.ToString(), out nanoseconds);

    //first make sure we have a value greater than zero
    if (nanoseconds > 0)
    {
        // double milliseconds = nanoseconds * 0.000001;
        TimeSpan time = TimeSpan.FromSeconds(Utilities.ConvertToMilliseconds(nanoseconds) / 1000);
        totalTime = time.ToString(@"m\:ss");
    }

    //build oour song data
    ListViewItem item = new ListViewItem();
    item.Text = song;
    item.SubItems.Add(totalTime);

    //now my first run at this gave me a cross-thread exception when trying to add multiple single mp3's
    //but I could add all the whole directories I wanted, o that is why we are now using BeginINvoke to access the ListView
    if (Playlist.InvokeRequired)
        Playlist.BeginInvoke(new MethodInvoker(() => Playlist.Items.Add(item)));
    else
        Playlist.Items.Add(item);            
}

This code uses the WindowsAPICodePack to calculate the time of each song.

PsychoCoder
  • 10,570
  • 12
  • 44
  • 60