{"id":261,"date":"2017-10-22T17:58:21","date_gmt":"2017-10-22T22:58:21","guid":{"rendered":"http:\/\/blog.cubicleninja.com\/?p=261"},"modified":"2017-10-22T17:58:21","modified_gmt":"2017-10-22T22:58:21","slug":"creating-a-cross-platform-soundboard-with-xamarin-forms-part-2","status":"publish","type":"post","link":"https:\/\/blog.cubicleninja.com\/?p=261","title":{"rendered":"Creating a Cross-Platform Soundboard with Xamarin Forms \u2013 Part 2"},"content":{"rendered":"<p>Continuing from the <a href=\"http:\/\/blog.cubicleninja.com\/?p=240\">post for part 1 of creating the soundboard<\/a>, we&#8217;ll add in a few more MP3 files to each platform, an interface that will retrieve a listing of all the available MP3 files, and a listview that will display buttons to play each of the MP3 files.<\/p>\n<p>To add more MP3 files, we&#8217;ll use the same process we did in part 1 to add the first MP3, click on\u00a0<strong>Add\u00a0<\/strong>-&gt;\u00a0<strong>Existing Item<\/strong> and then select the MP3 files from your computer in each of the following locations:<\/p>\n<ul>\n<li>For Android, right click on the Assets folder<\/li>\n<li>For iOS, right click on the Resource folder<\/li>\n<li>For UWP, right click on the Assets folder<\/li>\n<\/ul>\n<p>Before we create the interface for listing out the MP3 files, we&#8217;re going to add a very simple class to the PCL named\u00a0<strong>MessageCenterObject<\/strong>, this will be used as an easy method to communicate back and forth between our platform specific code and the PCL, in case the loading of the MP3 files takes an extended period of time.\u00a0 Right click on the\u00a0<strong>Portable<\/strong> project and select\u00a0<strong>Add\u00a0<\/strong>-&gt;\u00a0<strong>Class<\/strong>, then name the file\u00a0<strong>MessageCenterObject<\/strong>.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_13.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-270\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_13-237x300.png\" alt=\"Soundboard_13\" width=\"237\" height=\"300\" \/><\/a><\/p>\n<p>We&#8217;ll adjust it to be a public class and add two simple string properties.<\/p>\n<pre class=\"lang:c# decode:true \">namespace XamarinSoundboard\n{\n    public class MessageCenterObject\n    {\n        public string result { get; set; }\n        public string message { get; set; }\n    }\n}<\/pre>\n<p>This object will be used for passing messages back and forth between the platform-specific code and the PCL.\u00a0 To do this, we will be leveraging the <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/application-fundamentals\/messaging-center\/\">Messaging Center<\/a>.<\/p>\n<p>Now that we have a collection of MP3 files added to the project(s)\u00a0 for our soundboard, we&#8217;ll create the interface that will allow each platform to load in a list of available MP3 files. Right click on the <b>Portable<\/b> project at the top of your solution and then select <b>Add<\/b> -&gt; <b>Add New Item<\/b>. From the list of templates, select <b>Visual C#<\/b> -&gt; <b>Interface<\/b> and give it a name of <b>IListMP3Files.cs<\/b>.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_9.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-262\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_9-226x300.png\" alt=\"Soundboard_9\" width=\"226\" height=\"300\" \/><\/a><\/p>\n<p>Add an accessor for the interface to make it public, and give it a two parameterless methods named <strong>GetMP3Filenames<\/strong>\u00a0that returns a List of strings and a void method named <strong>LoadMP3Filenames<\/strong>.<\/p>\n<pre class=\"lang:c# decode:true\">namespace XamarinSoundboard\n{\n    public interface IListMP3Files\n    {\n        \/\/ This is a void method that will load the files on each platform as a background call\n        void LoadMP3Filenames();\n\n        \/\/ This method will return the loaded list of file names\n        List&lt;String&gt; GetMP3Filenames();\n    }\n}<\/pre>\n<p>That&#8217;s all we need for the interface file, so we&#8217;ll move on to the interface implementation in each of the platforms.\u00a0 As before, we&#8217;ll begin with the Android implementation and allow the code explain itself.\u00a0 Right-click on the Android project in the solution and select\u00a0<strong>Add<\/strong>\u00a0-&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the name\u00a0<strong>ListMP3FilesImplementation<\/strong><strong>.<\/strong><\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_10.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-265\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_10-265x300.png\" alt=\"Soundboard_10\" width=\"265\" height=\"300\" \/><\/a><\/p>\n<p>We want to add the public accessor to the new class, wire it up to the interface we created in the PCL (<b>IListMP3Files<\/b>), and then implement the functionality to load a list of MP3 file names from the assets folder.<\/p>\n<pre class=\"lang:c# decode:true\">using System.Collections.Generic;\nusing Xamarin.Forms;\nusing XamarinSoundboard.Droid;\nusing System.Linq;\n\n[assembly: Dependency(typeof(ListMP3FilesImplementation))]\nnamespace XamarinSoundboard.Droid\n{\n    public class ListMP3FilesImplementation : IListMP3Files\n    {\n        \/\/ This will hold the listing of the mp3 files we find in the assets folder\n        private List&lt;string&gt; mp3Files { get; set; }\n\n        \/\/ An instance of our MessageCenterObject to use for passing messages between the PCL and the platform code\n        private MessageCenterObject messageCenterObject;\n\n        public ListMP3FilesImplementation()\n        {\n            \/\/ When our class is instantiated, set up the message center object\n            messageCenterObject = new MessageCenterObject();\n            \/\/ This tells the application that our class is listening for a message named \"Load MP3 Files\" acting against\n            \/\/ a MessageCenterObject type of sender\n            MessagingCenter.Subscribe&lt;MessageCenterObject&gt;(messageCenterObject, \"Load MP3 Files\", (sender) =&gt;\n            {\n                \/\/ When we see this message fired in the application, start the process of loading the MP3 files\n                LoadMP3Filenames();\n            });\n        }\n\n        public List&lt;string&gt; GetMP3Filenames()\n        {\n            \/\/ Simply return the list of MP3 files\n            return mp3Files;\n        }\n\n        public void LoadMP3Filenames()\n        {\n            \/\/ Retrieve a listing of all the files in the assets\n            var files = global::Android.App.Application.Context.Assets.List(\"\");\n\n            \/\/ using Linq to retrieve a cleansed list of the names of the files that \n            \/\/ ended with .mp3\n            mp3Files = files.Where(x =&gt; x.EndsWith(\"mp3\"))\n                            .Select(x =&gt; x.Replace(\".mp3\", \"\"))\n                            .ToList&lt;string&gt;();\n            \n            \/\/ Once all of the files have been loaded, fire off a message stating that the files have been loaded\n            MessagingCenter.Send&lt;MessageCenterObject&gt;(messageCenterObject, \"MP3 Files Loaded\");\n        }\n    }\n}<\/pre>\n<p>We&#8217;ll move on to the iOS implementation,\u00a0right click on the iOS project then choose\u00a0<strong>Add<\/strong>\u00a0-&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the same name of\u00a0<strong>ListMP3FilesImplementation<\/strong>.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_11.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-267\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_11-253x300.png\" alt=\"Soundboard_11\" width=\"253\" height=\"300\" \/><\/a><\/p>\n<p>As before, give it a public accessor and wire it into the\u00a0<strong>IListMP3Files<\/strong> interface.<\/p>\n<pre class=\"lang:c# decode:true\">using Xamarin.Forms;\nusing XamarinSoundboard;\nusing XamarinSoundboard.iOS;\nusing System.Collections.Generic;\nusing Foundation;\nusing System.Linq;\n\n[assembly: Dependency(typeof(ListMP3FilesImplementation))]\nnamespace XamarinSoundboard.iOS\n{\n    public class ListMP3FilesImplementation : IListMP3Files\n    {\n        \/\/ This will hold the listing of the mp3 files we find in the assets folder\n        private List&lt;string&gt; mp3Files { get; set; }\n\n        \/\/ An instance of our MessageCenterObject to use for passing messages between the PCL and the platform code\n        private MessageCenterObject messageCenterObject;\n\n        public ListMP3FilesImplementation()\n        {\n            \/\/ When our class is instantiated, set up the message center object\n            messageCenterObject = new MessageCenterObject();\n            \/\/ This tells the application that our class is listening for a message named \"Load MP3 Files\" acting against\n            \/\/ a MessageCenterObject type of sender\n            MessagingCenter.Subscribe&lt;MessageCenterObject&gt;(messageCenterObject, \"Load MP3 Files\", (sender) =&gt;\n            {\n                \/\/ When we see this message fired in the application, start the process of loading the MP3 files\n                LoadMP3Filenames();\n            });\n        }\n\n        public List&lt;string&gt; GetMP3Filenames()\n        {\n            \/\/ Simply return the list of MP3 files\n            return mp3Files;\n        }\n\n        public void LoadMP3Filenames()\n        {\n            \/\/ Many iOS methods require an NSError out parameter\n            NSError error;\n\n            \/\/ Retrieve a listing of all the files in the assets in the main bundle's resource path\n            var files = NSFileManager.DefaultManager.GetDirectoryContent(NSBundle.MainBundle.ResourcePath, out error);\n\n            \/\/ using Linq to retrieve a cleansed list of the names of the files that \n            \/\/ ended with .mp3\n            mp3Files = files.Where(x =&gt; x.EndsWith(\"mp3\"))\n                            .Select(x =&gt; x.Replace(\".mp3\", \"\"))\n                            .ToList&lt;string&gt;();\n\n            \/\/ Once all of the files have been loaded, fire off a message stating that the files have been loaded\n            MessagingCenter.Send&lt;MessageCenterObject&gt;(messageCenterObject, \"MP3 Files Loaded\");\n        }\n    }\n}<\/pre>\n<p>Last, and quite possibly least, we&#8217;ll create the implementation class for UWP\u00a0 &#8211; right click on the Universal Windows project then choose\u00a0<strong>Add<\/strong>\u00a0-&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the same name of\u00a0<strong>ListMP3FilesImplementation.<\/strong><\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_12.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-268\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_12-240x300.png\" alt=\"Soundboard_12\" width=\"240\" height=\"300\" \/><\/a><\/p>\n<p>It probably won&#8217;t be surprising that I&#8217;m going to just provide the code for the class and not explain it beyond the comments&#8230;<\/p>\n<pre class=\"lang:c# decode:true\">using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Windows.Storage;\nusing Windows.Storage.FileProperties;\nusing Windows.Storage.Search;\nusing Xamarin.Forms;\nusing XamarinSoundboard.UWP;\n\n[assembly: Dependency(typeof(ListMP3FilesImplementation))]\nnamespace XamarinSoundboard.UWP\n{\n    public class ListMP3FilesImplementation : IListMP3Files\n    {\n        \/\/ This will hold the listing of the mp3 files we find in the assets folder\n        private List&lt;string&gt; mp3Files { get; set; }\n\n        \/\/ An instance of our MessageCenterObject to use for passing messages between the PCL and the platform code\n        private MessageCenterObject messageCenterObject;\n\n        public ListMP3FilesImplementation()\n        {\n            \/\/ When our class is instantiated, set up the message center object\n            messageCenterObject = new MessageCenterObject();\n            \/\/ This tells the application that our class is listening for a message named \"Load MP3 Files\" acting against\n            \/\/ a MessageCenterObject type of sender\n            MessagingCenter.Subscribe&lt;MessageCenterObject&gt;(messageCenterObject, \"Load MP3 Files\", (sender) =&gt;\n            {\n                \/\/ When we see this message fired in the application, start the process of loading the MP3 files\n                LoadMP3Filenames();\n            });\n        }\n\n        public List&lt;string&gt; GetMP3Filenames()\n        {\n            \/\/ Simply return the list of MP3 files\n            return mp3Files;\n        }\n\n        public async void LoadMP3Filenames()\n        {\n            try\n            {\n                \/\/ With UWP, there are a lot of different ways to parse \/ process files and directories\n                \/\/ In this case we're building out a QueryOptions object that will retrieve only the files that end with .mp3\n                var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, new string[] { \".mp3\" });\n\n                \/\/ When we query files in UWP, by default it will return the entire file object (including the binary data)\n                \/\/ As you can imagine, that ends up being very slow if you are processing a lot of files, we instead modify the query\n                \/\/ to only return the Name of all of the files\n                queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.None, new string[] { \"Name\" });\n\n                \/\/ Retrieve the StorageFolder object representing the Assets folder for the UWP application\n                StorageFolder folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(\"Assets\");\n\n                \/\/ Execute the query we built at the top of the method to \n                var queryResults = folder.CreateFileQueryWithOptions(queryOptions);\n                var files = await queryResults.GetFilesAsync();\n\n                \/\/ using Linq to retrieve a cleansed list of the names of the files that ended with .mp3\n                mp3Files = files.Select(x =&gt; x.Name.Replace(\".mp3\", \"\")).ToList&lt;string&gt;();\n\n                \/\/ Once all of the files have been loaded, fire off a message stating that the files have been loaded\n                MessagingCenter.Send&lt;MessageCenterObject&gt;(messageCenterObject, \"MP3 Files Loaded\");\n            }\n            catch (Exception err)\n            {\n                string tmp = err.Message;\n            }\n        }\n    }\n}\n<\/pre>\n<p>Now that we have the implementation in place for each of our platforms, all that is left to do is connect the two interfaces together so we can have a soundboard.<\/p>\n<p>Heading back up to the PCL, we need to open up MainPage.xaml to adjust it to list out all of our MP3 files.\u00a0 We accomplish this by making a very simple ListView that just contains a single button per row, the XAML will look like this<\/p>\n<pre class=\"lang:xhtml decode:true \">&lt;?xml version=\"1.0\" encoding=\"utf-8\" ?&gt;\n&lt;ContentPage xmlns=\"http:\/\/xamarin.com\/schemas\/2014\/forms\"\n             xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2009\/xaml\"\n             xmlns:local=\"clr-namespace:XamarinSoundboard\"\n             x:Class=\"XamarinSoundboard.MainPage\"&gt;\n    &lt;!-- Create a ListView named listView --&gt;\n    &lt;ListView x:Name=\"listView\" RowHeight=\"75\"&gt;\n        &lt;!-- We need to define an item template to determine how we want each cell to look  --&gt;\n        &lt;ListView.ItemTemplate&gt;\n            &lt;DataTemplate&gt;\n                &lt;ViewCell&gt;\n                    &lt;!-- our cells will consist of a simple black button with white text that displays the name of the MP3 file and fires our OnClick event --&gt;\n                    &lt;Button Text=\"{Binding .}\" BackgroundColor=\"Black\" TextColor=\"White\" HorizontalOptions=\"FillAndExpand\" VerticalOptions=\"FillAndExpand\" Clicked=\"SoundButton_Clicked\" \/&gt;\n                &lt;\/ViewCell&gt;\n            &lt;\/DataTemplate&gt;\n        &lt;\/ListView.ItemTemplate&gt;\n    &lt;\/ListView&gt;\n&lt;\/ContentPage&gt;<\/pre>\n<p>The code-behind for the xaml file (MainPage.xaml.cs) is a bit more complex this time around.\u00a0 We need to set-up the MessagingCenter to work with the messages going back and forth with the ListMP3Files implementations.\u00a0 The other new piece is using the\u00a0<strong>ItemsSource<\/strong> property of the ListView we created in the XAML file above to tell it what it needs to load \/ render for the items.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\nusing System.Collections.Generic;\nusing Xamarin.Forms;\n\nnamespace XamarinSoundboard\n{\n    public partial class MainPage : ContentPage\n    {\n        \/\/ An instance of our ListMP3Files interface\n        private IListMP3Files listMP3Files;\n\n        \/\/ An instance of our MessageCenterObject to use for passing messages between the PCL and the platform code\n        private MessageCenterObject messageCenterObject;\n\n        \/\/ The list of MP3 files\n        List&lt;string&gt; mp3FileList;\n\n        public MainPage()\n        {\n            InitializeComponent();\n            \n            \/\/ retrieve an instance of our platform specific interface\n            listMP3Files = DependencyService.Get&lt;IListMP3Files&gt;();\n\n            \/\/ set up the message center object\n            messageCenterObject = new MessageCenterObject();\n\n            \/\/ This tells the application that our class is listening for a message named \"MP3 Files Loaded\" acting against\n            \/\/ a MessageCenterObject type of sender\n            MessagingCenter.Subscribe&lt;MessageCenterObject&gt;(messageCenterObject, \"MP3 Files Loaded\", (sender) =&gt;\n            {\n                \/\/ Once we receive the message, we need to update our list of mp3s from the implementation\n                mp3FileList = listMP3Files.GetMP3Filenames();\n                \/\/ set the ItemsSource of the listview to be our mp3 file list\n                listView.ItemsSource = mp3FileList;\n            });\n\n            \/\/ Fire off a message to start Loading the MP3 files\n            MessagingCenter.Send&lt;MessageCenterObject&gt;(messageCenterObject, \"Load MP3 Files\");\n        }\n\n        private void SoundButton_Clicked(object sender, EventArgs e)\n        {\n            \/\/ Make use of the dependency service to call the platform specific implementation of our SoundPlayer\n            \/\/ we send through the name of the of the file from the button\n            DependencyService.Get&lt;ISoundPlayer&gt;().PlaySoundFile($\"{((Button)sender).Text}.mp3\");\n        }\n    }\n}\n<\/pre>\n<p>Once that code is in place, you can run the application on any of the three platforms and you will be presented with a scrollable list of the names of the MP3 files you previously added to your solution, and clicking (or tapping) on any of those names will play the sound files for you.<\/p>\n<p>That wraps up the Cross-Platform Soundboard with Xamarin.Forms.\u00a0 I glossed over some items that are very important to understand if you want to continue with Xamarin.Forms development (<a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/application-fundamentals\/messaging-center\/\">Messaging Center<\/a>\u00a0and <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/application-fundamentals\/dependency-service\/introduction\/\">Dependency Service<\/a>), so I recommend taking some time to dig into those and ensure they make sense (at least a little bit).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuing from the post for part 1 of creating the soundboard, we&#8217;ll add in a few more MP3 files to each platform, an interface that will retrieve a listing of all the available MP3 files, and a listview that will display buttons to play each of the MP3 files. To add more MP3 files, we&#8217;ll &hellip; <a href=\"https:\/\/blog.cubicleninja.com\/?p=261\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Creating a Cross-Platform Soundboard with Xamarin Forms \u2013 Part 2&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,3,4,6,9,10,12],"tags":[14,30,40,42],"class_list":["post-261","post","type-post","status-publish","format-standard","hentry","category-net","category-android","category-csharp","category-ios","category-mobile","category-uwp","category-xamarin","tag-android","tag-ios","tag-uwp","tag-xamarin"],"_links":{"self":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/261","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=261"}],"version-history":[{"count":0,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/261\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}