DotNetCurry Logo

Building a Sound Cloud Music Player in Windows 10

Posted by: Shoban Kumar , on 9/28/2015, in Category Windows Store Apps
Views: 5068
Abstract: Build a Sound Cloud Music Player app in Windows 10. Also use some of the unique new platform features in Windows 10 to develop a single app which runs on multiple devices

Microsoft's vision of apps that can be written once and will run on a wide variety of devices, is finally a reality with the release of Windows 10. The single, unified Windows core enables one app to run on every Windows device – be it a phone, a tablet, a laptop, a PC, or the Xbox console. And very soon the same apps will also run on the upcoming devices being added to the Windows family, including IoT devices like the Raspberry Pi 2, Microsoft HoloLens and the Surface Hub.

 

In a previous article Build a Windows 10 Universal App – First Look I wrote an introduction about why Windows 10 is different from previous versions of Windows and how you, as a developer, can take advantages of new features in Windows 10 and write better code.

In this article, we will start developing a better Music Player app and we will also try to use some of the unique new platform features in Windows 10 to develop a single app which runs on multiple devices.

This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Windows 10 Music Player - First Look

We are going to develop a Music Player in Windows 10 which will use Sound Cloud API to play our Likes, Playlists etc. Here is a screenshot of how the player will look in Desktop, Tablet and Phone.

win10-music-player

If you do not use Sound Cloud (there is no reason not to use Sound Cloud if you love Music), register for a new account and start Liking tracks and create playlists for your favourite tracks. We will be using these for our App.

