{"id":47485,"date":"2020-07-15T08:01:06","date_gmt":"2020-07-15T15:01:06","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/xamarin\/?p=47485"},"modified":"2020-09-15T11:17:41","modified_gmt":"2020-09-15T18:17:41","slug":"c-sharp-markup-for-xamarin-forms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/c-sharp-markup-for-xamarin-forms\/","title":{"rendered":"Introducing C# Markup for Xamarin.Forms"},"content":{"rendered":"<p><em>This is a guest blog by <a href=\"https:\/\/twitter.com\/vincenth_net\">Vincent Hoogendoorn<\/a>. Vincent is a hands-on .NET architect, Xamarin veteran, <a href=\"https:\/\/github.com\/VincentH-Net\/CSharpForMarkup\">CSharpForMarkup<\/a> author, <a href=\"https:\/\/github.com\/xamarin\/Xamarin.Forms\/pull\/8342\">contributor of the C# Markup feature<\/a> in Xamarin.Forms and co-founder of the <a href=\"https:\/\/www.meetup.com\/nl-NL\/Dutch-Mobile-NET-Developers-Group\/\">Dutch Mobile .NET Developers meetup<\/a>. Vincent works as Architect &amp; Lead Mobile at <a href=\"https:\/\/anywhere365.io\/\">Anywhere365<\/a>.<\/em><\/p>\n<h2>C# Markup<\/h2>\n<p>Xamarin.Forms 4.6 introduced <strong>C# Markup<\/strong>, a set of fluent helpers and classes that aim to make UI development in C# a <em>joy<\/em>.<\/p>\n<p>C# Markup helps developers write concise <em>declarative<\/em> UI markup and cleanly separate it from UI logic, all in C#. Developers get to enjoy C#&#8217;s first-class IDE support when writing markup. A single language for markup and logic reduces friction, markup scattering and cognitive load; there is less or no need for language bridging mechanisms like separate converters, styles, resource dictionaries, behaviours, triggers and markup extensions.<\/p>\n<h2>Example<\/h2>\n<p>Let&#8217;s introduce the main features of C# Markup by building this Twitter search page:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/xamarin\/wp-content\/uploads\/sites\/44\/2020\/07\/final.png\" alt=\"Twitter Search Example\" width=\"374\" height=\"669\" class=\"aligncenter size-full wp-image-47487\" srcset=\"https:\/\/devblogs.microsoft.com\/xamarin\/wp-content\/uploads\/sites\/44\/2020\/07\/final.png 374w, https:\/\/devblogs.microsoft.com\/xamarin\/wp-content\/uploads\/sites\/44\/2020\/07\/final-168x300.png 168w\" sizes=\"(max-width: 374px) 100vw, 374px\" \/><\/p>\n<p>The full source of this example can be found <a href=\"https:\/\/github.com\/VincentH-Net\/CSharpForMarkup\/tree\/formsexample\/CSharpMarkupIntro\">here<\/a>.<\/p>\n<h2>Build Top-Down with Hot Reload<\/h2>\n<p>C# Markup makes it easy to write markup using a top-down approach &#8211; so it reads like a story, filling in details while you progress. This short video shows the process from start to finish in 2.5 minutes (using <a href=\"https:\/\/github.com\/sthewissen\/Xamarin.Forms.DebugRainbows\">DebugRainbows<\/a> and <a href=\"https:\/\/www.livesharp.net\/\">LiveSharp<\/a>):<\/p>\n<p><iframe width=\"560\" height=\"315\" src=\"https:\/\/www.youtube-nocookie.com\/embed\/9YwWRvKqTpM\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<p>Note that this video is unedited and realtime; it was recorded in one go by replaying git commits from the command line.<\/p>\n<h2>Write the Page &#8211; Like a Story<\/h2>\n<p>At the highest level the page contains a header, search results and a footer. So, if we structure the markup top-down &#8211; to make it read like a story &#8211; the initial markup could be:<\/p>\n<p><strong>SearchPage.cs<\/strong><\/p>\n<pre><code class=\"csharp\">using Xamarin.Forms.Markup;\n\npublic partial class SearchPage\n{\n    void Build() =&gt; Content = \n        new StackLayout { Children = {\n            Header,\n            SearchResults,\n            Footer\n        }};\n\n    StackLayout Header =&gt; new StackLayout { };\n\n    CollectionView SearchResults =&gt; new CollectionView { };\n\n    Label Footer =&gt; new Label { };\n}\n<\/code><\/pre>\n<p>The <code>void Build() =&gt; Content =<\/code> pattern is a convention that lets you use <a href=\"https:\/\/www.livesharp.net\/\">LiveSharp<\/a> for stateful hot reload of C# Markup. If you don&#8217;t plan on using LiveSharp, omit the <code>Build()<\/code> method and put the <code>Content<\/code> assignment in the page constructor.<\/p>\n<p>For now C# Markup is an experimental feature. So we need to <a href=\"https:\/\/docs.microsoft.com\/xamarin\/xamarin-forms\/internals\/experimental-flags#enable-flags-in-your-app-class\">set a flag<\/a> to enable the feature:<\/p>\n<p><strong>App.cs<\/strong><\/p>\n<pre><code class=\"csharp\">Device.SetFlags(new string[]{ \"Markup_Experimental\" });\n<\/code><\/pre>\n<p>Next, let&#8217;s build out the page components. It is easy as 1-2-3 (and 4 for adding animation):<\/p>\n<h2>1 Build the Header &#8211; Layout, Binding and Styles<\/h2>\n<p>Now let&#8217;s create the header. We will use helpers for layout, binding and style:<\/p>\n<pre><code class=\"csharp\">StackLayout Header =&gt; new StackLayout { Children = {\n    new Button { Text = \"\\u1438\" } .Style (HeaderButton)\n                .Width (50)\n                .Bind (nameof(vm.BackCommand)),\n\n    new Entry { Placeholder = \"Search\" }\n               .FillExpandHorizontal ()\n               .Bind (nameof(vm.SearchText))\n}};\n<\/code><\/pre>\n<h3>Bind<\/h3>\n<p>The <code>Bind<\/code> helper knows the <a href=\"https:\/\/github.com\/xamarin\/Xamarin.Forms\/blob\/master\/Xamarin.Forms.Core\/Markup\/DefaultBindableProperties.cs\">default bindable property for most built-in view types<\/a>; <code>Bind<\/code>&#8216;s target property parameter is optional (you can <a href=\"https:\/\/docs.microsoft.com\/xamarin\/xamarin-forms\/user-interface\/csharp-markup#data-binding\">register a default bindable property<\/a> for your own \/ 3rd party view type).<\/p>\n<h3>Style<\/h3>\n<p>The <code>Style<\/code> helper refers to an instance of a <code>Style&lt;BindableObject&gt;<\/code> helper class, e.g.:<\/p>\n<p><strong>Styles.cs<\/strong><\/p>\n<pre><code class=\"csharp\">public static class Styles\n{\n    static Style&lt;Button&gt; headerButton;\n\n    public static Style&lt;Button&gt; HeaderButton =&gt; headerButton ??= new Style&lt;Button&gt;(\n        (Button.TextColorProperty, Color.CornflowerBlue),\n        (Button.FontSizeProperty , 24)\n    )   .BasedOn (Implicit.Buttons);\n}\n<\/code><\/pre>\n<h2>2 Build the Search Result &#8211; Enums for Grid Rows and Columns<\/h2>\n<p>A <code>Grid<\/code> would be a good choice for the layout of the tweets in the search results. We will use helpers and enums instead of numbers for the rows and columns:<\/p>\n<pre><code class=\"csharp\">enum TweetRow    { Separator, Title, Body, Actions }\nenum TweetColumn { AuthorImage, Content }\n\nCollectionView SearchResults =&gt; new CollectionView { ItemTemplate = new DataTemplate(() =&gt; \n    new Grid {\n        RowDefinitions = Rows.Define (\n            (TweetRow.Separator, 2   ),\n            (TweetRow.Title    , Auto),\n            (TweetRow.Body     , Auto),\n            (TweetRow.Actions  , 32  )\n        ),\n\n        ColumnDefinitions = Columns.Define (\n            (TweetColumn.AuthorImage, 70  ),\n            (TweetColumn.Content    , Star)\n        ),\n\n        Children = {\n            new BoxView { BackgroundColor = Color.Gray }\n                         .Row (TweetRow.Separator) .ColumnSpan (All&lt;TweetColumn&gt;()) .Top() .Height (0.5),\n\n            RoundImage ( 53, nameof(Tweet.AuthorImage) )\n                        .Row (TweetRow.Title, TweetRow.Actions) .Column (TweetColumn.AuthorImage) .CenterHorizontal () .Top () .Margins (left: 10, top: 4),\n\n            new Label { LineBreakMode = LineBreakMode.MiddleTruncation } .FontSize (16)\n                       .Row (TweetRow.Title) .Column (TweetColumn.Content) .Margins (right: 10)\n                       .Bind (nameof(Tweet.Header)),\n\n            new Label { } .FontSize (15)\n                       .Row (TweetRow.Body) .Column (TweetColumn.Content) .Margins (right: 10)\n                       .Bind (Label.FormattedTextProperty, nameof(Tweet.Body), \n                              convert: (List&lt;TextFragment&gt; fragments) =&gt; Format(fragments)),\n\n            LikeButton ( nameof(Tweet.IsLikedByMe) )\n                        .Row (TweetRow.Actions) .Column (TweetColumn.Content) .Left () .Top () .Size (24)\n                        .BindCommand (nameof(vm.LikeCommand), source: vm)\n        }\n    })}.Background (Color.FromHex(\"171F2A\")) \n       .Bind (nameof(vm.SearchResults));\n<\/code><\/pre>\n<h3>Bind Converters and Commands<\/h3>\n<p>Note that in the above example hw the <code>Bind<\/code> method enables you to specify inline converters:<\/p>\n<pre><code class=\"csharp\">new Label { }\n           .Bind (Label.FormattedTextProperty, nameof(Tweet.Body), \n                  convert: (List&lt;TextFragment&gt; fragments) =&gt; Format(fragments))\n<\/code><\/pre>\n<p>Also note that the <code>BindCommand<\/code> helper binds both the <code>Command<\/code> and <code>CommandParameter<\/code> properties. Here we use it to pass the list item (tweet) that contains a button with the <code>LikeCommand<\/code> parameter:<\/p>\n<pre><code class=\"csharp\">new Button { Text = \"Like\" }\n            .BindCommand (nameof(vm.LikeCommand), source: vm)\n<\/code><\/pre>\n<p><strong>SearchViewModel.cs<\/strong><\/p>\n<pre><code class=\"csharp\">public ICommand LikeCommand =&gt; likeCommand ??= new RelayCommand&lt;Tweet&gt;(Like);\n\nvoid Like(Tweet tweet) { ... }\n<\/code><\/pre>\n<h3>Create Views with Functions<\/h3>\n<p>In above example, note how <strong>easy<\/strong> it is to mix standard views with local functions that create views (<code>RoundImage()<\/code>, <code>Format()<\/code> and <code>LikeButton()<\/code>). These functions can be implemented right below the markup that uses them, to make the page read like a story:<\/p>\n<pre><code class=\"csharp\">ImageButton LikeButton(string isLikedPath) =&gt; new ImageButton { Source = \n    new FontImageSource { Color = Color.White }\n                         .Bind (FontImageSource.GlyphProperty, isLikedPath, \n                                convert: (bool like) =&gt; like ? \"\\u2764\" : \"\\u2661\")\n};\n\nFormattedString Format(List&lt;TextFragment&gt; fragments)\n{\n    var s = new FormattedString();\n    fragments?.ForEach(fragment =&gt; s.Spans.Add(\n        new Span { Text = fragment.Text, ... }\n    ));\n    return s;\n}\n<\/code><\/pre>\n<p>The <code>LikeButton()<\/code> is <em>declarative<\/em> (markup containing logic &#8211; an inline convertor), while <code>Format()<\/code> is <em>imperative<\/em> (logic containing markup &#8211; more <a href=\"https:\/\/dotnet.microsoft.com\/apps\/aspnet\/web-apps\/blazor\">Blazor<\/a> style). Although C# Markup aims to improve declarative markup, there are plenty of cases where it is fine to mix in imperative (procedural) functions to build markup.<\/p>\n<h2>3 Build the Footer &#8211; Make a Gesture<\/h2>\n<p>The footer contains a tappable hyperlink. Here we create it using the <code>BindTapGesture<\/code> gesture helper:<\/p>\n<pre><code class=\"csharp\">new Label { }\n           .FormattedText (\n               new Span { Text = \"See \" },\n               new Span { Text = \"C# Markup\", Style = Link }\n                         .BindTapGesture (nameof(vm.OpenHelpCommand)),\n               new Span { Text = \" for more information\" }\n            )\n<\/code><\/pre>\n<p>C# Markup contains helpers to conveniently add any type of gesture to any type of view, and to connect them to commands or events.<\/p>\n<h2>4 Animate the Header &#8211; Logic and Markup<\/h2>\n<p>We will need some UI logic for animation, but we don&#8217;t want to mix that logic with the markup. We can separate the logic from the markup by adding a <code>.logic.cs<\/code> partial class file:<\/p>\n<p><strong>SearchPage.logic.cs<\/strong><\/p>\n<pre><code class=\"csharp\">using Xamarin.Forms;\n\npublic partial class SearchPage : ContentPage\n{\n    readonly SearchViewModel vm;\n\n    public SearchPage(SearchViewModel vm)\n    {\n        BindingContext = this.vm = vm;\n        Build();\n    }\n}\n<\/code><\/pre>\n<p>Notice that the <code>logic.cs<\/code> file does not use the <code>Markup<\/code> namespace; this helps to maintain a clean separation of markup and logic. If a page does not need logic, you can omit the <code>.logic.cs<\/code> file and put the page constructor and the base class in the markup file.<\/p>\n<p>C# Markup offers the <code>Assign<\/code> and <code>Invoke<\/code> helpers to connect markup to UI logic. Here we use them to animate the header when the entry gets focus:<\/p>\n<p><strong>SearchPage.cs<\/strong><\/p>\n<pre><code class=\"csharp\">new StackLayout { Children = {\n    Header .Assign (out header),\n...\n\nnew Entry { Placeholder = \"Search\" }\n           .Invoke (entry =&gt; {\n                entry.Focused   += Search_FocusChanged; \n                entry.Unfocused += Search_FocusChanged; \n            })\n<\/code><\/pre>\n<p><strong>SearchPage.logic.cs<\/strong><\/p>\n<pre><code class=\"csharp\">View header;\n\nvoid Search_FocusChanged(object sender, FocusEventArgs e)\n{\n    ViewExtensions.CancelAnimations(header);\n    header.TranslateTo(e.IsFocused ? -56 : 0, 0, 250, Easing.CubicOut);\n}\n<\/code><\/pre>\n<h2>Done! Any Next Level Tips?<\/h2>\n<p>We have built the example page. Our story is done!<\/p>\n<p>This is a good moment to introduce some next level tips for working with C# Markup:<\/p>\n<h3>Code Snippets<\/h3>\n<p>When writing C# Markup pages for Xamarin.Forms, some code patterns are often repeated with minor variations. These <a href=\"https:\/\/vincenth.net\/blog\/2020\/06\/04\/csharp-markup-snippets\">C# Markup snippets<\/a> create some of these patterns for you, and let you specify variations with optional parameters. These snippets can save you a lot of typing.<\/p>\n<h3>Format Markup<\/h3>\n<p>You may have noticed that in the above examples the markup does not follow standard C# formatting conventions, while the logic does. Standard C# formatting conventions are historically geared towards logic &#8211; which is perfectly <em>logical<\/em> \ud83d\ude09 when you use a different language for declarative markup, like XAML.<\/p>\n<p>Declarative markup is by its nature deeply nested; standard logic-like formatting of markup leads to excessive indenting and many lines with only a single bracket on them. On the other hand, markup languages like XAML use a single line end + indent increase between a parent and a child &#8211; for good reason. The markup formatting used here aims to achieve similar readability (a C# Markup auto-format tool would really help though &#8211; working on that).<\/p>\n<h3>The Layout Line<\/h3>\n<p>The layout helpers (e.g. <code>Width<\/code> and <code>FillExpandHorizontal<\/code>) set properties that determine the <em>location<\/em> of the view content in the page. There are many layout helpers; by convention they are specified on a single line, ordered spatially outside-in. This is called the <strong>layout line<\/strong>. It helps to quickly scan markup to build a mental picture of the layout and to zoom in on the location of a view&#8217;s content. The layout line is described in detail in the <a href=\"https:\/\/docs.microsoft.com\/xamarin\/xamarin-forms\/user-interface\/csharp-markup#layout-line-convention\">C# Markup documentation<\/a>.<\/p>\n<p>The order of helpers does not matter at runtime; each helper sets different properties. You can order the helpers any way you like; the layout line is just a convention to improve source readability.<\/p>\n<h3>Bring Your Own Helpers<\/h3>\n<p>It only takes a single line to add your own helper to C# Markup. For example, this helper lets you use Steven Thewissen&#8217;s excellent <a href=\"https:\/\/github.com\/sthewissen\/Xamarin.Forms.DebugRainbows\">DebugRainbows<\/a> in C# Markup:<\/p>\n<pre><code class=\"csharp\">public static TBindable Rainbow&lt;TBindable&gt;(this TBindable bindable) where TBindable : BindableObject { DebugRainbow.SetShowColors(bindable, true); return bindable; }\n<\/code><\/pre>\n<p>So you can use it like this:<\/p>\n<pre><code class=\"csharp\">new StackLayout { Children = {\n    Header .Rainbow (),\n    SearchResults,\n    Footer\n}};\n<\/code><\/pre>\n<h2>Closing Remarks<\/h2>\n<p>C# Markup makes Xamarin.Forms a more attractive alternative for developers who like the single language, declarative approach of modern UI frameworks like Flutter or SwiftUI. For new Xamarin.Forms developers without XAML knowledge, C# Markup shortens the learning curve.<\/p>\n<p>Last but not least, C# Markup does not force you to choose. Apps with a XAML UI can mix-in C# Markup just fine, e.g. to implement parts of the UI that are too complex \/ dynamic for XAML.<\/p>\n<p><a href=\"https:\/\/github.com\/VincentH-Net\/CSharpForMarkup\">CSharpForMarkup<\/a> has been around for quite some time; e.g. developers that have been using it <a href=\"https:\/\/github.com\/xamarin\/Xamarin.Forms\/pull\/8342#issuecomment-581046347\">report<\/a>:<\/p>\n<blockquote>\n<p>Less obvious advantages become apparent after working this way for a longer time &#8230; many times easier to break down larger more complicated views into smaller more manageable pieces &#8230; much less use for Styles<\/p>\n<\/blockquote>\n<p>C# Markup offers <em>a lot<\/em> for Xamarin.Forms developers. Try it out, see if <em>you<\/em> like it!<\/p>\n<blockquote>\n<p>Note that C# Markup is also part of <a href=\"https:\/\/github.com\/dotnet\/maui\">.NET MAUI<\/a>, which supports both MVVM and MVU.<\/p>\n<p>This <a href=\"https:\/\/github.com\/dotnet\/maui\/issues\/119\">C# Markup for MVU and MVVM<\/a> MAUI Spec aims to let developers switch and mix MVVM and MVU patterns with minimal markup changes, combining &#8211; and improving on &#8211; the best of <a href=\"https:\/\/github.com\/Clancey\/Comet\">Comet<\/a> markup and Forms C# Markup.<\/p>\n<\/blockquote>\n<p>Be sure to check out the <a href=\"https:\/\/docs.microsoft.com\/xamarin\/xamarin-forms\/user-interface\/csharp-markup\">C# Markup documentation<\/a> for a full overview and more guidance. More is coming in the way of helpers and tooling. If you have questions or ideas, please submit them as comments on the <a href=\"https:\/\/github.com\/xamarin\/Xamarin.Forms\/pull\/8342\">C# Markup PR<\/a>. Thanks!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C# Markup for Xamarin.Forms enables developers to seamlessly create user interfaces in C# code with built in markup extensions. In this guest blog post Vincent will walk-through how to setup C# markup and all of the great built in features. <\/p>\n","protected":false},"author":579,"featured_media":47552,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5216,2,367],"tags":[8968,8969,589,16],"class_list":["post-47485","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-announcements","category-developers","category-xamarin-forms","tag-c-markup","tag-coded-ui","tag-community","tag-xamarin-forms"],"acf":[],"blog_post_summary":"<p>C# Markup for Xamarin.Forms enables developers to seamlessly create user interfaces in C# code with built in markup extensions. In this guest blog post Vincent will walk-through how to setup C# markup and all of the great built in features. <\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/47485","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\/579"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=47485"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/47485\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/47552"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=47485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=47485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=47485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}