PageSourceSearch

https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui

html ewers-peters.de collected 2026-10-03 20:46:49 UTC 184,178 bytes, 179 lines download raw bytes

1<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png"/><link rel="stylesheet" href="/_next/static/css/0d16c72050851854.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/7ffcc02a3c67389b.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/c20e76e7d11dec32.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/59fefb2212bd5b60.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-55b36f0cd6c208fe.js"/>
1<script src="/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js" async=""></script>
1<script src="/_next/static/chunks/9da6db1e-8a4e8c23641c31bf.js" async=""></script>
1<script src="/_next/static/chunks/1255-a242e0ce0372fddf.js" async=""></script>
1<script src="/_next/static/chunks/main-app-bda8a52fe841b3aa.js" async=""></script>
1<script src="/_next/static/chunks/4554-984b480a9db5f3fe.js" async=""></script>
1<script src="/_next/static/chunks/7563-4dfa0994a9b0e37c.js" async=""></script>
1<script src="/_next/static/chunks/9216-9272709ba40c1cfd.js" async=""></script>
1<script src="/_next/static/chunks/2810-6b74d77b4b0dc790.js" async=""></script>
1<script src="/_next/static/chunks/3091-bf1537ff6571a1bd.js" async=""></script>
1<script src="/_next/static/chunks/app/layout-6ae7b4b583d4bba3.js" async=""></script>
1<script src="/_next/static/chunks/app/not-found-b861935655c5698b.js" async=""></script>
1<script src="/_next/static/chunks/5001-fae0bebdd93f95e3.js" async=""></script>
1<script src="/_next/static/chunks/2423-c8b1388240338d7d.js" async=""></script>
1<script src="/_next/static/chunks/652-f40dcb480b4e356f.js" async=""></script>
1<script src="/_next/static/chunks/730-8191af9e9424b148.js" async=""></script>
1<script src="/_next/static/chunks/app/page-cc18ae1a3a7a5ed8.js" async=""></script>
1<script src="/_next/static/chunks/0dbeb660-9e82e99813552576.js" async=""></script>
1<script src="/_next/static/chunks/1545-7abf96433ff6553f.js" async=""></script>
1<script src="/_next/static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js" async=""></script>
1<meta name="next-size-adjust" content=""/><title>How to bind to functions from XAML in .NET MAUI</title><meta name="description" content="Learn how to bind to ViewModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates"/><link rel="canonical" href="https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui"/><meta property="og:title" content="How to bind to functions from XAML in .NET MAUI"/><meta property="og:description" content="Learn how to bind to ViewModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates"/><meta property="og:url" content="https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui"/><meta property="og:site_name" content="Julian&#x27;s Blog"/><meta property="og:image" content="https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="article"/><meta property="article:published_time" content="2025-02-20T11:04:53.443Z"/><meta property="article:author" content="https://blog.ewers-peters.de/@ewerspej"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="How to bind to functions from XAML in .NET MAUI"/><meta name="twitter:description" content="Learn how to bind to ViewModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates"/><meta name="twitter:image" content="https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png"/><link rel="icon" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1688308456278/eC0e0vVNd.png"/>
1<script type="text/javascript">window.addEventListener('message',function(e){var d=e.data;if(!d||d.__hnWidget!==1||typeof d.h!=='number'||d.h<=0)return;var f=document.querySelectorAll('.hn-widget-frame');for(var i=0;i<f.length;i++){if(f[i].contentWindow===e.source){f[i].style.height=d.h+'px';break;}}});</script>
1<script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script>
1</head><body class="__variable_c2612a __variable_246ccd __variable_3069d5 bg-background dark:bg-background"><div hidden=""><!--$--><!--/$--></div>
1<script>((a2, b2, c4, d3, e2, f2, g4, h3) => {
2          let i3 = document.documentElement, j2 = ["light", "dark"];
3          function k2(b3) {
4            var c5;
5            (Array.isArray(a2) ? a2 : [a2]).forEach((a3) => {
6              let c6 = a3 === "class", d4 = c6 && f2 ? e2.map((a4) => f2[a4] || a4) : e2;
7              c6 ? (i3.classList.remove(...d4), i3.classList.add(f2 && f2[b3] ? f2[b3] : b3)) : i3.setAttribute(a3, b3);
8            }), c5 = b3, h3 && j2.includes(c5) && (i3.style.colorScheme = c5);
9          }
10          if (d3) k2(d3);
11          else try {
12            let a3 = localStorage.getItem(b2) || c4, d4 = g4 && a3 === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : a3;
13            k2(d4);
14          } catch {
15          }
16        })("class","theme","system",null,["light","dark"],null,true,true)</script>
16<main id="main-content" class="min-h-screen bg-transparent"><header class="sticky top-0 z-50 w-full py-4 bg-background backdrop-blur-sm transition-transform duration-300 translate-y-0"><a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-background focus:px-4 focus:py-2 focus:text-sm focus:shadow">Skip to main content</a><div class="relative w-full mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem] px-4 sm:px-12 md:px-18 lg:px-16 xl:px-10"><div class="flex items-center justify-between gap-3 xl:hidden"><div class="flex items-center gap-3 min-w-0 flex-1"><a href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.ewers-peters.de&amp;utm_medium=referral&amp;utm_campaign=blog_header_logo&amp;utm_content=logo" class="inline-flex items-center justify-center text-lg text-muted-foreground hover:text-foreground transition border rounded-md h-9 w-9 shrink-0 border-border hover:bg-muted"><i class="text-xl fa-brands fa-hashnode" aria-hidden="true"></i><span class="sr-only">Hashnode</span></a><a title="Julian&#x27;s Blog" class="text-lg font-semibold text-foreground truncate min-w-0" href="/">Julian&#x27;s Blog</a></div><div class="flex items-center gap-2 shrink-0"><button class="gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent py-2 h-9 w-9 px-0 flex items-center justify-center text-muted-foreground hover:text-foreground bg-transparent border-border shadow-none" aria-label="Open search (press Control or Command and K)"><i class="fa-jelly fa-search text-sm" aria-hidden="true"></i><span class="sr-only">Open search (press Control or Command and K)</span></button><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent w-9 px-3 h-9 text-muted-foreground hover:text-foreground shadow-none" type="button" id="radix-_R_2j4anpfalb_" aria-haspopup="menu" aria-expanded="false" data-state="closed" data-slot="dropdown-menu-trigger"><i class="fa-jelly fa-sun text-sm scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" aria-hidden="true"></i><i class="fa-jelly fa-moon absolute text-sm scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" aria-hidden="true"></i><span class="sr-only">Toggle theme</span></button><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground py-2 h-9 w-10 px-0 shadow-none" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="radix-_R_3j4anpfalb_" data-state="closed" data-slot="sheet-trigger"><i class="fa-jelly fa-bars" aria-hidden="true"></i><span class="sr-only">Open menu</span></button></div></div><div class="hidden xl:grid gap-4 items-center xl:grid-cols-[1fr_auto_1fr] xl:gap-6 max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="flex items-center w-full gap-3 min-w-0"><a href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.ewers-peters.de&amp;utm_medium=referral&amp;utm_campaign=blog_header_logo&amp;utm_content=logo" class="inline-flex items-center justify-center text-lg text-muted-foreground hover:text-foreground transition border rounded-md h-9 w-9 border-border hover:bg-muted"><i class="text-xl fa-brands fa-hashnode" aria-hidden="true"></i><span class="sr-only">Hashnode</span></a><span class="h-6 border-l border-border"></span><p class="font-semibold text-foreground min-w-0 truncate text-lg sm:text-xl"><a title="Julian&#x27;
16s Blog" href="/">Julian&#x27;s Blog</a></p></div><div></div><div class="flex items-center justify-end gap-3 text-muted-foreground"><button class="gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent py-2 h-9 w-9 px-0 flex items-center justify-center text-muted-foreground hover:text-foreground bg-transparent border-border shadow-none" aria-label="Open search (press Control or Command and K)"><i class="fa-jelly fa-search text-sm" aria-hidden="true"></i><span class="sr-only">Open search (press Control or Command and K)</span></button><div class="flex items-center gap-3"><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent w-9 px-3 h-9 text-muted-foreground hover:text-foreground shadow-none" type="button" id="radix-_R_6t4anpfalb_" aria-haspopup="menu" aria-expanded="false" data-state="closed" data-slot="dropdown-menu-trigger"><i class="fa-jelly fa-sun text-sm scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" aria-hidden="true"></i><i class="fa-jelly fa-moon absolute text-sm scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" aria-hidden="true"></i><span class="sr-only">Toggle theme</span></button><a target="_blank" rel="noopener" class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground py-2 gap-2 px-3 h-9 shadow-none" href="https://hashnode.com"><i class="text-sm fa-jelly fa-pencil" aria-hidden="true"></i>Write</a></div></div></div></div></header><div data-slot="dialog-header" class="flex flex-col gap-2 text-center sm:text-left sr-only"><h2 id="radix-_R_8anpfalbH1_" data-slot="dialog-title" class="text-lg leading-none font-semibold">Command Palette</h2><p id="radix-_R_8anpfalbH2_" data-slot="dialog-description" class="text-muted-foreground text-sm">Search for a command to run...</p></div><section class="py-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="grid gap-5 max-w-full"><article class="w-full max-w-full rounded-xl border bg-card text-card-foreground"><div class="px-5 pt-12 pb-8 sm:px-8"><header class="space-y-5"><div class="space-y-4 text-center max-w-4xl mx-auto"><h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold leading-tight tracking-tight text-balance text-foreground">How to bind to functions from XAML in .NET MAUI</h1><div class="flex flex-wrap items-center justify-center gap-3 text-sm text-muted-foreground"><div class="flex items-center gap-2"><span>Published</span><time dateTime="2025-02-20T11:04:53.443Z">February 20, 2025</time></div><span class="text-muted-foreground">•</span><span>6<!-- --> min read</span><span class="text-muted-foreground">•</span><a href="/how-to-bind-to-functions-from-xaml-in-net-maui.md" target="_blank" rel="noopener" class="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"><i class="fa-brands fa-markdown" aria-hidden="true"></i>View as Markdown</a></div></div></header></div><div class="w-full"><img alt="How to bind to functions from XAML in .NET MAUI" width="1200" height="750" decoding="async" data-nimg="1" class="w-full h-auto" style="color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png"/></div><div class="px-5 py-12 sm:px-8 space-y-8"><div class="max-w-2xl mx-auto"><div class="flex flex-wrap items-center gap-5"><div class="flex items-center gap-5"><div class="flex gap-3 items-start"><a target="_blank" rel="noopener" href="https://hashnode.com/@ewerspej"><span class="relative flex shrink-0 overflow-hidden rounded-full h-10 w-10 border border-border transition-opacity hover:opacity-80"><span class="flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-semibold text-lg">J</span></span></a><div class="min-w-0"><div class="flex items-center gap-2"><a target="_blank" rel="noopener" class="font-medium text-foreground hover:text-primary transition-colors" href="https://hashnode.com/@ewerspej">Julian Ewers-Peters</a><div class="flex items-center gap-1.5 text-sm"><a target="_blank" rel="noopener" class="text-muted-foreground hover:text-foreground transition-colors" aria-label="LinkedIn" href="https://www.linkedin.com/in/jewerspeters/"><i class="fa-brands fa-linkedin" aria-hidden="true"></i></a></div></div><div class="text-sm text-muted-foreground line-clamp-2"><p>
16I am a passionate mobile app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. </p>
17</div></div></div></div></div></div><div class="relative"><aside class="hidden xl:block absolute top-0 left-0 h-full"><nav class="sticky top-22 w-44"><div class="hidden xl:block group/toc relative"><div class="grid"><div class="[grid-area:1/1] opacity-0 group-hover/toc:opacity-100 transition-opacity duration-200 max-h-[calc(100vh-3rem)] overflow-y-auto"><p class="text-sm font-semibold uppercase tracking-wide text-muted-foreground mb-3">On this page</p><nav aria-label="On this page"><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-introduction">Introduction</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-the-goal">The Goal</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-data-triggers">Data Triggers</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-funcvaluetoboolconverter">FuncValueToBoolConverter</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-prime-number-function">Prime number function</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-putting-it-together">Putting it together</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-conclusions-and-next-steps">Conclusions and next steps</a></nav></div><div class="[grid-area:1/1] relative flex flex-col gap-px transition-opacity duration-200 group-hover/toc:opacity-0 pointer-events-none self-start max-h-[calc(100vh-3rem)] overflow-hidden pl-3"><div class="absolute left-0 w-0.5 rounded-full bg-foreground transition-all duration-300 opacity-0" style="transform:translateY(0px);height:12px"></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:12px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:13px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:24px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:21px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:19px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:26px"></div></div></div></div></div></nav></aside><div class="max-w-2xl mx-auto min-w-0 contain-[inline-size]"><div class="prose dark:prose-invert prose-pre:bg-foreground prose-pre:text-background prose-pre:border prose-pre:border-border prose-pre:rounded prose-pre:p
17x-5 prose-pre:py-4 dark:prose-pre:bg-muted dark:prose-pre:text-foreground dark:prose-pre:border-border prose-pre:overflow-x-auto max-w-none text-base sm:text-lg [&amp;&gt;div&gt;p:first-child]:mt-0 [&amp;&gt;div&gt;p:first-child]:pt-0 min-w-0 wrap-break-word [&amp;_a]:break-all **:max-w-full"><div><h1 id="heading-introduction">Introduction</h1>
18<p>In .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a method and pass a value to it so that I can decide how to display the value in the UI.</p>
19<p>Normally, I would use a <em>trigger</em> when it comes to simply checking whether a property has a certain value and then update the UI accordingly. But, what if the evaluation isn’t as simple as checking for a specific value? What if we want to use a range of values or apply some more complex logic?</p>
20<p>One thing that I would like to avoid when using the MVVM pattern is moving business logic to data objects or even into the UI layer, because that’s usually not where it belongs. I want my business logic to be separate from data and UI layers and I would like it to be unit testable, so having evaluation functions for objects or arguments is desired.</p>
21<p>In this blog post I’ll demonstrate how we can use a trick in order to bind to functions from XAML by leveraging the capabilities of <em>data triggers</em>, <em>multi-bindings</em>, <em>multi-value converters</em> and <em>function delegates</em>.</p>
22<p>As always, I’ve added the code used here to my <a target="_blank" href="https://github.com/ewerspej/maui-samples" rel="noopener ugc">MAUI Samples repository</a>, so that you can follow along.</p>
23<h1 id="heading-the-goal">The Goal</h1>
24<p>The idea is to check whether the <code>Count</code> property of type <code>int</code> of the already existing <a target="_blank" href="https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Models/BindingItem.cs" rel="noopener ugc"><code>BindingItem</code></a> class is a prime number, and if it is, display the row the object resides in in a different color, in our case light green, without modifying the data class:</p>
25<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BindingItem</span>
26{
27    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
28    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Count { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
29}
30</code></pre>
31<p>Currently, adding items simply displays a new row with the <code>Count</code> value for the new item:</p>
32<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739961843055/aa225d06-a72c-466c-bfb0-1aa2b00c7b28.png" alt class="image--center mx-auto" /></p>
33<p>However, we would like to highlight all rows that contain a prime number, like so:</p>
34<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png" alt class="image--center mx-auto" /></p>
35<p>Things like this can usually be done using triggers. Let’s have a look.</p>
36<h1 id="heading-data-triggers">Data Triggers</h1>
37<p>A <a target="_blank" href="https://learn.microsoft.com/dotnet/maui/fundamentals/triggers#data-triggers" rel="noopener ugc">data trigger</a> uses a binding expression and a value to compare the property against and then applies a setter to update a specified set of visual properties of a control, like the background color:</p>
38<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Label</span>&gt;</span>
39  <span class="hljs-tag">&lt;<span class="hljs-name">Label.Triggers</span>&gt;</span>
40    <span class="hljs-tag">&lt;<span class="hljs-name">DataTrigger</span>
41      <span class="hljs-attr">TargetType</span>=<span class="hljs-string">"Label"</span>
42      <span class="hljs-attr">Binding</span>=<span class="hljs-string">"{Binding Count}"</span>
43      <span class="hljs-attr">Value</span>=<span class="hljs-string">"3"</span>&gt;</span>
44      <span class="hljs-tag">&lt;<span class="hljs-name">Setter</span> <span class="hljs-attr">Property</span>=<span class="hljs-string">"BackgroundColor"</span> <span class="hljs-attr">Value</span>=<span class="hljs-string">"LightGreen"</span> /&gt;</span>
45    <span class="hljs-tag">&lt;/<span class="hljs-name">DataTrigger</span>&gt;</span>
46  <span class="hljs-tag">&lt;/<span class="hljs-name">Label.Triggers</span>&gt;</span>
47<span class="hljs-tag">&lt;/<span class="hljs-name">Label</span>&gt;</span>
48</code></pre>
49<p>The flaw of this approach is that we would need to know the value that we want to evaluate against for each item beforehand, but we don’t. In order to check whether an integer is actually a prime number requires applying some logic, which we usually cannot directly do using a data trigger, and, as mentioned above, this logic doesn’t belong into the data layer.</p>
50<p>However, we can still make use of a data trigger to accomplish what we want, if we combine this with a converter that evaluates to <code>true</code> or <code>false</code> based on an evaluation function for the given property:</p>
51<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ContentPage.Resources</span>&gt;</span>
52  <span class="hljs-tag">&lt;<span class="hljs-name">converters:PrimeNumberToBoolConverter</span> <span class="hljs-attr">x:Key</span>=<span class="hljs-string">"IsPrimeConverter"</span> /&gt;</span>
53<span class="hljs-tag">&lt;/<span class="hljs-name">ContentPage.Resources</span>&gt;</span>
54
55<span class="hljs-tag">&lt;<span class="hljs-name">Label</span>&gt;</span>
56  <span class="hljs-tag">&lt;<span class="hljs-name">Label.Triggers</span>&gt;</span>
57    <span class="hljs-tag">&lt;<span class="hljs-name">DataTrigger</span>
58      <span class="hljs-attr">TargetType</span>=<span class="hljs-string">"Label"</span>
59      <span class="hljs-attr">Binding</span>=<span class="hljs-string">"{Binding Count, Converter={StaticResource IsPrimeConverter}}"</span>
60      <span class="hljs-attr">Value</span>=<span class="hljs-string">"True"</span>&gt;</span>
61      <span class="hljs-tag">&lt;<span class="hljs-name">Setter</span> <span class="hljs-attr">Property</span>=<span class="hljs-string">
61"BackgroundColor"</span> <span class="hljs-attr">Value</span>=<span class="hljs-string">"LightGreen"</span> /&gt;</span>
62    <span class="hljs-tag">&lt;/<span class="hljs-name">DataTrigger</span>&gt;</span>
63  <span class="hljs-tag">&lt;/<span class="hljs-name">Label.Triggers</span>&gt;</span>
64<span class="hljs-tag">&lt;/<span class="hljs-name">Label</span>&gt;</span>
65</code></pre>
66<p>We could implement a simple <code>IValueConverter</code> that contains the logic for checking for prime numbers, but this would potentially mean moving logic into a converter, which is not desirable in most scenarios, because we may want to apply some business logic from the ViewModel, as well. So, this logic doesn’t belong into the UI layer, either.</p>
67<p>💡 Yet, we can create a generalized converter that allows us to bind to a function delegate, which can then execute any kind of business logic from the ViewModel. This is exciting, let’s have a look.</p>
68<h1 id="heading-funcvaluetoboolconverter">FuncValueToBoolConverter</h1>
69<p>The heart of our desired functionality is the <em>FuncValueToBoolConverter</em> which is an <a target="_blank" href="https://learn.microsoft.com/dotnet/api/microsoft.maui.controls.imultivalueconverter" rel="noopener ugc">IMultiValueConverter</a> implementation.</p>
70<p>In our case it only takes two values: The first one is a function delegate of type <code>Func&lt;object, bool&gt;</code>, and the second value is an object which serves as the argument to be be passed to the function delegate:</p>
71<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FuncValueToBoolConverter</span> : <span class="hljs-title">IMultiValueConverter</span>
72{
73    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">object</span> <span class="hljs-title">Convert</span>(<span class="hljs-params"><span class="hljs-keyword">object</span>[] values, Type targetType, <span class="hljs-keyword">object</span> parameter, CultureInfo culture</span>)</span>
74    {
75        <span class="hljs-keyword">if</span> (values[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> Func&lt;<span class="hljs-keyword">object</span>, <span class="hljs-keyword">bool</span>&gt; func)
76        {
77            <span class="hljs-keyword">return</span> func(values[<span class="hljs-number">1</span>]);
78        }
79
80        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
81    }
82
83    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">object</span>[] <span class="hljs-title">ConvertBack</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> <span class="hljs-keyword">value</span>, Type[] targetTypes, <span class="hljs-keyword">object</span> parameter, CultureInfo culture</span>)</span>
84    {
85        <span class="hljs-keyword">return</span> [];
86    }
87}
88</code></pre>
89<p>This converter is a general-purpose <em>multi-value converter</em>. Its purpose is to take a function delegate that can evaluate an object based on some business logic and either returns <code>true</code> or <code>false</code>.</p>
90<p>In our case, we will use a method from the <a target="_blank" href="https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/ViewModels/BindingsViewModel.cs" rel="noopener ugc">BindingsViewModel</a> class, which we don’t have yet.</p>
91<h1 id="heading-prime-number-function">Prime number function</h1>
92<p>Let’s add a method which checks whether a number is prime to the ViewModel:</p>
93<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BindingsViewModel</span> : <span class="hljs-title">ObservableObject</span>
94{ 
95    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsPrime</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> number</span>)</span>
96    {
97        <span class="hljs-keyword">if</span> (number <span class="hljs-keyword">is</span> not <span class="hljs-keyword">int</span> intNumber || intNumber &lt; <span class="hljs-number">2</span>)
98        {
99            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
100        }
101
102        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">2</span>; i &lt;= Math.Sqrt(intNumber); i++)
103        {
104            <span class="hljs-keyword">if</span> (intNumber % i == <span class="hljs-number">0</span>)
105            {
106                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
107            }
108        }
109
110        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
111    }
112}
113</code></pre>
114<blockquote>
115<p><strong>Note</strong>: The <code>IsPrime()</code> method takes in an argument of type <code>object</code>, because that way, the <em>FuncValueToBoolConverter</em> can be reused in different contexts. We could also create a <em>FuncIntToBoolConverter</em> or similar in order to avoid boxing the integer.</p>
116</blockquote>
117<p>⁉️ But wait, we cannot bind to methods, and, apart from that, this static method is <code>private</code>. Fortunately, C# allows us to expose this method as a <code>public</code> function delegate auto-property <em>(which means that we can use it in a binding expression)</em>:</p>
118<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BindingsViewModel</span> : <span class="hljs-title">ObservableObject</span>
119{
120    <span class="hljs-comment">// expose the IsPrime() function as a delegate,</span>
121    <span class="hljs-comment">// note that this property must not be static</span>
122    <span class="hljs-keyword">public</span> Func&lt;<span class="hljs-keyword">object</span>, <span class="hljs-keyword">bool</span>&gt; IsPrimeFunc =&gt; IsPrime;
123
124    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsPrime</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> number</span>)</span>
125    {
126        <span class="hljs-comment">// see above</span>
127    }
128}
129</code></pre>
130<p>Time to put it all together and make the magic happen. 🪄</p>
131<h1 id="heading-putting-it-together">Putting it together</h1>
132<p>In our page, we first add the <a target="_blank" href="https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Converters/FuncValueToBoolConverter.cs" rel="noopener ugc">FuncValueToBoolConverter</a> as a static resource:</p>
133<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">ContentPage.Resources</span>&gt;</span>
134    <span class="hljs-tag">&lt;<span class="hljs-name">converters:FuncValueToBoolConverter</span> <span class="hljs-attr">x:Key</span>=<span class="hljs-string">"FuncValueToBoolConverter"</span> /&gt;</span>
135  <span class="hljs-tag">&lt;/<span class="hljs-name">ContentPage.Resources</span>&gt;</span>
136</code></pre>
137<p>Then, we can apply the converter together with a data trigger inside the <em>DataTemplate</em> of a <em>CollectionView</em>:</p>
138<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">CollectionView</span> <span class="hljs-attr">ItemsSource</span>=<span class="hljs-string">"{Binding Items}"</span>&gt;</span>
139  <span class="hljs-tag">&lt;<span class="hljs-name">CollectionView.ItemTemplate</span>&gt;</span>
140    <span class="hljs-tag">&lt;<span class="hljs-name">DataTemplate</span> <span class="hljs-attr">x:DataType</span>=<span class="hljs-string">"models:BindingItem"</span>&gt;</span>
141      <span class="hljs-tag">&lt;<span class="hljs-name">Grid</span>
142        <span class="hljs-attr">Padding</span>=<span class="hljs-string">"8"</span>
143        <span class="hljs-attr">ColumnDefinitions</span>=<span class="hljs-string">"*,*"</span>&gt;</span>
144        <span class="hljs-comment">&lt;!-- skipping irrelevant parts for brevity --&gt;</span>
145        <span class="hljs-tag">&lt;<span class="hljs-name">Grid.Triggers</span>&gt;</span>
146
147          <span class="hljs-tag">&lt;<span class="hljs-name">DataTrigger</span> <span class="hljs-attr">TargetType</span>=<span class="hljs-string">"Grid"</span> <span class="hljs-attr">Value</span>=<span class="hljs-string">"True"</span>&gt;</span>
148            <span class="hljs-tag">&lt;<span class="hljs-name">DataTrigger.Binding</span>&gt;</span>
149              <span class="hljs-tag">&lt;<span class="hljs-name">MultiBinding</span> <span class="hljs-attr">Converter</span>=<span class="hljs-string">"{StaticResource FuncValueToBoolConverter}"</span>&gt;</span>
150                <span class="hljs-tag">&lt;<span class="hljs-name">Binding</span>
151                  <span class="hljs-attr">Path</span>=<span class="hljs-string">"IsPrimeFunc"</span>
152                  <span class="hljs-attr">Source</span>=<span class="hljs-string">"{RelativeSource AncestorType={x:Type viewModels:BindingsViewModel}}"</span> /&gt;</span>
153                <span class="hljs-tag">&lt;<span class="hljs-name">Binding</span> <span class="hljs-attr">Path</span>=<span class="hljs-string">"Count"</span> /&gt;</span>
154              <span class="hljs-tag">&lt;/<span class="hljs-name">MultiBinding</span>&gt;</span>
155            <span class="hljs-tag">&lt;/<span class="hljs-name">DataTrigger.Binding</span>&gt;</span>
156            <span class="hljs-tag">&lt;<span class="hljs-name">DataTrigger.Setters</span>&gt;</span>
157              <span class="hljs-tag">&lt;<span class="hljs-name">Setter</span> <span class="hljs-attr">Property</span>=<span class="hljs-string">
157"BackgroundColor"</span> <span class="hljs-attr">Value</span>=<span class="hljs-string">"LightGreen"</span> /&gt;</span>
158            <span class="hljs-tag">&lt;/<span class="hljs-name">DataTrigger.Setters</span>&gt;</span>
159          <span class="hljs-tag">&lt;/<span class="hljs-name">DataTrigger</span>&gt;</span>
160
161        <span class="hljs-tag">&lt;/<span class="hljs-name">Grid.Triggers</span>&gt;</span>
162      <span class="hljs-tag">&lt;/<span class="hljs-name">Grid</span>&gt;</span>
163    <span class="hljs-tag">&lt;/<span class="hljs-name">DataTemplate</span>&gt;</span>
164  <span class="hljs-tag">&lt;/<span class="hljs-name">CollectionView.ItemTemplate</span>&gt;</span>
165<span class="hljs-tag">&lt;/<span class="hljs-name">CollectionView</span>&gt;</span>
166</code></pre>
167<p>Note the two separate bindings in the multi-binding. The first one must be pointing to the <code>IsPrimeFunc</code> property of the ViewModel <em>(using a</em> <a target="_blank" href="https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings" rel="noopener ugc"><em>relative binding</em></a><em>)</em> and the second one binds to the <code>Count</code> property of the current item.</p>
168<p>Running the app now, we will be greeted with green rows for every prime number when adding new items to the collection:</p>
169<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png" alt class="image--center mx-auto" /></p>
170<p>🤩 Isn’t this awesome? We managed to bind to a function to apply complex logic within a data binding context. I’m very happy with the result.</p>
171<blockquote>
172<p>You might be asking yourself whether this will also work with objects that have observable properties that frequently can change. The answer is: <strong>Yes, absolutely.</strong> Multi-bindings are evaluated everytime one of the properties of the child bindings changes, as long as it’s an observable property.</p>
173</blockquote>
174<h1 id="heading-conclusions-and-next-steps">Conclusions and next steps</h1>
175<p>I’ve demonstrated how we can take advantage of MAUI’s built-in capabilities in order to bind to a ViewModel method. We’ve done this by exposing it as a function delegate property and then using it in the context of a multi-binding expression together with a multi-value converter that executes the method for us. This let’s us evaluate values of data objects without having to add logic to the data class and without having to add critical business logic to converters.</p>
176<p>If you enjoyed this blog <a target="_blank" href="https://hashnode.com/@ewerspej" rel="noopener ugc">post</a>, then follow me on <a target="_blank" href="https://www.linkedin.com/in/jewerspeters" rel="noopener ugc"><strong>LinkedIn</strong></a>, subscribe to this <a target="_blank" href="https://hashnode.com/@ewerspej" rel="noopener ugc"><strong>blog</strong></a> and star the <a target="_blank" href="https://github.com/ewerspej/maui-samples" rel="noopener ugc"><strong>GitHub repository</strong></a> for this post so you don't miss out on any future posts and developments. And remember that sharing is caring.</p>
177</div></div></div></div><div class="max-w-2xl mx-auto border-t border-border pt-8 space-y-5"><div class="flex flex-wrap items-center gap-2"><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/data-binding">#<!-- -->data-binding</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/dotnet-maui">#<!-- -->dotnet-maui</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/maui">#<!-- -->maui</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/xaml">
177#<!-- -->xaml</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/user-interface">#<!-- -->user-interface</a></div><div class="flex items-center gap-2 text-sm text-muted-foreground" title="2,038 views"><i class="fa-regular fa-chart-simple h-4 w-4" aria-hidden="true"></i><span class="font-medium">2.0K<!-- --> views</span></div></div></div></article></div></div></div></section><section class="pb-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="max-w-4xl w-full mx-auto"><div class="rounded-xl border bg-card text-card-foreground p-5 sm:p-8"><section class="space-y-5"><div class="flex items-center justify-between"><h2 class="text-lg font-bold tracking-tight">Comments</h2><a target="_blank" rel="noopener" class="inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:border-primary hover:text-primary" href="https://hashnode.com/posts/how-to-bind-to-functions-from-xaml-in-net-maui/67b70c5573190a50ec0bd979"><i class="fa-regular fa-comment" aria-hidden="true"></i>Join the discussion</a></div><p class="text-sm text-muted-foreground py-5 text-center">No comments yet.<!-- --> <a target="_blank" rel="noopener" class="text-primary hover:underline" href="https://hashnode.com/posts/how-to-bind-to-functions-from-xaml-in-net-maui/67b70c5573190a50ec0bd979">Be the first to comment.</a></p></section></div></div></div></div></section><section class="pb-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="max-w-4xl w-full mx-auto"><section class="space-y-6"><h2 class="text-2xl font-bold tracking-tight text-balance">More from this blog</h2><div class="flex flex-col gap-6"><article aria-labelledby="_R_panpfalb_" class="h-full"><a class="block h-full" href="/white-labeling-net-maui-apps"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_panpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">White Labeling .NET MAUI Apps</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">A starting point for customized client apps using a single code base</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Jul 17, 2024</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>15 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">4.0K</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="White Labeling .NET MAUI Apps" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720778506691/48fecad5-d947-44b2-8446-b1db5657502a.png"/></div></div></div></div></a></article><article aria-labelledby="_R_19anpfalb_" class="h-full"><a class="block h-full" href="/maui-video-reel"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_19anpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Easily add a video reel to your MAUI app (like Instagram)</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">Introduction
178Social media apps like Instagram use full-page scrollable video controls called &quot;reels&quot; (named after the device used to store film - or garden hoses) which allow the user to swipe or scroll up and down in order to switch between videos (...</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">May 15, 2024</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>6 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">1.4K</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Easily add a video reel to your MAUI app (like Instagram)" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715761226824/743882bb-d579-43fa-a9a3-624cf033738f.png"/></div></div></div></div></a></article><article aria-labelledby="_R_1panpfalb_" class="h-full"><a class="block h-full" href="/add-automatic-route-registration-to-your-net-maui-app"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_1panpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Add automatic route registration to your .NET MAUI app</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">The next step in simplified route management for .NET</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Feb 16, 2024</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>5 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">2.3K</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Add automatic route registration to your .NET MAUI app" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708090435324/af960263-03f5-42b7-a84c-91ec60ae442a.png"/></div></div></div></div></a></article><article aria-labelledby="_R_29anpfalb_" class="h-full"><a class="block h-full" href="/introducing-route-generator-for-net"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_29anpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Introducing Route Generator for .NET</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">How to avoid navigation hell by having Routes auto-generated for you</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Dec 19, 2023</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>11 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">2.2K</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Introducing Route Generator for .NET" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702829964430/9154da3d-da17-4209-875f-a2df4b69e6a9.png"/></div></div></div></div></a></article><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 p-5 flex flex-col"><div class="flex flex-col items-center text-center gap-5"><div class="flex flex-col items-center gap-3"><span class="relative flex overflow-hidden rounded-full h-16 w-16 shrink-0"><span class="flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-medium text-lg">J</span></span><div class="space-y-1"><p class="text-base font-semibold text-foreground leading-tight">Julian&#x27;
178s Blog</p><p class="text-sm text-muted-foreground">20<!-- --> posts</p></div></div><div class="prose prose-sm dark:prose-invert max-w-xs mx-auto text-muted-foreground"><p>Hi, I am Julian, a passionate app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. I like writing Clean Code using TDD. Here, I write about my journey as a developer.</p>
179</div><div class="flex flex-wrap items-center justify-center gap-3"><a href="https://github.com/ewerspej" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="GitHub"><i class="fa-brands fa-github" aria-hidden="true"></i></a><a href="https://www.linkedin.com/in/jewerspeters" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="LinkedIn"><i class="fa-brands fa-linkedin" aria-hidden="true"></i></a><a href="https://www.ewers-peters.de" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Website"><i class="fa-jelly fa-globe" aria-hidden="true"></i></a><a href="https://hashnode.com/@ewerspej" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Hashnode"><i class="fa-brands fa-hashnode" aria-hidden="true"></i></a><a href="https://hachyderm.io/@ewerspej" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Mastodon"><i class="fa-brands fa-mastodon" aria-hidden="true"></i></a></div></div></div></div></section></div></div></div></section>
179<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","url":"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui","mainEntityOfPage":"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui","headline":"How to bind to functions from XAML in .NET MAUI","description":"Introduction\nIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a...","author":{"@type":"Person","name":"Julian Ewers-Peters","url":"https://hashnode.com/@ewerspej","image":"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png"},"publisher":{"@type":"Organization","name":"Julian's Blog","url":"https://blog.ewers-peters.de"},"datePublished":"2025-02-20T11:04:53.443Z","dateModified":"2025-02-20T11:04:53.443Z","image":{"@type":"ImageObject","url":"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png"},"keywords":"data binding, #dotnet-maui, #maui, xaml, User Interface"}</script>
179<footer class="px-4 pt-8 pb-20 sm:px-8 text-muted-foreground" aria-label="Footer"><div class="w-full max-w-4xl mx-auto"><div class="flex flex-col items-center gap-4 text-center text-sm"><section id="impressum" class="w-full max-w-2xl pt-3 text-left text-sm text-muted-foreground text-center [&amp;_*]:text-center [&amp;_*]:mx-auto" aria-label="Impressum"><div class="prose prose-sm prose-stone dark:prose-invert max-w-none text-muted-foreground prose-a:text-muted-foreground prose-a:no-underline prose-a:transition prose-a:hover:text-foreground [&amp;_ul]:list-none [&amp;_ol]:list-none"><p>This blog is published by Julian Ewers-Peters and hosted by Hashnode. It is publicly available under the domain <a href="https://blog.ewers-peters.de/" target="_blank" rel="noopener ugc">blog.ewers-peters.de</a>. You can find the <a href="https://www.ewers-peters.de/imprint/" target="_blank" rel="noopener ugc">Impressum</a> <em>(external link)</em> on the main website of Julian Ewers-Peters.</p></div><div class="my-6 h-px w-full bg-border" aria-hidden="true"></div></section><p class="font-medium"><time dateTime="2026"><span class="font-sans">©</span> <!-- -->2026</time> <!-- -->Julian&#x27;s Blog</p><nav aria-label="Footer navigation"><ul class="flex flex-wrap justify-center gap-4 text-sm tracking-wide"><li><a class="transition hover:text-foreground" href="/members">Members</a></li><li><a class="transition hover:text-foreground" href="/archive">Archive</a></li><li><a class="transition hover:text-foreground" target="_blank" rel="noopener" href="https://hashnode.com/privacy-policy">Privacy</a></li><li><a class="transition hover:text-foreground" target="_blank" rel="noopener" href="https://hashnode.com/terms">Terms</a></li></ul></nav><div class="pt-8 flex justify-center gap-6"><a target="_blank" rel="noopener" class="flex items-center gap-2 text-sm transition hover:text-foreground" title="Sitemap" href="/sitemap.xml"><i class="fa-regular fa-sitemap"></i><span>Sitemap</span></a><a target="_blank" rel="noopener" class="flex items-center gap-2 text-sm transition hover:text-foreground" title="RSS Feed" href="/rss.xml"><i class="fa-regular fa-rss"></i><span>RSS</span></a></div><div class="pt-10 text-center"><a target="_blank" rel="noopener" aria-label="Visit Hashnode (opens in a new tab)" class="inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-10 rounded-md text-xl px-5 py-4" href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.ewers-peters.de&amp;utm_medium=referral&amp;utm_campaign=blog_footer_logo&amp;utm_content=logo"><svg fill="none" viewBox="0 0 282 39" aria-hidden="true" class="h-6 w-auto min-w-[9.5rem] text-muted-foreground transition hover:text-foreground"><path fill="currentColor" d="M2.68 13.032c-3.573 3.505-3.573 9.363 0 12.936L13.032 36.32c3.505 3.573 9.363 3.573 12.936 0L36.32 25.968c3.573-3.573 3.573-9.431 0-12.936L25.968 2.68c-3.573-3.573-9.431-3.573-12.936 0zm12.211 1.935c2.507-2.521 6.582-2.544 9.104-.038s2.544 6.582.038 9.104-6.582 2.544-9.104.038-2.544-6.582-.038-9.104"></path><path fill="currentColor" d="M268.379 39q-6.322 0-10.35-4.108-4.002-4.134-4.002-10.483 0-6.296 4.002-10.43 4-4.135 10.163-4.135 6.028 0 9.817 3.734 3.814 3.735 3.814 9.497v2.8h-24.248v-4.054h17.633q-.187-2.747-2.054-4.508-1.868-1.76-4.962-1.76-3.28-.001-5.388 2.374-2.108 2.373-2.108 6.375 0 4.188 2.348 6.695 2.347 2.481 5.895 2.481 1.68 0 2.934-.453a8.5 8.5 0 0 0 2.321-1.307q1.041-.854 2.268-2.614l4.694 2.96q-1.707 2.748-3.494 4.135a12.7 12.7 0 0 1-3.975 2.081q-2.187.72-5.308.72m-33.496 0q-5.575 0-9.283-4.028-3.681-4.029-3.681-10.484 0-6.43 3.761-10.51 3.76-4.108 9.47-4.108 2.8 0 5.175 1.2 2.4 1.174 3.628 3.148l.026-.08V0h7.016v38.2h-6.722v-4.722l.64.8-.694.133q-1.28 2.108-3.788 3.361Q237.924 39 234.883 39m1.627-6.002q3.308 0 5.495-2.428t2.188-6.135q0-3.762-2.214-6.109-2.214-2.374-5.469-2.374-3.28 0-5.522 2.347-2.214 2.348-2.214 6.163 0 3.76 2.214 6.162 2.214 2.374 5.522 2.374M205.811 39q-6.376 0-10.537-4.161-4.161-4.188-4.161-10.404 0-6.321 4.161-10.456 4.188-4.135 10.537-4.135 6.348 0 10.537 4.161 4.188 4.161 4.188 10.43 0 6.216-4.188 10.404Q212.186 39 205.811 39m0-6.162q3.307 0 5.575-2.374 2.267-2.374 2.267-6.055 0-3.707-2.294-6.056-2.267-2.347-5.548-2.347-3.308 0-5.575 2.374-2.268 2.348-2.268 6.029t2.241 6.055q2.268 2.374 5.602 2.374M162.586 38.2V10.644h6.802v5.015l-.8-.987h.906q1.308-2.187 3.762-3.494 2.48-1.335 5.228-1.334 4.615 0 7.256 2.8 2.667 2.776 2.667 7.603V38.2h-
1797.069V21.794q0-2.854-1.387-4.401t-4.134-1.547q-2.614 0-4.402 1.974-1.76 1.974-1.76 4.881V38.2zm-30.181 0V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.069V21.794q0-2.827-1.387-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.814 2.027-1.814 4.962v15.391zm-14.416.8q-2.882 0-5.015-.64-2.108-.614-3.761-1.84-1.654-1.228-2.668-2.695l-.987-1.467 4.695-3.174.773 1.067a9.1 9.1 0 0 0 1.788 1.814 8.2 8.2 0 0 0 2.32 1.2q1.308.4 3.068.4 2.161 0 3.441-.8 1.308-.8 1.307-2.294 0-1.36-1.147-2.108-1.12-.747-5.068-1.654-5.335-1.253-7.656-3.334-2.32-2.107-2.321-5.362 0-3.76 3.148-6.002 3.148-2.267 7.923-2.267 2.454 0 4.348.507t3.388 1.493q1.493.988 2.454 2.188l.987 1.2-4.482 3.255-.667-.774a7.4 7.4 0 0 0-1.627-1.387 6.4 6.4 0 0 0-2.054-.88 9.5 9.5 0 0 0-2.401-.294q-1.974 0-3.201.8-1.2.774-1.2 1.921 0 1.254 1.12 2.027 1.147.748 5.229 1.681 5.334 1.227 7.629 3.308 2.294 2.055 2.294 5.308 0 4.029-3.228 6.43Q123.217 39 117.989 39M87.61 39q-4.508 0-7.335-2.454-2.828-2.481-2.828-6.482 0-4.188 3.254-6.669t8.483-2.48q2.082 0 4.028.426 1.974.427 3.361 1.12v-1.6q0-2.588-1.813-4.082-1.788-1.493-4.615-1.493-1.628 0-2.801.373a8.7 8.7 0 0 0-2.188.987q-.987.614-1.84 1.414l-.854.8-4.081-3.788 1.307-1.173a18.7 18.7 0 0 1 2.934-2.108q1.601-.933 3.601-1.44t4.589-.507q5.868 0 9.229 2.88 3.361 2.882 3.361 8.084V38.2h-6.749v-5.922l1.654 2.187h-1.76q-1.308 2.055-3.655 3.308Q90.545 39 87.611 39m1.814-4.988q3.148 0 5.149-2.187 2-2.215 2-5.309v-.08q-1.12-.693-2.72-1.094a13 13 0 0 0-3.388-.426q-2.828 0-4.562 1.2-1.734 1.173-1.734 3.521 0 2.028 1.468 3.201t3.787 1.174M49 38.2V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.07V21.794q0-2.827-1.386-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.815 2.027-1.814 4.962v15.391z"></path></svg></a></div></div></div></footer></main><!--$--><!--/$-->
179<script src="/_next/static/chunks/webpack-55b36f0cd6c208fe.js" id="_R_" async=""></script>
179<script>(self.__next_f=self.__next_f||[]).push([0])</script>
179<script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[16174,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"ThemeProvider\"]\n3:I[82180,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"TooltipProvider\"]\n4:I[63851,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"UrqlProvider\"]\n5:I[82714,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"ConsentManager\"]\n6:I[9766,[],\"\"]\n7:I[98924,[],\"\"]\n8:I[33293,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"4345\",\"static/chunks/app/not-found-b861935655c5698b.js\"],\"default\"]\n9:I[21447,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"Integrations\"]\na:I[35514,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"Analytics\"]\nc:I[24431,[],\"OutletBoundary\"]\ne:I[15278,[],\"AsyncMetadataOutlet\"]\n10:I[24431,[],\"ViewportBoundary\"]\n12:I[24431,[],\"MetadataBoundary\"]\n13:\"$Sreact.suspense\"\n15:I[57150,[],\"\"]\n:HL[\"/_next/static/media/22a5144ee8d83bca-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/72a2a501b11d0ccb.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/8934b430a4adeb19-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/8e9860b6e62d6359-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/c4b700dcb2187787-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/0d16c72050851854.css\",\"style\"]\n:HL[\"/_next/static/css/7ffcc02a3c67389b.css\",\"style\"]\n:HL[\"/_next/static/css/c20e76e7d11dec32.css\",\"style\"]\n:HL[\"/_next/static/css/59fefb2212bd5b60.css\",\"style\"]\n0:{\"P\":null,\"b\":\"RrCGzNVHUG674hJ_sjvms\",\"p\":\"\",\"c\":[\"\",\"how-to-bind-to-functions-from-xaml-in-net-maui?x-host=blog.ewers-peters.de\"],\"i\":false,\"f\":[[[\"\",{\"children\":[[\"slug\",\"how-to-bind-to-functions-from-xaml-in-net-maui\",\"c\"],{\"children\":[\"__PAGE__?{\\\"x-host\\\":\\\"blog.ewers-peters.de\\\"}\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/0d16c72050851854.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/7ffcc02a3c67389b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/c20e76e7d11dec32.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/59fefb2212bd5b60.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\","])</script>
179<script>self.__next_f.push([1,"null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"type\":\"text/javascript\",\"dangerouslySetInnerHTML\":{\"__html\":\"window.addEventListener('message',function(e){var d=e.data;if(!d||d.__hnWidget!==1||typeof d.h!=='number'||d.h\u003c=0)return;var f=document.querySelectorAll('.hn-widget-frame');for(var i=0;i\u003cf.length;i++){if(f[i].contentWindow===e.source){f[i].style.height=d.h+'px';break;}}});\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_c2612a __variable_246ccd __variable_3069d5 bg-background dark:bg-background\",\"children\":[\"$\",\"$L2\",null,{\"attribute\":\"class\",\"defaultTheme\":\"system\",\"enableSystem\":true,\"disableTransitionOnChange\":true,\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$L5\",null,{\"children\":[[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"$L8\",null,{}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L9\",null,{}],[\"$\",\"$La\",null,{}]]}]}]}]}]}]]}]]}],{\"children\":[[\"slug\",\"how-to-bind-to-functions-from-xaml-in-net-maui\",\"c\"],[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[\"$Lb\",null,[\"$\",\"$Lc\",null,{\"children\":[\"$Ld\",[\"$\",\"$Le\",null,{\"promise\":\"$@f\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$L10\",null,{\"children\":\"$L11\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$L12\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$13\",null,{\"fallback\":null,\"children\":\"$L14\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$15\",[]],\"s\":false,\"S\":false}\n11:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nd:null\n16:I[80622,[],\"IconMark\"]\nf:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"How to bind to functions from XAML in .NET MAUI\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Learn how to bind to ViewModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates\"}],[\"$\",\"link\",\"2\",{\"rel\":\"canonical\",\"href\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\"}],[\"$\",\"meta\",\"3\",{\"property\":\"og:title\",\"content\":\"How to bind to functions from XAML in .NET MAUI\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:description\",\"content\":\"Learn how to bind to ViewModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:url\",\"content\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:site_name\",\"content\":\"Julian's Blog\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:image\",\"content\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"11\",{\"property\":\"article:published_time\",\"content\":\"2025-02-20T11:04:53.443Z\"}],[\"$\",\"meta\",\"12\",{\"property\":\"article:author\",\"content\":\"https://blog.ewers-peters.de/@ewerspej\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:title\",\"content\":\"How to bind to functions from XAML in .NET MAUI\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:description\",\"content\":\"Learn how to bind to Vie
179wModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates\"}],[\"$\",\""])</script>
179<script>self.__next_f.push([1,"meta\",\"16\",{\"name\":\"twitter:image\",\"content\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\"}],[\"$\",\"link\",\"17\",{\"rel\":\"icon\",\"href\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1688308456278/eC0e0vVNd.png\"}],[\"$\",\"$L16\",\"18\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n14:\"$f:metadata\"\n17:I[652,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"8974\",\"static/chunks/app/page-cc18ae1a3a7a5ed8.js\"],\"BlogHeader\"]\n18:T4b60,\u003ch1 id=\"heading-introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a method and pass a value to it so that I can decide how to display the value in the UI.\u003c/p\u003e\n\u003cp\u003eNormally, I would use a \u003cem\u003etrigger\u003c/em\u003e when it comes to simply checking whether a property has a certain value and then update the UI accordingly. But, what if the evaluation isn’t as simple as checking for a specific value? What if we want to use a range of values or apply some more complex logic?\u003c/p\u003e\n\u003cp\u003eOne thing that I would like to avoid when using the MVVM pattern is moving business logic to data objects or even into the UI layer, because that’s usually not where it belongs. I want my business logic to be separate from data and UI layers and I would like it to be unit testable, so having evaluation functions for objects or arguments is desired.\u003c/p\u003e\n\u003cp\u003eIn this blog post I’ll demonstrate how we can use a trick in order to bind to functions from XAML by leveraging the capabilities of \u003cem\u003edata triggers\u003c/em\u003e, \u003cem\u003emulti-bindings\u003c/em\u003e, \u003cem\u003emulti-value converters\u003c/em\u003e and \u003cem\u003efunction delegates\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eAs always, I’ve added the code used here to my \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples\"\u003eMAUI Samples repository\u003c/a\u003e, so that you can follow along.\u003c/p\u003e\n\u003ch1 id=\"heading-the-goal\"\u003eThe Goal\u003c/h1\u003e\n\u003cp\u003eThe idea is to check whether the \u003ccode\u003eCount\u003c/code\u003e property of type \u003ccode\u003eint\u003c/code\u003e of the already existing \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Models/BindingItem.cs\"\u003e\u003ccode\u003eBindingItem\u003c/code\u003e\u003c/a\u003e class is a prime number, and if it is, display the row the object resides in in a different color, in our case light green, without modifying the data class:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingItem\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estring\u003c/span\u003e Name { \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e; \u003cspan class=\"hljs-keyword\"\u003eset\u003c/span\u003e; }\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eint\u003c/span\u003e Count { \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e; \u003cspan class=\"hljs-keyword\"\u003eset\u003c/span\u003e; }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eCurrently, adding items simply displays a new row with the \u003ccode\u003eCount\u003c/code\u003e value for the new item:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961843055/aa225d06-a72c-466c-bfb0-1aa2b00c7b28.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003eHowever, we would like to highlight all rows that contain a prime number, like so:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003eThings like this can usually be done using triggers. Let’s have a look.\u003c/p\u003e\n\u003ch1 id=\"heading-data-triggers\"\u003eData Triggers\u003c/h1\u003e\n\u003cp\u003eA \u003ca target=\"_blank\" href=\"https://learn.microsoft.com/dotnet/maui/fundamentals/triggers#data-triggers\"\u003edata trigger\u003c/a\u003e uses a binding expression "])</script>
179<script>self.__next_f.push([1,"and a value to compare the property against and then applies a setter to update a specified set of visual properties of a control, like the background color:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Label\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eBinding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Count}\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"3\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"BackgroundColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe flaw of this approach is that we would need to know the value that we want to evaluate against for each item beforehand, but we don’t. In order to check whether an integer is actually a prime number requires applying some logic, which we usually cannot directly do using a data trigger, and, as mentioned above, this logic doesn’t belong into the data layer.\u003c/p\u003e\n\u003cp\u003eHowever, we can still make use of a data trigger to accomplish what we want, if we combine this with a converter that evaluates to \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003efalse\u003c/code\u003e based on an evaluation function for the given property:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003econverters:PrimeNumberToBoolConverter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:Key\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"IsPrimeConverter\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Label\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eBinding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Count, Converter={StaticResource IsPrimeConverter}}\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"True\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Backgroun
179dColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eWe could implement a simple \u003ccode\u003eIValueConverter\u003c/code\u003e that contains the logic for checking for prime numbers, but this would potentially mean moving logic into a converter, which is not desirable in most scenarios, because we may want to apply some business logic from the ViewModel, as well. So, this logic doesn’t belong into the UI layer, either.\u003c/p\u003e\n\u003cp\u003e💡 Yet, we can create a generalized converter that allows us to bind to a function delegate, which can then execute any kind of business logic from the ViewModel. This is exciting, let’s have a look.\u003c/p\u003e\n\u003ch1 id=\"heading-funcvaluetoboolconverter\"\u003eFuncValueToBoolConverter\u003c/h1\u003e\n\u003cp\u003eThe heart of our desired functionality is the \u003cem\u003eFuncValueToBoolConverter\u003c/em\u003e which is an \u003ca target=\"_blank\" href=\"https://l"])</script>
179<script>self.__next_f.push([1,"earn.microsoft.com/dotnet/api/microsoft.maui.controls.imultivalueconverter\"\u003eIMultiValueConverter\u003c/a\u003e implementation.\u003c/p\u003e\n\u003cp\u003eIn our case it only takes two values: The first one is a function delegate of type \u003ccode\u003eFunc\u0026lt;object, bool\u0026gt;\u003c/code\u003e, and the second value is an object which serves as the argument to be be passed to the function delegate:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003esealed\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eFuncValueToBoolConverter\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eIMultiValueConverter\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eConvert\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e[] values, Type targetType, \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e parameter, CultureInfo culture\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (values[\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e] \u003cspan class=\"hljs-keyword\"\u003eis\u003c/span\u003e Func\u0026lt;\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e\u0026gt; func)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e func(values[\u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e]);\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n    }\n\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e[] \u003cspan class=\"hljs-title\"\u003eConvertBack\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003evalue\u003c/span\u003e, Type[] targetTypes, \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e parameter, CultureInfo culture\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e [];\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis converter is a general-purpose \u003cem\u003emulti-value converter\u003c/em\u003e. Its purpose is to take a function delegate that can evaluate an object based on some business logic and either returns \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn our case, we will use a method from the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/ViewModels/BindingsViewModel.cs\"\u003eBindingsViewModel\u003c/a\u003e class, which we don’t have yet.\u003c/p\u003e\n\u003ch1 id=\"heading-prime-number-function\"\u003ePrime number function\u003c/h1\u003e\n\u003cp\u003eLet’s add a method which checks whether a number is prime to the ViewModel:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003epartial\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingsViewModel\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eObservableObject\u003c/span\u003e\n{ \n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003eprivate\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eIsPrime\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e number\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (number \u
179003cspan class=\"hljs-keyword\"\u003eis\u003c/span\u003e not \u003cspan class=\"hljs-keyword\"\u003eint\u003c/span\u003e intNumber || intNumber \u0026lt; \u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003efor\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003evar\u003c/span\u003e i = \u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e; i \u0026lt;= Math.Sqrt(intNumber); i++)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (intNumber % i == \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e)\n            {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n            }\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eNote\u003c/strong\u003e: The \u003ccode\u003eIsPrime()\u003c/code\u003e method takes in an argument of type \u003ccode\u003eobject\u003c/code\u003e, because that way, the \u003cem\u003eFuncValueToBoolConverter\u003c/em\u003e can be reused in different contexts. We could al"])</script>
179<script>self.__next_f.push([1,"so create a \u003cem\u003eFuncIntToBoolConverter\u003c/em\u003e or similar in order to avoid boxing the integer.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e⁉️ But wait, we cannot bind to methods, and, apart from that, this static method is \u003ccode\u003eprivate\u003c/code\u003e. Fortunately, C# allows us to expose this method as a \u003ccode\u003epublic\u003c/code\u003e function delegate auto-property \u003cem\u003e(which means that we can use it in a binding expression)\u003c/em\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003epartial\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingsViewModel\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eObservableObject\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-comment\"\u003e// expose the IsPrime() function as a delegate,\u003c/span\u003e\n    \u003cspan class=\"hljs-comment\"\u003e// note that this property must not be static\u003c/span\u003e\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e Func\u0026lt;\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e\u0026gt; IsPrimeFunc =\u0026gt; IsPrime;\n\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003eprivate\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eIsPrime\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e number\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-comment\"\u003e// see above\u003c/span\u003e\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eTime to put it all together and make the magic happen. 🪄\u003c/p\u003e\n\u003ch1 id=\"heading-putting-it-together\"\u003ePutting it together\u003c/h1\u003e\n\u003cp\u003eIn our page, we first add the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Converters/FuncValueToBoolConverter.cs\"\u003eFuncValueToBoolConverter\u003c/a\u003e as a static resource:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003econverters:FuncValueToBoolConverter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:Key\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"FuncValueToBoolConverter\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen, we can apply the converter together with a data trigger inside the \u003cem\u003eDataTemplate\u003c/em\u003e of a \u003cem\u003eCollectionView\u003c/em\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eCollectionView\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eItemsSource\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Items}\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eCollectionView.ItemTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTemplate\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:DataType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"models:BindingItem\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eGrid\u003c/span\u003e\n        \u003cspan class=\"hljs-attr\"\u003ePadding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"8\"\u003c/span\u003e\n        \u003cspan class=\"hljs-attr\"\u003eColumnDefinitions\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"*,*\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"hljs-comment\"\u003e\u0026lt;!-- skipping irrelevant parts for brevity --\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eGrid.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n          \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Grid\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"True\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger.Binding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eMultiBinding\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eConverter\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{StaticResource FuncValueToBoolConverter}\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n                \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eBinding\u003c/span\u003e\n                  \u003cspan class=\"hljs-attr\"\u003ePath\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"IsPrimeFunc\"\u003c/span\u003e\n                  \u003cspan class=\"hljs-attr\"\u003eSource\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{RelativeSource AncestorType={x:Type viewModel"])</script>
179<script>self.__next_f.push([1,"s:BindingsViewModel}}\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n                \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eBinding\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ePath\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Count\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eMultiBinding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger.Binding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger.Setters\u003c/span\u003e\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"BackgroundColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger.Setters\u003c/span\u003e\u0026gt;\u003c/span\u003e\n          \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n        \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eGrid.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eGrid\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eCollectionView.ItemTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eCollectionView\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNote the two separate bindings in the multi-binding. The first one must be pointing to the \u003ccode\u003eIsPrimeFunc\u003c/code\u003e property of the ViewModel \u003cem\u003e(using a\u003c/em\u003e \u003ca target=\"_blank\" href=\"https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings\"\u003e\u003cem\u003erelative binding\u003c/em\u003e\u003c/a\u003e\u003cem\u003e)\u003c/em\u003e and the second one binds to the \u003ccode\u003eCount\u003c/code\u003e property of the current item.\u003c/p\u003e\n\u003cp\u003eRunning the app now, we will be greeted with green rows for every prime number when adding new items to the collection:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003e🤩 Isn’t this awesome? We managed to bind to a function to apply complex logic within a data binding context. I’m very happy with the result.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eYou might be asking yourself whether this will also work with objects that have observable properties that frequently can change. The answer is: \u003cstrong\u003eYes, absolutely.\u003c/strong\u003e Multi-bindings are evaluated everytime one of the properties of the child bindings changes, as long as it’s an observable property.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"heading-conclusions-and-next-steps\"\u003eConclusions and next steps\u003c/h1\u003e\n\u003cp\u003eI’ve demonstrated how we can take advantage of MAUI’s built-in capabilities in order to bind to a ViewModel method. We’ve done this by exposing it as a function delegate property and then using it in the context of a multi-binding expression together with a multi-value converter that executes the method for us. This let’s us evaluate values of data objects without having to add logic to the data class and without having to add critical business logic to converters.\u003c/p\u003e\n\u003cp\u003eIf you enjoyed this blog \u003ca target=\"_blank\" href=\"https://hashnode.com/@ewerspej\"\u003epost\u003c/a\u003e, then follow me on \u003ca target=\"_blank\" href=\"https://www.linkedin.com/in/jewerspeters\"\u003e\u003cstrong\u003eLinkedIn\u003c/strong\u003e\u003c/a\u003e, subscribe to this \u003ca target=\"_blank\" href=\"https://hashnode.com/@ewerspej\"\u003e\u003cstrong\u003eblog\u003c/strong\u003e\u003c/a\u003e and star the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples\"\u003e\u003cstrong\u003eGitHub repository\u003c/strong\u003e\u003c/a\u003e for this post so you don't miss out on any future posts and developments. And remember that sharing is caring.\u003c/p\u003e\n19:T2951,# Introduction\n\nIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally s
179ometimes wish it were possible to bind directly to a method and pass a value to it so that I can decide how to display the value in the UI.\n\nNormally, I wo"])</script>
179<script>self.__next_f.push([1,"uld use a *trigger* when it comes to simply checking whether a property has a certain value and then update the UI accordingly. But, what if the evaluation isn’t as simple as checking for a specific value? What if we want to use a range of values or apply some more complex logic?\n\nOne thing that I would like to avoid when using the MVVM pattern is moving business logic to data objects or even into the UI layer, because that’s usually not where it belongs. I want my business logic to be separate from data and UI layers and I would like it to be unit testable, so having evaluation functions for objects or arguments is desired.\n\nIn this blog post I’ll demonstrate how we can use a trick in order to bind to functions from XAML by leveraging the capabilities of *data triggers*, *multi-bindings*, *multi-value converters* and *function delegates*.\n\nAs always, I’ve added the code used here to my [MAUI Samples repository](https://github.com/ewerspej/maui-samples), so that you can follow along.\n\n# The Goal\n\nThe idea is to check whether the `Count` property of type `int` of the already existing [`BindingItem`](https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Models/BindingItem.cs) class is a prime number, and if it is, display the row the object resides in in a different color, in our case light green, without modifying the data class:\n\n```csharp\npublic class BindingItem\n{\n    public string Name { get; set; }\n    public int Count { get; set; }\n}\n```\n\nCurrently, adding items simply displays a new row with the `Count` value for the new item:\n\n![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739961843055/aa225d06-a72c-466c-bfb0-1aa2b00c7b28.png align=\"center\")\n\nHowever, we would like to highlight all rows that contain a prime number, like so:\n\n![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png align=\"center\")\n\nThings like this can usually be done using triggers. Let’s have a look.\n\n# Data Triggers\n\nA [data trigger](https://learn.microsoft.com/dotnet/maui/fundamentals/triggers#data-triggers) uses a binding expression and a value to compare the property against and then applies a setter to update a specified set of visual properties of a control, like the background color:\n\n```xml\n\u003cLabel\u003e\n  \u003cLabel.Triggers\u003e\n    \u003cDataTrigger\n      TargetType=\"Label\"\n      Binding=\"{Binding Count}\"\n      Value=\"3\"\u003e\n      \u003cSetter Property=\"BackgroundColor\" Value=\"LightGreen\" /\u003e\n    \u003c/DataTrigger\u003e\n  \u003c/Label.Triggers\u003e\n\u003c/Label\u003e\n```\n\nThe flaw of this approach is that we would need to know the value that we want to evaluate against for each item beforehand, but we don’t. In order to check whether an integer is actually a prime number requires applying some logic, which we usually cannot directly do using a data trigger, and, as mentioned above, this logic doesn’t belong into the data layer.\n\nHowever, we can still make use of a data trigger to accomplish what we want, if we combine this with a converter that evaluates to `true` or `false` based on an evaluation function for the given property:\n\n```xml\n\u003cContentPage.Resources\u003e\n  \u003cconverters:PrimeNumberToBoolConverter x:Key=\"IsPrimeConverter\" /\u003e\n\u003c/ContentPage.Resources\u003e\n\n\u003cLabel\u003e\n  \u003cLabel.Triggers\u003e\n    \u003cDataTrigger\n      TargetType=\"Label\"\n      Binding=\"{Binding Count, Converter={StaticResource IsPrimeConverter}}\"\n      Value=\"True\"\u003e\n      \u003cSetter Property=\"BackgroundColor\" Value=\"LightGreen\" /\u003e\n    \u003c/DataTrigger\u003e\n  \u003c/Label.Triggers\u003e\n\u003c/Label\u003e\n```\n\nWe could implement a simple `IValueConverter` that contains the logic for checking for prime numbers, but this would potentially mean moving logic into a converter, which is not desirable in most scenarios, because we may want to apply some business logic from the ViewModel, as well. So, this logic doesn’t belong into the UI layer, either.\n\n💡 Yet, we can create a generalized converter that allows us to bind to a function delegate, which can then execute any kind of business logic from the ViewModel. This is exciting, let’s have a look.\n\n# FuncValueToBoolConver"])</script>
179<script>self.__next_f.push([1,"ter\n\nThe heart of our desired functionality is the *FuncValueToBoolConverter* which is an [IMultiValueConverter](https://learn.microsoft.com/dotnet/api/microsoft.maui.controls.imultivalueconverter) implementation.\n\nIn our case it only takes two values: The first one is a function delegate of type `Func\u003cobject, bool\u003e`, and the second value is an object which serves as the argument to be be passed to the function delegate:\n\n```csharp\npublic sealed class FuncValueToBoolConverter : IMultiValueConverter\n{\n    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\n    {\n        if (values[0] is Func\u003cobject, bool\u003e func)\n        {\n            return func(values[1]);\n        }\n\n        return false;\n    }\n\n    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n    {\n        return [];\n    }\n}\n```\n\nThis converter is a general-purpose *multi-value converter*. Its purpose is to take a function delegate that can evaluate an object based on some business logic and either returns `true` or `false`.\n\nIn our case, we will use a method from the [BindingsViewModel](https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/ViewModels/BindingsViewModel.cs) class, which we don’t have yet.\n\n# Prime number function\n\nLet’s add a method which checks whether a number is prime to the ViewModel:\n\n```csharp\npublic partial class BindingsViewModel : ObservableObject\n{ \n    private static bool IsPrime(object number)\n    {\n        if (number is not int intNumber || intNumber \u003c 2)\n        {\n            return false;\n        }\n    \n        for (var i = 2; i \u003c= Math.Sqrt(intNumber); i++)\n        {\n            if (intNumber % i == 0)\n            {\n                return false;\n            }\n        }\n    \n        return true;\n    }\n}\n```\n\n\u003e **Note**: The `IsPrime()` method takes in an argument of type `object`, because that way, the *FuncValueToBoolConverter* can be reused in different contexts. We could also create a *FuncIntToBoolConverter* or similar in order to avoid boxing the integer.\n\n⁉️ But wait, we cannot bind to methods, and, apart from that, this static method is `private`. Fortunately, C# allows us to expose this method as a `public` function delegate auto-property *(which means that we can use it in a binding expression)*:\n\n```csharp\npublic partial class BindingsViewModel : ObservableObject\n{\n    // expose the IsPrime() function as a delegate,\n    // note that this property must not be static\n    public Func\u003cobject, bool\u003e IsPrimeFunc =\u003e IsPrime;\n\n    private static bool IsPrime(object number)\n    {\n        // see above\n    }\n}\n```\n\nTime to put it all together and make the magic happen. 🪄\n\n# Putting it together\n\nIn our page, we first add the [FuncValueToBoolConverter](https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Converters/FuncValueToBoolConverter.cs) as a static resource:\n\n```xml\n  \u003cContentPage.Resources\u003e\n    \u003cconverters:FuncValueToBoolConverter x:Key=\"FuncValueToBoolConverter\" /\u003e\n  \u003c/ContentPage.Resources\u003e\n```\n\nThen, we can apply the converter together with a data trigger inside the *DataTemplate* of a *CollectionView*:\n\n```xml\n\u003cCollectionView ItemsSource=\"{Binding Items}\"\u003e\n  \u003cCollectionView.ItemTemplate\u003e\n    \u003cDataTemplate x:DataType=\"models:BindingItem\"\u003e\n      \u003cGrid\n        Padding=\"8\"\n        ColumnDefinitions=\"*,*\"\u003e\n        \u003c!-- skipping irrelevant parts for brevity --\u003e\n        \u003cGrid.Triggers\u003e\n\n          \u003cDataTrigger TargetType=\"Grid\" Value=\"True\"\u003e\n            \u003cDataTrigger.Binding\u003e\n              \u003cMultiBinding Converter=\"{StaticResource FuncValueToBoolConverter}\"\u003e\n                \u003cBinding\n                  Path=\"IsPrimeFunc\"\n                  Source=\"{RelativeSource AncestorType={x:Type viewModels:BindingsViewModel}}\" /\u003e\n                \u003cBinding Path=\"Count\" /\u003e\n              \u003c/MultiBinding\u003e\n            \u003c/DataTrigger.Binding\u003e\n            \u003cDataTrigger.Setters\u003e\n              \u003cSetter Property=\"Backgroun
179dColor\" Value=\"LightGreen\" /\u003e\n            \u003c/DataTrigger.Setters\u003e\n          \u003c/DataTrigger\u003e\n\n        \u003c"])</script>
179<script>self.__next_f.push([1,"/Grid.Triggers\u003e\n      \u003c/Grid\u003e\n    \u003c/DataTemplate\u003e\n  \u003c/CollectionView.ItemTemplate\u003e\n\u003c/CollectionView\u003e\n```\n\nNote the two separate bindings in the multi-binding. The first one must be pointing to the `IsPrimeFunc` property of the ViewModel *(using a* [*relative binding*](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings)*)* and the second one binds to the `Count` property of the current item.\n\nRunning the app now, we will be greeted with green rows for every prime number when adding new items to the collection:\n\n![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png align=\"center\")\n\n🤩 Isn’t this awesome? We managed to bind to a function to apply complex logic within a data binding context. I’m very happy with the result.\n\n\u003e You might be asking yourself whether this will also work with objects that have observable properties that frequently can change. The answer is: **Yes, absolutely.** Multi-bindings are evaluated everytime one of the properties of the child bindings changes, as long as it’s an observable property.\n\n# Conclusions and next steps\n\nI’ve demonstrated how we can take advantage of MAUI’s built-in capabilities in order to bind to a ViewModel method. We’ve done this by exposing it as a function delegate property and then using it in the context of a multi-binding expression together with a multi-value converter that executes the method for us. This let’s us evaluate values of data objects without having to add logic to the data class and without having to add critical business logic to converters.\n\nIf you enjoyed this blog [post](https://hashnode.com/@ewerspej), then follow me on [**LinkedIn**](https://www.linkedin.com/in/jewerspeters), subscribe to this [**blog**](https://hashnode.com/@ewerspej) and star the [**GitHub repository**](https://github.com/ewerspej/maui-samples) for this post so you don't miss out on any future posts and developments. And remember that sharing is caring.b:[\"$\",\"main\",null,{\"id\":\"main-content\",\"className\":\"min-h-screen bg-transparent\",\"children\":[[\"$\",\"$L17\",null,{\"publication\":{\"__typename\":\"Publication\",\"id\":\"633fdcd4e1c45ecede087cc5\",\"url\":\"https://blog.ewers-peters.de\",\"canonicalURL\":\"https://blog.ewers-peters.de\",\"urlPattern\":\"simple\",\"title\":\"Julian's Blog\",\"displayTitle\":null,\"hasBadges\":true,\"descriptionSEO\":\"Hi, I am Julian, a passionate app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. I like writing Clean Code using TDD. Here, I writ\",\"seo\":{\"title\":null,\"description\":null,\"__typename\":\"SEO\"},\"publicMembers\":{\"totalDocuments\":1,\"__typename\":\"PublicMembers\"},\"about\":{\"html\":\"\u003cp\u003eHi, I am Julian, a passionate app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. I like writing Clean Code using TDD. Here, I write about my journey as a developer.\u003c/p\u003e\\n\",\"text\":\"Hi, I am Julian, a passionate app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. I like writing Clean Code using TDD. Here, I write about my journey as a developer.\\n\",\"__typename\":\"Content\"},\"features\":{\"proTeam\":{\"isEnabled\":false,\"__typename\":\"ProTeamFeature\"},\"newsletter\":{\"isEnabled\":false,\"__typename\":\"NewsletterFeature\"},\"viewCount\":{\"isEnabled\":true,\"__typename\":\"ViewCountFeature\"},\"readTime\":{\"isEnabled\":true,\"__typename\":\"ReadTimeFeature\"},\"textSelectionSharer\":{\"isEnabled\":true,\"__typename\":\"TextSelectionSharerFeature\"},\"gptBotCrawling\":{\"__typename\":\"GPTBotCrawlingFeature\",\"isEnabled\":false},\"headlessCMS\":{\"isEnabled\":false,\"__typename\":\"HeadlessCMSFeature\"},\"__typename\":\"PublicationFeatures\"},\"aeoSettings\":{\"isEnabled\":false,\"llmsTxtEnabled\":true,\"crawlers\":{\"gptBot\":false,\"claudeBot\":true,\"perplexityBot\":true,\"googleExtended\":true,\"ccBot\":true,\"__typename\":\"AeoCrawlerSettings\"},\"__typename\":\"PublicationAeoSettings\"},\"metaTags\":null,\"ogMetaData\":{\"image\":null,\"__typename\":\"OpenGra
179phMetaData\"},\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"name\":\"Julian Ewers-Peters\",\"username\":\"ewerspej\",\"profilePicture\""])</script>
179<script>self.__next_f.push([1,":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\"},\"preferences\":{\"__typename\":\"PublicationPreferences\",\"logo\":null,\"darkMode\":{\"__typename\":\"DarkModePreferences\",\"logo\":null,\"enabled\":true},\"navbarItems\":[],\"enabledPages\":{\"__typename\":\"EnabledPages\",\"badges\":true,\"newsletter\":true,\"members\":true},\"layout\":\"stacked\",\"disableFooterBranding\":false,\"isSubscriptionModalDisabled\":false},\"favicon\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1688308456278/eC0e0vVNd.png\",\"headerColor\":\"\",\"integrations\":{\"fbPixelID\":null,\"fathomSiteID\":null,\"fathomCustomDomainEnabled\":false,\"fathomCustomDomain\":null,\"hotjarSiteID\":null,\"matomoSiteID\":null,\"matomoURL\":null,\"gaTrackingID\":null,\"gTagManagerID\":null,\"plausibleAnalyticsEnabled\":false,\"wmPaymentPointer\":null,\"koalaPublicKey\":null,\"msClarityID\":null,\"openPanelClientId\":null,\"consentManagement\":{\"enabled\":false,\"version\":1,\"privacyPolicyURL\":null,\"__typename\":\"ConsentManagement\"},\"__typename\":\"PublicationIntegrations\"},\"imprintV2\":{\"__typename\":\"ImprintV2\",\"html\":\"\u003cp\u003eThis blog is published by Julian Ewers-Peters and hosted by Hashnode. It is publicly available under the domain \u003ca href=\\\"https://blog.ewers-peters.de/\\\"\u003eblog.ewers-peters.de\u003c/a\u003e. You can find the \u003ca href=\\\"https://www.ewers-peters.de/imprint/\\\"\u003eImpressum\u003c/a\u003e \u003cem\u003e(external link)\u003c/em\u003e on the main website of Julian Ewers-Peters.\u003c/p\u003e\\n\"},\"postsCount\":{\"totalDocuments\":20,\"__typename\":\"PostConnection\"},\"isTeam\":true,\"links\":{\"twitter\":\"\",\"instagram\":\"\",\"github\":\"https://github.com/ewerspej\",\"website\":\"https://www.ewers-peters.de\",\"hashnode\":\"https://hashnode.com/@ewerspej\",\"youtube\":\"\",\"dailydev\":\"\",\"linkedin\":\"https://www.linkedin.com/in/jewerspeters\",\"mastodon\":\"https://hachyderm.io/@ewerspej\",\"facebook\":null,\"bluesky\":null,\"__typename\":\"PublicationLinks\"},\"domainInfo\":{\"__typename\":\"DomainInfo\",\"hashnodeSubdomain\":\"ewerspej\",\"domain\":{\"__typename\":\"Domain\",\"host\":\"blog.ewers-peters.de\",\"ready\":true},\"wwwPrefixedDomain\":null},\"redirectionRules\":[],\"customRules\":[],\"totalRecommendedPublications\":0,\"allowContributorEdits\":false,\"widgets\":[],\"post\":{\"__typename\":\"Post\",\"id\":\"67b70c5573190a50ec0bd979\",\"cuid\":\"cm7d8jhz700020ajua4g62kv0\",\"title\":\"How to bind to functions from XAML in .NET MAUI\",\"subtitle\":null,\"slug\":\"how-to-bind-to-functions-from-xaml-in-net-maui\",\"brief\":\"Introduction\\nIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a...\",\"featured\":false,\"publishedAt\":\"2025-02-20T11:04:53.443Z\",\"updatedAt\":null,\"url\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\",\"readTimeInMinutes\":6,\"views\":2038,\"reactionCount\":0,\"responseCount\":0,\"replyCount\":0,\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"name\":\"Julian Ewers-Peters\",\"username\":\"ewerspej\",\"deactivated\":false,\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"bio\":{\"html\":\"\u003cp\u003eI am a passionate mobile app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. \u003c/p\u003e\\n\",\"__typename\":\"Content\"},\"socialMediaLinks\":{\"website\":\"https://www.ewers-peters.de\",\"github\":\"https://github.com/ewerspej\",\"twitter\":\"\",\"facebook\":\"\",\"stackoverflow\":\"https://stackoverflow.com/users/4308455/ewerspej\",\"linkedin\":\"https://www.linkedin.com/in/jewerspeters/\",\"__typename\":\"SocialMediaLinks\"}},\"coAuthors\":[],\"seo\":{\"title\":\"How to bind to functions from XAML in .NET MAUI\",\"description\":\"Learn how to bind to Vie
179wModel functions from XAML in .NET MAUI using a combination of data triggers, multi-bindings, converters, and function delegates\",\"__typename\":\"SEO\"},\"coverImage\":{\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\",\"isPortrait\":false,\"attribution\":\"\",\"isAttributionHidden\":false,\"photographer\":\"\",\"__typename\":\"CoverImage\"},\"content\":{\"html\":\"$18\",\"markdown\":\"$19\",\"__typename\":\"Content\"},\"prefer"])</script>
179<script>self.__next_f.push([1,"ences\":{\"pinnedToBlog\":false,\"disableComments\":false,\"stickCoverToBottom\":false,\"isDelisted\":false,\"__typename\":\"PostPreferences\"},\"series\":null,\"tags\":[{\"id\":\"5d6ae470cfa64df7547fa340\",\"slug\":\"data-binding\",\"name\":\"data binding\",\"__typename\":\"Tag\"},{\"id\":\"62afa5bfd8afe74aa51fe160\",\"slug\":\"dotnet-maui\",\"name\":\"#dotnet-maui\",\"__typename\":\"Tag\"},{\"id\":\"62e13d7d657a4fae5a0e7856\",\"slug\":\"maui\",\"name\":\"#maui\",\"__typename\":\"Tag\"},{\"id\":\"586b3bb145a6fccdef0f17f2\",\"slug\":\"xaml\",\"name\":\"xaml\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94823\",\"slug\":\"user-interface\",\"name\":\"User Interface\",\"__typename\":\"Tag\"}],\"ogMetaData\":{\"image\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\",\"__typename\":\"OpenGraphMetaData\"},\"canonicalUrl\":null,\"hasLatexInPost\":false,\"faq\":[],\"features\":{\"tableOfContents\":{\"isEnabled\":true,\"items\":[{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-0\",\"level\":1,\"slug\":\"introduction\",\"title\":\"Introduction\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-1\",\"level\":1,\"slug\":\"the-goal\",\"title\":\"The Goal\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-2\",\"level\":1,\"slug\":\"data-triggers\",\"title\":\"Data Triggers\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-3\",\"level\":1,\"slug\":\"funcvaluetoboolconverter\",\"title\":\"FuncValueToBoolConverter\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-4\",\"level\":1,\"slug\":\"prime-number-function\",\"title\":\"Prime number function\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-5\",\"level\":1,\"slug\":\"putting-it-together\",\"title\":\"Putting it together\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-6\",\"level\":1,\"slug\":\"conclusions-and-next-steps\",\"title\":\"Conclusions and next steps\",\"parentId\":null}],\"__typename\":\"TableOfContents\"},\"__typename\":\"PostFeatures\"}},\"posts\":{\"edges\":[{\"node\":{\"__typename\":\"Post\",\"id\":\"67b70c5573190a50ec0bd979\",\"title\":\"How to bind to functions from XAML in .NET MAUI\",\"slug\":\"how-to-bind-to-functions-from-xaml-in-net-maui\",\"publishedAt\":\"2025-02-20T11:04:53.443Z\",\"cuid\":\"cm7d8jhz700020ajua4g62kv0\",\"url\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\",\"subtitle\":null,\"brief\":\"Introduction\\nIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a...\",\"readTimeInMinutes\":6,\"views\":2038,\"tags\":[{\"id\":\"5d6ae470cfa64df7547fa340\",\"slug\":\"data-binding\",\"name\":\"data binding\",\"__typename\":\"Tag\"},{\"id\":\"62afa5bfd8afe74aa51fe160\",\"slug\":\"dotnet-maui\",\"name\":\"#dotnet-maui\",\"__typename\":\"Tag\"},{\"id\":\"62e13d7d657a4fae5a0e7856\",\"slug\":\"maui\",\"name\":\"#maui\",\"__typename\":\"Tag\"},{\"id\":\"586b3bb145a6fccdef0f17f2\",\"slug\":\"xaml\",\"name\":\"xaml\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94823\",\"slug\":\"user-interface\",\"name\":\"User Interface\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"username\":\"ewerspej\",\"name\":\"Julian Ewers-Peters\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"followersCount\":156},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"669822ca3646a1b339524626\",\"title\":\"White Labeling .NET MAUI Apps\",\"slug\":\"white-labeling-net-maui-apps\",\"publishedAt\":\"2024-07-17T20:00:10.345Z\",\"cuid\":\"clyq9o67d000408l
17902r8z7vhh\",\"url\":\"https://blog.ewers-peters.de/white-labeling-net-maui-apps\",\"subtitle\":\"A starting point for customized client apps using a single code base\",\"brief\":\"Introduction\\nHave you ever come across two (or more) products that looked almost exactly the same and only differed in things like color and the company logo printed on them? This is a common practice called white labeling where one company develops .."])</script>
179<script>self.__next_f.push([1,".\",\"readTimeInMinutes\":15,\"views\":4028,\"tags\":[{\"id\":\"669822c8c763134581f2baee\",\"slug\":\"white-labeling\",\"name\":\"white-labeling\",\"__typename\":\"Tag\"},{\"id\":\"5794f65abecb9ebac0d5fc55\",\"slug\":\"dotnet\",\"name\":\"dotnet\",\"__typename\":\"Tag\"},{\"id\":\"62e13d7d657a4fae5a0e7856\",\"slug\":\"maui\",\"name\":\"#maui\",\"__typename\":\"Tag\"},{\"id\":\"64ef01a7e0dc6d75cfaccf3a\",\"slug\":\"rebranding\",\"name\":\"Rebranding\",\"__typename\":\"Tag\"},{\"id\":\"59c86596ea46c0424704f997\",\"slug\":\"customization\",\"name\":\"customization\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"username\":\"ewerspej\",\"name\":\"Julian Ewers-Peters\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"followersCount\":156},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1720778506691/48fecad5-d947-44b2-8446-b1db5657502a.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"66447315cec90639d090d60b\",\"title\":\"Easily add a video reel to your MAUI app (like Instagram)\",\"slug\":\"maui-video-reel\",\"publishedAt\":\"2024-05-15T08:32:20.999Z\",\"cuid\":\"clw7kcykn00030ajs8xlxdn81\",\"url\":\"https://blog.ewers-peters.de/maui-video-reel\",\"subtitle\":null,\"brief\":\"Introduction\\nSocial media apps like Instagram use full-page scrollable video controls called \\\"reels\\\" (named after the device used to store film - or garden hoses) which allow the user to swipe or scroll up and down in order to switch between videos (...\",\"readTimeInMinutes\":6,\"views\":1418,\"tags\":[{\"id\":\"56744721958ef13879b94aec\",\"slug\":\"instagram\",\"name\":\"instagram\",\"__typename\":\"Tag\"},{\"id\":\"65ffccc412e12dacdb715efc\",\"slug\":\"reels\",\"name\":\"reels\",\"__typename\":\"Tag\"},{\"id\":\"62e13d7d657a4fae5a0e7856\",\"slug\":\"maui\",\"name\":\"#maui\",\"__typename\":\"Tag\"},{\"id\":\"5794f65abecb9ebac0d5fc55\",\"slug\":\"dotnet\",\"name\":\"dotnet\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94823\",\"slug\":\"user-interface\",\"name\":\"User Interface\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"username\":\"ewerspej\",\"name\":\"Julian Ewers-Peters\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"followersCount\":156},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1715761226824/743882bb-d579-43fa-a9a3-624cf033738f.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"65cf80cd0c26fc9df719b029\",\"title\":\"Add automatic route registration to your .NET MAUI app\",\"slug\":\"add-automatic-route-registration-to-your-net-maui-app\",\"publishedAt\":\"2024-02-16T15:35:41.682Z\",\"cuid\":\"clsotakj6000109l60tbyf0pk\",\"url\":\"https://blog.ewers-peters.de/add-automatic-route-registration-to-your-net-maui-app\",\"subtitle\":\"The next step in simplified route management for .NET\",\"brief\":\"Introduction\\nIn December of 2023, I introduced the epj.RouteGenerator package which provides a facility to have route identifiers for .NET-based frontend applications auto-generated for you, taking away a few pain points with string-based route navig...\",\"readTimeInMinutes\":5,\"views\":2303,\"tags\":[{\"id\":\"5794f65abecb9ebac0d5fc55\",\"slug\":\"dotnet\",\"name\":\"dotnet\",\"__typename\":\"Tag\"},{\"id\":\"62afa5bfd8afe74aa51fe160\",\"slug\":\"dotnet-maui\",\"name\":\"#dotnet-maui\",\"__typename\":\"Tag\"},{\"id\":\"63a015cdd1e499a9b8afd0e1\",\"slug\":\"sourcegenerators\",\"name\":\"sourcegenerators\",\"__typename\":\"Tag\"},{\"id\":\"574125dadf1e4d3563843066\",\"slug\":\"navigation\",\"name\":\"navigation\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"username\":\"ewerspej\",\"name\":\"Julian Ewers-Peters\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"followersCount\":156},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1708090435324/af960263-03f5-42b7-a84c-91ec60ae442a.png\",\""])</script>
179<script>self.__next_f.push([1,"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"65816def99f070fc32f1d2ae\",\"title\":\"Introducing Route Generator for .NET\",\"slug\":\"introducing-route-generator-for-net\",\"publishedAt\":\"2023-12-19T10:18:23.068Z\",\"cuid\":\"clqc6z8ws000408l6azacajfq\",\"url\":\"https://blog.ewers-peters.de/introducing-route-generator-for-net\",\"subtitle\":\"How to avoid navigation hell by having Routes auto-generated for you\",\"brief\":\"Introduction\\nModern .NET applications have adopted the concept of routes for navigation. Usually, these routes are string-based, which has a couple of implications. One of the problems is the volatility of route names, which I am addressing in this b...\",\"readTimeInMinutes\":11,\"views\":2208,\"tags\":[{\"id\":\"63a015cdd1e499a9b8afd0e1\",\"slug\":\"sourcegenerators\",\"name\":\"sourcegenerators\",\"__typename\":\"Tag\"},{\"id\":\"5794f65abecb9ebac0d5fc55\",\"slug\":\"dotnet\",\"name\":\"dotnet\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"},{\"id\":\"5f2596db669da9610ee17dc2\",\"slug\":\"routes\",\"name\":\"routes\",\"__typename\":\"Tag\"},{\"id\":\"574125dadf1e4d3563843066\",\"slug\":\"navigation\",\"name\":\"navigation\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"633fda9b6971ffceb981f681\",\"username\":\"ewerspej\",\"name\":\"Julian Ewers-Peters\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\",\"followersCount\":156},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1702829964430/9154da3d-da17-4209-875f-a2df4b69e6a9.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"}],\"__typename\":\"PostConnection\"},\"redirectedPost\":null,\"staticPage\":null},\"posts\":[\"$b:props:children:0:props:publication:post\"],\"host\":\"blog.ewers-peters.de\"}],\"$L1a\",\"$L1b\",null,\"$L1c\",\"$L1d\",null,\"$L1e\"]}]\n1f:I[62031,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"ReadingModeToggle\"]\n20:I[81356,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"Image\"]\n21:I[52619,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"8974\",\"static/chunks/app/page-cc18ae1a3a7a5ed8.js\"],\"\"]\n22:I[33884,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"Avatar\"]\n23:I[33884,[\"590\","])</script>
179<script>self.__next_f.push([1,"\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"AvatarImage\"]\n24:I[33884,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"AvatarFallback\"]\n25:I[78655,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"TableOfContents\"]\n28:I[51285,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"CommentsSection\"]\n29:I[20730,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"default\"]\n1a:[\"$\",\"section\",null,{\"className\":\"py-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"grid gap-5 max-w-full\",\"children\":[\"$\",\"article\",null,{\"className\":\"w-full max-w-full rounded-xl border bg-card text-card-foreground\",\"children\":[[\"$\",\"div\",null,{\"className\":\"px-5 pt-12 pb-8 sm:px-8\",\"children\":[\"$\",\"header\",null,{\"className\":\"space-y-5\",\"children\":[\"$\",\"div\",null,{\"className\":\"space-y-4 text-center max-w-4xl mx-auto\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold leading-tight tracking-tight text-balance text-foreground\",\"children\":\"How to bind to functions from XAML in .NET MAUI\"}],null,[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center justify-center gap-3 text-sm text-muted-foreground\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2\",\"children\":[[\"$\",\"span\",null,{\"children\":\"Published\"}],[\"$\",\"time\",null,{\"dateTime\":\"2025-02-20T11:04:53.443Z\",\"children\":\"February 20, 2025\"}]]}],[[\"$\",\"span\",null,{\"className\":\"text-muted-foreground\",\"children\":\"•\"}],[\"$\",\"span\",null,{\"children\":[6,\" min "])</script>
179<script>self.__next_f.push([1,"read\"]}]],[\"$\",\"span\",null,{\"className\":\"text-muted-foreground\",\"children\":\"•\"}],[\"$\",\"$L1f\",null,{\"slug\":\"how-to-bind-to-functions-from-xaml-in-net-maui\"}]]}]]}]}]}],[\"$\",\"div\",null,{\"className\":\"w-full\",\"children\":[\"$\",\"$L20\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\",\"alt\":\"How to bind to functions from XAML in .NET MAUI\",\"width\":1200,\"height\":750,\"className\":\"w-full h-auto\",\"priority\":true}]}],[\"$\",\"div\",null,{\"className\":\"px-5 py-12 sm:px-8 space-y-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center gap-5\",\"children\":[[\"$\",\"div\",\"633fda9b6971ffceb981f681\",{\"className\":\"flex items-center gap-5\",\"children\":[false,[\"$\",\"div\",null,{\"className\":\"flex gap-3 items-start\",\"children\":[[\"$\",\"$L21\",null,{\"href\":\"https://hashnode.com/@ewerspej\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":[\"$\",\"$L22\",null,{\"className\":\"h-10 w-10 border border-border transition-opacity hover:opacity-80\",\"children\":[[\"$\",\"$L23\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png?auto=compress,format\u0026format=webp\",\"alt\":\"Julian Ewers-Peters\"}],[\"$\",\"$L24\",null,{\"className\":\"bg-muted text-muted-foreground font-semibold text-lg\",\"children\":\"J\"}]]}]}],[\"$\",\"div\",null,{\"className\":\"min-w-0\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2\",\"children\":[[\"$\",\"$L21\",null,{\"href\":\"https://hashnode.com/@ewerspej\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"font-medium text-foreground hover:text-primary transition-colors\",\"children\":\"Julian Ewers-Peters\"}],[\"$\",\"div\",null,{\"className\":\"flex items-center gap-1.5 text-sm\",\"children\":[null,[\"$\",\"$L21\",null,{\"href\":\"https://www.linkedin.com/in/jewerspeters/\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground hover:text-foreground transition-colors\",\"aria-label\":\"LinkedIn\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-linkedin\",\"aria-hidden\":true}]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"text-sm text-muted-foreground line-clamp-2\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp\u003eI am a passionate mobile app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. \u003c/p\u003e\\n\"}}]]}]]}]]}]]}]}],null,[\"$\",\"div\",null,{\"className\":\"relative\",\"children\":[[\"$\",\"aside\",null,{\"className\":\"hidden xl:block absolute top-0 left-0 h-full\",\"children\":[\"$\",\"nav\",null,{\"className\":\"sticky top-22 w-44\",\"children\":[\"$\",\"$L25\",null,{\"headings\":[{\"id\":\"heading-introduction\",\"text\":\"Introduction\",\"level\":1},{\"id\":\"heading-the-goal\",\"text\":\"The Goal\",\"level\":1},{\"id\":\"heading-data-triggers\",\"text\":\"Data Triggers\",\"level\":1},{\"id\":\"heading-funcvaluetoboolconverter\",\"text\":\"FuncValueToBoolConverter\",\"level\":1},{\"id\":\"heading-prime-number-function\",\"text\":\"Prime number function\",\"level\":1},{\"id\":\"heading-putting-it-together\",\"text\":\"Putting it together\",\"level\":1},{\"id\":\"heading-conclusions-and-next-steps\",\"text\":\"Conclusions and next steps\",\"level\":1}]}]}]}],[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto min-w-0 contain-[inline-size]\",\"children\":\"$L26\"}]]}],false,\"$L27\"]}]]}]}]}]}]}]\n1b:[\"$\",\"section\",null,{\"className\":\"pb-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"max-w-4xl w-full mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"rounded-xl border bg-card text-card-foreground p-5 sm:p-8\",\"children\":[\"$\",\"$L28\",null,{\"host\":\"blog.ewers-peters.de\",\"slug\":\"how-to-bind-to-functions-from-xaml-in-net-maui\",\"postUrl\":\"https://hashnode.com/posts/how-to-bind-to-functions-from-xaml-in-net-maui/67b70c5573190a50ec0bd979\",\"initialEdges\":[],\"initialPageInfo\":{\"hasNextPage\":false,\"endCursor\":\"10\",\"totalDocuments\":0,\"__typename\":\"PageInfo\"},\"totalComments\":0}]}]}]}]}]}]\n1c:[\"$\",\"section\",null,{\"className\":\"pb-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-"])</script>
179<script>self.__next_f.push([1,"[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"max-w-4xl w-full mx-auto\",\"children\":[\"$\",\"section\",null,{\"className\":\"space-y-6\",\"children\":[[\"$\",\"h2\",null,{\"className\":\"text-2xl font-bold tracking-tight text-balance\",\"children\":\"More from this blog\"}],[\"$\",\"div\",null,{\"className\":\"flex flex-col gap-6\",\"children\":[[\"$\",\"$L29\",\"669822ca3646a1b339524626\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:1:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L29\",\"66447315cec90639d090d60b\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:2:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L29\",\"65cf80cd0c26fc9df719b029\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:3:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L29\",\"65816def99f070fc32f1d2ae\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:4:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"div\",\"author-card\",{\"ref\":\"$undefined\",\"className\":\"rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 p-5 flex flex-col\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center text-center gap-5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center gap-3\",\"children\":[[\"$\",\"$L22\",null,{\"className\":\"h-16 w-16 shrink-0\",\"children\":[[\"$\",\"$L23\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1688308456278/eC0e0vVNd.png?auto=compress,format\u0026format=webp\",\"alt\":\"Publication avatar\"}],[\"$\",\"$L24\",null,{\"className\":\"bg-muted text-muted-foreground font-medium text-lg\",\"children\":\"J\"}]]}],[\"$\",\"div\",null,{\"className\":\"space-y-1\",\"children\":[[\"$\",\"p\",null,{\"className\":\"text-base font-semibold text-foreground leading-tight\",\"children\":\"Julian's Blog\"}],[\"$\",\"p\",null,{\"className\":\"text-sm text-muted-foreground\",\"children\":[20,\" posts\"]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-sm dark:prose-invert max-w-xs mx-auto text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp\u003eHi, I am Julian, a passionate app and software developer with a focus on C# .NET, Xamarin.Forms and .NET MAUI. I like writing Clean Code using TDD. Here, I write about my journey as a developer.\u003c/p\u003e\\n\"}}],[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center justify-center gap-3\",\"children\":[[],[[\"$\",\"a\",\"GitHub\",{\"href\":\"https://github.com/ewerspej\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"GitHub\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-github\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"LinkedIn\",{\"href\":\"https://www.linkedin.com/in/jewerspeters\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"LinkedIn\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-linkedin\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"Website\",{\"href\":\"https://www.ewers-peters.de\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Website\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-jelly fa-globe\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"Hashnode\",{\"href\":\"https://hashnode.com/@ewerspej\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Hashnode\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-hashnode\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"Mastodon\",{\"href\":\"https://hachyderm.io/@ewerspej\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Mastodon\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-mastodon\",\"aria-hidden\":true}]}]]]}]]}]}]]}]]}]}]}]}]}]\n2a:T47c,{\"@context\":\"https://schema.org\",\"@type\":\"Article\",\"url\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\",\"mainEntityOfPage\":\"https://blog.ewers-peters.de/how-to-bind-to-functions-from-xaml-in-net-maui\",\"headline\":\"How to bind to functions from XAML in .NET MAUI\",\"description\":\"Introduction\\nIn .NET MAUI, we sometimes face t"])</script>
179<script>self.__next_f.push([1,"he situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a...\",\"author\":{\"@type\":\"Person\",\"name\":\"Julian Ewers-Peters\",\"url\":\"https://hashnode.com/@ewerspej\",\"image\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1681658518017/JLn4ontCk.png\"},\"publisher\":{\"@type\":\"Organization\",\"name\":\"Julian's Blog\",\"url\":\"https://blog.ewers-peters.de\"},\"datePublished\":\"2025-02-20T11:04:53.443Z\",\"dateModified\":\"2025-02-20T11:04:53.443Z\",\"image\":{\"@type\":\"ImageObject\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1740049255733/b809a78a-457c-4b8d-a316-fde8cdec4b59.png\"},\"keywords\":\"data binding, #dotnet-maui, #maui, xaml, User Interface\"}1d:[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$2a\"}}]\n2b:T4c31,\u003ch1 id=\"heading-introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn .NET MAUI, we sometimes face the situation that we would like to evaluate a specific value, e.g. a property from a child object, against some logic from the ViewModel. I personally sometimes wish it were possible to bind directly to a method and pass a value to it so that I can decide how to display the value in the UI.\u003c/p\u003e\n\u003cp\u003eNormally, I would use a \u003cem\u003etrigger\u003c/em\u003e when it comes to simply checking whether a property has a certain value and then update the UI accordingly. But, what if the evaluation isn’t as simple as checking for a specific value? What if we want to use a range of values or apply some more complex logic?\u003c/p\u003e\n\u003cp\u003eOne thing that I would like to avoid when using the MVVM pattern is moving business logic to data objects or even into the UI layer, because that’s usually not where it belongs. I want my business logic to be separate from data and UI layers and I would like it to be unit testable, so having evaluation functions for objects or arguments is desired.\u003c/p\u003e\n\u003cp\u003eIn this blog post I’ll demonstrate how we can use a trick in order to bind to functions from XAML by leveraging the capabilities of \u003cem\u003edata triggers\u003c/em\u003e, \u003cem\u003emulti-bindings\u003c/em\u003e, \u003cem\u003emulti-value converters\u003c/em\u003e and \u003cem\u003efunction delegates\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eAs always, I’ve added the code used here to my \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples\" rel=\"noopener ugc\"\u003eMAUI Samples repository\u003c/a\u003e, so that you can follow along.\u003c/p\u003e\n\u003ch1 id=\"heading-the-goal\"\u003eThe Goal\u003c/h1\u003e\n\u003cp\u003eThe idea is to check whether the \u003ccode\u003eCount\u003c/code\u003e property of type \u003ccode\u003eint\u003c/code\u003e of the already existing \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Models/BindingItem.cs\" rel=\"noopener ugc\"\u003e\u003ccode\u003eBindingItem\u003c/code\u003e\u003c/a\u003e class is a prime number, and if it is, display the row the object resides in in a different color, in our case light green, without modifying the data class:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingItem\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estring\u003c/span\u003e Name { \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e; \u003cspan class=\"hljs-keyword\"\u003eset\u003c/span\u003e; }\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eint\u003c/span\u003e Count { \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e; \u003cspan class=\"hljs-keyword\"\u003eset\u003c/span\u003e; }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eCurrently, adding items simply displays a new row with the \u003ccode\u003eCount\u003c/code\u003e value for the new item:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961843055/aa225d06-a72c-466c-bfb0-1aa2b00c7b28.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003eHowever, we would like to highlight all rows that contain a prime number, like so:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003eThings like this can usually be done using triggers. Let’s have a look.\u003c/p\u003e\n\u003ch1 id=\"heading-data-triggers\"\u003eData Triggers\u003c/h1\u003e\n\u003cp\u003eA \u003ca target=\"_blank\" href=\"ht"])</script>
179<script>self.__next_f.push([1,"tps://learn.microsoft.com/dotnet/maui/fundamentals/triggers#data-triggers\" rel=\"noopener ugc\"\u003edata trigger\u003c/a\u003e uses a binding expression and a value to compare the property against and then applies a setter to update a specified set of visual properties of a control, like the background color:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Label\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eBinding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Count}\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"3\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"BackgroundColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe flaw of this approach is that we would need to know the value that we want to evaluate against for each item beforehand, but we don’t. In order to check whether an integer is actually a prime number requires applying some logic, which we usually cannot directly do using a data trigger, and, as mentioned above, this logic doesn’t belong into the data layer.\u003c/p\u003e\n\u003cp\u003eHowever, we can still make use of a data trigger to accomplish what we want, if we combine this with a converter that evaluates to \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003efalse\u003c/code\u003e based on an evaluation function for the given property:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003econverters:PrimeNumberToBoolConverter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:Key\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"IsPrimeConverter\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Label\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eBinding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Count, Converter={StaticResource IsPrimeConverter}}\"\u003c/span\u003e\n      \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"True\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Backgroun
179dColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLabel\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eWe could implement a simple \u003ccode\u003eIValueConverter\u003c/code\u003e that contains the logic for checking for prime numbers, but this would potentially mean moving logic into a converter, which is not desirable in most scenarios, because we may want to apply some business logic from the ViewModel, as well. So, this logic doesn’t belong into the UI layer, either.\u003c/p\u003e\n\u003cp\u003e💡 Yet, we can create a generalized converter that allows us to bind to a function delegate, which can then execute any kind of business logic from the ViewModel. This is exciting, let’s have a look.\u003c/p\u003e\n\u003ch1 id=\"heading-funcvaluetoboolconverter\"\u003eFuncValueToBoolConvert"])</script>
179<script>self.__next_f.push([1,"er\u003c/h1\u003e\n\u003cp\u003eThe heart of our desired functionality is the \u003cem\u003eFuncValueToBoolConverter\u003c/em\u003e which is an \u003ca target=\"_blank\" href=\"https://learn.microsoft.com/dotnet/api/microsoft.maui.controls.imultivalueconverter\" rel=\"noopener ugc\"\u003eIMultiValueConverter\u003c/a\u003e implementation.\u003c/p\u003e\n\u003cp\u003eIn our case it only takes two values: The first one is a function delegate of type \u003ccode\u003eFunc\u0026lt;object, bool\u0026gt;\u003c/code\u003e, and the second value is an object which serves as the argument to be be passed to the function delegate:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003esealed\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eFuncValueToBoolConverter\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eIMultiValueConverter\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eConvert\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e[] values, Type targetType, \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e parameter, CultureInfo culture\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (values[\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e] \u003cspan class=\"hljs-keyword\"\u003eis\u003c/span\u003e Func\u0026lt;\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e\u0026gt; func)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e func(values[\u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e]);\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n    }\n\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e[] \u003cspan class=\"hljs-title\"\u003eConvertBack\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003evalue\u003c/span\u003e, Type[] targetTypes, \u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e parameter, CultureInfo culture\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e [];\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis converter is a general-purpose \u003cem\u003emulti-value converter\u003c/em\u003e. Its purpose is to take a function delegate that can evaluate an object based on some business logic and either returns \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn our case, we will use a method from the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/ViewModels/BindingsViewModel.cs\" rel=\"noopener ugc\"\u003eBindingsViewModel\u003c/a\u003e class, which we don’t have yet.\u003c/p\u003e\n\u003ch1 id=\"heading-prime-number-function\"\u003ePrime number function\u003c/h1\u003e\n\u003cp\u003eLet’s add a method which checks whether a number is prime to the ViewModel:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003epartial\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingsViewModel\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eObservableObject\u003c/span\u003e\n{ \n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003eprivate\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eIsPrime\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e number\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (number \u
179003cspan class=\"hljs-keyword\"\u003eis\u003c/span\u003e not \u003cspan class=\"hljs-keyword\"\u003eint\u003c/span\u003e intNumber || intNumber \u0026lt; \u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003efor\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003evar\u003c/span\u003e i = \u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e; i \u0026lt;= Math.Sqrt(intNumber); i++)\n        {\n            \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (intNumber % i == \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e)\n            {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e;\n            }\n        }\n\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eNote\u003c/strong\u003e: The \u003ccode\u003eIsP"])</script>
179<script>self.__next_f.push([1,"rime()\u003c/code\u003e method takes in an argument of type \u003ccode\u003eobject\u003c/code\u003e, because that way, the \u003cem\u003eFuncValueToBoolConverter\u003c/em\u003e can be reused in different contexts. We could also create a \u003cem\u003eFuncIntToBoolConverter\u003c/em\u003e or similar in order to avoid boxing the integer.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e⁉️ But wait, we cannot bind to methods, and, apart from that, this static method is \u003ccode\u003eprivate\u003c/code\u003e. Fortunately, C# allows us to expose this method as a \u003ccode\u003epublic\u003c/code\u003e function delegate auto-property \u003cem\u003e(which means that we can use it in a binding expression)\u003c/em\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-csharp\"\u003e\u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003epartial\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBindingsViewModel\u003c/span\u003e : \u003cspan class=\"hljs-title\"\u003eObservableObject\u003c/span\u003e\n{\n    \u003cspan class=\"hljs-comment\"\u003e// expose the IsPrime() function as a delegate,\u003c/span\u003e\n    \u003cspan class=\"hljs-comment\"\u003e// note that this property must not be static\u003c/span\u003e\n    \u003cspan class=\"hljs-keyword\"\u003epublic\u003c/span\u003e Func\u0026lt;\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e\u0026gt; IsPrimeFunc =\u0026gt; IsPrime;\n\n    \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-keyword\"\u003eprivate\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003ebool\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eIsPrime\u003c/span\u003e(\u003cspan class=\"hljs-params\"\u003e\u003cspan class=\"hljs-keyword\"\u003eobject\u003c/span\u003e number\u003c/span\u003e)\u003c/span\u003e\n    {\n        \u003cspan class=\"hljs-comment\"\u003e// see above\u003c/span\u003e\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eTime to put it all together and make the magic happen. 🪄\u003c/p\u003e\n\u003ch1 id=\"heading-putting-it-together\"\u003ePutting it together\u003c/h1\u003e\n\u003cp\u003eIn our page, we first add the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples/blob/main/MauiSamples/MauiSamples/Converters/FuncValueToBoolConverter.cs\" rel=\"noopener ugc\"\u003eFuncValueToBoolConverter\u003c/a\u003e as a static resource:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003econverters:FuncValueToBoolConverter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:Key\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"FuncValueToBoolConverter\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContentPage.Resources\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen, we can apply the converter together with a data trigger inside the \u003cem\u003eDataTemplate\u003c/em\u003e of a \u003cem\u003eCollectionView\u003c/em\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eCollectionView\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eItemsSource\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{Binding Items}\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eCollectionView.ItemTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTemplate\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ex:DataType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"models:BindingItem\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eGrid\u003c/span\u003e\n        \u003cspan class=\"hljs-attr\"\u003ePadding\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"8\"\u003c/span\u003e\n        \u003cspan class=\"hljs-attr\"\u003eColumnDefinitions\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"*,*\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"hljs-comment\"\u003e\u0026lt;!-- skipping irrelevant parts for brevity --\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eGrid.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n          \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eTargetType\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Grid\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"True\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger.Binding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eMultiBinding\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eConverter\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{StaticResource FuncValueToBoolConverter}\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\n                \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eBinding\u003c/span\u003e\n                  \u003cspan class=\"hljs-"])</script>
179<script>self.__next_f.push([1,"attr\"\u003ePath\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"IsPrimeFunc\"\u003c/span\u003e\n                  \u003cspan class=\"hljs-attr\"\u003eSource\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"{RelativeSource AncestorType={x:Type viewModels:BindingsViewModel}}\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n                \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eBinding\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ePath\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"Count\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eMultiBinding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger.Binding\u003c/span\u003e\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eDataTrigger.Setters\u003c/span\u003e\u0026gt;\u003c/span\u003e\n              \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eSetter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eProperty\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"BackgroundColor\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eValue\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"LightGreen\"\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n            \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger.Setters\u003c/span\u003e\u0026gt;\u003c/span\u003e\n          \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTrigger\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\n        \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eGrid.Triggers\u003c/span\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eGrid\u003c/span\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eDataTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eCollectionView.ItemTemplate\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eCollectionView\u003c/span\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNote the two separate bindings in the multi-binding. The first one must be pointing to the \u003ccode\u003eIsPrimeFunc\u003c/code\u003e property of the ViewModel \u003cem\u003e(using a\u003c/em\u003e \u003ca target=\"_blank\" href=\"https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings\" rel=\"noopener ugc\"\u003e\u003cem\u003erelative binding\u003c/em\u003e\u003c/a\u003e\u003cem\u003e)\u003c/em\u003e and the second one binds to the \u003ccode\u003eCount\u003c/code\u003e property of the current item.\u003c/p\u003e\n\u003cp\u003eRunning the app now, we will be greeted with green rows for every prime number when adding new items to the collection:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1739961869013/cfb73ecd-e119-42dd-aac2-a2d2a13bc933.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003cp\u003e🤩 Isn’t this awesome? We managed to bind to a function to apply complex logic within a data binding context. I’m very happy with the result.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eYou might be asking yourself whether this will also work with objects that have observable properties that frequently can change. The answer is: \u003cstrong\u003eYes, absolutely.\u003c/strong\u003e Multi-bindings are evaluated everytime one of the properties of the child bindings changes, as long as it’s an observable property.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"heading-conclusions-and-next-steps\"\u003eConclusions and next steps\u003c/h1\u003e\n\u003cp\u003eI’ve demonstrated how 
179we can take advantage of MAUI’s built-in capabilities in order to bind to a ViewModel method. We’ve done this by exposing it as a function delegate property and then using it in the context of a multi-binding expression together with a multi-value converter that executes the method for us. This let’s us evaluate values of data objects without having to add logic to the data class and without having to add critical business logic to converters.\u003c/p\u003e\n\u003cp\u003eIf you enjoyed this blog \u003ca target=\"_blank\" href=\"https://hashnode.com/@ewerspej\" rel=\"noopener ugc\"\u003epost\u003c/a\u003e, then follow me on \u003ca target=\"_blank\" href=\"https://www.linkedin.com/in/jewerspeters\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eLinkedIn\u003c/strong\u003e\u003c/a\u003e, subscribe to this \u003ca target=\"_blank\" href=\"https://hashnode.com/@ewerspej\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eblog\u003c/strong\u003e\u003c/a\u003e and star the \u003ca target=\"_blank\" href=\"https://github.com/ewerspej/maui-samples\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eGitHub repository\u003c/strong\u003e\u003c/a\u003e for this post so you don't miss out on any future posts and developments. And remember that sharing is caring.\u003c/p\u003e\n26:[\"$\",\"div\",null,{\"className\":\"prose dark:prose-invert prose-pre:bg-foregr"])</script>
179<script>self.__next_f.push([1,"ound prose-pre:text-background prose-pre:border prose-pre:border-border prose-pre:rounded prose-pre:px-5 prose-pre:py-4 dark:prose-pre:bg-muted dark:prose-pre:text-foreground dark:prose-pre:border-border prose-pre:overflow-x-auto max-w-none text-base sm:text-lg [\u0026\u003ediv\u003ep:first-child]:mt-0 [\u0026\u003ediv\u003ep:first-child]:pt-0 min-w-0 wrap-break-word [\u0026_a]:break-all **:max-w-full\",\"children\":[[],[\"$\",\"div\",null,{\"suppressHydrationWarning\":true,\"dangerouslySetInnerHTML\":{\"__html\":\"$2b\"}}],[],\"$L2c\",\"$L2d\",\"$L2e\",\"$L2f\",\"$L30\"]}]\n27:[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto border-t border-border pt-8 space-y-5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center gap-2\",\"children\":[[\"$\",\"$L21\",\"5d6ae470cfa64df7547fa340\",{\"href\":\"/tag/data-binding\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"data-binding\"]}],[\"$\",\"$L21\",\"62afa5bfd8afe74aa51fe160\",{\"href\":\"/tag/dotnet-maui\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"dotnet-maui\"]}],[\"$\",\"$L21\",\"62e13d7d657a4fae5a0e7856\",{\"href\":\"/tag/maui\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"maui\"]}],[\"$\",\"$L21\",\"586b3bb145a6fccdef0f17f2\",{\"href\":\"/tag/xaml\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"xaml\"]}],[\"$\",\"$L21\",\"56744721958ef13879b94823\",{\"href\":\"/tag/user-interface\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"user-interface\"]}]]}],[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2 text-sm text-muted-foreground\",\"title\":\"2,038 views\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-chart-simple h-4 w-4\",\"aria-hidden\":true}],[\"$\",\"span\",null,{\"className\":\"font-medium\",\"children\":[\"2.0K\",\" views\"]}]]}]]}]\n31:I[32478,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"CodeHighlighter\"]\n32:I[7714,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"EmbedHandler\"]\n33:I[54960,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"ImageZoom\"]\n34:I[66
17971,[\"590\",\"static/chunks/0dbeb660-9e82e998135"])</script>
179<script>self.__next_f.push([1,"52576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"MathJaxHandler\"]\n35:I[91337,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"MermaidHandler\"]\n2c:[\"$\",\"$L31\",null,{}]\n2d:[\"$\",\"$L32\",null,{}]\n2e:[\"$\",\"$L33\",null,{}]\n2f:[\"$\",\"$L34\",null,{\"hasLatex\":false}]\n30:[\"$\",\"$L35\",null,{}]\n36:Tc98,M268.379 39q-6.322 0-10.35-4.108-4.002-4.134-4.002-10.483 0-6.296 4.002-10.43 4-4.135 10.163-4.135 6.028 0 9.817 3.734 3.814 3.735 3.814 9.497v2.8h-24.248v-4.054h17.633q-.187-2.747-2.054-4.508-1.868-1.76-4.962-1.76-3.28-.001-5.388 2.374-2.108 2.373-2.108 6.375 0 4.188 2.348 6.695 2.347 2.481 5.895 2.481 1.68 0 2.934-.453a8.5 8.5 0 0 0 2.321-1.307q1.041-.854 2.268-2.614l4.694 2.96q-1.707 2.748-3.494 4.135a12.7 12.7 0 0 1-3.975 2.081q-2.187.72-5.308.72m-33.496 0q-5.575 0-9.283-4.028-3.681-4.029-3.681-10.484 0-6.43 3.761-10.51 3.76-4.108 9.47-4.108 2.8 0 5.175 1.2 2.4 1.174 3.628 3.148l.026-.08V0h7.016v38.2h-6.722v-4.722l.64.8-.694.133q-1.28 2.108-3.788 3.361Q237.924 39 234.883 39m1.627-6.002q3.308 0 5.495-2.428t2.188-6.135q0-3.762-2.214-6.109-2.214-2.374-5.469-2.374-3.28 0-5.522 2.347-2.214 2.348-2.214 6.163 0 3.76 2.214 6.162 2.214 2.374 5.522 2.374M205.811 39q-6.376 0-10.537-4.161-4.161-4.188-4.161-10.404 0-6.321 4.161-10.456 4.188-4.135 10.537-4.135 6.348 0 10.537 4.161 4.188 4.161 4.188 10.43 0 6.216-4.188 10.404Q212.186 39 205.811 39m0-6.162q3.307 0 5.575-2.374 2.267-2.374 2.267-6.055 0-3.707-2.294-6.056-2.267-2.347-5.548-2.347-3.308 0-5.575 2.374-2.268 2.348-2.268 6.029t2.241 6.055q2.268 2.374 5.602 2.374M162.586 38.2V10.644h6.802v5.015l-.8-.987h.906q1.308-2.187 3.762-3.494 2.48-1.335 5.228-1.334 4.615 0 7.256 2.8 2.667 2.776 2.667 7.603V38.2h-7.069V21.794q0-2.854-1.387-4.401t-4.134-1.547q-2.614 0-4.402 1.974-1.76 1.974-1.76 4.881V38.2zm-30.181 0V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.069V21.794q0-2.827-1.387-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.814 2.027-1.814 4.962v15.391zm-14.416.8q-2.882 0-5.015-.64-2.108-.614-3.761-1.84-1.654-1.228-2.668-2.695l-.987-1.467 4.695-3.174.773 1.067a9.1 9.1 0 0 0 1.788 1.814 8.2 8.2 0 0 0 2.32 1.2q1.308.4 3.068.4 2.161 0 3.441-.8 1.308-.8 1.307-2.294 0-1.36-1.147-2.108-1.12-.747-5.068-1.654-5.335-1.253-7.656-3.334-2.32-2.107-2.321-5.362 0-3.76 3.148-6.002 3.148-2.267 7.923-2.267 2.454 0 4.348.507t3.388 1.493q1.493.988 2.454 2.188l.987 1.2-4.482 3.255-.667-.774a7.4 7.4 0 0 0-1.627-1.387 6.4 6.4 0 0 0-2.054-.88 9.5 9.5 0 0 0-2.401-.294q-1.974 0-3.201.8-1.2.774-1.2 1.921 0 1.254 1.12 2.027 1.147.748 5.229 1.681 5.334 1.227 7.629 3.308 2.294 2.055 2.294 5.308 0 4.029-3.228 6.43Q123.217 39 117.989 39M87.61 39q-4.508 0-7.335-2.454-2.828-2.481-2.828-6.482 0-4.188 3.254-6.669t8.483-2.48q2.082 0 4.028.426 1.974.427 3.361 1.12v-1.6q0-2.588-1.813-4.082-1.788-1.493-4.615-1.493-1.628 0-2.801.373a8.7 8.7 0 0 0-2.188.987q-.987.614-1.84 1.414l-.854.8-4.081-3.788 1.307-1.173a18.7 18.7 0 0 1 2.934-2.108q1.601-.933 3.601-1.44t4.589-.507q5.868 0 9.229 2.88 3.361 2.882 3.361 8.084V38.2h-6.749v-5.922l1.654 2.187h-1.76q-1.308 2.055-3.655 3.308Q90.545 39 87.611 39m1.814-4.988q3.148 0 5.149-2.187 2"])</script>
179<script>self.__next_f.push([1,"-2.215 2-5.309v-.08q-1.12-.693-2.72-1.094a13 13 0 0 0-3.388-.426q-2.828 0-4.562 1.2-1.734 1.173-1.734 3.521 0 2.028 1.468 3.201t3.787 1.174M49 38.2V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.07V21.794q0-2.827-1.386-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.815 2.027-1.814 4.962v15.391z1e:[\"$\",\"footer\",null,{\"className\":\"px-4 pt-8 pb-20 sm:px-8 text-muted-foreground\",\"aria-label\":\"Footer\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-4xl mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center gap-4 text-center text-sm\",\"children\":[[\"$\",\"section\",null,{\"id\":\"impressum\",\"className\":\"w-full max-w-2xl pt-3 text-left text-sm text-muted-foreground text-center [\u0026_*]:text-center [\u0026_*]:mx-auto\",\"aria-label\":\"Impressum\",\"children\":[[\"$\",\"div\",null,{\"className\":\"prose prose-sm prose-stone dark:prose-invert max-w-none text-muted-foreground prose-a:text-muted-foreground prose-a:no-underline prose-a:transition prose-a:hover:text-foreground [\u0026_ul]:list-none [\u0026_ol]:list-none\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp\u003eThis blog is published by Julian Ewers-Peters and hosted by Hashnode. It is publicly available under the domain \u003ca href=\\\"https://blog.ewers-peters.de/\\\" target=\\\"_blank\\\" rel=\\\"noopener ugc\\\"\u003eblog.ewers-peters.de\u003c/a\u003e. You can find the \u003ca href=\\\"https://www.ewers-peters.de/imprint/\\\" target=\\\"_blank\\\" rel=\\\"noopener ugc\\\"\u003eImpressum\u003c/a\u003e \u003cem\u003e(external link)\u003c/em\u003e on the main website of Julian Ewers-Peters.\u003c/p\u003e\"}}],[\"$\",\"div\",null,{\"className\":\"my-6 h-px w-full bg-border\",\"aria-hidden\":\"true\"}]]}],[\"$\",\"p\",null,{\"className\":\"font-medium\",\"children\":[[\"$\",\"time\",null,{\"dateTime\":\"2026\",\"children\":[[\"$\",\"span\",null,{\"className\":\"font-sans\",\"children\":\"©\"}],\" \",2026]}],\" \",\"Julian's Blog\"]}],[\"$\",\"nav\",null,{\"aria-label\":\"Footer navigation\",\"children\":[\"$\",\"ul\",null,{\"className\":\"flex flex-wrap justify-center gap-4 text-sm tracking-wide\",\"children\":[[\"$\",\"li\",\"/members\",{\"children\":[\"$\",\"$L21\",null,{\"href\":\"/members\",\"className\":\"transition hover:text-foreground\",\"target\":\"$undefined\",\"rel\":\"$undefined\",\"children\":\"Members\"}]}],[\"$\",\"li\",\"/archive\",{\"children\":[\"$\",\"$L21\",null,{\"href\":\"/archive\",\"className\":\"transition hover:text-foreground\",\"target\":\"$undefined\",\"rel\":\"$undefined\",\"children\":\"Archive\"}]}],[\"$\",\"li\",\"https://hashnode.com/privacy-policy\",{\"children\":[\"$\",\"$L21\",null,{\"href\":\"https://hashnode.com/privacy-policy\",\"className\":\"transition hover:text-foreground\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":\"Privacy\"}]}],[\"$\",\"li\",\"https://hashnode.com/terms\",{\"children\":[\"$\",\"$L21\",null,{\"href\":\"https://hashnode.com/terms\",\"className\":\"transition hover:text-foreground\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":\"Terms\"}]}]]}]}],[\"$\",\"div\",null,{\"className\":\"pt-8 flex justify-center gap-6\",\"children\":[[\"$\",\"$L21\",null,{\"href\":\"/sitemap.xml\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"flex items-center gap-2 text-sm transition hover:text-foreground\",\"title\":\"Sitemap\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-sitemap\"}],[\"$\",\"span\",null,{\"children\":\"Sitemap\"}]]}],[\"$\",\"$L21\",null,{\"href\":\"/rss.xml\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"flex items-center gap-2 text-sm transition hover:text-foreground\",\"title\":\"RSS Feed\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-rss\"}],[\"$\",\"span\",null,{\"children\":\"RSS\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"pt-10 text-center\",\"children\":[\"$\",\"$L21\",null,{\"href\":\"https://hashnode.com/?utm_source=https%3A%2F%2Fblog.ewers-peters.de\u0026utm_medium=referral\u0026utm_campaign=blog_footer_logo\u0026utm_content=logo\",\"target\":\"_blank\",\"rel\":\"noopener\",\"aria-label\":\"Visit Hashnode (opens in a new tab)\",\"children\":[\"$\",\"svg\",null,{\"fill\":\"none\",\"viewBox\":\"0 0 282 39\",\"aria-hidden\":\"true\",\"className\":\"h-6 w-auto min-w-[9.5rem] text-muted-foreground transition hover:text-foreground\",\"children\":[[\"$\",\"path\",null,{\"fill\":\"currentColor\",\"d\":\"M2.68 13.032c-3.573 3.505-3.573 9.363 0 12.936L13.032 36.32c3.505 3.573 9.363 3.573 12.936 0L36.32 25.968c3.573-3.573 3.573-9.43
1791"])</script>
179<script>self.__next_f.push([1," 0-12.936L25.968 2.68c-3.573-3.573-9.431-3.573-12.936 0zm12.211 1.935c2.507-2.521 6.582-2.544 9.104-.038s2.544 6.582.038 9.104-6.582 2.544-9.104.038-2.544-6.582-.038-9.104\"}],[\"$\",\"path\",null,{\"fill\":\"currentColor\",\"d\":\"$36\"}]]}],\"className\":\"inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [\u0026_svg]:pointer-events-none [\u0026_svg]:size-4 [\u0026_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-10 rounded-md text-xl px-5 py-4\",\"ref\":null}]}]]}]}]}]\n"])</script>
179</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.