Register a new Client Application by visiting the Sound Cloud Developer Website (https://developers.soundcloud.com/). Keep a note of the Client Id Value.

Project

Fire up Visual Studio 2015 and create a New Blank App (Universal Windows) under Templates > Visual C# > Windows. Right click the new project and click on Manage NuGet Packages. Search for Newtonsoft.json library and install the package. You may also want to add some images that are included with the Source Code of this article. We will be using those to design the App.

If you want to learn more about new Controls in Windows 10, I would recommend downloading Developer Samples from GitHub (https://github.com/Microsoft/Windows-universal-samples) and playing around with the sample projects.

Shell

One of the new controls introduced in Windows 10 is the SplitView. It is a very simple but useful control which can be used to build quick Navigation related features in our App. This control is also very flexible in terms of customization and presents unique experiences across different devices.

We will change the new project to use a Custom Page that will hold the SplitView control and this page will act as the shell for the whole app giving it a Single Page App like experience.

- Add a Blank Page by Right Clicking the Project > Add > New Item. Name this new Page as AppShell.xaml

- Add the following xaml code to AppShell.xaml

<Page.Resources>
        <DataTemplate x:Key="NavMenuItemTemplate" x:DataType="local:NavMenuItem" >
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="48" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <FontIcon x:Name="Glyph" FontSize="16" Glyph="{x:Bind SymbolAsChar}" VerticalAlignment="Center" HorizontalAlignment="Center" ToolTipService.ToolTip="{x:Bind Label}"/>
                <TextBlock x:Name="Text" Grid.Column="1" Text="{x:Bind Label}" />
            </Grid>
        </DataTemplate>
    </Page.Resources>

    <Grid Background="#273140">
        <!-- Adaptive triggers -->
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="720" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="RootSplitView.DisplayMode" Value="CompactInline"/>
                        <Setter Target="RootSplitView.IsPaneOpen" Value="True"/>
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="RootSplitView.DisplayMode" Value="Overlay"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <!-- Top-level navigation menu + app content -->
        <SplitView x:Name="RootSplitView"
                   DisplayMode="Inline"
                   OpenPaneLength="256"
                   IsTabStop="False"
                   Background="#273140"
                   >
            <SplitView.Pane>
                <Grid Background="#273140">
                    <!-- A custom ListView to display the items in the pane. -->
                    <controls:NavMenuListView x:Name="NavMenuList" Background="#273140"
                                          TabIndex="3"
                                          Margin="0,48,0,0"
                                          ItemContainerStyle="{StaticResource NavMenuItemContainerStyle}"
                                          ItemTemplate="{StaticResource NavMenuItemTemplate}"
                                          ItemInvoked="NavMenuList_ItemInvoked" >
                    </controls:NavMenuListView>
                </Grid>

            </SplitView.Pane>

            <Frame x:Name="frame" x:FieldModifier="Public">
                <Frame.ContentTransitions>
                    <TransitionCollection>
                        <NavigationThemeTransition>
                            <NavigationThemeTransition.DefaultNavigationTransitionInfo>
                                <EntranceNavigationTransitionInfo/>
                            </NavigationThemeTransition.DefaultNavigationTransitionInfo>
                        </NavigationThemeTransition>
                    </TransitionCollection>
                </Frame.ContentTransitions>
            </Frame>
        </SplitView>

        <ToggleButton x:Name="TogglePaneButton"
                      Style="{StaticResource SplitViewTogglePaneButtonStyle}"
                      IsChecked="{Binding IsPaneOpen, ElementName=RootSplitView, Mode=TwoWay}"
                      AutomationProperties.Name="Menu"
                      ToolTipService.ToolTip="Menu"/>

        <MediaElement x:FieldModifier="Public"  AudioCategory="BackgroundCapableMedia"  x:Name="mPlayer" RequestedTheme="Default" CompositeMode="MinBlend"/>
    </Grid>

In the above code, we have added the following:

1. SplitView control (RootSplitView) which has a custom Navigation control (NavMenuList) in its Pane and a Frame (frame) which will hold our Pages.

2. ToggleButton (TogglePaneButton) which will be the Hamburger menu button.

3. Data template for binding items for the custom Navigation Control.

4. Common MediaElement control (mPlayer) that will be used to play the music.

We also use some custom style templates which we will add shortly to our project.

- Add a new folder named Controls and add a class file in it and name it NavMenuItem.cs . Add the following code to the newly created file.

/// <summary>
/// Data to represent an item in the nav menu.
/// </summary>
public class NavMenuItem
{
    public string Label { get; set; }
    public Symbol Symbol { get; set; }
    public char SymbolAsChar
    {
        get
        {
            return (char)this.Symbol;
        }
    }

    public Type DestPage { get; set; }
    public object Arguments { get; set; }
}

The above class represents a Menu Item which holds the Item title, Icon and destination page.

- Add the following code to AppShell.xaml.cs

// Declare the top level nav items
private List<NavMenuItem> navlist = new List<NavMenuItem>(
    new[]
    {
        new NavMenuItem()
        {
            Symbol = Symbol.Play,
            Label ="Now Playing",
            DestPage = typeof(NowPlaying)
        },
        new NavMenuItem()
        {
            Symbol = Symbol.Emoji2,
            Label = "Likes",
            DestPage = typeof(Likes)
        },
        new NavMenuItem()
        {
            Symbol = Symbol.List,
            Label = "Playlists",
            DestPage = typeof(MainPage)
        },
        new NavMenuItem()
        {
            Symbol = Symbol.Contact,
            Label = "Me",
            DestPage = typeof(MainPage)
        }

    });

public static AppShell Current = null;

/// <summary>
/// Initializes a new instance of the AppShell, sets the static 'Current' reference,
/// adds callbacks for Back requests and changes in the SplitView's DisplayMode, and
/// provide the nav menu list with the data to display.
/// </summary>
public AppShell()
{
    this.InitializeComponent();

    this.Loaded += (sender, args) =>
    {
        Current = this;

    };
    NavMenuList.ItemsSource = navlist;
}


public Frame AppFrame { get { return this.frame; } }


/// <summary>
/// Navigate to the Page for the selected <paramref name="listViewItem"/>.
/// </summary>
/// <param name="sender"></param>
/// <param name="listViewItem"></param>
private void NavMenuList_ItemInvoked(object sender, ListViewItem listViewItem)
{
    var item = (NavMenuItem)((NavMenuListView)sender).ItemFromContainer(listViewItem);

    if (item != null)
    {
        if (item.DestPage != null &&
            item.DestPage != this.AppFrame.CurrentSourcePageType)
        {
            this.AppFrame.Navigate(item.DestPage, item.Arguments);
        }
    }
}



private void Page_Loaded(object sender, RoutedEventArgs e)
{
    ((Page)sender).Focus(FocusState.Programmatic);
    ((Page)sender).Loaded -= Page_Loaded;
}

}

In the above code, we do the following

1. We create a new List of objects of NavMenuItem class and populate it with required menu options and suitable icons.

2. We also add an Item Invoked event handler to handle click events and navigate to subpages.

- If you notice, we are using some custom styles for our controls and we will use a common styles page to hold these style templates. Add a new folder to the Project named Styles and add a new Resource Dictionary file and name it Styles.xaml. Add the following code to the newly created file (code truncated for brevity. Check the source code)

<Style x:Key="SplitViewTogglePaneButtonStyle" TargetType="ToggleButton">
        <Setter Property="FontSize" Value="20" />
        <Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
        ...
        <Setter Property="UseSystemFocusVisuals" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <Grid Background="{TemplateBinding Background}" x:Name="LayoutRoot">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Checked"/>
                                <VisualState x:Name="CheckedPointerOver">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="CheckedPressed">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="CheckedDisabled">
                                    <Storyboard>
                                        ...
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ContentPresenter x:Name="ContentPresenter"
                                          Content="{TemplateBinding Content}"
                                          Margin="{TemplateBinding Padding}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          AutomationProperties.AccessibilityView="Raw" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="PageTitleTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource BodyTextBlockStyle}">
    ...
    </Style>

    <Style x:Key="NavMenuItemContainerStyle" TargetType="ListViewItem">
        <Setter Property="MinWidth" Value="{StaticResource SplitViewCompactPaneThemeLength}"/>
        <Setter Property="Height" Value="48"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="Background" Value="#273140"/>
        <Setter Property="Foreground" Value="#7f96a3"/>
        <Setter Property="Template">
            <Setter.Value>
               ...
            </Setter.Value>
        </Setter>
    </Style>

