WPF版 タスクトレイアプリ(TSR)のひな形 R2.00


::: note
* タスクトレイ(通知領域)に常駐するひな形です。
* 多数作り方があって結構面倒。色々公開されているがこれが一番簡単だと思う。
* AI生成コードに対して閉じるボタン無効化、メニュー追加、セパレータ、選択無効化、ミューテックスによる二重起動防止等の改修を行いました。
* 割り込みベクタ int 21h を使ってる訳でもないがWindow閉じてもタスクトレイに常駐している事からTSRと呼んでます。

:::


* 音符を右クリックするとメニューが出ます。
* 新規にセパレータ入れました。
* DESABLE 選ぶとEXITが無効化されます。写真通りグレーアウトします。
* ENABLE 選ぶとEXITは有効化されます。

![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/2c025e17-2d8a-4b32-a6a5-2c0fc3e7cf6b.jpeg)


* OPENを選ぶと空のWindow(MainWindow.xaml)が出ます。(又は音符ダブルクリックで直に出る)
* そのMainWindow.xamlにAPPコードを記述して下さい。
* 起動時にMainWindowを出したい時はthis.Hide();を消して下さい。
* 職員情報を閲覧するアプリを開発・運用していて、P.W.が入ったCSVが深夜更新されるので、それを毎朝6:00にAES暗号化して誰でもアクセスできる所に作成。そのAPPはTSR化して常時稼働させてます。
* WindowをXで閉じても実際はタスクトレイに最小化され、メニュー等で何時でも呼び出しできる所が良いです。
* servicesで作るのが最善だがハードルが高い。
* 他には項目の隣にアイコンやチェックボックス入れたり、階層メニューにしたり。(未実装)
* このひな形だけでも十分機能しますので利用して完成度の高いAPPを構築して下さい。

<FONT COLOR="BLUE">

### 1.WPF
</FONT>

```c#
///////////////////////////////
// TASKTRAY TSR SAMPLE R2.00
// WPF And .Netframefork
// inf102 2025.
//////////////////////////////

using System;
using System.Drawing;         // Icon用
using System.Windows;
using System.Windows.Forms;

// 参照を追加 System.Windows.Forms / System.Drawing
// 二重起動防止はApp.xaml.cs

namespace TSRTEST01 {

    public partial class MainWindow : Window {

        private NotifyIcon notifyIcon;
        public MainWindow() {
            InitializeComponent();

            SetupNotifyIcon();
            this.Hide(); // 起動時にwindow出したい時は消す

        }
                    
        private void SetupNotifyIcon(){
            notifyIcon = new NotifyIcon();
            notifyIcon.Icon = new Icon("C:\\TMP\\icon.ico");  // アイコンファイル
            notifyIcon.Visible = true;
            notifyIcon.Text = "WPF-TSR-TEST";

            var menuItem1 = new ToolStripMenuItem("MsgBox EXIT DESABLE(&M)");
            var menuItem2 = new ToolStripMenuItem("WinOPEN EXIT ENABLE(&Y)");
            var menuItem3 = new ToolStripMenuItem("EXIT(&E)");

            // 右クリックメニューの作成
            var contextMenu = new ContextMenuStrip();

            // MENU1 MSGBOX/////////////////////////////////////////////
            menuItem1.Click += (s, e) => {
                System.Windows.MessageBox.Show("EXIT DESABLE");
                menuItem3.Enabled=false;
            };
            contextMenu.Items.Add(menuItem1);
            
            ///////////////////////////////////////////////////////////
            // MENU2 (WINDOW OPEN) ////////////////////////////////////
            menuItem2.Click += (s, e) => {
                this.Show();
                this.WindowState = WindowState.Normal;
                this.Activate();
                menuItem3.Enabled=true;
            };

            contextMenu.Items.Add(menuItem2);
            contextMenu.Items.Add(new ToolStripSeparator()); // 区切り線

            ///////////////////////////////////////////////////////////     
            // MENU3 (EXIT) ///////////////////////////////////////////          
            menuItem3.Click += (s, e) => {
                notifyIcon.Visible = false;
                System.Windows.Application.Current.Shutdown();
            };
            contextMenu.Items.Add(menuItem3);
            //////////////////////////////////////////////////////////     

            // メニュー追加
            notifyIcon.ContextMenuStrip = contextMenu;

            // ダブルクリックでウィンドウを表示
            notifyIcon.DoubleClick += (s, e) =>{
                this.Show();
                this.WindowState = WindowState.Normal;
                this.Activate();
            };

             // 最小化時にウィンドウを隠す
            this.StateChanged += (s, e) =>{
                if (this.WindowState == WindowState.Minimized){
                    this.Hide();
                }
            };
        }

        protected override void OnClosed(EventArgs e)        {
            notifyIcon.Visible = false;
            notifyIcon.Dispose();
            base.OnClosed(e);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            this.Hide();
            e.Cancel = true;
        }
    }
}


```
<FONT COLOR="BLUE">

### 2.XAML
</FONT>

```xml
<Window x:Class="TSRTEST01.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:TSRTEST01" mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Closing="Window_Closing">
    <Grid>
        
    </Grid>
</Window>
```
<FONT COLOR="BLUE">

### 3.APPXaml.cs (二重起動防止用 無くても動作はする)
</FONT>

```c#
using System;
using System.Threading;
using System.Windows;

namespace TSRTEST01 {

    public partial class App : Application {
        private static Mutex mutex = null;

        protected override void OnStartup(StartupEventArgs e) {
            const string mutexName = "TSR-WPF-TEST01"; // APP毎に変える事

            bool createdNew;
            mutex = new Mutex(true, mutexName, out createdNew);

            if (!createdNew){
                // すでに起動中ならアプリを終了
                MessageBox.Show("すでにアプリは起動しています。", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
                Shutdown();
                return;
            }

            base.OnStartup(e);
        }

        protected override void OnExit(ExitEventArgs e)        {
            if (mutex != null){
                mutex.ReleaseMutex();
                mutex.Dispose();
            }
            base.OnExit(e);
        }
    }
}
```