أيقونات علبة النظام والإشعارات المنبثقة في تطبيقات Windows — مزالق NotifyIcon وكيفية اختيار AppNotification المناسب

· · علبة النظام, NotifyIcon, الإشعارات المنبثقة, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, تطبيق مقيم, تطوير Windows, الاستشارات التقنية

“أريد إخطار المستخدم بهدوء عند انتهاء معالجة ما” — “أريد أن تستمر المراقبة حتى بعد إغلاق النافذة” — هذان متطلبان كلاسيكيان يظهران معًا غالبًا في تطبيقات الأعمال: البقاء مقيمًا في علبة النظام (منطقة الإشعارات) وإرسال إشعارات منبثقة (Toast). لكن بمجرد الشروع في التنفيذ الفعلي، تبدأ مشكلات صغيرة بالظهور واحدة تلو الأخرى — تطبيق كان من المفترض أن يُغلق لا يستطيع الخروج فعليًا، أو تختفي الأيقونة بعد إعادة تشغيل Explorer، أو إشعار منبثق كان يعمل جيدًا أثناء التطوير لا يظهر أبدًا لدى العميل.

فضلًا عن ذلك، تُعد الإشعارات المنبثقة مجالًا استمرت فيه واجهة البرمجة بالتغيّر عبر الأجيال منذ عصر UWP. ليس من غير المألوف أن تبحث على الإنترنت فتجد كودًا تجريبيًا يستخدم، بمعايير اليوم، واجهة برمجية أصبحت الآن في وضع الصيانة فقط في أفضل الأحوال. يتناول هذا المقال كلًا من تصميم الإقامة في علبة النظام باستخدام NotifyIcon وكيفية اختيار واجهة برمجية للإشعارات المنبثقة اعتبارًا من عام 2026، مع المزالق العملية التي يُرجَّح أن تصادفها.

1. الخلاصة أولًا

  • لا يزال أساس الإقامة في علبة النظام هو System.Windows.Forms.NotifyIcon. لن تظهر الأيقونة ما لم تقم بتعيين Icon وضبط Visible = true (القيمة الافتراضية هي false). يمكن أن يصل نص تلميح الأداة Text إلى 127 حرفًا في .NET 6 وما بعده (63 حرفًا قبل ذلك).12
  • لا يوجد في WPF أي عنصر تحكم مكافئ لـ NotifyIcon. ينص جدول المقارنة الرسمي صراحةً على “لا يوجد عنصر تحكم مكافئ”. تفعيل كل من UseWPF وUseWindowsForms في ملف csproj واستخدام NotifyIcon الخاص بـ WinForms هو الحد الأدنى للإعداد.34
  • “إبقاء التطبيق في علبة النظام حتى عند النقر على زر الإغلاق” يُنفَّذ بضبط e.Cancel = true في FormClosing واستدعاء Hide(). قدّم دائمًا طريقة خروج منفصلة وصريحة (عنصر “إنهاء” في قائمة علبة النظام)، وتحقق من CloseReason حتى لا تعيق أبدًا إيقاف تشغيل النظام.5
  • واجهة البرمجة الموصى بها للإشعارات المنبثقة واضحة اعتبارًا من عام 2026. بغض النظر عمّا إذا كنت تبني تطبيق WinForms أو WPF أو تطبيقًا غير مُعبَّأ، فإن AppNotificationManager الخاص بـ Windows App SDK (حزمة NuGet: Microsoft.WindowsAppSDK) هو الموصى به. أما Windows.UI.Notifications فهي “للصيانة فقط”، ومكوّن الإشعارات في Community Toolkit تمت أرشفته.67
  • تعمل AppNotification الخاصة بـ Windows App SDK حتى من ملف exe عادي بدون هوية حزمة. تتولى AppNotificationManager.Default.Register() تلقائيًا تسجيل خادم COM والمعالجة المكافئة لـ AUMID، لذا لم تعد بحاجة لإعداد اختصار في قائمة ابدأ كما كان مطلوبًا في الأجيال الأقدم. مع ذلك، الإشعارات من العمليات المرفوعة الصلاحيات (مسؤول) غير مدعومة.8910
  • الإشعار المنبثق ليس وسيلة “مضمونة الوصول” إلى المستخدم. أثناء تفعيل مساعد التركيز (عدم الإزعاج)، لا تظهر اللافتة وتذهب مباشرة إلى مركز الإشعارات، ويمكن للمستخدم إيقاف الإشعارات كليًا من الإعدادات. يجب ألا يفترض تصميمك أن “الإشعار المنبثق قد ظهر” كشرط مسبق لسير عمل الأعمال.1112
  • لا يمكن لخدمة Windows إرسال إشعارات منبثقة (الجلسة رقم 0 لا تحتوي على واجهة مستخدم). النهج القياسي هو وضع وكيل مقيم في علبة النظام ضمن جلسة المستخدم والتنسيق معه عبر IPC.13

2. أساسيات الإقامة في علبة النظام باستخدام NotifyIcon

System.Windows.Forms.NotifyIcon هو المكوّن الذي يضع أيقونة في منطقة الإشعارات عند الطرف الأيمن من شريط المهام. تستخدمه التطبيقات التي تستمر في العمل بالخلفية كنقطة دخول لعرض الحالة وقائمة التشغيل.1

private NotifyIcon _trayIcon = null!;

