title: Why do Switch and ListView controls in MAUI not update with 2-way binding?
body: <p>This question is about two MAUI controls (<code>Switch</code> and <code>ListView</code>) - I'm asking about them both in the same question as I'm <em>expecting</em> the root cause of the problem to be the same for both controls. It's entirely possible that they're different problems that just share some common symptoms though. (<code>CollectionView</code> has similar issues, but other confounding factors that make it trickier to demonstrate.)</p>
  <p>I'm using 2-way data binding in my MAUI app: changes to the data can either come directly from the user, or from a background polling task that checks whether the canonical data has been changed elsewhere. The problem I'm facing is that changes to the view model are not <em>visually</em> propagated to the <code>Switch.IsToggled</code> and <code>ListView.SelectedItem</code> properties, even though the controls do raise events showing that they've &quot;noticed&quot; the property changes. Other controls (e.g. <code>Label</code> and <code>Checkbox</code>) <em>are</em> visually updated, indicating that the view model notification is working fine and the UI itself is generally healthy.</p>
  <p>Build environment: Visual Studio 2022 17.2.0 preview 2.1<br />
  App environment: Android, either emulator &quot;Pixel 5 - API 30&quot; or a real Pixel 6</p>
  <p>The sample code is all below, but the fundamental question is whether this a bug somewhere in my code (do I need to &quot;tell&quot; the controls to update themselves for some reason?) or possibly a bug in MAUI (in which case I should presumably report it)?</p>
  <h1>Sample code</h1>
  <p>The sample code below can be added directly a &quot;File new project&quot; MAUI app (with a name of &quot;MauiPlayground&quot; to use the same namespaces), or it's all available <a href="https://github.com/jskeet/DemoCode/tree/main/MauiPlayground" rel="noreferrer">from my demo code repo</a>. Each example is independent of the other - you can try just one. (Then update <code>App.cs</code> to set <code>MainPage</code> to the right example.)</p>
  <p>Both examples have a very simple situation: a control with two-way binding to a view-model, and a button that updates the view-model property (to simulate &quot;the data has been modified elsewhere&quot; in the real app). In both cases, the control remains unchanged visually.</p>
  <p>Note that I've specified <code>{Binding ..., Mode=TwoWay}</code> in both cases, even though that's the default for those properties, just to be super-clear that that <em>isn't</em> the problem.</p>
  <p>The <code>ViewModelBase</code> code is shared by both examples, and is simply a convenient way of raising <code>INotifyPropertyChanged.PropertyChanged</code> without any extra dependencies:</p>
  <p><strong>ViewModelBase.cs:</strong></p>
  <pre class="lang-cs prettyprint-override"><code>using System.ComponentModel;
  using System.Runtime.CompilerServices;
  
  namespace MauiPlayground;
  
  public class ViewModelBase : INotifyPropertyChanged
  {
      public event PropertyChangedEventHandler PropertyChanged;
  
      public bool SetProperty&lt;T&gt;(ref T field, T value, [CallerMemberName] string name = null)
      {
          if (EqualityComparer&lt;T&gt;.Default.Equals(field, value))
          {
              return false;
          }
          field = value;
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
          return true;
      }
  }
  </code></pre>
  <h2>Switch sample code</h2>
  <p><strong>SwitchDemo.xaml</strong></p>
  <pre class="lang-xml prettyprint-override"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
  &lt;ContentPage xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
               xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
               x:Class=&quot;MauiPlayground.SwitchDemo&quot;&gt;
      &lt;StackLayout&gt;
          &lt;Label Text=&quot;Switch binding demo&quot; /&gt;
          &lt;HorizontalStackLayout&gt;
              &lt;Switch x:Name=&quot;switchControl&quot;
                      IsToggled=&quot;{Binding Toggled, Mode=TwoWay}&quot;
                      Toggled=&quot;Toggled&quot; /&gt;
              &lt;CheckBox IsChecked=&quot;{Binding Toggled, Mode=TwoWay}&quot; /&gt;
              &lt;Label Text=&quot;{Binding Toggled}&quot; /&gt;
          &lt;/HorizontalStackLayout&gt;
  
          &lt;Button Text=&quot;Toggle&quot; Clicked=&quot;Toggle&quot; /&gt;
          &lt;Label x:Name=&quot;manualLabel1&quot; Text=&quot;Value set in button click handler&quot; /&gt;
          &lt;Label x:Name=&quot;manualLabel2&quot; Text=&quot;Value set in toggled handler&quot; /&gt;
      &lt;/StackLayout&gt;
  &lt;/ContentPage&gt;
  </code></pre>
  <p><strong>SwitchDemo.cs</strong></p>
  <pre class="lang-cs prettyprint-override"><code>namespace MauiPlayground;
  
  public partial class SwitchDemo : ContentPage
  {
      public SwitchDemo()
      {
          InitializeComponent();
          BindingContext = new ViewModel();
      }
  
      private void Toggle(object sender, EventArgs e)
      {
          var vm = (ViewModel)BindingContext;
          vm.Toggled = !vm.Toggled;
          manualLabel1.Text = $&quot;Set in click handler: {switchControl.IsToggled}&quot;;
      }
  
      private void Toggled(object sender, ToggledEventArgs e) =&gt;
          manualLabel2.Text = $&quot;Set in toggled handler: {switchControl.IsToggled}&quot;;
  
      private class ViewModel : ViewModelBase
      {
          private bool toggled;
          public bool Toggled
          {
              get =&gt; toggled;
              set =&gt; SetProperty(ref toggled, value);
          }
      }
  }
  </code></pre>
  <p>Screenshot of the emulator after clicking on the &quot;Toggle&quot; button, which updates the view-model:</p>
  <p><a href="https://i.stack.imgur.com/CfR7A.png" rel="noreferrer"><img src="https://i.stack.imgur.com/CfR7A.png" alt="Demonstration of Switch issue" /></a></p>
  <p>Notes:</p>
  <ul>
  <li>The checkbox (bound to the same VM property) has updated</li>
  <li>The label next to the checkbox (bound to the same VM property) has updated</li>
  <li>The label below the button indicates that <code>switch.IsToggled</code> is true</li>
  <li>The label below that indicates that the <code>Switch.Toggled</code> event has been raised</li>
  <li>The <code>Switch</code> itself has not changed visible state</li>
  </ul>
  <p>Clicking on the <code>Switch</code> control directly <em>does</em> visually toggle it.</p>
  <h2>ListView sample code</h2>
  <p><strong>ListViewDemo.xaml</strong></p>
  <pre class="lang-xml prettyprint-override"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
  &lt;ContentPage xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
               xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
               x:Class=&quot;MauiPlayground.ListViewDemo&quot;&gt;
      &lt;StackLayout&gt;
          &lt;Label Text=&quot;ListView binding demo&quot; /&gt;
          &lt;ListView x:Name=&quot;listView&quot; ItemsSource=&quot;{Binding Items}&quot;
                    SelectedItem=&quot;{Binding SelectedItem, Mode=TwoWay}&quot;
                    VerticalOptions=&quot;Start&quot;
                    ItemSelected=&quot;ItemSelected&quot;/&gt;
          &lt;Label Text=&quot;{Binding SelectedItem}&quot; /&gt;
          &lt;Button Text=&quot;Toggle&quot; Clicked=&quot;Toggle&quot; /&gt;
          &lt;Label x:Name=&quot;manualLabel1&quot; Text=&quot;Text set in button click handler&quot; /&gt;
          &lt;Label x:Name=&quot;manualLabel2&quot; Text=&quot;Text set in item selected handler&quot; /&gt;
      &lt;/StackLayout&gt;
  &lt;/ContentPage&gt;
  </code></pre>
  <p><strong>ListViewDemo.cs</strong></p>
  <pre class="lang-cs prettyprint-override"><code>namespace MauiPlayground;
  
  public partial class ListViewDemo : ContentPage
  {
      public ListViewDemo()
      {
          InitializeComponent();
          BindingContext = new ViewModel();
      }
  
      private void Toggle(object sender, EventArgs e)
      {
          var vm = (ViewModel)BindingContext;
          vm.SelectedItem = vm.SelectedItem == &quot;First&quot; ? &quot;Second&quot; : &quot;First&quot;;
          manualLabel1.Text = $&quot;Set in click handler: {listView.SelectedItem}&quot;;
      }
  
      private void ItemSelected(object sender, EventArgs e) =&gt;
          manualLabel2.Text = $&quot;Set in item selected handler: {listView.SelectedItem}&quot;;
  
      private class ViewModel : ViewModelBase
      {
          public List&lt;string&gt; Items { get; } = new List&lt;string&gt; { &quot;First&quot;, &quot;Second&quot; };
  
          private string selectedItem = &quot;First&quot;;
          public string SelectedItem
          {
              get =&gt; selectedItem;
              set =&gt; SetProperty(ref selectedItem, value);
          }
      }
  }
  </code></pre>
  <p>Screenshot of the emulator after clicking on the &quot;Toggle&quot; button, which updates the view-model:</p>
  <p><a href="https://i.stack.imgur.com/Z52zP.png" rel="noreferrer"><img src="https://i.stack.imgur.com/Z52zP.png" alt="Demonstration of ListView issue" /></a></p>
  <p>Notes:</p>
  <ul>
  <li>The label below the list view (bound to the same VM property) has updated</li>
  <li>The label below the button indicates that <code>listView.SelectedItem</code> has the new value</li>
  <li>The label below that indicates that the <code>ListView.ItemSelected</code> event has been raised</li>
  <li>The <code>ListView</code> itself appears to have <em>no</em> selected item</li>
  </ul>
  <p>Interestingly, the list view <em>does</em> actually change appearance: before clicking on the button, the first item is visually selected (in orange). Selecting an item from the list manually updates all the properties, but we <em>don't</em> see the selected item in orange.</p>