.netshellmaui

.net Maui How can I capture a press/click event on a shell FlyoutItem


So what I want to do is capture the press/click event on a flyout menu item so that I can cause menu items to appear or disappear. This is linked to an issue I described at .net Maui databinding to shell flyout item IsVisible property which was happily solved.

It would seem that the FlyOutItem does not have a click handler assigned, unlike the MenuItem. I tried using the MenuItem, but I couldn't get it to work (perhaps I did it wrong - see later)

I looked at using a behaviour, but the usage was beyond my puny human mind. I looked at using handlers, ahhh my poor head.

Since clicking/pressing on the FlyoutItemtem causes something to happen surely I can get hold of that event and add my own bit of code.

Here is some basic code I'm using:

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"
    xmlns:parameters="clr-namespace:TSDZ2Monitor.Pages.Parameters"
    xmlns:assistlevels="clr-namespace:TSDZ2Monitor.Pages.Parameters.AssistLevels"
    xmlns:graphvariables="clr-namespace:TSDZ2Monitor.Pages.Parameters.GraphVariables"
  
    Shell.FlyoutBehavior="Flyout"
    FlyoutHeaderBehavior="Default"
    FlyoutVerticalScrollMode="Auto"
    FlyoutBackgroundColor="{StaticResource FlyoutBackgroundColor}">

  <Shell.BindingContext>
    <local:ControlMenuViewModelM />
  </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 ShowMainMenu}"
    ContentTemplate="{DataTemplate pages:BluetoothPage}" />

  <ShellContent 
    Title="Tracks"
    Icon="tracks.svg"
    IsVisible="{Binding ShowMainMenu}"
    ContentTemplate="{DataTemplate pages:TracksPage}" />

  <ShellContent 
    Title="Parameters"                         
    Icon="parameters.svg"
    IsVisible="{Binding ShowMainMenu}"
                                                <-------this what I want to intercept
    ContentTemplate="{DataTemplate pages:ParametersPage}" />

  <ShellContent 
    Title="Settings"
    Icon="settings.svg"
    IsVisible="{Binding ShowMainMenu}"
    ContentTemplate="{DataTemplate pages:SettingsPage}" />

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

  
  
  <ShellContent 
    Title="Assist Level"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:AssistLevelPage}" />
  
  <ShellContent 
    Title="Assist Level Power"
    IsVisible="{Binding ShowAssistLevels}"
    ContentTemplate="{DataTemplate assistlevels:AssistLevelPowerPage}" />
  
  <ShellContent
    Title="Assist Level Torque"
    IsVisible="{Binding ShowAssistLevels}"
    ContentTemplate="{DataTemplate assistlevels:AssistLevelTorquePage}" />
  
  <ShellContent 
    Title="Assist LevelCadence"
    IsVisible="{Binding ShowAssistLevels}"
    ContentTemplate="{DataTemplate assistlevels:AssistLevelCadencePage}" />
  
  <ShellContent 
    Title="Assist LevelEMTB"
    IsVisible="{Binding ShowAssistLevels}"
    ContentTemplate="{DataTemplate assistlevels:AssistLevelEMTBPage}" />
    

  <ShellContent 
    Title="Battery"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:BatteryPage}" />

  <ShellContent 
    Title="Motor"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:MotorPage}" />

  <ShellContent 
    Title="Motor Temperature"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:MotorTemperaturePage}" />
  
  <ShellContent 
    Title="SoC"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:SoCPage}" />
  
  <ShellContent 
    Title="Startup Boost"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:StartupBoostPage}" />
  
  <ShellContent 
    Title="Street Mode"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:StreetModePage}" />
  
  <ShellContent 
    Title="Technical"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:TechnicalPage}" />
  
  <ShellContent 
    Title="Torque Sensor"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:TorqueSensorPage}" />
  
  <ShellContent 
    Title="Trip Memories"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:TripMemoriesPage}" />
  
  <ShellContent 
    Title="Various"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:VariousPage}" />
  
  <ShellContent 
    Title="Wheel"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:WheelPage}" />
  
  <ShellContent 
    Title="Graph Variables"
    IsVisible="{Binding ShowParameters}"
    ContentTemplate="{DataTemplate parameters:GraphVariablesPage}" />
  
  <ShellContent 
    Title="Battery Current Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsBatteryCurrentPage}" />
  
  <ShellContent 
    Title="Battery Voltage Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsBatteryVoltagePage}" />
  
  <ShellContent 
    Title="Cedence Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsCadencePage}" />
  
  <ShellContent 
    Title="Human Power Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsHumanPowerPage}" />
  
  <ShellContent 
    Title="Motor Current Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorCurrentPage}" />
  
  <ShellContent 
    Title="Motor FOC Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorFOCPage}" />
  
  <ShellContent 
    Title="Motor Power Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorPowerPage}" />
  
  <ShellContent 
    Title="Motor PWM Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorPWMPage}" />
  
  <ShellContent 
    Title="Motor Speed Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorSpeedPage}" />
  
  <ShellContent 
    Title="Motor Temperature Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsMotorTemperaturePage}" />
  
  <ShellContent 
    Title="Speed Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsSpeedPage}" />
  
  <ShellContent 
    Title="Trip Distance Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsTripDistancePage}" />
  
  <ShellContent 
    Title="Watts / Km Graph"
    IsVisible="{Binding ShowGraphParameters}"
    ContentTemplate="{DataTemplate graphvariables:VarsWattsKmPage}" />
  
</Shell>

ViewModels/ControlMenuViewModel.cs

namespace TSDZ2Monitor;

public partial class ControlMenuViewModel : ObservableObject
{
  [ObservableProperty]
  bool showMainMenu = true;

  [ObservableProperty]
  bool showParameters = false;

  [ObservableProperty]
  bool showAssistLevels = false;

  [ObservableProperty]
  bool showGraphParameters = false;

  public ICommand ShowParametersCommand => new Command(ChangeMenuControl);
  public void ChangeMenuControl()
  {
    Console.WriteLine($"Before {ShowMainMenu} {ShowParameters} {ShowAssistLevels} {ShowGraphParameters}");
    ShowMainMenu = !ShowMainMenu;
    ShowParameters = !ShowParameters;
    Console.WriteLine($"After  {ShowMainMenu} {ShowParameters}  {ShowAssistLevels} {ShowGraphParameters}");
  }
}

I can turn on and off the menus manually and from a button on a display page (Thanks to ColeX - MSFT), but I'm stumped here. Glad to hear of any ideas, including refactoring

Oh here's what I did with a MenuItem

  <MenuItem 
    Text="Parameters - by menuitem"
    
    Command="{Binding ShowParametersCommand}" />

It crashed my app

[Choreographer] Skipped 984 frames!  The application may be doing too much work on its main thread.
**System.ArgumentOutOfRangeException:** 'Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')'

and then again

??? It failed at the end of the method to change MenuControl(). I have no idea what index it is referring to!

Whatever, I would prefer to use the FlyoutItem, but if needs be I'll use the MenuItem (perhaps it's a better use case anyway?

Sorry, I am aware there are two issues here, but they are linked.

G


Solution

  • I noted (finally) this little gem in the docs for shell:

    Note: 
    
    There's also a Shell.FlyoutItemIsVisible attached property, which can be set on FlyoutItem, MenuItem, Tab, and ShellContent objects.
    

    There are in fact a number of properties in operation here:

    Flyout items are visible in the flyout by default. However, an item can be hidden in the flyout with the FlyoutItemIsVisible property, and removed from the flyout with the IsVisible property:
    

    and also the shell.FlyoutItemIsVisible just mentioned.

    Using this solves my problem. Doh!