private void InitializeTrayIcon()
{
    var menu = new ContextMenuStrip();
    menu.Items.Add("فتح", null, (_, _) => ShowMainWindow());
    menu.Items.Add(new ToolStripSeparator());
    menu.Items.Add("خروج", null, (_, _) => ExitApplication());

    _trayIcon = new NotifyIcon
    {
        Icon = Properties.Resources.AppIcon, // الأيقونة (Icon) إلزامية؛ بدونها لن يظهر شيء
        Text = "أداة مراقبة الطلبات ── قيد التشغيل",     // تلميح الأداة (بحد أقصى 127 حرفًا في .NET 6+)
        ContextMenuStrip = menu,
        Visible = true                        // القيمة الافتراضية false؛ لا يظهر إلا بعد ضبطها على true
    };
    _trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}

هناك ثلاث نقاط تستحق الانتباه.

  • لا تظهر الأيقونة إلا عند اجتماع Icon وVisible = true معًا. القيمة الافتراضية لـ Visible هي false.1
  • يوجد حد أقصى لطول Text. الحد الأقصى 127 حرفًا في .NET 6 وما بعده، و63 حرفًا في .NET 5 وما قبله وكذلك .NET Framework؛ وتجاوز هذا الحد يُطلق استثناء ArgumentException. حشو الكثير من معلومات الحالة في تلميح الأداة طريقة جيدة لتجعل تطبيقك يُطلق استثناءً فجأة في مرحلة ما لاحقًا.214
  • اربط قائمة النقر بزر الفأرة الأيمن باستخدام ContextMenuStrip. غالبًا ما تكون أيقونة علبة النظام نقطة الدخول الوحيدة التي يراها المستخدم، لذا وفّر على الأقل عنصري “فتح” و”إنهاء”.15

نقطة أخرى: أيقونة علبة النظام ليست فقط نقطة دخول للإشعارات، بل هي أيضًا مؤشر حالة مرئي باستمرار. وبما أنه يمكن استبدال Icon وText أثناء تشغيل التطبيق، فإن عكس الحالة الراهنة — أيقونة عادية عند سلامة الأمور، وأيقونة تحذير عند وجود مشكلة، أو عرض وقت آخر معالجة في تلميح الأداة — يعني أن المستخدم يمكنه إلقاء نظرة على الأيقونة ومعرفة الحالة الحالية حتى لو فاته إشعار منبثق. الإشعار المنبثق هو القناة الخاصة بـ”شيء حدث للتو”، بينما أيقونة علبة النظام هي القناة الخاصة بـ”هذه هي الحالة الآن” — هذا هو تقسيم الأدوار.

3. تصميم “البقاء في علبة النظام عند الإغلاق” ومسار الخروج الصحيح

من السلوكيات الكلاسيكية للتطبيقات المقيمة: النقر على زر الإغلاق لا ينهي العملية، بل يبقيها في علبة النظام. التنفيذ نفسه سهل الكتابة عبر إلغاء FormClosing، لكن كتابته بإهمال تؤدي إلى نوعين من المشكلات: لا تعود هناك طريقة للخروج من التطبيق، وإعاقة إيقاف تشغيل Windows.

private bool _exitRequested;

protected override void OnFormClosing(FormClosingEventArgs e)
{
    // إذا ضغط المستخدم على × فقط، أخفِ النافذة بدلاً من إغلاقها
    if (!_exitRequested && e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true;
        Hide();
        return;
    }
    // الإغلاق الطبيعي عبر قائمة «خروج» أو إيقاف تشغيل النظام وما شابه
    base.OnFormClosing(e);
}

private void ExitApplication()
{
    _exitRequested = true;
    _trayIcon.Visible = false;
    _trayIcon.Dispose();
    Close();
}

يُطلَق FormClosing قبل إغلاق النموذج، ويؤدي ضبط e.Cancel = true إلى إلغاء الإغلاق. عند القيام بذلك، تحقق دائمًا من CloseReason. السلوك الصحيح هو الإلغاء فقط في حالة UserClosing (إجراء من المستخدم)، وعدم الإلغاء مطلقًا في حالات الإغلاق الناتجة عن نظام التشغيل مثل WindowsShutDown.516

يتبع WPF نفس الفكرة: توثّق الوثائق الرسمية نفسها نمط ضبط e.Cancel = true في Window.Closing واستدعاء Hide().17

يُلاحظ أن التطبيق المقيم، بحكم طبيعته، معرّض دائمًا لمشكلة “إذا تم تشغيله مرتين، تتم معالجة نفس الحدث مرتين”. تناولنا منع تعدد الإطلاق في مقال منع تشغيل تطبيق Windows أكثر من مرة — أقفال Mutex المسمّاة وتفعيل النسخة الجاري تشغيلها عند التشغيل الثاني. ومقال لماذا نستخدم .NET Generic Host وBackgroundService في تطبيق سطح المكتب هو الأساس لكيفية بناء المحتوى الفعلي للتطبيق المقيم — حلقة المراقبة أو المعالجة الدورية.

4. الإقامة في علبة النظام في تطبيق WPF

لا يوجد في WPF عنصر تحكم مكافئ لـ NotifyIcon. هذه فجوة موثّقة في المنصة، مذكورة صراحةً بعبارة “لا يوجد عنصر تحكم مكافئ” في جدول المقارنة الرسمي “عناصر تحكم WPF المكافئة لعناصر تحكم WinForms”.3

هناك خياران واقعيان.

الخيار نظرة عامة متى يُختار
استخدام NotifyIcon الخاص بـ WinForms مع WPF تفعيل كل من UseWPF وUseWindowsForms في csproj لا اعتمادية إضافية. كافٍ إذا كنت بحاجة فقط إلى أيقونة وقائمة
مكتبة خارجية (مثل H.NotifyIcon) تطبيقات لأيقونة علبة النظام مصممة لـ WPF/WinUI اختر هذا إذا أردت الكتابة بـ XAML أو تحتاج إلى نافذة منبثقة أكثر ثراءً. ضع في اعتبارك أنها ليست مكوّنًا رسميًا من Microsoft

