Hello!
I posted this in discussions earlier - allow me to frame it as a question instead (don't mean to spam)
--
I recently came across Xamarin Forms Shell and would like to implement it, but the one caveat I'm having is that, as of now, there is no way to bind the shell's MenuItem's visibility to anything, for the MenuItem doesn't have an IsVisible property.
I would like to be able to show/hide a set of menu items based on whether a user is logged in or not, but I can't seem to figure out how without doing some dirty adding/removing items based on certain events being fired, but it's getting ugly and losing it's xamarin.forms-ness.
A possible workaround I'm trying to implement for the time being is to extend the MenuItem, add a visibility property, and bind to that, but I'm having difficulty working with the property from within the DataTemplate.
The CustomMenuItem's 'IsMenuItemVisible' property binds without problem, but it doesn't seem to be read properly.
Could someone point out to me what am I doing wrong? I'd really appreciate it..
XAML:
"""
Shell ...
xmlns:landing="clr-namespace:App.Pages.Landing"
xmlns:pages="clr-namespace:App.Pages"
x:Class="App.Pages.LandingShell"
x:Name="ParentShell"
NavigationPage.HasNavigationBar="False"
NavigationPage.HasBackButton="False"
Shell.NavBarIsVisible="False">
<pages:CustomMenuItem
x:Name="LoginMenuItem"
Text="Login"
Command="{Binding GoToLoginPageCommand}"
IsMenuItemVisible="{Binding IsLoginMenuVisible}" />
<pages:CustomMenuItem
x:Name="CreateAccountMenuItem"
Text="Create an Account"
Command="{Binding GoToCreateAccountPageCommand}"
IsMenuItemVisible="{Binding IsLoginMenuVisible}" />
<pages:CustomMenuItem
x:Name="GreetingMenuItem"
Text="{Binding Greeting}"
IsMenuItemVisible="{Binding IsLogoutMenuVisible}" />
<pages:CustomMenuItem
x:Name="LogoutMenuItem"
Text="Logout"
Command="{Binding LogoutCommand}"
IsMenuItemVisible="{Binding IsLogoutMenuVisible}" />
<Shell.MenuItemTemplate>
<DataTemplate>
<Grid IsVisible="{Binding IsMenuItemVisible}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.8*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Text}"
Grid.Column="1"
Style="{StaticResource LabelStyle}" />
</Grid>
</DataTemplate>
</Shell.MenuItemTemplate>
/Shell>
"""
CS :
"""
namespace App.Pages
{
public partial class LandingShell : Shell, INotifyPropertyChanged
{
public LandingShell()
{
IsLoginMenuVisible = !IsLoggedIn;
IsLogoutMenuVisible = IsLoggedIn;
// -- Handle Login/Logout view
MessagingCenter.Subscribe<EventArgs, User>(this, "UpdateMenu", (args, usr) =>
{
IsLoggedIn = true;
Greeting = $"Hello {usr.Username}!";
IsLoginMenuVisible = !IsLoggedIn;
IsLogoutMenuVisible = IsLoggedIn;
});
MessagingCenter.Subscribe<EventArgs>(this, "UpdateMenuLoggedOut", args =>
{
IsLoggedIn = false;
IsLoginMenuVisible = !IsLoggedIn;
IsLogoutMenuVisible = IsLoggedIn;
Greeting = string.Empty;
});
BindingContext = this;
InitializeComponent();
}
...
private string _greeting;
public string Greeting
{
get => _greeting;
set
{
if (value == _greeting) return;
_greeting = value;
OnPropertyChanged(nameof(Greeting));
}
}
private bool _isLoggedIn;
public bool IsLoggedIn
{
get => _isLoggedIn;
set
{
if (value == _isLoggedIn) return;
_isLoggedIn = value;
OnPropertyChanged(nameof(IsLoggedIn));
}
}
private bool _isLoginMenuVisible;
public bool IsLoginMenuVisible
{
get => _isLoginMenuVisible;
set
{
if (value == _isLoginMenuVisible) return;
_isLoginMenuVisible = value;
OnPropertyChanged(nameof(IsLoginMenuVisible));
}
}
private bool _isLogoutMenuVisible;
public bool IsLogoutMenuVisible
{
get => _isLogoutMenuVisible;
set
{
if (value == _isLogoutMenuVisible) return;
_isLogoutMenuVisible = value;
OnPropertyChanged(nameof(IsLogoutMenuVisible));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CustomMenuItem : MenuItem, INotifyPropertyChanged
{
public static readonly BindableProperty IsMenuItemVisibleProperty =
BindableProperty.Create(
propertyName: "IsMenuItemVisible",
returnType: typeof(bool),
declaringType: typeof(CustomMenuItem),
defaultValue: false,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: IsMenuItemVisiblePropertyChanged);
public bool IsMenuItemVisible
{
get { return (bool)GetValue(IsMenuItemVisibleProperty); }
set {
SetValue(IsMenuItemVisibleProperty, value);
OnPropertyChanged(nameof(IsMenuItemVisible));
}
}
private static void IsMenuItemVisiblePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
Console.Write("MenuItem Changed");
var menuItem = (CustomMenuItem)bindable;
menuItem.IsMenuItemVisible = (bool)newValue;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
"""
Thanks in advance!