1

I have a method that makes a list of all the Songs in the Media Library. The method has 2 aspects it either returns the SongsCollection from the MediaLibrary or returns a list of custom objects.

public static Task<object> GetSongList(bool lib = true, bool albumArt = true)
{
     MediaLibrary mediaLib = new MediaLibrary();
     var songs = mediaLib.Songs;

     if (lib)
     {
          return songs;
     }
     else
     {
          var list = new List<MusicTitle>();
          foreach (var song in songs)
          {
               list.Add(new MusicTitle()
               {
                   Artist = song.Artist.Name,
                   Title = song.Name,
                   Duration = (new DateTime(song.Duration.Ticks)).ToString("mm:ss"),
                   Album = song.Album.Name, 
                   Art = albumArt ? GetAlbumArt(song, 100) : null
               });
          }

          return list;
      }
  }

MusicTitle is the custom class with some properties. since this return 2 types of results i set the return type as object and cast the result appropriately. This holds up the UI for a bit so i need this to be async. So as the methods shows i added Task<object> to the method signature and when i do that return songs; and return lists; gives compile following compile errors.

Cannot implicitly convert type 'Microsoft.Xna.Framework.Media.SongCollection' to 'System.Threading.Tasks.Task<object>'

Cannot implicitly convert type 'System.Collections.Generic.List<KVKWindowsPhoneHelper.Core.MediaLibrary.MusicTitle>' to 'System.Threading.Tasks.Task<object>'

What should i do? I tried casting the return types to object but did not work. How can i make this method async?

EDIT

The Following code is in side the Page's OnNavigatedTo() method This throws the UnauthorizedAccessException

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    // Set Song List
    List<AlphaKeyGroup<MusicTitle>> songList = await Task.Run(() => AlphaKeyGroup<MusicTitle>.CreateGroups(ListHelper.GetSongList(false, false) as List<MusicTitle>, Thread.CurrentThread.CurrentUICulture, (MusicTitle s) => { return s.Title; }, true));
    listSongs.ItemsSource = songList;
    // Set Artist List
    List<AlphaKeyGroup<MusicArtist>> artistList = await Task.Run(() => AlphaKeyGroup<MusicArtist>.CreateGroups(ListHelper.GetArtistList(false) as List<MusicArtist>, Thread.CurrentThread.CurrentUICulture, (MusicArtist ar) => { return ar.Artist; }, true));
    listArtist.ItemsSource = artistList;
    // Set Album List
    List<AlphaKeyGroup<MusicAlbum>> albumList = await Task.Run(() => AlphaKeyGroup<MusicAlbum>.CreateGroups(ListHelper.GetAlbumList(false, true) as List<MusicAlbum>, Thread.CurrentThread.CurrentUICulture, (MusicAlbum al) => { return al.Album; }, true));
    listAlbums.ItemsSource = albumList;
    // Set Genre List
    List<AlphaKeyGroup<MusicGenre>> genreList = await Task.Run(() => AlphaKeyGroup<MusicGenre>.CreateGroups(ListHelper.GetGenreList(false) as List<MusicGenre>, Thread.CurrentThread.CurrentUICulture, (MusicGenre g) => { return g.Genre; }, true));
    listGenres.ItemsSource = genreList;
    // Set PlayList
    List<AlphaKeyGroup<MusicPlaylist>> playList = await Task.Run(() => AlphaKeyGroup<MusicPlaylist>.CreateGroups(ListHelper.GetPlayList(false) as List<MusicPlaylist>, Thread.CurrentThread.CurrentUICulture, (MusicPlaylist pl) => { return pl.Playlist; }, true));
    listPlaylist.ItemsSource = playList;

}

EDIT 2

In my Phone there are 233 songs, The exception is thrown at a method in a ListHelper class i wrote that includes all the methods to get the Songs, Albums, Playlist, Genres and Artists. The method that throws the exception is the method where i get the Album Art for the Album. This is the Method.

public static BitmapImage GetAlbumArt(Song song, int size = 100 )
{
    BitmapImage img = new BitmapImage(); // EXCEPTION IS THROWN HERE
    img.DecodePixelHeight = size;
    img.DecodePixelWidth = size;

    if (song.Album.HasArt)
    {               
         img.SetSource(song.Album.GetAlbumArt());
    }
    else
    {
         img.UriSource = new Uri("/Images/cover.png", UriKind.Relative);
    }
    return img;
  }
Kasun Kodagoda
  • 3,956
  • 5
  • 31
  • 54

1 Answers1

1

Your method isn't doing any asynchronous work, hence there is no reason to make it return a Task. Instead, if you want to keep your UI responsive, wrap it inside a Task.Run which will queue it up on a ThreadPool thread:

public static object GetSongList(bool lib = true, bool albumArt = true)
{
     MediaLibrary mediaLib = new MediaLibrary();
     var songs = mediaLib.Songs;

     if (lib)
     {
          return songs;
     }
     else
     {
          var list = new List<MusicTitle>();
          foreach (var song in songs)
          {
               list.Add(new MusicTitle()
               {
                   Artist = song.Artist.Name,
                   Title = song.Name,
                   Duration = (new DateTime(song.Duration.Ticks)).ToString("mm:ss"),
                   Album = song.Album.Name, 
                   Art = albumArt ? GetAlbumArt(song, 100) : null
               });
          }

          return list;
      }
}

And use it inside a Task.Run:

var obj = await Task.Run(() => GetSongList());

As an unrelated side note, i would advise you to see if you might be able to create a common base class / interface as a return type instead of object, for type safety.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thanks, This works. UI is not held no more. But when this runs a uNauthorizedAccessException is thrown with additional information that includes Invalid cross-thread access message.. What should i do? – Kasun Kodagoda Aug 02 '14 at 14:51
  • Hmm, are you accessing any UI elements inside this method? – Yuval Itzchakov Aug 02 '14 at 14:54
  • You cant set the `ItemSource` inside a threadpool thread. Use `Deployment.Current.Dispatcher.Invoke` and set itemsource inside it – Yuval Itzchakov Aug 02 '14 at 15:25
  • Using `Dispatcher.BeginInvoke` doesn't solve my problem. It still holds the UI till the lists are populated. – Kasun Kodagoda Aug 02 '14 at 15:52
  • Do you use it inside the `Task.Run`? Im not sure why you haven't posted that code in your original question. You have no other choice then updating your `ItemSource` inside `BeginInvoke`, since you cant access it from a background thread. How big is your list that it causes your UI to freeze? – Yuval Itzchakov Aug 02 '14 at 16:11
  • Do me a favor and add the code you're saying causes an `UnauthorizedAccessException` inside your original question. I cant understand what isn't working. – Yuval Itzchakov Aug 02 '14 at 16:17
  • How is your `OnNavigatedTo` method called? I'm assuming the exception is thrown at `listSongs.ItemsSource = songList;` – Yuval Itzchakov Aug 02 '14 at 16:30
  • OnNavigatedTo is called when the app navigates to the Page, I have updated the question with new code, indicating the place where the exception is thrown. – Kasun Kodagoda Aug 02 '14 at 16:43
  • Thats because you cant create a `BitmapImage` on a background thread. See http://stackoverflow.com/a/3034930/1870803 i suggest you run that helper method on the UI thread. – Yuval Itzchakov Aug 02 '14 at 17:33
  • 1
    Thanks a lot. I will look in to it. :) I'll comment if i need help. Thanks – Kasun Kodagoda Aug 02 '14 at 17:46