الخيار الأول ليس سوى إدراج خاصيتين جنبًا إلى جنب في ملف مشروع بأسلوب SDK.4

<PropertyGroup>
  <TargetFramework>net8.0-windows</TargetFramework>
  <UseWPF>true</UseWPF>
  <UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>

يمكنك بعدها ببساطة إنشاء System.Windows.Forms.NotifyIcon مباشرة من Application الخاص بـ WPF. ستكون القائمة فقط من نوع WinForms (ContextMenuStrip)، لكن بالنسبة لشيء بسيط مثل قائمة علبة النظام، نادرًا ما يمثل ذلك مشكلة عملية.

5. ثلاثة مزالق لأيقونات علبة النظام

5.1. تنتقل الأيقونات افتراضيًا إلى منطقة “الأيقونات المخفية” — لا يمكن إجبارها على الظهور دائمًا

يظهر دائمًا طلب بإبقاء أيقونة تطبيقك مرئية بشكل دائم على شريط المهام، لكن هذا لا يمكن تحقيقه برمجيًا. تنص الوثائق الرسمية صراحةً على أن الأيقونات تُضاف افتراضيًا إلى منطقة الفائض (الأيقونات المخفية)، والمستخدم وحده من يقرر ما إذا كانت الأيقونة سترتقي للظهور الدائم في منطقة الإشعارات.18

في Windows 11، يقوم المستخدم بتفعيل أو إيقاف ذلك لكل تطبيق عبر “الإعدادات → التخصيص → شريط المهام → أيقونات إضافية في علبة النظام”. إذا كانت الرؤية الدائمة مهمة تشغيليًا، فإن التصرف الصحيح هو توثيق ذلك في دليل التثبيت أو المساعدة (“يرجى تفعيل هذا الإعداد”)، وليس محاولة فرضها في الكود.

5.2. تختفي الأيقونة عند إعادة تشغيل Explorer — TaskbarCreated

عندما يتعطل Explorer أو يُعاد تشغيله ويعيد بناء شريط المهام، تختفي أي أيقونات مسجَّلة في علبة النظام. يقوم الغلاف (Shell) ببث رسالة TaskbarCreated إلى جميع النوافذ من المستوى الأعلى عند إنشاء شريط المهام، والقاعدة على مستوى Win32 هي أن تُعيد تسجيل أيقونتك عند استلام هذه الرسالة.19

يتعامل NotifyIcon في WinForms مع هذه الرسالة داخليًا ويعيد التسجيل تلقائيًا، لذا لا تحتاج عادةً للتفكير في الأمر. إذا كنت تستدعي Shell_NotifyIcon مباشرة عبر P/Invoke (أو تصون تطبيقًا أصليًا لديه أيقونة في علبة النظام)، فتأكد من وجود منطق إعادة التسجيل هذا. عرض “تختفي الأيقونة فقط عند إعادة تشغيل Explorer، بينما العملية لا تزال حية” هو غالبًا هذه المشكلة بعينها.

5.3. نسيان استدعاء Dispose يترك أيقونة شبحية

على مستوى Win32، يجب حذف أيقونة علبة النظام صراحةً (NIM_DELETE) بمجرد عدم الحاجة إليها، وفي WinForms تقع هذه المسؤولية على عاتق NotifyIcon.Dispose().18 تأكد من أن مسار الخروج يمر دائمًا عبر Visible = false ثم Dispose(). أي مطوّر لتطبيقات مقيمة قد رأى هذا المشهد مرة واحدة على الأقل: تنتهي العملية بشكل غير طبيعي دون تنظيف، فتبقى أيقونة شبحية عالقة في علبة النظام حتى يمرر المستخدم مؤشر الفأرة فوقها.

كما في الكود الوارد في الفصل 3، من الأسلم تنظيف أيقونة علبة النظام كجزء من معالج قائمة “إنهاء” قبل استدعاء Close().

6. الإشعارات المنبثقة — أي واجهة برمجة يجب استخدامها

الإشعار المنبثق هو اللافتة التي تظهر في الزاوية اليمنى السفلى من الشاشة ثم تبقى في مركز الإشعارات. طريقة إرسالها من تطبيق سطح مكتب ظلت تتغيّر خلال العقد الماضي، ولا تزال نتائج البحث مليئة بأمثلة قديمة. إليك الوضع اعتبارًا من عام 2026.6

واجهة البرمجة حزمة NuGet / مساحة الاسم الحالة (اعتبارًا من 2026) القرار
Windows App SDK AppNotification Microsoft.WindowsAppSDK / Microsoft.Windows.AppNotifications موصى بها رسميًا لـ WinForms وWPF وWin32 غير المُعبَّأ استخدمها في أي عمل جديد
Community Toolkit Notifications Microsoft.Toolkit.Uwp.Notifications تمت أرشفتها. لم تعد جزءًا من Community Toolkit الحالية؛ آخر تحديث كان عام 2022 فقط لصيانة الكود الحالي. لا تعتمدها في عمل جديد
استخدام WinRT مباشرة Windows.UI.Notifications (عبر Windows TFM) للصيانة فقط. موصى بها بشكل أساسي لـ UWP لا تعتمدها في عمل جديد
NotifyIcon.ShowBalloonTip System.Windows.Forms واجهة برمجية للإشعارات الفقاعية. تُعرض كإشعار منبثق على Windows 10؛ وعلى Windows 11 تكون عرضًا مؤقتًا لا يبقى في مركز الإشعارات مناسبة للحالات البسيطة، لكن انتبه إلى أن سلوكها يختلف بين إصدارات نظام التشغيل

