0

I have a .Net MAUI app. I define a menu in AppShell.xaml. One MenuItem is this:

    <MenuItem Text="{Binding LoginText}"
          IconImageSource="{Binding LoginIcon}"
          Command="{Binding ToggleLoginCommand}" />

I set binding context in AppShell.xaml.cs:

public AppShell()
{
    InitializeComponent();

    BindingContext = new Menu();
    ...

This is the Menu class:

public partial class Menu : ObservableObject
{
    protected const string _textSignIn = "Sign In";
    protected const string _textSignOut = "Sign Out";
    protected const string _iconSignIn = "IconSignIn";
    protected const string _iconSignOut = "IconSignOut";
...
    public Menu()
    {
        _loginIcon = App.IsLoggedIn ? _iconSignOut : _iconSignIn;
        _loginText = App.IsLoggedIn ? _textSignOut : _textSignIn;
    }

    [ObservableProperty]
    private string _loginIcon;

    [ObservableProperty]
    private string _loginText;

...
    [ICommand]
    protected async Task ToggleLoginAsync()
    {
        ...
        _loginIcon = App.IsLoggedIn ? _iconSignOut : _iconSignIn;
        _loginText = App.IsLoggedIn ? _textSignOut : _textSignIn;
        ...
    }

In Styles.xaml:

<FontImage x:Key="IconSignIn"
           FontFamily="FontAwesome"
           Size="22"
           Color="{StaticResource LaticreteColor}"
           Glyph="{x:Static local:IconFont.SignInAlt}"/>
<FontImage x:Key="IconSignOut"
           FontFamily="FontAwesome"
           Size="22"
           Color="{StaticResource LaticreteColor}"
           Glyph="{x:Static local:IconFont.SignOutAlt}"/>  

In the hamburger menu, the text is always "Sign In", and after _loginText is changed, the MenuItem looks the same.

And the icon doesn't show up at all. What is missing?

ADDED:

Here is the generated property:

     /// <inheritdoc cref="_loginText"/>
    [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    public string LoginText
    {
        get => _loginText;
        set
        {
            if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(_loginText, value))
            {
                OnLoginTextChanging(value);
                OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.LoginText);
                _loginText = value;
                OnLoginTextChanged(value);
                OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.LoginText);
            }
        }
    }
David Shochet
  • 5,035
  • 11
  • 57
  • 105
  • you are setting `_loginText` which is the **private field**, not the **public property** `LoginText` which triggers a `PropertyChanged` event – Jason Dec 08 '22 at 16:31
  • @Jason That is because I use CommunityToolkit.mvvm. It generates observable property from the private field. Please see my update. – David Shochet Dec 08 '22 at 16:54
  • 2
    I understand that. But in `ToggleLoginAsync` you are not setting the public property, so you are not triggering `PropertyChanged` – Jason Dec 08 '22 at 16:56
  • @Jason Great, thank you! After I assign the changed value to LoginText, it changes correctly. Maybe you could tell why the icon doesn't show up, even initially (after I apply your fix, of course)? – David Shochet Dec 08 '22 at 17:04
  • I'd try hardcoding the value in the XAML just to verify that you are using the correct name, the image is valid, etc – Jason Dec 08 '22 at 17:07
  • @Jason It shows up if I use Static Resource: IconImageSource="{StaticResource IconSignIn}". But how can I bind it? – David Shochet Dec 08 '22 at 17:30
  • Maybe https://stackoverflow.com/a/45292333/199364 – ToolmakerSteve Dec 08 '22 at 22:04
  • The cause is when you set the `Image.Source` 's value as a string, it will call the `ImageSource.From(string file)`. But the _iconSignIn's value is the key in the `Style.xaml` not a file name. In addition, the `FontImage` can't be declared in the code behind. So it seems you can't get the instance of it in the `Menu.cs` from the `Style.xaml`. You may have to use two image file instead of the `FontImage` in the `Style.xaml`. – Liyun Zhang - MSFT Dec 09 '22 at 03:39
  • @LiyunZhang-MSFT It is not clear to me. Are you suggesting using images instead of FontAwesome? – David Shochet Dec 09 '22 at 11:39
  • @ToolmakerSteve That solution is about images, and I am dealing with a font FontAwesome... Do you think it may be applicable? – David Shochet Dec 09 '22 at 16:06
  • @ToolmakerSteve OK, I use .png now instead of the font. It works great. Thanks. – David Shochet Dec 09 '22 at 20:15
  • @Jason As you gave me a fix for my initial question, would you like to make it an answer? I would mark it as such. Thank you again. – David Shochet Dec 09 '22 at 20:16
  • 1
    Yes, using the images instead of FontAwesome is easy to bind. – Liyun Zhang - MSFT Dec 12 '22 at 00:57

1 Answers1

1

you are doing this

protected async Task ToggleLoginAsync()
{
    ...
    _loginIcon = App.IsLoggedIn ? _iconSignOut : _iconSignIn;
    _loginText = App.IsLoggedIn ? _textSignOut : _textSignIn;
    ...
}

_loginText is a private field and does not raise PropertyChanged. In order to raise PropertyChanged you need to set the public property

LoginText = App.IsLoggedIn ? _textSignOut : _textSignIn;
Jason
  • 86,222
  • 15
  • 131
  • 146