Aplikacja WPF działająca w tray’u Windows

a close up of a text description on a computer screen

Aplikacja WPF działająca w tray’u Windows

Windows tray’a na pewno każdy zna. Jest to ten obszar paska zadań, w którym widoczne są ikony programów działających w tle. Klikając na nie, możemy łatwo wyświetlić okno danej aplikacji, a pod PPM zazwyczaj dostępne jest jakieś menu kontekstowe. Niektóre aplikacje wyświetlają też od czasu do czasu powiadomienia w postaci dymków. Całkiem fajna rzecz. Dzisiaj zaprezentuję, jak się do tego obszaru dostać z poziomu projektu WPF platformy .NET.

WPF i Windows Forms

Pisząc aplikację w technologii WPF musimy zdać sobie sprawę, że przy użyciu wbudowanych przestrzeni nazw do systemowego tray’a się nie dobierzemy. Przynajmniej nie przy użyciu metody, którą za chwilę zaprezentuję. Czego więc będziemy potrzebowali? Konkretnie referencji do przestrzeni nazw System.Windows.Forms, a więc poprzednika WPF’a. W najnowszym Visual Studio 2022 opcja ta jest dostępna we właściwościach projektu WPF:

Należy te opcję zaznaczyć przed przejściem do dalszych etapów. W starszych wersjach Visual Studio referencję do tej przestrzeni nazw dodawało się przy użyciu narzędzia „Reference Manager”.

Tworzymy klasę do obsługi tray’a

Tworzymy… a właściwie wklejamy gotowca, bo już taką klasę przygotowałem. Kod prezentuje się następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System;
using System.IO;
using System.Windows.Forms;

namespace WindowsTrayTest
{
    public class WindowsTray
    {
        private ContextMenuStrip m_menu;
        private NotifyIcon ni;
        private System.Windows.Window window;

        public WindowsTray(System.Windows.Window window)
        {
            this.window = window;
            ni = new NotifyIcon();
            Stream iconStream = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/WindowsTrayTest;component/Icons/16px.ico")).Stream; //tutaj podajemy ścieżkę do pliku *.ico
            ni.Icon = new System.Drawing.Icon(iconStream);
            ni.Text = "Tray test";
            ni.Visible = true;
            ni.DoubleClick += TrayOpen_Click;
            /* menu */
            m_menu = new ContextMenuStrip();
            m_menu.Items.Add("Otwórz").Click += TrayOpen_Click;
            m_menu.Items.Add("Zamknij").Click += TrayExit_Click;
            ni.ContextMenuStrip = m_menu;
        }

        private void TrayExit_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
        }

        private void TrayOpen_Click(object sender, EventArgs e)
        {
            window.Show();
            window.Activate();
        }

        public void ShowTrayInformation(string Title, string Content, ToolTipIcon icon, int time)
        {
            ni.BalloonTipTitle = Title;
            ni.BalloonTipText = Content;
            ni.BalloonTipIcon = icon;
            ni.Visible = true;
            ni.ShowBalloonTip(time);
            ni.BalloonTipClicked += delegate (object sender, EventArgs args)
            {
                window.Show();
                window.Activate();
            };
        }
    }
}

Z rzeczy, o których należy pamiętać i które mogą wymagać modyfikacji to string określający lokalizację ikony, która ma być wyświetlana w zasobniku systemowym. W moim przypadku jest to: pack://application:,,,/WindowsTrayTest;component/Icons/16px.ico. WindowsTrayTest – to nazwa utworzonego projektu, a /Icons/16px.ico to ścieżka do pliku z ikoną. Żeby ikona wylądowała w pliku *.exe należy ustawić akcję kompilacji na „Zasób” dla tejże ikony:

Używanie klasy w praktyce

Użycie klasy jest bardzo proste. Wystarczy utworzyć obiekt tej klasy przekazując w parametrze referencję do okna głównego aplikacji:

1
tray = new WindowsTray(this);

