1

Is there a solution to set IsVisible to a ShellItem dynamically? I have to following problem: I have a flyout menu with contains to ShellItem, one is Login and the other one is Logout.

<ShellItem x:Name="LoginItem" Route="login" Title="{resources:Translate Login}" Icon="icon_LogIn.png" >
    <ShellContent ContentTemplate="{DataTemplate views:Account.LoginPage}"/>
</ShellItem>

<ShellItem x:Name="LogoutItem" Route="Logout" Title="{resources:Translate Logout}" Icon="icon_logOut.png" >
    <ShellContent ContentTemplate="{DataTemplate views:Account.SignOutPage}"/>
</ShellItem>

Now not both of them has to be visible in the flyout menu. If the User is logged in, only the logout item should be visible. And if the User is not logged in only the "Login" item should be visible. As I see the flyout menu is build on the application start and then never again, so how can I achieve this? And there is no ShellItem.Behavior etc.

And by the way, what is the difference between ShellItem and FlyoutItem?

Cfun
  • 8,442
  • 4
  • 30
  • 62
Dario M.
  • 415
  • 7
  • 21

2 Answers2

0

In your AppShell.xaml.cs create and manage a property that reflects the authenticated status IsUserAuthenticated to be bound to IsVisible.

InvertedBoolConverter is from Xamarin.CommunityToolkit package

<Shell.Resource>
   <ResourceDictionary>
       <xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
   </ResourceDictionary>
</Shell.Resource>


<ShellItem x:Name="LoginItem" Route="login" Title="{resources:Translate Login}" Icon="icon_LogIn.png"
           IsVisible="{Binding IsUserAuthenticated,
                       Converter={StaticResource InvertedBoolConverter}}">
    <ShellContent ContentTemplate="{DataTemplate views:Account.LoginPage}"/>
</ShellItem>

<ShellItem x:Name="LogoutItem" Route="Logout" Title="{resources:Translate Logout}" Icon="icon_logOut.png"
           IsVisible="{Binding IsUserAuthenticated}">
    <ShellContent ContentTemplate="{DataTemplate views:Account.SignOutPage}"/>
</ShellItem>

Related question

How can you restrict/control the navigation routes the user can visit based on login status/role?

Cfun
  • 8,442
  • 4
  • 30
  • 62
0

You can use <Shell.FlyoutHeaderTemplate> to add custom menu items and then use the binding to hide/show menus.

Step 1. Implement the InverseBoolConverter class

namespace MyMobileApp.Converters
{
    public class InverseBoolConverter : Xamarin.Forms.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return !(bool)value;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return !(bool)value;
        }
    }
}

Step 2: Add InverseBoolConverter reference in AppShell.xaml class.Then add xmlns:converter="clr-namespace:MyMobileApp.Converters" on top (within Shell Tag). and then add <converter:InverseBoolConverter x:Key="cvInverse"></converter:InverseBoolConverter> just after <ResourceDictionary> tag.

Step 3: Add below code in AppShell.xaml just after </FlyoutItem> tag.

<Shell.FlyoutHeaderTemplate>
    <DataTemplate>
        <StackLayout BackgroundColor="White">
            <!--Header-->
            <StackLayout Orientation="Horizontal" BackgroundColor="LightGray" Padding="20" IsVisible="{Binding IsUserLogedin}">
                <Label Text="Watchlist" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Center">
                    <Label.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding WatchlistCommand}"/>
                    </Label.GestureRecognizers>
                </Label>
                <Label Text="Logout" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="End" VerticalTextAlignment="Center">
                    <Label.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding LogoutCommand}"/>
                    </Label.GestureRecognizers>
                </Label>
            </StackLayout>
            <StackLayout Orientation="Horizontal" BackgroundColor="LightGray" Padding="20" IsVisible="{Binding IsUserLogedin, Converter={StaticResource cvInverse}}">
                <Label Text="Register" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Center">
                    <Label.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding RegisterCommand}"/>
                    </Label.GestureRecognizers>
                </Label>
                <Label Text="Login" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="End" VerticalTextAlignment="Center">
                    <Label.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding LoginCommand}"/>
                    </Label.GestureRecognizers>
                </Label>
            </StackLayout>
        </StackLayout>
    </DataTemplate>
</Shell.FlyoutHeaderTemplate>

Step 4: Implement bindings in AppShell.xaml.cs class

public Xamarin.Forms.Command LoginCommand { get; }
public Xamarin.Forms.Command RegisterCommand { get; }
public Xamarin.Forms.Command LogoutCommand { get; }
public Xamarin.Forms.Command WatchlistCommand { get; }

private bool _isUserLoggedIn;
public bool IsUserLogedin
{
    get => _isUserLoggedIn;
    set => SetProperty(ref _isUserLoggedIn, value);
}
public AppShell()
{   
    InitializeComponent();  
    LoginCommand = new Xamarin.Forms.Command(MenuClicked);
    RegisterCommand = new Xamarin.Forms.Command(MenuClicked);
    LogoutCommand = new Xamarin.Forms.Command(MenuClicked);
    WatchlistCommand = new Xamarin.Forms.Command(MenuClicked);
    BindingContext = this;
}
private async void MenuClicked(object obj)
{
    
}
protected bool SetProperty<T>(ref T backingStore, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "", System.Action onChanged = null)
        {
            if (System.Collections.Generic.EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;

            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            return true;
        }