maui icon indicating copy to clipboard operation
maui copied to clipboard

Add IsVisible Property to ToolbarItem

Open Codelisk opened this issue 3 years ago • 23 comments

Description

I would love to add a IsVisible Property for the ToolbarItem

https://github.com/xamarin/Xamarin.Forms/issues/3838

Public API Changes

ToolbarItem.IsVisible=true; //new Api

Intended Use-Case

"At times you need to check if a toolbar item is visible, and to set it thus."

Related: https://xamarin.uservoice.com/forums/258559/suggestions/15508584

Codelisk avatar Dec 08 '21 10:12 Codelisk

We've moved this issue to the Future milestone. This means that it is not going to be worked on for the coming release. We will reassess the issue following the current release and consider this item at that time.

ghost avatar Apr 06 '22 08:04 ghost

Here is a workaround I use (EDIT: doesn't work in later version av MAUI. Scroll down for another approach). It's deeply inspired by: https://stackoverflow.com/questions/50619388/how-to-hide-navigation-toolbar-icon-in-xamarin?msclkid=79b0a582ce9f11ec8b8fcfa78892ae51

Add a custom control to your project:

internal class BindableToolbarItem : ToolbarItem
{
    public static readonly BindableProperty IsVisibleProperty = 
        BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(BindableToolbarItem), true, BindingMode.OneWay, propertyChanged: OnIsVisibleChanged);

    public bool IsVisible
    {
        get => (bool)GetValue(IsVisibleProperty);
        set => SetValue(IsVisibleProperty, value);
    }

    protected override void OnParentChanged()
    {
        base.OnParentChanged();

        RefreshVisibility();
    }

    private static void OnIsVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var item = bindable as BindableToolbarItem;

        item.RefreshVisibility();
    }

    private void RefreshVisibility()
    {
        if (Parent == null)
        {
            return;
        }

        bool value = IsVisible;

        var toolbarItems = ((ContentPage) Parent).ToolbarItems;

        if (value && !toolbarItems.Contains(this))
        {
            Application.Current.Dispatcher.Dispatch(() => { toolbarItems.Add(this); });
        }
        else if (!value && toolbarItems.Contains(this))
        {
            Application.Current.Dispatcher.Dispatch(() => { toolbarItems.Remove(this); });
        }
    }
}

And then use it like this:

<ContentPage.ToolbarItems>

    <controls:BindableToolbarItem 
        Command="{Binding MyCommand}"
        Text="Do stuff"
        IsVisible="{Binding Model.Foo}"
        Order="Primary"
        Priority="1" />

</ContentPage.ToolbarItems>

pekspro avatar May 08 '22 09:05 pekspro

@pekspro: not sure if you've noticed, but that workaround doesn't look like it works anymore with current .NET Maui. What I'm seeing is that a BindableToolbarItem that's been removed from the parent's collection of ToolbarItems will also stop receiving IsVisible binding changes. Are you seeing the same thing?

mfeingol avatar Jan 18 '23 04:01 mfeingol

@mfeingol, yes, I have noticed that. Will post later today how I solved it. It's not pretty.

pekspro avatar Jan 18 '23 06:01 pekspro

Here is my new workaround. I have my toolbar item like this:

<ContentPage.ToolbarItems>
    <ToolbarItem 
            x:Name="ToolbarItemAlbumMode"
            Text="Show album"
            Order="Secondary"
            Priority="0" />
</ContentPage.ToolbarItems>

And then I have a hidden swich control that I use for binding:

<VerticalStackLayout IsVisible="False">
    <Switch 
        x:Name="ToolbarHelperAlbum"
        IsToggled="{Binding HasAlbum, Mode=OneWay}"
        Toggled="ToolbarHelper_ToggleChanged"
        />
</VerticalStackLayout>

And then in the code behind:

protected override void OnAppearing()
{
    base.OnAppearing();
    
    RefreshToolbarItems();
}

private void RefreshToolbarItems()
{
    SetToolbarItemVisibility(ToolbarItemAlbumMode, ViewModel.HasAlbum);
}

private void ToolbarHelper_ToggleChanged(object sender, ToggledEventArgs e)
{
    RefreshToolbarItems();
}

public void SetToolbarItemVisibility(ToolbarItem toolbarItem, Microsoft.Maui.Controls.Switch switchControl)
{
    SetToolbarItemVisibility(toolbarItem, switchControl.IsToggled);
}

