0

I am trying to open the MainWindow from the system tray if the application is already running and the user tried to run a new instance of the program (single instance)

I was following this tutorial : https://www.codeproject.com/Articles/32908/C-Single-Instance-App-With-the-Ability-To-Restore

It worked perfectly if the MainWindow is minimized , However, when I close the window which I am just hiding, and re-open it from the newly created instance by passing a message (using the main window handler), I get the window opened but it's a black screen.

Closing MainWindow:

private void main_Closing(object sender, System.ComponentModel.CancelEventArgs e) {

    // If the window is visible, then hide it
    if (IsVisible)
        Visibility = Visibility.Hidden;

    e.Cancel = true;

}

Showing MainWindow using MainWinow handler

public const int SW_SHOWNORMAL = 1;

[DllImportAttribute("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImportAttribute("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ShowToFront(IntPtr window) {
    ShowWindow(window, SW_SHOWNORMAL);
    SetForegroundWindow(window);
}

How I call it in my MainWindow.xaml.cs

public void ShowWindow() {

    WinApi.ShowToFront(new WindowInteropHelper(this).Handle);

}

I was able to have the normal window by replacing

public void ShowWindow() {

    WinApi.ShowToFront(new WindowInteropHelper(this).Handle);

}

with

public void ShowWindow() {

    Visibility = Visibility.Visible;
    WindowState = WindowState.Normal;
}

Howerver, I am still wondering why the Handler opened my MainWindow with a black screen ?

Normal: Normal View

Black screen: enter image description here

Muhannad
  • 467
  • 4
  • 28

3 Answers3

1

I don't have a direct answer to your question, but I can indirectly answer it by proposing a simpler approach to your problem to avoid most of the WinForm, ptr handlers, and importing all dlls stuff. This approach will allow you to deal with almost only WPF code and I tested it and the background color problem does NOT exist

First: In MainWindow.xaml.cs

  1. Make MainWindow a Singleton
  2. Similar to what you did, hide MainWindow instead of closing it when the user tries to close MainWindow.

Your MainWindow.xml.cs should look like this without your other additional code:

using System.Windows;

namespace WPFSystemTray
{
public partial class MainWindow : Window
{
    private static MainWindow instance;

    public static MainWindow Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new MainWindow();
            }
            return instance;
        }
    }

    private MainWindow()
    {
        InitializeComponent();
        this.Closing += MainWindow_Closing;
    }

    private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        if (!App.IsExitApplication)
        {
            e.Cancel = true;
            this.Hide();
        }
    }
}
}

Second: Remove the following code from App.xaml.cs

StartupUri="MainWindow.xaml"

Third: Add the following references to your WPF project

  1. System.Drawing
  2. System.Windows.Forms

References Screenshot

Fourth: Add Icon to Resources

  1. Right click on your WPF project and select Properties OR double click on Properties in your WPF project.
  2. Select Resources
  3. Select ICONS
  4. Select Add Resources
  5. Select Add Existing File... to pick from your PC OR select Add New Icon to create new one.
  6. If you use different icon, please make sure you rename your icon to Dapino-Summer-Holiday-Palm-tree.ico OR change it in the code in App.xaml.cs I got my test icon from this link

ICONS Screenshot

Fifth: Add Image to Resources

  1. Follow the same instructions above (in Fourth)
  2. Make sure you change Icons to Images
  3. If you select different image, make sure you rename it to palm-tree-icon.png OR make sure you change the name in the code in App.xaml.cs I got my test icon from this link

enter image description here

Sixth: App.xaml.cs

  1. Add constructor and Startup method.
  2. Create the instance of your app and app tray
  3. Added two items to task tray (App Name & Exit)
  4. One of the items has icon to show you how if you don't know.