- Change the OnLaunched event handler code in App.xaml.cs to the following to use our custom AppShell and not the default Frame

protected override void OnLaunched(LaunchActivatedEventArgs e)
{

    #if DEBUG
    if (System.Diagnostics.Debugger.IsAttached)
    {
        this.DebugSettings.EnableFrameRateCounter = true;
    }
    #endif
    AppShell shell = Window.Current.Content as AppShell;
    
    
    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (shell == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        shell = new AppShell();

        // Set the default language
        shell.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

        shell.AppFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        
    }

    // Place the frame in the current Window
    Window.Current.Content = shell;

    if (shell.AppFrame.Content == null)
    {
        // When the navigation stack isn't restored, navigate to the first page
        // suppressing the initial entrance animation.
        shell.AppFrame.Navigate(typeof(MainPage), e.Arguments, new Windows.UI.Xaml.Media.Animation.SuppressNavigationTransitionInfo());
    }
    // Ensure the current window is active
    Window.Current.Activate();
}

- Add the following code to App.xaml.cs. These variables will hold values that will be used in other pages.

public static string SoundCloudClientId = "<INSERT YOUR CLIENT ID";
public static int SCUserID = 0;
public static string SoundCloudLink = "http://api.soundcloud.com/";

public static string SoundCloudAPIUsers = "users/";
public static List<SoundCloudTrack> nowPlaying = new List<SoundCloudTrack>();
public static int nowplayingTrackId = 0;

 

Sound Cloud API

If you are new to Sound Cloud API, I would recommend going through the HTTP API Reference on this page (https://developers.soundcloud.com/docs/api/reference). SoundCloud objects (sounds, users etc) can be accessed by using HTTP methods GET, POST, PUT and DELETE and the response is a JSON string.

We will be using Json.NET which is a high performance, popular JSON framework to serialize and deserialize Sound Cloud objects from Sound Cloud website and custom Sound Cloud objects in our App. I used the website http://json2csharp.com/ to quickly create classes (and modified for our app) for Sound Cloud objects that we will be adding in our next step.

- Add the following new class files to our project

public class SoundCloudTrack
{
    public int id { get; set; }
    public string created_at { get; set; }
    public int user_id { get; set; }
    public int duration { get; set; }
    ...
    public SoundCloudUser user { get; set; }
    ...
    public SoundCloudCreatedWith created_with { get; set; }
    public string attachments_uri { get; set; }
}
public class SoundCloudCreatedWith
{
    public int id { get; set; }
    public string name { get; set; }
    public string uri { get; set; }
    public string permalink_url { get; set; }
}
public class SoundCloudUser
{
    public int id { get; set; }
    public string permalink { get; set; }
    public string username { get; set; }
    public string uri { get; set; }
    public string permalink_url { get; set; }
    public string avatar_url { get; set; }
}

Landing Page

We will use MainPage.xaml as the landing page for our app which will handle login and navigate to subsequent pages.

- Add the following code to MainPage.xaml .

<Grid Background="#edf3fb">
    <StackPanel VerticalAlignment="Center">
        <ProgressRing x:Name="loginProgress" HorizontalAlignment="Center" IsActive="True" Foreground="#273140" />
        <TextBlock HorizontalAlignment="Center" Text="Please wait..." Style="{StaticResource SubtitleTextBlockStyle}" Foreground="#273140"/>
    </StackPanel>       
</Grid>

In the above code we add a ProgressRing control and TextBlock to show the progress.

- Add the following code to MainPage.xaml.cs

using Newtonsoft.Json;
...

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace MusicPlayer
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        this.Loaded += MainPage_Loaded;
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        //Get User
        GetUserDetails();
    }

    private async void GetUserDetails()
    {
        try
        {
            string responseText = await GetjsonStream(App.SoundCloudLink + App.SoundCloudAPIUsers + "shoban-kumar" + ".json?client_id=" + App.SoundCloudClientId);
            SoundCloudUser user = JsonConvert.DeserializeObject<SoundCloudUser>(responseText);

            App.SCUserID = user.id;

            //Get Likes 
            GetLikes();
        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }
    }

    private async void GetLikes()
    {

        try
        {

            string responseText = await GetjsonStream(App.SoundCloudLink + App.SoundCloudAPIUsers + App.SCUserID + "/favorites.json?client_id=" + App.SoundCloudClientId);
            List<SoundCloudTrack> likes = JsonConvert.DeserializeObject<List<SoundCloudTrack>>(responseText);
            App.nowPlaying = likes;

            loginProgress.IsActive = false;

            AppShell shell = Window.Current.Content as AppShell;
            shell.AppFrame.Navigate(typeof(NowPlaying));
        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }

    }

    public async Task<string> GetjsonStream(string url) //Function to read from given url
    {
        HttpClient client = new HttpClient();
        HttpResponseMessage response = await client.GetAsync(url);
        HttpResponseMessage v = new HttpResponseMessage();
        return await response.Content.ReadAsStringAsync();
    }
}
}

