{"id":33796,"date":"2017-10-16T13:58:02","date_gmt":"2017-10-16T20:58:02","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=33796"},"modified":"2019-04-04T11:03:20","modified_gmt":"2019-04-04T18:03:20","slug":"adaptive-ui-xamarin-forms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/adaptive-ui-xamarin-forms\/","title":{"rendered":"Adaptive UI with Xamarin.Forms"},"content":{"rendered":"<p>\t\t\t\tXamarin.Forms has supported iOS, Android, and Windows for a long time. We&#8217;ve also added new platforms to keep up with the changing landscape, such as Tizen and macOS, with Linux and Windows WPF on the horizon. These platforms run on a wide variety of devices including phones, tablets, desktops, and TVs. This presents an interesting challenge for us as developers; how do we design an adaptive UI that intelligently scales and makes sense on each idiom it&#8217;s presented on?<\/p>\n<h3>Respond to Screen Size<\/h3>\n<p>By default, Xamarin.Forms Layouts and Controls are designed to be responsive and will often grow and shrink to take advantage of the available screen space. If more control is needed, the Xamarin.Forms Page class has a <code>SizeChanged<\/code> event we can use to determine the available screen-size at runtime. This event is raised when the <em>Page<\/em> is displayed, allowing us to adjust or change our Page\u2019s UI based on the <em>Width<\/em> or <em>Height<\/em> of the screen.<\/p>\n<pre class=\"EnlighterJSRAW\">public partial class MainPage : ContentPage\r\n{\r\n   public MainPage()\r\n   {\r\n      InitializeComponent();\r\n      SizeChanged += MainPageSizeChanged;\r\n   }\r\n\r\n   void MainPageSizeChanged(object sender, EventArgs e)\r\n   {\r\n      imgMonkey.WidthRequest = Math.Min(this.Width, 400);\r\n   }\r\n}\r\n\r\n<\/pre>\n<h3>Respond to Orientation<\/h3>\n<p>We can use the <code>SizeChanged<\/code> event to respond to orientation changes. <code>SizeChanged<\/code> will be raised when the screen is rotated. We can determine the orientation by comparing the <em>Page\u2019s<\/em> <em>Width<\/em> and <em>Height<\/em> properties.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"EnlighterJSRAW \">public partial MainPage : ContentPage\r\n{\r\n    public MainPage()\r\n    {\r\n        InitializeComponent();\r\n        SizeChanged += MainPageSizeChanged;\r\n    }\r\n\r\n    void MainPageSizeChanged(object sender, EventArgs e)\r\n    {\r\n        bool isPortrait = this.Height &gt; this.Width;\r\n        stackPanel.Orientation = (isPortrait ? StackOrientation.Horizontal : StackOrientation.Vertical);\r\n    }\r\n}<\/pre>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-33818\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Screen-Shot-2017-10-16-at-4.45.10-PM.jpg\" alt=\"Portrait and Landscape Orientations\" width=\"800\" \/><\/p>\n<h3><\/h3>\n<h3>Respond to Device Type<\/h3>\n<p>We often think about responsive and adaptive in terms of screen size, but we also need to respond to how the user interacts with the device. For instance, we use touch on mobile devices, and a mouse with a keyboard on a desktop PC.\nXamarin.Forms provides a static <em>Idiom<\/em> property on the <em>Device<\/em> class that we can use to check the device type. Devices are sorted into four categories: Phone, Tablet, Desktop, and TV.<\/p>\n<pre class=\"EnlighterJSRAW\">switch (Device.Idiom)\r\n{\r\n    case TargetIdiom.Desktop:\r\n    ...\r\n    case TargetIdiom.Phone:\r\n    ...\r\n    case TargetIdiom.Tablet:\r\n    ...\r\n    case TargetIdiom.TV:\r\n    ...\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>We can use <code>Device.Idiom<\/code> to change the UI we present. We have several options you can use depending on a number of changes you need to make.<\/p>\n<p>For small changes, like adjusting button sizes, we update the controls and layouts directly.<\/p>\n<pre class=\"EnlighterJSRAW\">if (Device.Idiom == TargetIdiom.Desktop)\r\n{\r\n    buttonAbout.HeightRequest = 25;\r\n    buttonAbout.WidthRequest = 40;\r\n}\r\nelse\r\n{\r\n    buttonAbout.HeightRequest = 40;\r\n    buttonAbout.WidthRequest = 70;\r\n}<\/pre>\n<p>When you need to add or remove portions of your UI, Xamarin.Forms provides a <code>ContentView<\/code> which allows us to create composite controls. We define portions of reusable UI with behavior, and then decide which composite controls to display based on the <em>Idiom<\/em>. You can think of a <code>ContentView<\/code> as a partial Xamarin.Forms Page.<\/p>\n<pre class=\"EnlighterJSRAW\">void AddColorPicker ()\r\n{\r\n    IMyColorPicker colorPicker;\r\n\r\n    if (Device.Idiom == TargetIdiom.Desktop)\r\n        colorPicker = new ColorPickerMouseView();\r\n    else\r\n        colorPicker = new ColorPickerTouchView();\r\n\r\n    colorPicker.VerticalOptions = LayoutOptions.Start;\r\n    colorPicker.ColorChanged += ColorPickerColorChanged;\r\n\r\n    mainLayout.Children.Add(colorPicker)\r\n}<\/pre>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-33821\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Screen-Shot-2017-10-16-at-4.45.10-PM-1.jpg\" alt=\"\" width=\"800\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>When your UI changes are significant between device types, you can create multiple pages. We use <code>Device.Idiom<\/code> to decide which page to display or you can even use <code>ContentViews<\/code> within these pages to avoid duplicate code.<\/p>\n<p>And If you\u2019re using MVVM, you can define a single View Model that is used for each version of your Page, further reducing repeated code!<\/p>\n<pre class=\"EnlighterJSRAW\">ContentPage page;\r\n\r\nif(Device.Idiom == TargetIdiom.Desktop)\r\n    page = new DetailsDesktopPage(new MyDetailViewModel(...));\r\nelse if(Device.Idiom == TargetIdiom.TV)\r\n    page = new DetailsTVPage(new MyDetailViewModel(...));\r\nelse \/\/Phone or Tablet\r\n    page = new DetailsMobilePage(new MyDetailViewModel(...));\r\n\r\nNavigation.PushAsync(page);<\/pre>\n<p>&nbsp;<\/p>\n<p>If you want to learn more about adaptive UI design, you can find excellent documentation <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/user-interface\/layouts\/device-orientation\/\">here<\/a> and <a href=\"https:\/\/developer.xamarin.com\/api\/type\/Xamarin.Forms.ContentView\/\">here<\/a>.<\/p>\n<p>Head to the <a href=\"https:\/\/university.xamarin.com\">Xamarin University website<\/a> for more great Xamarin training, including free Self-Guided courses!<\/p>\n<p><a href=\"https:\/\/forums.xamarin.com\/105067\/creating-adaptive-uis-with-xamarin-forms\/p1?new=1\">Discuss this post on the forums.<\/a>\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Xamarin.Forms has supported iOS, Android, and Windows for a long time. We&#8217;ve also added new platforms to keep up with the changing landscape, such as Tizen and macOS, with Linux and Windows WPF on the horizon. These platforms run on a wide variety of devices including phones, tablets, desktops, and TVs. This presents an interesting [&hellip;]<\/p>\n","protected":false},"author":559,"featured_media":43803,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2,367],"tags":[16],"class_list":["post-33796","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","category-xamarin-forms","tag-xamarin-forms"],"acf":[],"blog_post_summary":"<p>Xamarin.Forms has supported iOS, Android, and Windows for a long time. We&#8217;ve also added new platforms to keep up with the changing landscape, such as Tizen and macOS, with Linux and Windows WPF on the horizon. These platforms run on a wide variety of devices including phones, tablets, desktops, and TVs. This presents an interesting [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/33796","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\/559"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=33796"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/33796\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/43803"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=33796"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=33796"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=33796"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}