Microsoft support for Xamarin ended on May 1, 2024 for all Xamarin SDKs including Xamarin.Forms. Upgrade your Xamarin & Xamarin.Forms projects to .NET 8 and .NET MAUI with our migration guides.
Introducing C# Markup for Xamarin.Forms

This is a guest blog byVincent Hoogendoorn. Vincent is a hands-on .NET architect, Xamarin veteran,CSharpForMarkup author,contributor of the C# Markup feature in Xamarin.Forms and co-founder of theDutch Mobile .NET Developers meetup. Vincent works as Architect & Lead Mobile atAnywhere365.
C# Markup
Xamarin.Forms 4.6 introducedC# Markup, a set of fluent helpers and classes that aim to make UI development in C# ajoy.
C# Markup helps developers write concisedeclarative UI markup and cleanly separate it from UI logic, all in C#. Developers get to enjoy C#’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.
Example
Let’s introduce the main features of C# Markup by building this Twitter search page:

The full source of this example can be foundhere.
Build Top-Down with Hot Reload
C# Markup makes it easy to write markup using a top-down approach – 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 (usingDebugRainbows andLiveSharp):
Note that this video is unedited and realtime; it was recorded in one go by replaying git commits from the command line.
Write the Page – Like a Story
At the highest level the page contains a header, search results and a footer. So, if we structure the markup top-down – to make it read like a story – the initial markup could be:
SearchPage.cs
using Xamarin.Forms.Markup;public partial class SearchPage{ void Build() => Content = new StackLayout { Children = { Header, SearchResults, Footer }}; StackLayout Header => new StackLayout { }; CollectionView SearchResults => new CollectionView { }; Label Footer => new Label { };}Thevoid Build() => Content = pattern is a convention that lets you useLiveSharp for stateful hot reload of C# Markup. If you don’t plan on using LiveSharp, omit theBuild() method and put theContent assignment in the page constructor.
For now C# Markup is an experimental feature. So we need toset a flag to enable the feature:
App.cs
Device.SetFlags(new string[]{ "Markup_Experimental" });Next, let’s build out the page components. It is easy as 1-2-3 (and 4 for adding animation):
1 Build the Header – Layout, Binding and Styles
Now let’s create the header. We will use helpers for layout, binding and style:
StackLayout Header => new StackLayout { Children = { new Button { Text = "\u1438" } .Style (HeaderButton) .Width (50) .Bind (nameof(vm.BackCommand)), new Entry { Placeholder = "Search" } .FillExpandHorizontal () .Bind (nameof(vm.SearchText))}};Bind
TheBind helper knows thedefault bindable property for most built-in view types;Bind‘s target property parameter is optional (you canregister a default bindable property for your own / 3rd party view type).
Style
TheStyle helper refers to an instance of aStyle<BindableObject> helper class, e.g.:
Styles.cs
public static class Styles{ static Style<Button> headerButton; public static Style<Button> HeaderButton => headerButton ??= new Style<Button>( (Button.TextColorProperty, Color.CornflowerBlue), (Button.FontSizeProperty , 24) ) .BasedOn (Implicit.Buttons);}2 Build the Search Result – Enums for Grid Rows and Columns
AGrid 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:
enum TweetRow { Separator, Title, Body, Actions }enum TweetColumn { AuthorImage, Content }CollectionView SearchResults => new CollectionView { ItemTemplate = new DataTemplate(() => new Grid { RowDefinitions = Rows.Define ( (TweetRow.Separator, 2 ), (TweetRow.Title , Auto), (TweetRow.Body , Auto), (TweetRow.Actions , 32 ) ), ColumnDefinitions = Columns.Define ( (TweetColumn.AuthorImage, 70 ), (TweetColumn.Content , Star) ), Children = { new BoxView { BackgroundColor = Color.Gray } .Row (TweetRow.Separator) .ColumnSpan (All<TweetColumn>()) .Top() .Height (0.5), RoundImage ( 53, nameof(Tweet.AuthorImage) ) .Row (TweetRow.Title, TweetRow.Actions) .Column (TweetColumn.AuthorImage) .CenterHorizontal () .Top () .Margins (left: 10, top: 4), new Label { LineBreakMode = LineBreakMode.MiddleTruncation } .FontSize (16) .Row (TweetRow.Title) .Column (TweetColumn.Content) .Margins (right: 10) .Bind (nameof(Tweet.Header)), new Label { } .FontSize (15) .Row (TweetRow.Body) .Column (TweetColumn.Content) .Margins (right: 10) .Bind (Label.FormattedTextProperty, nameof(Tweet.Body), convert: (List<TextFragment> fragments) => Format(fragments)), LikeButton ( nameof(Tweet.IsLikedByMe) ) .Row (TweetRow.Actions) .Column (TweetColumn.Content) .Left () .Top () .Size (24) .BindCommand (nameof(vm.LikeCommand), source: vm) } })}.Background (Color.FromHex("171F2A")) .Bind (nameof(vm.SearchResults));Bind Converters and Commands
Note that in the above example hw theBind method enables you to specify inline converters:
new Label { } .Bind (Label.FormattedTextProperty, nameof(Tweet.Body), convert: (List<TextFragment> fragments) => Format(fragments))Also note that theBindCommand helper binds both theCommand andCommandParameter properties. Here we use it to pass the list item (tweet) that contains a button with theLikeCommand parameter:
new Button { Text = "Like" } .BindCommand (nameof(vm.LikeCommand), source: vm)SearchViewModel.cs
public ICommand LikeCommand => likeCommand ??= new RelayCommand<Tweet>(Like);void Like(Tweet tweet) { ... }Create Views with Functions
In above example, note howeasy it is to mix standard views with local functions that create views (RoundImage(),Format() andLikeButton()). These functions can be implemented right below the markup that uses them, to make the page read like a story:
ImageButton LikeButton(string isLikedPath) => new ImageButton { Source = new FontImageSource { Color = Color.White } .Bind (FontImageSource.GlyphProperty, isLikedPath, convert: (bool like) => like ? "\u2764" : "\u2661")};FormattedString Format(List<TextFragment> fragments){ var s = new FormattedString(); fragments?.ForEach(fragment => s.Spans.Add( new Span { Text = fragment.Text, ... } )); return s;}TheLikeButton() isdeclarative (markup containing logic – an inline convertor), whileFormat() isimperative (logic containing markup – moreBlazor 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.
3 Build the Footer – Make a Gesture
The footer contains a tappable hyperlink. Here we create it using theBindTapGesture gesture helper:
new Label { } .FormattedText ( new Span { Text = "See " }, new Span { Text = "C# Markup", Style = Link } .BindTapGesture (nameof(vm.OpenHelpCommand)), new Span { Text = " for more information" } )C# Markup contains helpers to conveniently add any type of gesture to any type of view, and to connect them to commands or events.
4 Animate the Header – Logic and Markup
We will need some UI logic for animation, but we don’t want to mix that logic with the markup. We can separate the logic from the markup by adding a.logic.cs partial class file:
SearchPage.logic.cs
using Xamarin.Forms;public partial class SearchPage : ContentPage{ readonly SearchViewModel vm; public SearchPage(SearchViewModel vm) { BindingContext = this.vm = vm; Build(); }}Notice that thelogic.cs file does not use theMarkup namespace; this helps to maintain a clean separation of markup and logic. If a page does not need logic, you can omit the.logic.cs file and put the page constructor and the base class in the markup file.
C# Markup offers theAssign andInvoke helpers to connect markup to UI logic. Here we use them to animate the header when the entry gets focus:
SearchPage.cs
new StackLayout { Children = { Header .Assign (out header),...new Entry { Placeholder = "Search" } .Invoke (entry => { entry.Focused += Search_FocusChanged; entry.Unfocused += Search_FocusChanged; })SearchPage.logic.cs
View header;void Search_FocusChanged(object sender, FocusEventArgs e){ ViewExtensions.CancelAnimations(header); header.TranslateTo(e.IsFocused ? -56 : 0, 0, 250, Easing.CubicOut);}Done! Any Next Level Tips?
We have built the example page. Our story is done!
This is a good moment to introduce some next level tips for working with C# Markup:
Code Snippets
When writing C# Markup pages for Xamarin.Forms, some code patterns are often repeated with minor variations. TheseC# Markup snippets create some of these patterns for you, and let you specify variations with optional parameters. These snippets can save you a lot of typing.
Format Markup
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 – which is perfectlylogical 😉 when you use a different language for declarative markup, like XAML.
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 – for good reason. The markup formatting used here aims to achieve similar readability (a C# Markup auto-format tool would really help though – working on that).
The Layout Line
The layout helpers (e.g.Width andFillExpandHorizontal) set properties that determine thelocation 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 thelayout line. It helps to quickly scan markup to build a mental picture of the layout and to zoom in on the location of a view’s content. The layout line is described in detail in theC# Markup documentation.
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.
Bring Your Own Helpers
It only takes a single line to add your own helper to C# Markup. For example, this helper lets you use Steven Thewissen’s excellentDebugRainbows in C# Markup:
public static TBindable Rainbow<TBindable>(this TBindable bindable) where TBindable : BindableObject { DebugRainbow.SetShowColors(bindable, true); return bindable; }So you can use it like this:
new StackLayout { Children = { Header .Rainbow (), SearchResults, Footer}};Closing Remarks
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.
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.
CSharpForMarkup has been around for quite some time; e.g. developers that have been using itreport:
Less obvious advantages become apparent after working this way for a longer time … many times easier to break down larger more complicated views into smaller more manageable pieces … much less use for Styles
C# Markup offersa lot for Xamarin.Forms developers. Try it out, see ifyou like it!
Note that C# Markup is also part of.NET MAUI, which supports both MVVM and MVU.
ThisC# Markup for MVU and MVVM MAUI Spec aims to let developers switch and mix MVVM and MVU patterns with minimal markup changes, combining – and improving on – the best ofComet markup and Forms C# Markup.
Be sure to check out theC# Markup documentation 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 theC# Markup PR. Thanks!
Author

Proud Business Operations Program Manager at Microsoft for the global Xamarin & .NET Developer communities. Apart from managing developer content, Jayme also collaborates with Microsoft PM teams, MVPs, Community Leaders, and Event Coordinators to organize product launches, MVP Summit, Microsoft /Build, .NET Conf, and other corporate events through the year. You can find her living the "City Life" in New York, or traveling around the world, Carmen Sandiego-style.
12 comments
Discussion is closed.Login to edit/delete existing comments.
Bryan Krenzin Vincent,
i have been following your work for a while concerning c# markup. I downloaded all your original work on this when you wrote your first articles and found it very intuitive. Keep up the good work. I’m an “old school” programmer and understand all the “why’s” of using Xaml, but found using c# for the UI much easier.
BryanDenis Dzyuba Read moreThis is so similar to Apple’s SwiftUI and comes so soon after the last WWDC that it makes me wonder whether the idea behind it is to better map the new Xamarin/C# code to SwiftUI for easy rendering on iOS 14, iPadOS14, and Mac OS Big Sur. Although of course, it may be just following the similar tendencies, like ditching the (mostly) XML-based mark-up files and doing it all in the code.
Then again, while SwiftUI does follow this tendency, the most amazing thing about it is that it’s data-driven, as opposed to event-driven: it takes control over the...Read lessThis is so similar to Apple’s SwiftUI and comes so soon after the last WWDC that it makes me wonder whether the idea behind it is to better map the new Xamarin/C# code to SwiftUI for easy rendering on iOS 14, iPadOS14, and Mac OS Big Sur. Although of course, it may be just following the similar tendencies, like ditching the (mostly) XML-based mark-up files and doing it all in the code.
Then again, while SwiftUI does follow this tendency, the most amazing thing about it is that it’s data-driven, as opposed to event-driven: it takes control over the source of truth and remove the pain of handling all possible states of all UI elements which, according to Apple, at least, makes bugs inevitable in the event-driven code.
So, is C# Markup also data-driven? The Bind Converters and Commands section kind of hints at something along these lines…Vincent Hoogendoorn Read moreHi Denis, Maui will offer both MVVM and MVU update patterns; the (Comet style) MVU pattern in Maui could be what you are looking for.
C# Markup for Maui is being shaped to support both MVVM and MVU with minimal differences in the markup. The goal is:
"Let Maui developers switch and mix MVVM and MVU patterns with minimal markup changes, combining - and improving on - the best of Comet markup and Forms C# Markup."
So you will be able to use data-driven updates with C# Markup in Maui.
Read lessHi Denis, Maui will offer both MVVM and MVU update patterns; the (Comet style) MVU pattern in Maui could be what you are looking for.
C# Markup for Maui is being shaped to support both MVVM and MVU with minimal differences in the markup. The goal is:
“Let Maui developers switch and mix MVVM and MVU patterns with minimal markup changes, combining – and improving on – the best of Comet markup and Forms C# Markup.”
So you will be able to use data-driven updates with C# Markup in Maui.
Sam Wheat Looks interesting. I’m glad you guys are experimenting and pushing the technology forward.
Will this be coming to WPF / UWP / Maui?
Vincent Hoogendoorn Read moreThanks Sam! Yes, C# Markup as it is in Forms today is already part of the Maui repository.
We have been shaping a much more advanced version for .MAUI ever since Maui was announced publicly.
In the mean time, this C# Markup Part 2 PR is in review - with a bit of luck it will be in a Forms release soon. This will also be included in Maui.
Read lessThanks Sam! Yes, C# Markup as it is in Forms today is already part of the Maui repository.
We have beenshaping a much moreadvanced version for .MAUI ever since Maui was announced publicly.
In the mean time, thisC# Markup Part 2 PR is in review – with a bit of luck it will be in a Forms release soon. This will also be included in Maui.
Will Fawthrop Read moreThe fact that you guys even wrote this shows just how truly out of touch you are with XF. XF as whole is extremely buggy, the developer experience is very painful and slow. That you build on your Mac and what you build on your windows PC don't always align. You guys need to focus on the basics of hardening this entire platform with tooling in mind. The fact your just giving as a way to do a page with fancy code behind is not hitting the mark. We need a more polished and refined platform/ecosystem/development experience than this crap.
Read lessThe fact that you guys even wrote this shows just how truly out of touch you are with XF. XF as whole is extremely buggy, the developer experience is very painful and slow. That you build on your Mac and what you build on your windows PC don’t always align. You guys need to focus on the basics of hardening this entire platform with tooling in mind. The fact your just giving as a way to do a page with fancy code behind is not hitting the mark. We need a more polished and refined platform/ecosystem/development experience than this crap.

David Ortinau
Read moreHi Will, this is a guest post from a contributor to the platform.
I'm sorry to hear you're having toolchain problems. Build and deploy times ought to be faster than ever, so please file feedback through Visual Studio with logs for us to troubleshoot.
Hot Restart is the best productivity option for your daily iOS development on a Mac.
There is a core team focus on the fundamentals as you can see from the sprint plans here https://github.com/xamarin/Xamarin.Forms/projects.
Read lessHi Will, this is a guest post from a contributor to the platform.
I’m sorry to hear you’re having toolchain problems. Build and deploy times ought to be faster than ever, so please file feedback through Visual Studio with logs for us to troubleshoot.
Hot Restart is the best productivity option for your daily iOS development on a Mac.
There is a core team focus on the fundamentals as you can see from the sprint plans herehttps://github.com/xamarin/Xamarin.Forms/projects.
Oleg Mikhailov There is a typo in phrase “Xamarin.Forms 4.6 introduced C# Markup, a set of fluent helpers and classes that aim to make UI development in C# a joy.”
Judging by how the code looks, it should be “a joke”, not “a joy” 🙂