هناك ملاحظتان إضافيتان.

  • تنتمي ShowBalloonTip إلى فئة “واجهة برمجية قديمة، لكنها في الواقع لا تزال فعّالة”. على Windows 10، تُعرض الفقاعات كإشعار منبثق وتبقى في مركز الإشعارات حتى يتم إغلاقها. أما على Windows 11، فتتصرف بشكل أقرب إلى الفقاعة التقليدية المؤقتة ولا تبقى في مركز الإشعارات بعد اختفائها. كما يتم الآن تجاهل تحديد مدة العرض (timeout)، وتتبع بدلًا من ذلك إعدادات إمكانية الوصول في نظام التشغيل. هذا كافٍ تمامًا لتطبيق مقيم في علبة النظام يريد فقط إعطاء تنبيه خفيف حول تغيّر في حالته، لكن إذا أردت أن يبقى سجل في مركز الإشعارات، فتذكّر أنها لن تفيدك على Windows 11.20
  • الطريقة الكلاسيكية لاستخدام Windows.UI.Notifications الخام من تطبيق سطح مكتب لديها قيد معروف: لن يظهر الإشعار المنبثق ما لم تكن قد سجّلت اختصارًا يحمل AUMID (AppUserModelID) في قائمة ابدأ.21 وما تتناوله الفصل التالي، AppNotification، هو ما يزيل هذا العناء تمامًا.

7. تنفيذ بسيط لـ AppNotification من Windows App SDK

تعمل واجهة برمجة إشعارات التطبيق في Windows App SDK حتى من ملف exe عادي وغير مُعبَّأ، دون هوية حزمة.8 المتطلبات الأساسية على مستوى المشروع هي إطار عمل هدف خاص بـ Windows (TFM) (مثل net8.0-windows10.0.19041.0)، و<WindowsPackageType>None</WindowsPackageType> (للحالة غير المُعبَّأة)، وتثبيت وقت تشغيل Windows App SDK لدى جهة النشر.9

using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;

// مرة واحدة فقط عند بدء التشغيل؛ ترتيب تسجيل المعالج ← Register() مهم
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
    // تصل هنا نقرات الإشعار المنبثق أو ضغطات الأزرار (يتم التمييز عبر args.Arguments)
    if (args.Arguments.TryGetValue("action", out var action) && action == "openLog")
    {
        ShowLogWindow();
    }
};
AppNotificationManager.Default.Register();

// عرض الإشعار
var notification = new AppNotificationBuilder()
    .AddText("اكتمل استيراد الطلبات")
    .AddText("12 جديدًا، وخطأ واحد")
    .AddButton(new AppNotificationButton("فتح السجل")
        .AddArgument("action", "openLog"))
    .BuildNotification();
AppNotificationManager.Default.Show(notification);

النقاط العملية التي يجب الانتباه لها هي:

  • اشترك في NotificationInvoked قبل استدعاء Register(). أخطئ في الترتيب وستفوّت الحدث عندما يؤدي النقر على الإشعار المنبثق إلى تشغيل التطبيق.9
  • استدعِ AppNotificationManager.Default.Unregister() قبل خروج التطبيق. تنص الوثائق الرسمية صراحةً على أن استدعاء Unregister قبل الخروج ينظّف الموارد ويضمن تشغيل التطبيق بشكل صحيح عند النقر على إشعار في المرة القادمة. أدرج هذا في معالج قائمة “إنهاء” من الفصل 3، إلى جانب NotifyIcon.Dispose() (توجد أيضًا UnregisterAll() منفصلة لحالات مثل إلغاء تثبيت تطبيق لن يستخدم الإشعارات مطلقًا بعد الآن، والتي تمسح معلومات التسجيل بالكامل).22
  • بالنسبة للتطبيقات غير المُعبَّأة، تتولى Register() تلقائيًا تسجيل خادم COM والمعالجة المكافئة لـ AUMID. لم تعد بحاجة إلى إعداد الاختصار أو تسجيل السجل (Registry) الذي كان مطلوبًا في الجيل الأقدم من WinRT الخام. إذا اخترت التعبئة عبر MSIX، فستحتاج بدلًا من ذلك إلى الإعلان عن مُنشِّط COM في Package.appxmanifest.9
  • كن حذرًا إذا اخترت النشر ذاتي الاحتواء (self-contained) الذي يضمّن وقت تشغيل Windows App SDK. تعتمد AppNotificationManager على حزمة Singleton الخاصة بـ Windows App SDK، وهناك اعتبارات موثّقة رسميًا لاستدعائها من تطبيق ذاتي الاحتواء. تحقق من ذلك قبل أن تستقر على طريقة النشر.23
  • الإشعارات من العمليات المرفوعة الصلاحيات (مسؤول) غير مدعومة. لا يُطلق Show استثناءً، بل يفشل بصمت، ما يجعل السبب الجذري صعب الملاحظة. بالنسبة للتطبيقات التي تحتاج إلى رفع الصلاحيات، فكّر في عزل الإشعار فقط في عملية غير مرفوعة الصلاحيات (تناولنا نهج العزل هذا في مقال “طريقة عملية لعزل الأجزاء التي تتطلب صلاحيات المسؤول فقط في تطبيق Windows”، /blog/2026/03/16/001-windows-admin-broker-deep-dive/).10
  • عند النقر على إشعار منبثق، إذا لم يكن التطبيق قيد التشغيل حاليًا، سيتم تشغيل عملية جديدة. في تطبيق مقيم في علبة النظام، تحتاج إلى الجمع بين هذا ومنع تعدد الإطلاق (الفصل 3) بحيث يُسلَّم العمل إلى النسخة القائمة بدلًا من ذلك.