Następnie możemy użyć metody zawartej w tejże klasie o nazwie ShowTrayInformation(). Przyjmuje ona następujące parametry: Tytuł komunikatu, treść komunikatu, ikonę (do wyboru są trzy), oraz czas przez jaki ma być wyświetlany komunikat. Uwaga – czas ma zastosowanie dla systemów Windows 7 i starszych. Wynika to z przemodelowania obszaru powiadomień w nowszych wersjach systemu Windows.

Poniżej prezentuję kompletny kod MainWindow.cs oraz powiązany z nim kod *.xaml. Wprowadziłem w nim modyfikację, aby okno nie zamykało się po kliknięciu „krzyżyka” tylko minimalizowało – właśnie do systemowego tray’a:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System.ComponentModel;
using System.Windows;

namespace WindowsTrayTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        WindowsTray tray;
        public MainWindow()
        {
            InitializeComponent();
            tray = new WindowsTray(this); //utworzenie obiektu odpowiedzialnego za obsługę obszaru powiadomień
            Closing += OnClosingWindow; //przechwycenia zdarzenia zamykania okna
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.ToolTipIcon icon = System.Windows.Forms.ToolTipIcon.None;
            if(err_radioButton.IsChecked == true) { icon = System.Windows.Forms.ToolTipIcon.Error; }
            if (warn_radioButton.IsChecked == true) { icon = System.Windows.Forms.ToolTipIcon.Warning; }
            if (info_radioButton.IsChecked == true) { icon = System.Windows.Forms.ToolTipIcon.Info; }
            tray.ShowTrayInformation(Title_textBox.Text, Context_textBox.Text, icon, 3000);
        }

        private void OnClosingWindow(object sender, CancelEventArgs e)
        {
            e.Cancel = true; //akcja zamknięcia aplikacji nie zostanie wykonana
            Hide(); //okno zostanie zminimalizowane
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Window x:Class="WindowsTrayTest.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       xmlns:local="clr-namespace:WindowsTrayTest"
       mc:Ignorable="d"
       Title="MainWindow" Height="380" Width="440" MinWidth="360" MinHeight="380">
    <Grid>
        <Button Content="Wyświetl informację" Margin="0,0,10,9" Height="19" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="101" Click="Button_Click"/>
        <TextBox x:Name="Title_textBox" Margin="10,10,10,0" TextWrapping="Wrap" Text="Tytuł" Height="32" VerticalAlignment="Top"/>
        <TextBox x:Name="Context_textBox" TextWrapping="Wrap" Text="Treść" VerticalAlignment="Top" Height="177" Margin="10,47,10,0"/>
        <TextBlock HorizontalAlignment="Left" Margin="10,229,0,0" TextWrapping="Wrap" VerticalAlignment="Top"><Run Language="pl-pl" Text="Ikona"/></TextBlock>
        <RadioButton x:Name="info_radioButton" Content="Informacja" HorizontalAlignment="Left" Margin="10,249,0,0" VerticalAlignment="Top" GroupName="group1"/>
        <RadioButton x:Name="warn_radioButton" Content="Ostrzeżenie" HorizontalAlignment="Left" Margin="10,268,0,0" VerticalAlignment="Top" GroupName="group1"/>
        <RadioButton x:Name="err_radioButton" Content="Błąd" HorizontalAlignment="Left" Margin="10,287,0,0" VerticalAlignment="Top" GroupName="group1"/>
    </Grid>
</Window>

Efekt końcowy

Efektem końcowym jest prosta aplikacja wyświetlająca powiadomienie, którego treść podał użytkownik. Akcja jest wykonywana po kliknięciu przycisku „wyświetl informację”. Aplikację można zamknąć z poziomu jej ikonki w systemowym tray’u – należy kliknąć na nią PPM i wybrać opcję zamknij. Kliknięcie „krzyżyka” w oknie aplikacji powoduje tylko jej zminimalizowanie do obszaru powiadomień.

Opublikuj komentarz


The reCAPTCHA verification period has expired. Please reload the page.