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

Time:06-07

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

CodePudding user response:

I've noted that if I set the IsVisible flags to false then I get an error about Active Shell Item not set. Have you added any Shell Items to your Shell?

I'm suspecting that the IsVisible flag is causing the render to fail if set false on the first item(s) in the AppShell.xaml list

I just tested and indeed if the first item is attached to a flag then my app crashes, however if I don't let the first iten use the IsVisible flag then the design works as expected.

This is using the MenuItem to control the display of items. I would still like a solution to using FlyoutItem.

I may try setting the text value of a dummy first item in the shell list to blank.

Workaround, workarounds ...

Edit: I noticed on rereading the docs that;

The MenuItem class has a Clicked event, and a Command property. Therefore, MenuItem objects enable scenarios that execute an action in response to the MenuItem being tapped.

https://docs.microsoft.com/en-us/dotnet/maui/fundamentals/shell/flyout

Guess I'm barking up the wrong tree :(

I'm still getting issues with the first item in whatever underlying list is being used is not there - index out of bounds.

Further EDIT after experimentation:

So I created an almost blank Maui solution. I had a basic MainPage.

In the AppShell.xaml I first removed the ShellItem:

<Shell
    x:Class="ShellItemsTest.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:ShellItemsTest"
    Shell.FlyoutBehavior="Flyout">


</Shell>

This went into "break" mode and gave the error message:

System.InvalidOperationException
  Message=Active Shell Item not set. Have you added any Shell Items to your Shell?

Ok, so fair enough

Then I added a new Shell item and set it's property IsVisible="False"

    <ShellContent
        Title="Home"
        IsVisible="False"
        ContentTemplate="{DataTemplate local:MainPage}"
        Route="MainPage" />

This too broke and gave the same message.

I don't know what to think here? Is this by design or should is it a bug. I'm inclined to think the latter as my expectation would be that there is a shell item, it just shouldn't be rendered?

I then experimented with two then three shell items having different combinations of IsVisible true/false.

The upshot seems to be that if no items are visible then get a break and error.

If at least one item set visible, no matter what the position then ok.

If two items are set false and the third set true the app runs, but if I change the third item to false (under hot reload) then the app goes into break mode and I also see the error:

**System.ArgumentOutOfRangeException:** 'Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')'

The moral of the story seems to be that you must have at least one flyout item visible at any time.

I do not know if there are any timing issues involved here - is it working asynchronously.

I will raise a report in the github for .net maui and see what the devs say. :)

G

CodePudding user response:

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!

  • Related