question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

NativeMenu binding for "About" on MacOS not working?

See original GitHub issue

I’m trying to update the About window in the MacOS version of my application.

Following this post:

https://github.com/AvaloniaUI/Avalonia/issues/3541

The problem is that I can’t work out how to bind the AboutCommand

<NativeMenu.Menu>
    <NativeMenu>
      <NativeMenuItem Header="About My App" Command="{Binding AboutCommand}" />
    </NativeMenu>
  </NativeMenu.Menu>

So this is placed in my App.axaml file and then I’ve added

            AboutCommand = MiniCommand.CreateFromTask(async () =>
            {
                AboutDialogWindow = new AboutDialog();
                var mainWindow = (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
                await AboutDialogWindow.ShowDialog(mainWindow);
            });

in the Initialize() of App.axaml.cs but I get

[Binding] Error in binding to ‘Avalonia.Controls.NativeMenuItem’.‘Command’: ‘Null value in expression ‘’.’ (NativeMenuItem #6480969)

I do this and the menu is correctly display “About My App” - but it’s greyed out.

I’ve also tried placing the Native Menu in the MainWindow.axaml file, but that doesn’t override it (the default Avalonia About box appears).

I’m obviously just misunderstanding how to implement this. Any advice would be greatly appreciated.

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:22 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
josephnaraicommented, Apr 22, 2022

Ok, the issue can now be closed. I’ve finally figured out all the parts that need to be implemented. I’ll include sample code here so that if anyone else is having difficulty they have a reference. I still feel that this is overly complicated to replace the AboutAvalonia native menu item and there should be a simpler way to achieve this in a non MVVM application.

Firstly, create a ViewModelBase class:

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace DApps.ViewModels
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!EqualityComparer<T>.Default.Equals(field, value))
            {
                field = value;
                RaisePropertyChanged(propertyName);
                return true;
            }
            return false;
        }

        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Then create an AppViewModel class with your AboutCommand public property

using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using DApps.Helpers;
using DApps.Views;

namespace DApps.ViewModels
{
    public class AppViewModel : ViewModelBase
    {
        public MiniCommand AboutCommand { get; }

        public AppViewModel()
        {
            AboutCommand = MiniCommand.CreateFromTask(async () =>
            {
                AboutDialog dialog = new();
                Avalonia.Controls.Window mainWindow = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
                await dialog.ShowDialog(mainWindow);
            });
        }
    }
}

Create your AboutDialog.axaml

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp.UI"
        MaxWidth="400"
        MaxHeight="475"
        MinWidth="430"
        MinHeight="475"
        Title="About My App"
        Background="{StaticResource Background2}"
        x:Class="MyApp.Views.AboutDialog">
  <Grid Background="{StaticResource Background2}">
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
      <StackPanel Orientation="Vertical" VerticalAlignment="Center">
        <Rectangle Margin="10" Height="1" Fill="{StaticResource Background3}" />
        <TextBlock Text="About" Margin="3" FontSize="14" FontWeight="SemiBold" TextAlignment="Center" Foreground="{StaticResource Foreground2}" />
        <TextBlock x:Name="AboutAppInfo" Text="AppInfo" Margin="1" TextAlignment="Center" Foreground="{StaticResource Foreground2}" />
        </StackPanel>
    </StackPanel>
  </Grid>
</Window>

and code behind class for the about dialog (the code behind can be much simpler than I have here - this is from the code in the avaloina source)

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace DApps.Views
{
    public class AboutDialog : Window
    {
        private static readonly Version s_version = typeof(AboutDialog).Assembly.GetName().Version;

        public static string Version { get; } = s_version.ToString(2);

        public static bool IsDevelopmentBuild { get; } = s_version.Revision == 999;

        public AboutDialog()
        {
            AvaloniaXamlLoader.Load(this);
            DataContext = this;
        }

        public static void OpenBrowser(string url)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                // If no associated application/json MimeType is found xdg-open opens retrun error
                // but it tries to open it anyway using the console editor (nano, vim, other..)
                ShellExec($"xdg-open {url}", waitForExit: false);
            }
            else
            {
                using Process process = Process.Start(new ProcessStartInfo
                {
                    FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? url : "open",
                    Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? $"{url}" : "",
                    CreateNoWindow = true,
                    UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                });
            }
        }

        private static void ShellExec(string cmd, bool waitForExit = true)
        {
            var escapedArgs = cmd.Replace("\"", "\\\"");

            using var process = Process.Start(
                new ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = $"-c \"{escapedArgs}\"",
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                }
            );
            if (waitForExit)
            {
                process.WaitForExit();
            }
        }
    }
}

And finally at the top of App.axaml

<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MyApp"
             xmlns:vm="using:MyApps.ViewModels"
             x:DataType="vm:AppViewModel"
             x:Class="MyApp.App">

  <Application.Name>My App</Application.Name>

  <NativeMenu.Menu>
    <NativeMenu>
      <NativeMenuItem Header="About My App" Command="{CompiledBinding AboutCommand}"/>
    </NativeMenu>
  </NativeMenu.Menu>

and in the code behind App.axaml.cs

using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using DApps.Views;
using DApps.ViewModels;

namespace DApps
{
    public class App : Application
    {
        public App()
        {
            DataContext = new AppViewModel();
        }

        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);
        }

        public override void OnFrameworkInitializationCompleted()
        {
            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
            {
                desktop.MainWindow = new MainWindow();
            }

            base.OnFrameworkInitializationCompleted();
        }
    }
}

I hope this might help someone else trying to implement this simple feature.

0reactions
timuniecommented, Apr 20, 2022

I still think a static command would be ok in your case. If not, you need to set somehow your App as the DataContext of your Menu and I don’t really see how this should work.

Read more comments on GitHub >

github_iconTop Results From Across the Web

OS X Native Menu Bar
I'm using Python binding and don't have access to Obj-c/Cocoa to handle native OSX stuff. Previously I used wxPython (Python bindings to ...
Read more >
712496 - [Mac] Favicons don't appear in the native menu ...
[Mac] Favicons don't appear in the native menu binding in Firefox 9.0 ... and the problem is not present there, so this seems...
Read more >
Is there a way to show KeyBindings with the JMenuBar ...
My application has a lot of key binds for the Menu actions, and I want them to display in the Menu Bar similar...
Read more >
macos - Postgres.app Could not start on port 5432
In my case, I had dBeaver running connected to my postgresql instance. I made a config change and clicked "Stop" and "Start" and...
Read more >
Configure domain access in Directory Utility on Mac
To establish binding, use a computer name that does not contain a hyphen. If the advanced options are hidden, click the disclosure triangle...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found