{"id":28814,"date":"2016-11-30T11:00:55","date_gmt":"2016-11-30T19:00:55","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=28814"},"modified":"2016-11-30T11:00:55","modified_gmt":"2016-11-30T19:00:55","slug":"creating-platform-specifics-in-xamarin-forms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/creating-platform-specifics-in-xamarin-forms\/","title":{"rendered":"Creating Platform-Specifics in Xamarin.Forms"},"content":{"rendered":"<p>\t\t\t\tRecently on the blog, <a href=\"https:\/\/blog.xamarin.com\/bringing-platform-specific-functionality-to-xamarin-forms-apps\/\">Pierce introduced Platform-Specifics<\/a>, which allow you to consume functionality that\u2019s only available on a specific platform without having to implementing custom renderers or effects. With the use of a simple Fluent API, you can make platform-specific tweaks from shared code in Xamarin.Forms.<\/p>\n<p>At the time of writing, Xamarin.Forms includes the following Platform-Specifics:<\/p>\n<ul>\n<li>Blur support for any <code>VisualElement<\/code> on iOS.<\/li>\n<li>A translucent navigation bar on iOS.<\/li>\n<li>The ability to set the operating mode for a soft keyboard input area on Android.<\/li>\n<li>Toolbar placement options on Windows.<\/li>\n<li>A partially collapsible <code>MasterDetailPage<\/code> navigation bar on Windows.<\/li>\n<\/ul>\n<p>In addition, vendors can create their own Platform-Specifics with Effects. An Effect provides the desired functionality, which is then exposed through a Platform-Specific. The result is an Effect that can more easily be consumed through XAML and a fluent code API.<\/p>\n<p>In this blog post, I&#8217;m going to\u00a0demonstrate how to expose an Effect through a Platform-Specific that adds a shadow to the text displayed\u00a0by a\u00a0<code>Label<\/code>. All the code can be found in a\u00a0<a href=\"https:\/\/developer.xamarin.com\/samples\/xamarin-forms\/UserInterface\/ShadowPlatformSpecific\/\" target=\"_blank\">sample application<\/a>.\u00a0However, the\u00a0blog post will focus on the implementation for the iOS platform.<\/p>\n<h2>Creating a Platform-Specific<\/h2>\n<p>The process for creating a Platform-Specific is:<\/p>\n<ol>\n<li>Implement the desired functionality as an Effect.<\/li>\n<li>Create a Platform-Specific class that will expose the Effect.<\/li>\n<li>Add an attached property to the Platform-Specific class to allow the Platform-Specific to be consumed through XAML.<\/li>\n<li>Add extension methods in the Platform-Specific class to allow it to be consumed through a fluent code API.<\/li>\n<li>Modify the Effect implementation so that the Effect is only applied if the Platform-Specific has been invoked on the same platform as the Effect.<\/li>\n<\/ol>\n<p>Each item will now be considered in turn.<\/p>\n<h2>Implement the Desired Functionality as an Effect<\/h2>\n<p>In the <a href=\"https:\/\/developer.xamarin.com\/samples\/xamarin-forms\/UserInterface\/ShadowPlatformSpecific\/\" target=\"_blank\">sample application<\/a>, the iOS project provides the <code>LabelShadowEffect<\/code> class, which adds a shadow to a <code>Label<\/code> by setting the following properties:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"Shadow effect\">Control.Layer.CornerRadius = 5;\nControl.Layer.ShadowColor = UIColor.Black.CGColor;\nControl.Layer.ShadowOffset = new CGSize(5, 5);\nControl.Layer.ShadowOpacity = 1.0f;\n<\/pre>\n<p>For more information about creating this Effect, see <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/effects\/creating\/\">Creating an Effect<\/a> and <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/effects\/passing-parameters\/attached-properties\/\">Passing Effect Parameters as Attached Properties<\/a>.<\/p>\n<h2>Create a Platform-Specific Class<\/h2>\n<p>A Platform-Specific is created as a <code>public static<\/code> class:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"Shadow class\">namespace MyCompany.Forms.PlatformConfiguration.iOS\n{\n\tusing System.Linq;\n\tusing Xamarin.Forms;\n\tusing Xamarin.Forms.PlatformConfiguration;\n\tusing FormsElement = Xamarin.Forms.Label;\n\n\tpublic static class Shadow\n\t{\n\t\tconst string EffectName = \"MyCompany.LabelShadowEffect\";\n        ...\n\n\t\tstatic void AttachEffect(FormsElement element)\n\t\t{\n\t\t\tIElementController controller = element;\n\t\t\tif (controller == null || controller.EffectIsAttached(EffectName))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telement.Effects.Add(Effect.Resolve(EffectName));\n\t\t}\n\n\t\tstatic void DetachEffect(FormsElement element)\n\t\t{\n\t\t\tIElementController controller = element;\n\t\t\tif (controller == null || !controller.EffectIsAttached(EffectName))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar toRemove = element.Effects.FirstOrDefault(e =&gt; e.ResolveId == Effect.Resolve(EffectName).ResolveId);\n\t\t\tif (toRemove != null)\n\t\t\t{\n\t\t\t\telement.Effects.Remove(toRemove);\n\t\t\t}\n\t\t}\n\t}\n}<\/pre>\n<p>The <code>AttachEffect<\/code> and <code>DetachEffect<\/code> methods will be used\u00a0to add or remove the Effect, and will be invoked by additional code that&#8217;s added to the <code>Shadow<\/code> class.<\/p>\n<h2>Add an Attached Property<\/h2>\n<p>An attached property must be added to the Platform-Specific class to allow consumption through XAML:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"Shadow class with attached property\">public static class Shadow\n{\n    ...\n\tpublic static readonly BindableProperty IsShadowedProperty = \n       BindableProperty.CreateAttached(\"IsShadowed\", typeof(bool), typeof(Shadow), false, propertyChanged: OnIsShadowedPropertyChanged);\n\n\tpublic static bool GetIsShadowed(BindableObject element)\n\t{\n\t\treturn (bool)element.GetValue(IsShadowedProperty);\n\t}\n\n\tpublic static void SetIsShadowed(BindableObject element, bool value)\n\t{\n\t\telement.SetValue(IsShadowedProperty, value);\n\t}\n\n\tstatic void OnIsShadowedPropertyChanged(BindableObject element, object oldValue, object newValue)\n\t{\n\t\tif ((bool)newValue)\n\t\t{\n\t\t\tAttachEffect(element as FormsElement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDetachEffect(element as FormsElement);\n\t\t}\n\t}\n        ...\n}<\/pre>\n<p>The <code>IsShadowed<\/code> attached property is used to add the effect to, and remove it from, the control that the <code>Shadow<\/code> class is attached to. The attached property registers the <code>OnIsShadowedPropertyChanged<\/code> method that will be executed when the value of the property changes. In turn, this method calls the <code>AttachEffect<\/code> or <code>DetachEffect<\/code>\u00a0method.<\/p>\n<h2>Add Extension Methods<\/h2>\n<p>Extension methods must be added to the Platform-Specific to allow consumption through a fluent code API:<\/p>\n<pre class=\"lang:c# decode:true\" title=\"Shadow class with extension methods\">public static class Shadow\n{\n    ...\n\tpublic static bool IsShadowed(this IPlatformElementConfiguration&lt;iOS, FormsElement&gt; config)\n\t{\n\t\treturn GetIsShadowed(config.Element);\n\t}\n\n\tpublic static IPlatformElementConfiguration&lt;iOS, FormsElement&gt; SetIsShadowed(this IPlatformElementConfiguration&lt;iOS, FormsElement&gt; config, bool value)\n\t{\n\t\tSetIsShadowed(config.Element, value);\n\t\treturn config;\n\t}\n    ...\n}<\/pre>\n<p>The <code>IsShadowed<\/code> and <code>SetIsShadowed<\/code> extension methods invoke the get and set accessors for the <code>IsShadowed<\/code> attached property, respectively. Each extension method operates on the <code>IPlatformElementConfiguration&lt;iOS, FormsElement&gt;<\/code> type, which specifies that the Platform-Specific can only be invoked on <code>Label<\/code> instances from iOS.<\/p>\n<h2>Modify the Effect Implementation<\/h2>\n<p>The Effect implementation must be modified so that it can only be invoked\u00a0from the same platform the Platform-Specific is implemented\u00a0for:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"Effect implementation\">if (((Label)Element).OnThisPlatform().IsShadowed())\n{\n\tControl.Layer.CornerRadius = 5;\n\tControl.Layer.ShadowColor = UIColor.Black.CGColor;\n\tControl.Layer.ShadowOffset = new CGSize(5, 5);\n\tControl.Layer.ShadowOpacity = 1.0f;\n}\nelse if (!((Label)Element).OnThisPlatform().IsShadowed())\n{\n\tControl.Layer.ShadowOpacity = 0;\n}<\/pre>\n<p>A shadow\u00a0is added to the <code>Label<\/code> text provided that the <code>IsShadowed<\/code> attached property is set to <code>true<\/code>, and provided that the <code>Shadow<\/code> Platform-Specific has been invoked on the same platform that the Effect is implemented for. This check is performed with the <code>OnThisPlatform<\/code> method.<\/p>\n<h2>Consuming the Platform-Specific<\/h2>\n<p>The <code>Shadow<\/code> Platform-Specific can be consumed in XAML by setting the <code>Shadow.IsShadowed<\/code> attached property to a <code>boolean<\/code> value:<\/p>\n<pre class=\"lang:xml decode:true \" title=\"Shadow platform-specific through XAML\">&lt;ContentPage xmlns:ios=\"clr-namespace:MyCompany.Forms.PlatformConfiguration.iOS\" ...&gt;\n\u00a0 ...\n\u00a0 &lt;Label Text=\"Label Shadow Effect\" ios:Shadow.IsShadowed=\"true\" ... \/&gt;\n\u00a0 ...\n&lt;\/ContentPage&gt;<\/pre>\n<p>Alternatively, the <code>Shadow<\/code> platform-specific can be consumed through the fluent code API:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"Shadow platform-specific through fluent code API\">using Xamarin.Forms.PlatformConfiguration;\nusing MyCompany.Forms.PlatformConfiguration.iOS;\n\n...\n\nshadowLabel.On&lt;iOS&gt;().SetIsShadowed(true);\n<\/pre>\n<p>This results in a shadow being applied to the text displayed by a <code>Label<\/code>:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-28817\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/screenshot.png\" alt=\"screenshot\" width=\"169\" height=\"145\" \/><\/p>\n<h2>Wrapping Up<\/h2>\n<p>The result of exposing an Effect as a Platform-Specific is that the Effect can be more easily consumed through XAML and through a fluent code API.<\/p>\n<p>For more information about consuming Platform-Specifics, see <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/user-interface\/platform-specifics\/consuming\/\">Consuming Platform-Specifics<\/a>. For more information about creating Platform-Specifics, see <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/user-interface\/platform-specifics\/creating\/\">Creating Platform-Specifics<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently on the blog, Pierce introduced Platform-Specifics, which allow you to consume functionality that\u2019s only available on a specific platform without having to implementing custom renderers or effects. With the use of a simple Fluent API, you can make platform-specific tweaks from shared code in Xamarin.Forms. At the time of writing, Xamarin.Forms includes the following [&hellip;]<\/p>\n","protected":false},"author":543,"featured_media":28817,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[6,4,16],"class_list":["post-28814","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-ios","tag-xamarin-platform","tag-xamarin-forms"],"acf":[],"blog_post_summary":"<p>Recently on the blog, Pierce introduced Platform-Specifics, which allow you to consume functionality that\u2019s only available on a specific platform without having to implementing custom renderers or effects. With the use of a simple Fluent API, you can make platform-specific tweaks from shared code in Xamarin.Forms. At the time of writing, Xamarin.Forms includes the following [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/28814","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=28814"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/28814\/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=28814"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=28814"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=28814"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}