Home > database >  .net Maui databinding to shell flyout item IsVisible property
.net Maui databinding to shell flyout item IsVisible property

Time:05-31

So I have a flyout menu and I have a need to make flyout items disappear on certain conditions. To help me develop my ideas and understanding I have a flyout with 6 items, one of which is titled Bluetooth. I have created a button in my first flyout page called ShowParameters. I can cause this to make a property in the viewmodel be true or false. This seems to work fine. I have bound the Bluetooth flyout item's IsVisible property to this ShowParameters property. This is not updating the IsVisible property on the click of the button. It seems that PropertyChanged?.Invoke is not linked to the Flyout item or the Flyout item isn't linked to the property, though I can cause this to appear or disappear from within the xaml itself. Clearly being a noob at this stuff I've done something really stupid. Could someone point out how stupid I am please ;)

AppShell.xaml

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="TSDZ2Monitor.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:sys="clr-namespace:System;assembly=netstandard"
  
    xmlns:local="clr-namespace:TSDZ2Monitor"

    xmlns:pages="clr-namespace:TSDZ2Monitor.Pages"
  
    Shell.FlyoutBehavior="Flyout"
    FlyoutHeaderBehavior="Default"
    FlyoutVerticalScrollMode="Auto"
    FlyoutBackgroundColor="{StaticResource FlyoutBackgroundColor}">

  <Shell.BindingContext>
    <local:ShowParametersViewModel/>
  </Shell.BindingContext>

  <Shell.FlyoutHeaderTemplate>
    <DataTemplate>
      <Grid BackgroundColor="{StaticResource FlyoutBackgroundColor}"
            HeightRequest="200">
        <Image 
               HeightRequest="200"
               Source="bicycle.svg"
               Margin="10, 10, 10, 10"
               Opacity="0.6" />
        <Label Text="TSDZ2 Monitor"
               TextColor="White"
               FontAttributes="Bold" />
      </Grid>
    </DataTemplate>
  </Shell.FlyoutHeaderTemplate>

  <Shell.FlyoutFooterTemplate>
    <DataTemplate>
      <StackLayout>
        <Label Text="TSDZ2"
               TextColor="GhostWhite"
               FontAttributes="Bold"
               HorizontalOptions="Center" />
        <Label Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat='{0:MMMM dd, yyyy}'}"
               TextColor="GhostWhite"
               HorizontalOptions="Center" />
      </StackLayout>
    </DataTemplate>
  </Shell.FlyoutFooterTemplate>


  <Shell.ItemTemplate>
    <DataTemplate>
      <Grid ColumnDefinitions="0.2*, 0.8*">
        <Image Grid.Column="0" 
               Source="{Binding FlyoutIcon}"
               Margin="0, 20, 0, 10"
               VerticalOptions="Center"
               HeightRequest="30" />
        <Label Grid.Column="1"
               Text="{Binding Title}"
               TextColor="Yellow"
               FontSize="20"
               FontAttributes="Bold"
               VerticalTextAlignment="Center" />
      </Grid>
    </DataTemplate>
  </Shell.ItemTemplate>


  <ShellContent 
    Title="Display"
    Icon="speedometer.svg"
    ContentTemplate="{DataTemplate pages:DisplayPage}" />
  
  <ShellContent 
    Title="Bluetooth"
    Icon="bluetooth.svg"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate pages:BluetoothPage}" />



  <ShellContent 
    Title="About"
    Icon="about.svg"
    ContentTemplate="{DataTemplate pages:AboutPage}" />

</Shell>

DisplayPage.xaml in folder Pages

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             
             xmlns:local="clr-namespace:TSDZ2Monitor"
             
             x:Class="TSDZ2Monitor.Pages.DisplayPage"
             Title="Display Page">

  <ContentPage.BindingContext>
    <local:ShowParametersViewModel />
  </ContentPage.BindingContext>

  <StackLayout>
    <Label Text="Main Display"
           VerticalOptions="Center" 
           HorizontalOptions="Center" />
    
    <Button Text="Show parameters" 
            FontSize="20"
            Command="{Binding ShowParametersCommand}"/>
  </StackLayout>
</ContentPage>

my ShowParametersViewModel.cs (in folder ViewModels)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace TSDZ2Monitor
{
  //public class ShowParametersViewModel : BindableObject
  public class ShowParametersViewModel : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    bool showParameters = true;
    public bool ShowParameters
    {
      get { return showParameters; }
      set
      {
        if (value == showParameters) return;
        showParameters = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ShowParameters"));
      }
    }

    public ICommand ShowParametersCommand => new Command(ChangeShowParameters);

    public void ChangeShowParameters()
    {
      Console.WriteLine($"Before {ShowParameters}");
      ShowParameters = !ShowParameters;
      Console.WriteLine($"After {ShowParameters}");
     }
  }
}

If I change the Shell.BindingContext in AppShell.xaml to

  <Shell.BindingContext>
    <local:ShowParametersViewModel  ShowParameters="false"/>
  </Shell.BindingContext>

it makes the Bluetooth flyout menu item disappear and vice versa

For now you might want to check out the repo https://github.com/gfmoore/TSDZ2Monitor but this may well change.

CodePudding user response:

Code looks good. Perhaps a bug in Shell - symptom suggests it doesn't rebuild flyout items list when visibility of a ShellContent changes.

Let's see if we can force it.

In App.xaml.cs (the cross-platform one, where MainPage is set):

    public void ForcePageLayout()
    {
        (MainPage as IView).InvalidateArrange();
    }

In ShowParameters setter, at end:

    ((App)Application.Current).ForcePageLayout();

If that fixes it, then please open an issue at .net maui github. Refer back to this Q&A.

CodePudding user response:

The issue happened because you're manipulating the different data(viewmodel) between pages but we need shared data .

The following two parts of code is totally identical .

<Shell.BindingContext>
    <local:ShowParametersViewModel />
</Shell.BindingContext>
public AppShell()
{
    InitializeComponent();
    BindingContext = new ShowParametersViewModel();
}

Actually it creates a new instance of viewmodel class , and set it to BindingContext, the same way in DisplayPage .

So there are two different viewmodels , you just change A , but B never get changed.

Solution

Initialize ShowParametersViewModel in code behind instead of in xaml(DisplayPage ), so that they will share the same viewmodel .

public DisplayPage ()
{
    InitializeComponent();
    this.BindingContext = AppShell.Current.BindingContext;
}
  • Related