8. استخدام متقدم — إشعارات منبثقة بشريط تقدّم وتنظيف الإشعارات

8.1. تحديث التقدّم داخل الإشعار المنبثق — UpdateAsync

بالنسبة لعمل طويل مثل “استيراد 120 سجل طلب”، النهج الصحيح ليس إطلاق إشعار جديد مرارًا وتكرارًا، بل تحديث شريط التقدّم داخل إشعار منبثق واحد. تحتوي AppNotification على آلية ربط بيانات وتحديث مصمّمة تحديدًا لهذا الغرض.24

يتم البناء على ثلاث مراحل. أولًا، حوّل كل قيمة في شريط التقدّم إلى عنصر نائب باستخدام طرق Bind، وامنح الإشعار هوية عبر TagGroup إذا لزم الأمر)، ثم اعرضه. بعد ذلك، كل ما عليك فعله هو تمرير AppNotificationProgressData إلى UpdateAsync، وسيُعاد كتابة محتوى الإشعار المنبثق المعروض بالفعل فقط.

const string tag = "import-progress";
const string group = "batch";

// العرض الأول: اربط القيمة وحدد القيمة الأولية في Progress
var notification = new AppNotificationBuilder()
    .AddText("جارٍ استيراد بيانات الطلبات")
    .AddProgressBar(new AppNotificationProgressBar()
        .BindStatus().BindValue().BindValueStringOverride())
    .SetTag(tag)
    .SetGroup(group)
    .BuildNotification();

notification.Progress = new AppNotificationProgressData(1) // رقم التسلسل
{
    Status = "جارٍ الاستيراد...", Value = 0, ValueStringOverride = "0/120 عنصرًا"
};
AppNotificationManager.Default.Show(notification);

// في كل مرة يتقدم فيها التقدم: زِد رقم التسلسل وحدّث
var data = new AppNotificationProgressData(2)
{
    Status = "جارٍ الاستيراد...", Value = 45 / 120.0, ValueStringOverride = "45/120 عنصرًا"
};
await AppNotificationManager.Default.UpdateAsync(data, tag, group);

رقم التسلسل هو عدد صحيح غير صفري يشير إلى ترتيب التحديث؛ إذا وصلت عدة تحديثات خارج ترتيبها، يُعرض العدد الأكبر منها. توجد هذه الآلية حتى لا تتراجع التحديثات المُرسَلة بشكل متزامن من عمل غير متزامن إلى قيمة قديمة، لذا احرص دائمًا على أن يتزايد بشكل رتيب.25

8.2. “التحديث” مقابل “الاستبدال” — متى تستخدم أيًا منهما

يمكنك أيضًا تحديث المظهر عبر إعادة Show لإشعار جديد بنفس Tag — وهو “استبدال” — لكن السلوك يختلف عن “التحديث”.24

  الاستبدال (إعادة Show بنفس Tag) التحديث (UpdateAsync)
الموضع داخل مركز الإشعارات ينتقل إلى الأعلى يبقى في مكانه
ما يمكن تغييره كل شيء، بما في ذلك التخطيط فقط شريط التقدّم المرتبط والنص الرئيسي
النافذة المنبثقة يمكن أن تظهر مرة أخرى لا تظهر مرة أخرى (تُعاد كتابتها بصمت)
إذا كان المستخدم قد أغلقها بالفعل تُسلَّم دائمًا تفشل

التوجيه الرسمي واضح هنا أيضًا: التحديث للتقدّم المؤقت، والاستبدال عند الاكتمال. لا داعي لإظهار نافذة منبثقة كل مرة ينتقل فيها التقدّم من 50% إلى 65%، لكن رسالة “اكتمل الاستيراد (3 أخطاء)” ينبغي أن تصل حتى لو كان المستخدم قد أغلق إشعار التقدّم بالفعل. عمليًا، النمط الشائع هو إزالة شريط التقدّم عند الاكتمال واستبداله بتخطيط مختلف مزوَّد بأزرار، مثل “عرض تفاصيل الأخطاء”.

8.3. تنظيف الإشعارات القديمة المتبقية في مركز الإشعارات

تتراكم الإشعارات المنبثقة في مركز الإشعارات حتى يقوم المستخدم بمسحها. إذا كان التطبيق المقيم يرسل عدة إشعارات كل يوم، فإن مركز الإشعارات يمتلئ بالإشعارات القديمة الخاصة بتطبيقك، وقد ينتهي الأمر بدفن الإشعارات المهمة.

يتيح لك إرفاق Tag / Group حذف إشعارات تطبيقك الخاصة برمجيًا (حسب الوسم، أو المجموعة، أو الكل). يمكنك أيضًا تعيين مدة انتهاء صلاحية. من المفيد تحديد قواعد التنظيف — “مسح إشعارات التقدّم عند الاكتمال”، “تقصير مدة الصلاحية للإشعارات المعلوماتية” — في نفس الوقت الذي تصمم فيه كيفية إرسال الإشعارات.26

9. تصميم الحالات التي “لا تصل” فيها الإشعارات

الإشعار المنبثق ليس آلية توصيل مضمونة. توجد مسارات طبيعية تمامًا لا يُعرض فيها الإشعار أو لا يُلاحَظ.

