{"id":24081,"date":"2016-01-27T11:23:02","date_gmt":"2016-01-27T19:23:02","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=24081"},"modified":"2019-04-04T09:07:29","modified_gmt":"2019-04-04T16:07:29","slug":"simplifying-events-with-commanding","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/simplifying-events-with-commanding\/","title":{"rendered":"Simplifying Events with Commanding"},"content":{"rendered":"<p>\t\t\t\tUtilizing data binding in Xamarin.Forms applications can greatly simplify app development by automatically synchronizing an app&#8217;s data to its user interface with very little work. Previously, we <a href=\"https:\/\/blog.xamarin.com\/introduction-to-data-binding\/\" target=\"_blank\">took a look at setting up your project to begin data binding<\/a> and then saw it in action in an app. We then explored <a href=\"https:\/\/blog.xamarin.com\/advanced-data-binding-for-ios-android-and-windows\/\" target=\"_blank\">more advanced data binding scenarios<\/a> where values are formatted and converted as they are passed between <em>source<\/em> and <em>target<\/em> by the binding engine.<\/p>\n<p>In this blog post, I\u2019m going to explore a Xamarin.Forms feature called <em>commanding<\/em>, that allows data bindings to make method calls directly to a ViewModel.<\/p>\n<h2>Introduction to Commanding<\/h2>\n<p>The traditional approach for executing a method in response to an interaction with the UI is to call the method from a <code>Clicked<\/code> event handler of a <code>Button<\/code> or a <code>Tapped<\/code> event handler of a <code>TapGestureRecognizer<\/code>. However, with <em>commanding<\/em>, data bindings can make method calls directly to a ViewModel from the following classes:<\/p>\n<ul>\n<li><code>Button<\/code><\/li>\n<li><code>MenuItem<\/code><\/li>\n<li><code>ToolbarItem<\/code><\/li>\n<li><code>SearchBar<\/code><\/li>\n<li><code>TextCell<\/code><\/li>\n<li><code>ImageCell<\/code><\/li>\n<li><code>ListView<\/code><\/li>\n<li><code>TapGestureRecognizer<\/code><\/li>\n<\/ul>\n<p>To support commanding, two public properties are defined on the majority of these classes:<\/p>\n<ul>\n<li><code>Command<\/code>, of type <code>System.Windows.Input.ICommand<\/code>.<\/li>\n<li><code>CommandParameter<\/code>, of type<code> object<\/code>.<\/li>\n<\/ul>\n<h2>Implementing a Command<\/h2>\n<p>In order to implement <em>commanding<\/em>, a ViewModel should define one or more properties of type <code>ICommand<\/code>. The <code>ICommand<\/code> interface defines two methods and one event:<\/p>\n<pre class=\"lang:c# decode:true\" title=\"ICommand\">public interface ICommand\n{\n   void Execute(object arg);\n   bool CanExecute(object arg)\n   event EventHandler CanExecuteChanged; \n} \n<\/pre>\n<p>The <code>Command<\/code> and <code>Command&lt;T&gt;<\/code> classes provided by Xamarin.Forms implement the <code>ICommand<\/code> interface, where <code>T<\/code> is the type of the arguments to <code>Execute<\/code> and <code>CanExecute<\/code>. As well as implementing the <code>ICommand<\/code> interface, these classes also include a <code>ChangeCanExecute<\/code> method, which causes the <code>Command<\/code> object to fire the <code>CanExecuteChanged<\/code> event.<\/p>\n<p>Within a ViewModel, there should be an object of type <code>Command<\/code> or <code>Command&lt;T&gt;<\/code> for each public property in the ViewModel of type <code>ICommand<\/code>. The <code>Command<\/code> or <code>Command&lt;T&gt;<\/code> constructor requires an <code>Action<\/code> callback object, that is called when the <code>Button<\/code> calls the <code>ICommand.Execute<\/code> method. The <code>CanExecute<\/code> method is an optional constructor parameter, and takes the form of a <code>Func<\/code> that returns a <code>bool<\/code>.<\/p>\n<p>The following code example shows a\u00a0<a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>\u00a0command implementation\u00a0that&#8217;s used to calculate a square root:<\/p>\n<pre class=\"lang:c# decode:true\" title=\"DemoViewModel class\">public class DemoViewModel : INotifyPropertyChanged\n{\n\tpublic int Number { get; set; }\n\tpublic double SquareRootResult { get; private set; }\n\tpublic ICommand SquareRootCommand { get; private set; }\n    ...\n\n\tpublic DemoViewModel ()\n\t{\n\t\tNumber = 25;\n\t\tSquareRootCommand = new Command (CalculateSquareRoot);\n        ...\n\t}\n\n\tvoid CalculateSquareRoot ()\n\t{\n\t\tSquareRootResult = Math.Sqrt (Number);\n\t\tOnPropertyChanged (\"SquareRootResult\");\t\t\t\n\t}\n    ...\n}<\/pre>\n<p>The <code>SquareRootCommand<\/code> is data bound to the\u00a0<code>Command<\/code> property of a <code>Button<\/code>, as shown in the following code example from the <a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>:<\/p>\n<pre class=\"lang:xml decode:true\" title=\"HomePage class\">&lt;Label Text=\"Demo 1 - Command\" FontAttributes=\"Bold\" \/&gt;\n&lt;StackLayout Orientation=\"Horizontal\"&gt;\n\t&lt;Label Text=\"Enter number:\" \/&gt;\n\t&lt;Entry Text=\"{Binding Number, Mode=TwoWay}\" WidthRequest=\"50\" \/&gt;\n&lt;\/StackLayout&gt;\n&lt;Button Text=\"Calculate Square Root\" Command=\"{Binding SquareRootCommand}\" HorizontalOptions=\"Center\" \/&gt;\n&lt;StackLayout Orientation=\"Horizontal\"&gt;\n\t&lt;Label Text=\"Square root =\" \/&gt;\n\t&lt;Label Text=\"{Binding SquareRootResult}\" \/&gt;\n&lt;\/StackLayout&gt;<\/pre>\n<p>When the\u00a0<code>Button<\/code>\u00a0is clicked, it calls the\u00a0<code>ICommand.Execute<\/code>\u00a0method of the object bound to its\u00a0<code>Command<\/code>\u00a0property. Therefore, the\u00a0<code>CalculateSquareRoot<\/code>\u00a0method is called, with the value of the <code>Number<\/code> property being used in the calculation. The square root of this value is calculated, and the\u00a0<code>Label<\/code>\u00a0that binds to the\u00a0<code>SquareRootResult<\/code>\u00a0property is updated with the result.<\/p>\n<p>The following screenshots show the result of the <code>SquareRootCommand<\/code> being executed:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-24158\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/commanding-demo1.png\" alt=\"commanding-demo1\" width=\"1280\" height=\"153\" \/><\/p>\n<h2>Passing a Parameter to a Command<\/h2>\n<p>A parameter can be passed to an <code>ICommand.Execute<\/code> method by using the <code>Command&lt;T&gt;<\/code> class to instantiate the command.\u00a0The following code example shows a\u00a0<a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>\u00a0command implementation\u00a0that&#8217;s used to calculate a square root of a value passed into the command as a parameter:<\/p>\n<pre class=\"lang:c# decode:true\" title=\"DemoViewModel class\">public\u00a0class\u00a0DemoViewModel\u00a0:\u00a0INotifyPropertyChanged\n{\n\u00a0\u00a0\u00a0\u00a0public\u00a0double\u00a0SquareRootWithParameterResult\u00a0{\u00a0get;\u00a0private\u00a0set;\u00a0}\n\u00a0\u00a0\u00a0\u00a0public\u00a0ICommand\u00a0SquareRootWithParameterCommand\u00a0{\u00a0get;\u00a0private\u00a0set;\u00a0}\n    ...\n\n\u00a0\u00a0\u00a0\u00a0public\u00a0DemoViewModel\u00a0()\n\u00a0\u00a0\u00a0\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SquareRootWithParameterCommand\u00a0=\u00a0new\u00a0Command&lt;string&gt;\u00a0(CalculateSquareRoot);\n        ...\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0void\u00a0CalculateSquareRoot\u00a0(string\u00a0value)\n\u00a0\u00a0\u00a0\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0double\u00a0num\u00a0=\u00a0Convert.ToDouble\u00a0(value);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SquareRootWithParameterResult\u00a0=\u00a0Math.Sqrt\u00a0(num);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OnPropertyChanged\u00a0(\"SquareRootWithParameterResult\");\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0\u00a0}\n    ...\n}<\/pre>\n<p>The <code>SquareRootWithParameterCommand<\/code> is data bound to the <code>Command<\/code> property of a <code>Button<\/code>, as shown in the following code example from the\u00a0<a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>:<\/p>\n<pre class=\"lang:xml decode:true\" title=\"HomePage class\">&lt;Label Text=\"Demo 2 - Command with Parameter\" FontAttributes=\"Bold\" \/&gt;\n&lt;StackLayout Orientation=\"Horizontal\"&gt;\n\t&lt;Label Text=\"Enter number:\" \/&gt;\n\t&lt;Entry x:Name=\"entry\" Text=\"100\" WidthRequest=\"50\" \/&gt;\n&lt;\/StackLayout&gt;\n&lt;Button Text=\"Calculate Square Root\" \n        Command=\"{Binding SquareRootWithParameterCommand}\" \n        CommandParameter=\"{Binding Source={x:Reference entry}, Path=Text}\" HorizontalOptions=\"Center\" \/&gt;\n&lt;StackLayout Orientation=\"Horizontal\"&gt;\n\t&lt;Label Text=\"Square root =\" \/&gt;\n\t&lt;Label Text=\"{Binding SquareRootWithParameterResult}\" \/&gt;\n&lt;\/StackLayout&gt;<\/pre>\n<p>As well as the <code>Button<\/code> binding its <code>Command<\/code> property to the <code>SquareRootWithParameterCommand<\/code>, it also passes the <code>Text<\/code> property value of the <code>Entry<\/code> to the <code>SquareRootWithParameterCommand<\/code> through its <code>CommandParameter<\/code> property.<\/p>\n<p>When the <code>Button<\/code> is clicked, it calls the <code>ICommand.Execute<\/code> method of the object bound to its <code>Command<\/code> property. The argument to the <code>Execute<\/code> method is the object set to the <code>CommandParameter<\/code> property of the <code>Button<\/code>. Therefore, when the <code>Button<\/code> is clicked, the <code>CalculateSquareRoot<\/code> method is called, with the value that the <code>CommandParameter<\/code> of the <code>Button<\/code> binds to being passed as a parameter to the method. The square root of this value is calculated, and the <code>Label<\/code> that binds to the <code>SquareRootWithParameterResult<\/code> property is updated with the result.<\/p>\n<p>The following screenshots show the result of the <code>SquareRootWithParameterCommand<\/code> being executed:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-24159\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/commanding-demo2.png\" alt=\"commanding-demo2\" width=\"1280\" height=\"153\" \/><\/p>\n<h2>Calling an Asynchronous Method<\/h2>\n<p>An asynchronous method can be invoked by a command by using the\u00a0<code>async<\/code>\u00a0and\u00a0<code>await<\/code>\u00a0keywords when specifying the command&#8217;s\u00a0<code>Action<\/code>\u00a0callback. This indicates that the callback is a\u00a0<code>Task<\/code>\u00a0and should be awaited. The following code example shows a\u00a0<a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>\u00a0command implementation\u00a0that simulates a file downloading through an asynchronous method:<\/p>\n<pre class=\"lang:c# decode:true\" title=\"DemoViewModel class\">public\u00a0class\u00a0DemoViewModel\u00a0:\u00a0INotifyPropertyChanged\n{\n    ...\n\u00a0\u00a0\u00a0\u00a0bool\u00a0canDownload\u00a0=\u00a0true;\n\u00a0\u00a0\u00a0\u00a0string\u00a0simulatedDownloadResult;\n\n\u00a0\u00a0\u00a0\u00a0public\u00a0string\u00a0SimulatedDownloadResult\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0get\u00a0{\u00a0return\u00a0simulatedDownloadResult;\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0private\u00a0set\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if\u00a0(simulatedDownloadResult\u00a0!=\u00a0value)\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0simulatedDownloadResult\u00a0=\u00a0value;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OnPropertyChanged\u00a0(\"SimulatedDownloadResult\");\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0public\u00a0ICommand\u00a0SimulateDownloadCommand\u00a0{\u00a0get;\u00a0private\u00a0set;\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0public\u00a0DemoViewModel\u00a0()\n\u00a0\u00a0\u00a0\u00a0{\n        ...\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SimulateDownloadCommand\u00a0=\u00a0\n            new\u00a0Command\u00a0(async\u00a0()\u00a0=&gt;\u00a0await\u00a0SimulateDownloadAsync\u00a0(),\u00a0()\u00a0=&gt;\u00a0canDownload);\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0async\u00a0Task\u00a0SimulateDownloadAsync\u00a0()\n\u00a0\u00a0\u00a0\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0CanInitiateNewDownload\u00a0(false);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SimulatedDownloadResult\u00a0=\u00a0string.Empty;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0await\u00a0Task.Run\u00a0(()\u00a0=&gt;\u00a0SimulateDownload\u00a0());\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SimulatedDownloadResult\u00a0=\u00a0\"Simulated\u00a0download\u00a0complete\";\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0CanInitiateNewDownload\u00a0(true);\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0void\u00a0CanInitiateNewDownload\u00a0(bool\u00a0value)\n\u00a0\u00a0\u00a0\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0canDownload\u00a0=\u00a0value;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0((Command)SimulateDownloadCommand).ChangeCanExecute\u00a0();\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0void\u00a0SimulateDownload\u00a0()\n\u00a0\u00a0\u00a0\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0Simulate\u00a0a\u00a05\u00a0second\u00a0pause\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var\u00a0endTime\u00a0=\u00a0DateTime.Now.AddSeconds\u00a0(5);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0while\u00a0(true)\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if\u00a0(DateTime.Now\u00a0&gt;=\u00a0endTime)\u00a0{\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0}\n    ...\n}<\/pre>\n<p>The\u00a0<code>SimulateDownloadCommand<\/code>\u00a0is data bound to the\u00a0<code>Command<\/code>\u00a0property of a\u00a0<code>Button<\/code>, as shown in the following code example from the\u00a0<a href=\"https:\/\/github.com\/davidbritch\/xamarin-forms\/tree\/master\/Commanding\" target=\"_blank\">sample application<\/a>:<\/p>\n<pre class=\"lang:xml decode:true\" title=\"HomePage class\">&lt;Label Text=\"Demo 3 - Async Command with CanExecute\" FontAttributes=\"Bold\" \/&gt;\n&lt;Button Text=\"Simulate 5 Second Download\" HorizontalOptions=\"Center\"\n        Command=\"{Binding SimulateDownloadCommand}\" \/&gt;\n&lt;StackLayout Orientation=\"Horizontal\"&gt;\n\t&lt;Label Text=\"Result: \" \/&gt;\n\t&lt;Label Text=\"{Binding SimulatedDownloadResult}\" \/&gt;\n&lt;\/StackLayout&gt;<\/pre>\n<p>When the <code>Button<\/code> is clicked the <code>SimulateDownloadAsync<\/code> method is invoked and the <code>Button<\/code> is disabled and re-enabled once the five second simulation has completed. This is achieved by specifying a <code>ICommand.CanExecute<\/code> method in the <code>SimulateDownloadCommand<\/code> constructor. The <code>Button<\/code> calls <code>CanExecute<\/code> when its <code>Command<\/code> property is first set. If <code>CanExecute<\/code> returns <code>false<\/code>, the <code>Button<\/code> disables itself and doesn\u2019t generate <code>Execute<\/code> calls. The <code>Button<\/code> also handles the <code>ICommand.CanExecuteChanged<\/code> event. Therefore, whenever the ViewModel fires the <code>CanExecuteChanged<\/code> event by calling the <code>Command.CanExecuteChanged<\/code> method, the <code>Button<\/code> calls <code>CanExecute<\/code> again to determine if it should be enabled.<\/p>\n<p>The following screenshots demonstrate the disabled <code>Button<\/code>, while the simulated download asynchronously\u00a0executes:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-24160\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/commanding-demo3.png\" alt=\"commanding-demo3\" width=\"1280\" height=\"103\" \/><\/p>\n<p>Note that because the simulated download is executed on a background thread, it\u2019s still possible to interact with the UI while the simulated download is executing.<\/p>\n<h2>Wrapping Up<\/h2>\n<p><em>Commanding<\/em> allows data bindings to make method calls directly to a ViewModel and is supported by eight Xamarin.Forms classes. To implement commanding, a ViewModel defines one or more properties of type <code>ICommand<\/code>, which are then connected to the <code>Command<\/code> properties of UI controls through data binding. Within the ViewModel, there should then be an object of type <code>Command<\/code> or <code>Command&lt;T&gt;<\/code> for each public property in the ViewModel of type <code>ICommand<\/code>.<\/p>\n<p>For more information about asynchronous programming, see our <a href=\"https:\/\/docs.microsoft.com\/xamarin\/cross-platform\/platform\/async\/\" target=\"_blank\">Async Support Overview<\/a>. For more information about data binding, see <a href=\"http:\/\/developer.xamarin.com\/guides\/xamarin-forms\/user-interface\/xaml-basics\/data_binding_basics\/\" target=\"_blank\">Data Binding Basics<\/a>. For more information about data binding and MVVM, see <a href=\"http:\/\/developer.xamarin.com\/guides\/xamarin-forms\/user-interface\/xaml-basics\/data_bindings_to_mvvm\/\" target=\"_blank\">From Data Bindings to MVVM<\/a>. <a href=\"https:\/\/university.xamarin.com\/\" target=\"_blank\">Xamarin University<\/a> also provides a class on data binding in Xamarin.Forms.\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Utilizing data binding in Xamarin.Forms applications can greatly simplify app development by automatically synchronizing an app&#8217;s data to its user interface with very little work. Previously, we took a look at setting up your project to begin data binding and then saw it in action in an app. We then explored more advanced data binding [&hellip;]<\/p>\n","protected":false},"author":543,"featured_media":24159,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[4,16],"class_list":["post-24081","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-xamarin-platform","tag-xamarin-forms"],"acf":[],"blog_post_summary":"<p>Utilizing data binding in Xamarin.Forms applications can greatly simplify app development by automatically synchronizing an app&#8217;s data to its user interface with very little work. Previously, we took a look at setting up your project to begin data binding and then saw it in action in an app. We then explored more advanced data binding [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/24081","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/543"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=24081"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/24081\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=24081"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=24081"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=24081"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}