public void SetToolbarItemVisibility(ToolbarItem toolbarItem, bool value)
{
    if (value && !ToolbarItems.Contains(toolbarItem))
    {
        ToolbarItems.Add(toolbarItem);
    }
    else if (!value)
    {
        ToolbarItems.Remove(toolbarItem);
    }
}

pekspro avatar Jan 18 '23 16:01 pekspro

Thanks, @pekspro. Having a shadow element to hold the binding makes sense (and is awful!)

@jfversluis I don't suppose anyone has it on the radar to do MenuItem.IsVisible properly?

mfeingol avatar Jan 18 '23 16:01 mfeingol

This issue comes from Xamarin and was over 4 years old from 2018 why nobody solve this big problem and everybody must work with this workaround ? The workaround don't work also with Maui if you use Modal Page after Pop the Modal Page the workaround crashed !!!

AHComp avatar Jan 27 '23 11:01 AHComp

@AHComp: what kind of crash? What's the exception and stack trace?

mfeingol avatar Jan 27 '23 14:01 mfeingol

Simple the toolbaritems freezing so the binding to an bool property in the view model don't work any more.

The toolbaritems collection still work and changed but the rendering crashed.

I test in my App if I use PushModalAsync it crashed and if I use PushAsync it work.

Same with Shell if I Set the Parameter in the Page.

I test only in android no other platform.

AHComp avatar Jan 27 '23 19:01 AHComp

I don't use PushModalAsync anywhere so I can't help with that.

mfeingol avatar Jan 27 '23 20:01 mfeingol

Why do you have to beg Microsoft for 4 years or even longer for a simple function to be implemented correctly?

AHComp avatar Jan 27 '23 21:01 AHComp

https://stackoverflow.com/questions/70719111/how-do-i-add-a-toolbar-item-net-maui

You can find a lot of more stuff if you google 5 minutes.

AHComp avatar Jan 27 '23 21:01 AHComp

A workaround I made for this is to just add it in the code behind. The display of this is related to the user's permission, which wont change for the life of the user's session, so it doesnt strictly need to be bindable.

Menu item that is always present goes in XAML

	<ContentPage.ToolbarItems>
		<ToolbarItem Text="Help" IconImageSource="help_24dp" Clicked="ShowHelpToolbarItem_Clicked" Order="Secondary"></ToolbarItem>
	</ContentPage.ToolbarItems>

and the menu item that can be conditionally added goes in the code behind constructor (i have a base class which sets up a view model Model property and does the binding context linkage, you'd just need to have some kind of way in the constructor to determine if the thing should be visible. That can come from an injected service on the constructor or some other type of data access):

public partial class ItemPage : PageBase<ItemPageViewModel>
{
	public ItemPage()
	{ 
		InitializeComponent();
		if (Model.CanTakePicture) 
		{
			this.ToolbarItems.Add(new ToolbarItem("Add Picture", "add_a_photo_24dp", () => PicToolbarItem_Clicked(this, null), ToolbarItemOrder.Secondary));
		}
	}

shadowfoxish avatar Feb 02 '23 20:02 shadowfoxish

I try don't work with modal pages ! I have all my toolbaritems in a custom collection. And add in the constructor. The page will not reload after close the modal page.

AHComp avatar Feb 02 '23 20:02 AHComp

In the newest release with .Net 7 the workaround works never more ! The reason is because the toolbar item which revomed will destoyed !

When the developers implement the visible feature ?

AHComp avatar Feb 22 '23 16:02 AHComp

This is yet to be implemented. A very valid request but unfortunately not in existence yet.

aicukltd avatar Apr 18 '23 14:04 aicukltd

When will this be implemented?

SirElTomato avatar Jul 11 '23 13:07 SirElTomato

we also need this.

eramrit78 avatar Jul 26 '23 13:07 eramrit78

I also need this.

rickyn avatar Aug 06 '23 16:08 rickyn

This would be a useful addition, any idea when it could be included?

tmacintosh avatar Aug 14 '23 21:08 tmacintosh

I also need this.

pme442 avatar Oct 02 '23 18:10 pme442

Need this too :(

BeepBeepBopBop avatar Oct 02 '23 19:10 BeepBeepBopBop

The workaround from XF crashed in .Net 8. We need a better solution for .Net 8.

AlleSchonWeg avatar Jan 11 '24 14:01 AlleSchonWeg

It would be useful if the team gave us an explanation why it's not planned. Just closing it as not planned seems strange because clearly there's demand for this feature.

imsam67 avatar Jan 16 '24 17:01 imsam67