الحالة السلوك
تفعيل مساعد التركيز (عدم الإزعاج) لا تظهر اللافتة؛ يذهب الإشعار مباشرة إلى مركز الإشعارات
إيقاف الإشعارات، لكل تطبيق أو على مستوى النظام لا يظهر شيء
AppNotification من عملية مرفوعة الصلاحيات غير مدعومة. لا يظهر شيء
تشغيل تطبيق بملء الشاشة، إلخ قد يقمعه نظام التشغيل حسب تقديره الخاص

يُسمى “عدم الإزعاج” على Windows 10 باسم “مساعد التركيز (Focus assist)”، ويُتحكم فيه على Windows 11 عبر مفتاح “عدم الإزعاج” ضمن “الإعدادات → النظام → الإشعارات”، ويمكن أن يتحكم فيه المستخدم أو سياسة المجموعة. بل توجد قاعدة تنص على تفعيله تلقائيًا للساعة الأولى بعد ترقية نظام التشغيل.11 لا يعني مساعد التركيز “عدم إرسال الإشعارات” بل “قمع اللافتة وتوجيهها مباشرة إلى مركز الإشعارات”، لذا سيظل بإمكان المستخدم رؤيتها إذا فتح مركز الإشعارات لاحقًا.12

الدلالة التصميمية بسيطة:

  • تعامل مع الإشعار المنبثق كقناة مساعدة تساعد المستخدمين على الاستجابة بشكل أسرع إذا لاحظوها، واحرص على أن تكون المعلومات الإلزامية لسير عمل الأعمال قابلة للتأكيد دائمًا عبر شاشة أو قائمة داخل التطبيق. لا تبنِ سير عمل يفترض أن “الإشعار المنبثق قد ظهر على الأرجح”.
  • بالنسبة للإشعارات التي يجب أن تقاطع المستخدم حتمًا (مثل تحذير إيقاف طارئ)، توجد آلية لاختراق وضع عدم الإزعاج بموافقة المستخدم عبر AppNotificationScenario.Urgent،27 لكن الإفراط في استخدامها سيدفع المستخدمين إلى إيقاف جميع إشعارات تطبيقك تمامًا. اقتصر استخدامها على الحالات العاجلة حقًا فقط.

للاطلاع على سؤال أوسع حول كيفية عرض أشياء مثل أخطاء الأجهزة الخارجية على الشاشة، راجع مقال أفضل الممارسات للتحقق من حالة الأجهزة الخارجية وعرضها.

10. عندما تحتاج إلى إرسال إشعارات من خدمة Windows

إنه متطلب طبيعي: “عندما تكتشف خدمة مقيمة مشكلة ما، أريد إخطار المستخدم المسجَّل دخوله عبر إشعار منبثق” — لكن لا يمكن للخدمة إرسال إشعار منبثق مباشرة. منذ Windows Vista، تعمل الخدمات ضمن جلسة مخصصة تُدعى الجلسة رقم 0 ولا تملك القدرة على عرض واجهة مستخدم تتفاعل مع المستخدم.13

النهج القياسي هو فصل واجهة المستخدم إلى عملية تعمل ضمن جلسة المستخدم.

[الجلسة 0]                        [جلسة المستخدم]
 خدمة Windows   <-- IPC -->   وكيل مقيم في علبة النظام
 (المراقبة/منطق الأعمال)   أنابيب مسمّاة، إلخ   (NotifyIcon + AppNotification)

في البيئات التي يسجّل فيها عدة مستخدمين الدخول في آن واحد (RDP، الأجهزة المشتركة)، سينتهي الأمر بتشغيل الوكيل مرة واحدة لكل جلسة. تحديد “من ينبغي إخطاره” (الجميع، أم مستخدم معيّن فقط) كمتطلب مسبقًا يجعل التصميم أكثر ثباتًا.

11. الخلاصة

  • تعتمد الإقامة في علبة النظام على NotifyIcon. اعرضها عبر Icon + Visible = true، وحافظ على Text ضمن حد 127 حرفًا (.NET 6 وما بعده)، وتأكد من استدعاء Dispose() بشكل موثوق في مسار الخروج. استخدم الأيقونة وتلميح الأداة أيضًا كمؤشر مرئي دائم للحالة الراهنة.
  • “البقاء في علبة النظام حتى عند الإغلاق بزر ×” يُنفَّذ بإلغاء FormClosing، لكن ألغِ فقط في حالة CloseReason.UserClosing ووفّر دائمًا قائمة “إنهاء” صريحة.
  • المستخدم وحده من يقرر ما إذا كانت الأيقونة ستظهر دائمًا. تعامل مع اختفاء الأيقونة عند إعادة تشغيل Explorer عبر إعادة التسجيل عند TaskbarCreated (يقوم WinForms بذلك تلقائيًا).
  • بالنسبة لأي عمل جديد يتعلق بالإشعارات المنبثقة، فإن AppNotificationManager الخاص بـ Windows App SDK هو الخيار الوحيد الحقيقي. يعمل حتى من ملف exe غير مُعبَّأ بدون هوية حزمة، وتُؤتمِت Register() عملية التسجيل المرهقة. إشعارات Community Toolkit تمت أرشفتها، وWinRT الخام في وضع الصيانة فقط.
  • بالنسبة للتقدّم المؤقت في عمل طويل، أعِد كتابة شريط التقدّم بصمت داخل إشعار منبثق واحد باستخدام UpdateAsync، واستخدم الاستبدال لإظهار نافذة منبثقة عند الاكتمال — هذا هو النمط القياسي. أرفق Tag/Group، وأدرج تنظيف الإشعارات القديمة ضمن التصميم.
  • الإشعار المنبثق قناة مساعدة قد لا تصل بسبب عدم الإزعاج أو إيقاف الإشعارات. صمّم بحيث تكون المعلومات الإلزامية قابلة للتأكيد دائمًا داخل التطبيق، وانتبه إلى أن الإشعارات من العمليات المرفوعة الصلاحيات غير مدعومة.
  • تتحقق الإشعارات من الخدمة عبر بنية العزل المكوّنة من وكيل مقيم في علبة النظام ضمن جلسة المستخدم بالإضافة إلى IPC.