In the above code, we are doing the following:

o Fetch user details by sending a request to SoundCloud API and fetch user Id which will be used by future requests to Sound Cloud. The user name (shoban-kumar) is used in the sample code

string responseText = await GetjsonStream(App.SoundCloudLink + App.SoundCloudAPIUsers + "shoban-kumar" + ".json?client_id=" + App.SoundCloudClientId);

This username is unique and you can get your user name from navigating to your profile page and checking the url for the value.

profile

- Once the user id is retrieved, we GET the list of tracks that are liked by you using GetLikes method.

- Once the list of tracks are retrieved, we populate the common variable nowPlaying which is a list of SoundCloudTrack objects.

- We also navigate to a new page called NowPlaying which we will add in our next step.

Now Playing

Add a new Blank Page to our project and name it NowPlaying.xaml. Add the following xaml code to the page.

<Grid Background="#edf3fb">
        <!-- Adaptive triggers -->
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="720" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
        ...
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
           ...
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Image Source="Assets/BG.png" Stretch="Fill"/>

        <Grid VerticalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <!-- Black ring around Album art with opacity .10 -->
            <Ellipse Height="525" Grid.Row="0" Width="525" Fill="Black" Opacity=".10" x:Name="AlbumartRing"/>

            <!-- Album Art -->
            <Ellipse Height="500" Grid.Row="0" Width="500" x:Name="Albumart" >
                <Ellipse.Fill>
                    <ImageBrush x:Name="albumrtImage" ImageSource="Assets\Albumart.png" Stretch="UniformToFill" />
                </Ellipse.Fill>
                <Ellipse.Clip >
                    <RectangleGeometry Rect="0,0,500,250" x:Name="AlbumartClip"/>
                </Ellipse.Clip>
            </Ellipse>

            <!-- Bottom part of album art with controls -->
            <Ellipse Height="500" Grid.Row="0" Width="500" x:Name="AlbumartContainerDown" Fill="#34353c" >
                <Ellipse.Clip >
                    <RectangleGeometry x:Name="AlbumartContainerDownClip" Rect="0,250,500,500" />
                </Ellipse.Clip>
            </Ellipse>

            <TextBlock x:Name="txtSongTitle" Grid.Row="0" HorizontalAlignment="Center"   Text="Song Title " FontSize="25" Foreground="White" Style="{StaticResource HeaderTextBlockStyle}" TextTrimming="WordEllipsis"  />
            <TextBlock x:Name="txtAlbumTitle"  Grid.Row="0" Text="Label " HorizontalAlignment="Center" FontWeight="Light" FontSize="20"  Foreground="#9799a5" Style="{StaticResource BodyTextBlockStyle}" TextTrimming="WordEllipsis"/>
            <StackPanel Margin="0,350,0,0" x:Name="ControlContainer" Grid.Row="0" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Center">
                <Button x:Name="btnPrev" Background="Transparent" Height="80" Width="80" Click="btnPrev_Click">
                    <Image  Source="Assets/Prev.png"  Stretch="Uniform"/>
                </Button>
                <Button x:Name="btnPlay" Background="Transparent" Height="120" Width="120" Click="btnPlay_Click">
                    <Image Source="Assets/Play.png"  Stretch="Uniform"/>
                </Button>
                <Button x:Name="btnNext" Background="Transparent" Height="80" Width="80" Click="btnNext_Click">
                    <Image  Source="Assets/Next.png" Stretch="Uniform"/>
                </Button>
            </StackPanel>

        </Grid>


    </Grid>