Your App.xaml.cs should look like this.

    using System;
    using System.Windows;

    namespace WPFSystemTray
    {
    public partial class App : Application
    {
        public static bool IsExitApplication;

    public App()
    {
        Startup += App_Startup;
    }

    private void App_Startup(object sender, StartupEventArgs e)
    {
        WPFSystemTray.MainWindow.Instance.Show();

        System.Windows.Forms.NotifyIcon notifyIcon = new System.Windows.Forms.NotifyIcon();
        notifyIcon.DoubleClick += _notifyIcon_DoubleClick;
        notifyIcon.Icon = WPFSystemTray.Properties.Resources.Dapino_Summer_Holiday_Palm_tree;
        notifyIcon.Visible = true;

        CreateContextMenu(notifyIcon);
    }

    private void _notifyIcon_DoubleClick(object sender, EventArgs e)
    {
        ShowMainWindow();
    }

    private void CreateContextMenu(System.Windows.Forms.NotifyIcon notifyIcon)
    {
        if (notifyIcon != null)
        {
            notifyIcon.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
            notifyIcon.ContextMenuStrip.Items.Add("Application Name",
                WPFSystemTray.Properties.Resources.palm_tree_icon).Click 
                += NotifyIcon_ApplicationName_Click;

            notifyIcon.ContextMenuStrip.Items.Add("Exit").Click += NotifyIcon_Exit_Click;
        }
    }

    private void NotifyIcon_ApplicationName_Click(object sender, EventArgs e)
    {
        ShowMainWindow();
    }

    private void NotifyIcon_Exit_Click(object sender, EventArgs e)
    {
        IsExitApplication = true;

        MainWindow.Close();

        (sender as System.Windows.Forms.ToolStripItem).Owner.Dispose();
    }

    public void ShowMainWindow()
    {
        if (!MainWindow.IsVisible)
        {
            MainWindow.Show();
        }
        else
        {
            if (MainWindow.WindowState == WindowState.Minimized)
            {
                MainWindow.WindowState = WindowState.Normal;
            }

            MainWindow.Activate();
        }
    }
    }
    }

Build and test

  1. First instance, it will open MainWindow
  2. To make selections in the tray, right click on the icon and select (App Name OR Exit).
  3. Double clicking on Icon will open the app again. To change it to only one click: Change notifyIcon.DoubleClick to notifyIcon.Click event in App.xaml.cs
  4. I tried to minimize the changes to make it easier for you to add to your project.
  5. I hope that you agree that this is much simpler and better than the example you were following in codeproject.com.
  • When you say "double clicking on Icon" do you mean on the icon on the system tray or the icon for the program (like a new instance of the program). Because in my question I meant trying a new process of your program so what I will do is make the new process (instant) of my program opens the already running instance's MainWindow the kills itself. That's why I am using the handler. – Muhannad Oct 23 '18 at 18:27
  • I mean in the system tray. Yes, this does what you want. When you try to open new instance of the program from program icon, it will open existing previous instance if there is one. If it does not exist (no instance is running), it will create new one. – Mohammed Alshair Oct 23 '18 at 22:49
1

I cant explicitly remember when or where, but hiding a Window basically ends its life such as a modal window. When I have needed to do a similar, instead of hiding or making visible false, I just set the Window's LEFT property to some obscene negative value so the user could not see.

Example, your actual Window dimensions are 800x450. I might set my LEFT position to -900. Since my window is worst-case 800 wide, going -900 left will not make it visible, but also not release the window and its resources either. So when you try to reset its visibility, you just set its left position back to 0. Now, if a user has multiple monitors, that might be a different issue... maybe set the left to -4000.

DRapp
  • 47,638
  • 12
  • 72
  • 142
  • That sounds like a hacky way of doing it yet I will totally consider it for sure ( it's not like I am against hacky ways!).Thank you very much. – Muhannad Oct 23 '18 at 18:29
  • @Muhannad, yes, may be a hack, but if you have a modal window you want to come back to and hide it, it loses its modality and terminates its read state. – DRapp Oct 24 '18 at 14:05
0

For WPF there is so much easier way of doing this.

Just add activated function into your MainWindow or another form you like.

and add this.show();

    public  void Window_Activated(object sender, EventArgs e)
    {
        this.Show();
    }

It did a trick for me. And it avoids long coding.

XtaXCraft
  • 27
  • 4