مقالات ذات صلة

مجالات الاستشارة ذات الصلة

تتعامل شركة KomuraSoft LLC ذات المسؤولية المحدودة مع تصميم وتنفيذ أدوات المراقبة ووكلاء الإشعارات المقيمين في علبة النظام، ومراجعة بنية الأنظمة المقيمة المدمجة مع خدمات Windows، واستكشاف أخطاء التطبيقات المقيمة الحالية وإصلاحها.

روابط مرجعية

  1. Microsoft Learn, NotifyIcon Class / NotifyIcon.Visible Property. حول كون NotifyIcon المكوّن الذي يعرض أيقونة في منطقة الإشعارات، وضرورة ضبط خاصية Icon، وكون القيمة الافتراضية لـ Visible هي false.  2 3

  2. Microsoft Learn, NotifyIcon.Text Property. حول حد نص تلميح الأداة، وإطلاق استثناء ArgumentException عند تجاوزه.  2

  3. Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls. حول عدم وجود عنصر تحكم في WPF مكافئ لـ NotifyIcon.  2

  4. Microsoft Learn, MSBuild properties for .NET desktop projects. حول إمكانية تفعيل كل من UseWPF وUseWindowsForms معًا.  2

  5. Microsoft Learn, Form.FormClosing Event. حول إطلاق الحدث قبل إغلاق النموذج، وإمكانية إلغاء الإغلاق عبر Cancel.  2

  6. Microsoft Learn, Windows notifications overview. حول واجهة برمجة الإشعارات الموصى بها حسب نوع التطبيق، بما في ذلك التوصية باستخدام Windows App SDK لـ WinForms/WPF/Win32 غير المُعبَّأ، وحالة “للصيانة فقط” لـ Windows.UI.Notifications.  2

  7. Microsoft Learn, Windows Community Toolkit — Notifications (archive). حول أرشفة مكوّن Notifications واستبداله بإشعارات تطبيق Windows App SDK. 

  8. Microsoft Learn, Use the Windows App SDK in an existing project. حول دعم واجهة برمجة إشعارات التطبيق في Windows App SDK لكل من التطبيقات المُعبَّأة وغير المُعبَّأة دون الحاجة لهوية حزمة.  2

  9. Microsoft Learn, Quickstart: App notifications in the Windows App SDK. حول استخدام AppNotificationManager/AppNotificationBuilder، والتسجيل التلقائي عبر Register() للتطبيقات غير المُعبَّأة، وضرورة الاشتراك في NotificationInvoked قبل Register()، والإعلانات في ملف البيان للتطبيقات المُعبَّأة.  2 3 4

  10. Microsoft Learn, App notifications overview. حول عدم دعم إرسال الإشعارات من تطبيق مرفوع الصلاحيات.  2

  11. Microsoft Learn, Notifications don’t appear or aren’t displayed. حول قمع الإشعارات عبر عدم الإزعاج (Quiet Hours / Focus Assist)، والتفعيل التلقائي للساعة الأولى بعد ترقية نظام التشغيل، وخطوات التحقق عند عدم ظهور الإشعارات.  2

  12. Microsoft Learn, Focus assist. حول كون مساعد التركيز يقمع عرض لافتة الإشعار ويوجهها مباشرة إلى مركز الإشعارات.  2

  13. Microsoft Learn, Service Changes for Windows Vista / Interactive Services. حول تخصيص الجلسة 0 للخدمات وعدم دعمها للتفاعل مع المستخدم، وكون الطريقة الموصى بها لعرض شيء للمستخدم هي استخدام CreateProcessAsUser لإنشاء عملية في جلسة المستخدم والتنسيق عبر IPC.  2

  14. Microsoft Learn, NotifyIcon.Text maximum text length increased. حول التغيير الجذري في .NET 6 الذي وسّع الحد الأقصى لطول Text من 63 إلى 127 حرفًا. 

  15. Microsoft Learn, How to associate a shortcut menu with a Windows Forms NotifyIcon component. حول ربط قائمة النقر بزر الفأرة الأيمن عبر ContextMenuStrip. 

  16. Microsoft Learn, FormClosingEventArgs Class. حول إمكانية تحديد سبب إغلاق النموذج عبر CloseReason. 

  17. Microsoft Learn, How to close a window or dialog box (WPF). حول نمط حدث Closing في WPF بضبط e.Cancel = true واستدعاء Hide() للإخفاء بدلًا من الإغلاق. 

  18. Microsoft Learn, Notification Area (Windows Shell). حول إضافة الأيقونات افتراضيًا إلى منطقة الفائض، وكون المستخدم وحده من يقرر ترقية أيقونة إلى منطقة الإشعارات، وضرورة حذف الأيقونات غير المطلوبة عبر NIM_DELETE.  2

  19. Microsoft Learn, The Taskbar. حول بث رسالة TaskbarCreated عند إنشاء شريط المهام، وضرورة إعادة تسجيل أيقونة علبة النظام عند استلامها. 

  20. Microsoft Learn, Shell_NotifyIconW function. حول عرض الإشعارات الفقاعية كإشعار منبثق (لافتة) وبقائها في مركز الإشعارات على Windows 10، وكونها عرضًا مؤقتًا لا يبقى في مركز الإشعارات على Windows 11، وتبعية مدة العرض لإعدادات إمكانية الوصول. 

  21. Microsoft Learn, Send a local toast notification from a C# app (Win32/الواجهة الكلاسيكية). حول ضرورة تسجيل اختصار يحمل AUMID في قائمة ابدأ عند استخدام ToastNotificationManager الخام. 

  22. Microsoft Learn, AppNotificationManager.Register Method. حول استدعاء Unregister قبل خروج التطبيق لتنظيف الموارد وضمان صحة عمليات تشغيل الإشعارات اللاحقة، واستخدام UnregisterAll لمسح معلومات التسجيل عند عدم استخدام الإشعارات مطلقًا بعد ذلك. 

  23. Microsoft Learn, AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture. حول اعتماد صنف AppNotificationManager على حزمة Singleton الخاصة بـ Windows App SDK، والاعتبارات الخاصة باستدعائه من تطبيق ذاتي الاحتواء. 

  24. Microsoft Learn, App notification progress bar and data binding. حول ربط البيانات عبر طرق Bind، والتحديث عبر AppNotificationProgressData وUpdateAsync، والفروق بين التحديث والاستبدال (الموضع داخل مركز الإشعارات، ونطاق ما يمكن تغييره، وإعادة ظهور النافذة المنبثقة، والسلوك بعد إغلاق المستخدم للإشعار)، وضرورة النظر في الاستبدال عند الاكتمال.  2

  25. Microsoft Learn, AppNotificationProgressData(UInt32) Constructor. حول كون رقم التسلسل عددًا صحيحًا غير صفري يحدد ترتيب التحديث، وعرض البيانات ذات الرقم الأكبر عند استلام عدة تحديثات. 

  26. Microsoft Learn, Remove app notifications. حول إرفاق الوسوم/المجموعات، وحذف الإشعارات المعروضة بالفعل، وتعيين مدة انتهاء الصلاحية. 

  27. Microsoft Learn, App notification content. حول أسبقية الإشعارات المهمة ذات scenario=”urgent” على إعدادات عدم الإزعاج بموافقة المستخدم. 

أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

كيف يمكنني إنشاء تطبيق مقيم في علبة النظام باستخدام WPF؟
لا يوجد في WPF عنصر تحكم مكافئ لـ NotifyIcon، لذا فإن الحد الأدنى للإعداد هو تفعيل كل من UseWPF وUseWindowsForms في ملف csproj واستخدام NotifyIcon الخاص بـ WinForms. إذا أردت أسلوب كتابة متوافقًا مع XAML، فإن مكتبة خارجية مثل H.NotifyIcon تُعد خيارًا متاحًا، لكن ضع في اعتبارك أنها ليست مكوّنًا رسميًا من Microsoft عند اتخاذ قرار اعتمادها.
ما هي واجهة برمجة التطبيقات التي ينبغي استخدامها للإشعارات المنبثقة؟
بالنسبة لأي تطوير جديد اعتبارًا من عام 2026، سواء كنت تبني تطبيق WinForms أو WPF أو تطبيقًا غير مُعبَّأ، فإن AppNotificationManager الخاص بـ Windows App SDK هو الخيار الموصى به رسميًا. أما Microsoft.Toolkit.Uwp.Notifications فقد تم أرشفتها ولم تعد جزءًا من Community Toolkit الحالية، في حين أن ToastNotificationManager الخاص بـ WinRT أصبح في وضع الصيانة فقط. إذا كان الكود الحالي لديك يعمل بالفعل بأحد هذين الخيارين، فلا داعي للتسرع في إعادة كتابته، لكن لا يوجد سبب لاختيارهما في عمل جديد.
ماذا يجب أن أتحقق منه عندما لا يظهر الإشعار المنبثق؟
ابدأ بإعدادات نظام التشغيل. إذا كانت ميزة «مساعد التركيز» (عدم الإزعاج) مُفعّلة، فإن الإشعارات تتخطى شكل اللافتة وتذهب مباشرة إلى مركز الإشعارات، كما يمكن إيقاف الإشعارات لكل تطبيق على حدة أو على مستوى النظام بالكامل من تطبيق الإعدادات. على مستوى التنفيذ، السببان الشائعان هما أن AppNotification غير مدعومة عند إرسالها من عملية مرفوعة الصلاحيات (مسؤول)، وأن Windows.UI.Notifications الخام لا تعرض شيئًا عند استدعائها من تطبيق سطح مكتب بدون AUMID.
هل يمكن لخدمة Windows إرسال إشعارات منبثقة؟
لا، لا يمكنها ذلك. منذ Windows Vista، تعمل الخدمات ضمن الجلسة رقم 0 (Session 0) ولا تملك القدرة على عرض واجهة مستخدم للتفاعل مع المستخدم. إذا كنت بحاجة لإخطار المستخدم، فإن النمط القياسي هو وضع عملية وكيل مقيمة في علبة النظام ضمن جلسة المستخدم، وجعلها تتواصل مع الخدمة عبر آلية اتصال بين العمليات (IPC) مثل الأنابيب المسمّاة، على أن يتولى الوكيل نفسه مسؤولية عرض الإشعار.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.

روابط عامة

العودة إلى المدونة