In the code, we add the following controls to build our player user Interface.

  • Three Ellipse controls
  • ImageBrush control which will hold a default album art picture
  • Two TextBlocks to display Song Title and User name
  • Three buttons which will act as Play, Next and Previous (We will be adding more buttons later)
  • and two VisualStateTriggers for different widths. These triggers change few properties of controls when the width of the App is changed and this helps us give different experience for different devices without writing any additional C# code.

Add the following code to NowPlaying.xaml.cs

namespace MusicPlayer
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class NowPlaying : Page
    {
        AppShell shell = Window.Current.Content as AppShell;
        public NowPlaying()
        {
            this.InitializeComponent();
            this.Loaded += NowPlaying_Loaded;
        }

        private void NowPlaying_Loaded(object sender, RoutedEventArgs e)
        {
            if (App.nowPlaying.Count > 0)
            {
                SoundCloudTrack currentTrack = App.nowPlaying[App.nowplayingTrackId];
                LoadTrack(currentTrack);
            }

        }

        private async void LoadTrack(SoundCloudTrack currentTrack)
        {
            try
            {
                //Stop player, set new stream uri and play track
                shell.mPlayer.Stop();
                Uri streamUri = new Uri(currentTrack.stream_url + "?client_id=" + App.SoundCloudClientId);
                shell.mPlayer.Source = streamUri;
                shell.mPlayer.Play();

                //Change album art
                string albumartImage = Convert.ToString(currentTrack.artwork_url);
                if (string.IsNullOrWhiteSpace(albumartImage))
                {
                    albumartImage = @"ms-appx:///Assets\Albumart.png";

                }
                else
                {
                    albumartImage = albumartImage.Replace("-large", "-t500x500");
                }

                albumrtImage.ImageSource = new BitmapImage(new Uri(albumartImage));

                //Change Title and User name
                txtSongTitle.Text = currentTrack.title;
                txtAlbumTitle.Text = Convert.ToString(currentTrack.user.username);

            }
            catch (Exception ex)
            {
                MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
                await showMessgae.ShowAsync();
            }
        }

        private void btnPlay_Click(object sender, RoutedEventArgs e)
        {
            
        }

        private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            App.nowplayingTrackId += 1;
            if (App.nowplayingTrackId >= App.nowPlaying.Count)
            {
                App.nowplayingTrackId = 0;
            }

            SoundCloudTrack currentTrack = App.nowPlaying[App.nowplayingTrackId];
            LoadTrack(currentTrack);

        }

        private void btnPrev_Click(object sender, RoutedEventArgs e)
        {
            App.nowplayingTrackId -= 1;
            if (App.nowplayingTrackId < 0)
            {
                App.nowplayingTrackId = App.nowPlaying.Count - 1;
            }

            SoundCloudTrack currentTrack = App.nowPlaying[App.nowplayingTrackId];
            LoadTrack(currentTrack);
        }

    }
}

In the above code, once the page is loaded, we start playing the first Track in the nowPlaying list and set the Title, User name and album art based on the Track properties that we fetched from Sound Cloud.

Press F5 to debug the app and if there are no compile errors, you should login without errors and our app should start playing the first track from your Likes list in Sound Cloud.

sound-cloud-player

Play around with the width of the app or deploy the app on different devices like Tablets and Phone to see how Adaptive Triggers change control appearances based on how you use the app.

You will notice that minimizing our app will stop the music and pressing volume controls will not show the default media controls. We will change this behavior and add more features in future articles.

Check out the 2nd part of this article - Universal Windows 10 Platform (UWP) Sound Cloud Music App for Windows Phone & Raspberry Pi2

Conclusion

With the introduction of new controls and features like Adaptive Triggers, the Universal Windows app experience has improved significantly for Windows 10 in comparison to the Windows 8.1 experience. You now have to build just one Universal Windows app that will run on all Windows 10 devices.

Download the entire source code of this article (Github)

  • Please Share this article if you think it was worth reading. Thanks!
Further Reading - Articles You May Like!


Page copy protected against web site content infringement by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
comments powered by Disqus