Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a tray-icon-only C# application in MonoMac (no dock icon)?

I am trying to create an application that will have a tray icon only, and not appear in the taskbar. (similar to Dropbox) I need to create both Windows and Mac version of the application, so I tried using MonoMac to create the Mac front-end.

What is the best way to create a tray-only application in MonoMac?

All the resources I have found say to do one of two things:

  • Add <key>LSUIElement</key><string>1</string> to the Info.plist file.
  • Add the following code to the FinishedLaunching event in the AppDelegate class: NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Accessory;

I have tried all combinations of these two, but it seems that as soon as I try to instantiate a C# System.Timers.Timer, the icon reappears in the dock at the bottom of the screen. Am I missing something about how OSX handles background applications?

What am I doing wrong? Is there a better way to make a background application that has an upper tray icon but no bottom dock icon in OSX?

(This is very similar to this SO question, but that question was from a couple years ago and was never fully answered, so I'm hoping there might be a more complete answer out there.)


Here's the code I have so far:

public partial class AppDelegate : NSApplicationDelegate
{
    MyServiceObject currentServiceObject;

    public AppDelegate () { }

    public override void FinishedLaunching (NSObject notification)
    {
        // Construct menu that will be displayed when tray icon is clicked
        var notifyMenu = new NSMenu();
        var exitMenuItem = new NSMenuItem("Quit My Application", 
            (a,b) => { System.Environment.Exit(0); }); // Just add 'Quit' command
        notifyMenu.AddItem(exitMenuItem);

        // Display tray icon in upper-right-hand corner of the screen
        var sItem = NSStatusBar.SystemStatusBar.CreateStatusItem(30);
        sItem.Menu = notifyMenu;
        sItem.Image = NSImage.FromStream(System.IO.File.OpenRead(
            NSBundle.MainBundle.ResourcePath + @"/notify-icon.icns"));
        sItem.HighlightMode = true;

        // Remove the system tray icon from upper-right hand corner of the screen
        // (works without adjusting the LSUIElement setting in Info.plist)
        NSApplication.SharedApplication.ActivationPolicy = 
            NSApplicationActivationPolicy.Accessory;

        // Start running the program -- If I comment out then no dock icon appears
        currentServiceObject = new MyServiceObject();
    }
}
like image 831
Matt Avatar asked Jul 23 '12 23:07

Matt


2 Answers

I found the problem, and it wasn't related to the application settings at all. Evidently, there are some operations that MacOS does not allow an 'Agent applications' to perform. As soon as one of those methods is called, the application is forced to appear in the dock. The code that was tripping up my application was a call to:

System.Windows.Forms.Cursor.Position.ToString()

Removing that line, and replacing it with the following MonoMac method allowed the application to remain hidden:

NSEvent.CurrentMouseLocation.ToString()
like image 164
Matt Avatar answered Oct 20 '22 03:10

Matt


I was able to get this working by setting the value of "Application is agent (UIElement)" key to 1 in the info.plist file. Even though it should be a BOOL value, MonoDevelop makes it a string, but setting it to 1 seems to work. You can also set an empty string the for the "Icon file" but it's not necessary.

info.plist

like image 26
berg Avatar answered Oct 20 '22 04:10

berg