إقامة تطبيقات Windows في علبة النظام والإشعارات المنبثقة ── مطبات NotifyIcon وكيفية اختيار AppNotification
· آخر تحديث: · غو كومورا · علبة النظام, NotifyIcon, الإشعارات المنبثقة, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, تطبيق مقيم, تطوير Windows, الاستشارات التقنية
سجل التعديلات (3 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- أضفنا علامات اتجاه غير مرئية (RLM) بعد الشرطات الواقعة بين كلمات إنجليزية، حتى يُعرض ترتيب الكلمات بشكل صحيح في التخطيط من اليمين إلى اليسار. نص المقال نفسه لم يتغيّر.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621700)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
غو كومورا (2026). إقامة تطبيقات Windows في علبة النظام والإشعارات المنبثقة ── مطبات NotifyIcon وكيفية اختيار AppNotification. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621700 https://comcomponent.com/ar/blog/windows-tray-icon-toast-notification-guide/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621700
- DOI (هذه النسخة)
- 10.5281/zenodo.22241037
«أريد إخطار المستخدم بهدوء عند انتهاء معالجة» — «أريد أن تستمر المراقبة حتى بعد إغلاق النافذة» ── في تطبيقات الأعمال، الإقامة في علبة النظام (منطقة الإشعارات) والإشعارات المنبثقة (Toast) مطلبان كلاسيكيان يظهران معاً غالباً. لكن بمجرد التنفيذ تبدأ مشكلات صغيرة بالظهور واحدة تلو الأخرى: تطبيق كان يُفترض أن يُغلق لا يستطيع الخروج فعلاً، أو تختفي الأيقونة بعد إعادة تشغيل Explorer، أو إشعار منبثق كان يعمل أثناء التطوير لا يظهر أبداً لدى العميل.
فضلاً عن ذلك، تُعد الإشعارات المنبثقة مجالاً استمر فيه تعاقب أجيال الواجهة منذ عصر UWP. ليس نادراً أن تبحثوا فتجدوا كوداً تجريبياً يستخدم، بمعايير اليوم، واجهة صارت في أفضل الأحوال في وضع الصيانة فقط. يرتّب هذا المقال تصميم الإقامة في العلبة عبر NotifyIcon، وكيفية اختيار واجهة الإشعارات المنبثقة اعتباراً من عام 2026، مع المطبات العملية التي يُرجَّح أن تصادفوها.
كيفية قراءة هذا المقال
أحد عشر فصلاً وطويل، لذا نعرض مداخل حسب الغرض. لا يلزم القراءة من الرأس بالترتيب.
| الغرض | أين تقرأون |
|---|---|
| معرفة الصورة الكاملة فقط | الفصل 1 (الخلاصة) والفصل 11 (الخلاصة الختامية) |
| بناء تطبيق مقيم في العلبة من الآن | الفصول 2 إلى 5 ── أساسيات NotifyIcon، وتصميم مسار الإنهاء، والجمع مع WPF، وثلاثة مطبات |
| تقرير أي واجهة إشعار منبثق تُختار | جدول المقارنة في الفصل 6. إن كان الحكم وحده يكفي فهذا يكفي |
| تنفيذ AppNotification | الفصل 7 ── الحد الأدنى للتنفيذ، وإعداد csproj، وإدخال زمن التشغيل لدى جهة التوزيع |
| صنع إشعارات متقدّمة مثل شريط التقدّم | الفصل 8. يمكن تخطّيه في مرحلة التشغيل الأولى |
| التحقيق في «الإشعار لا يظهر» | الفصل 9 ── أين تُراجع إعدادات نظام التشغيل، وقيود العملية المرتفعة |
| إخطار المستخدم من خدمة Windows | الفصل 10 |
المصطلحات المستخدمة في هذا المقال
| المصطلح | المعنى |
|---|---|
| منطقة الإشعارات (علبة النظام) | المنطقة التي تصطف فيها الأيقونات في الطرف الأيمن من شريط المهام. بمصطلح Windows «منطقة الإشعارات (notification area)»1 |
| الإشعار المنبثق (Toast) | إشعار يظهر كلافته في أسفل يمين الشاشة ويبقى في مركز الإشعارات |
| TFM | Target Framework Moniker. المعرّف المكتوب في <TargetFramework> داخل csproj، ويمثّل إصدار .NET والمنصة المستهدفة مثل net8.0-windows10.0.19041.0 |
| MSIX | صيغة حزمة تطبيقات Windows. التطبيق الموزَّع بهذه الصيغة يُسمّى تطبيقاً «مُعبَّأ (packaged)» |
| تطبيق غير مُعبَّأ (unpackaged) | ملف exe تقليدي غير معبّأ بـ MSIX. تطبيقات سطح المكتب بـ WPF وWinForms وWin32 تكون كذلك افتراضياً2 |
| AUMID | AppUserModelID. معرّف يستخدمه Windows لتعريف التطبيق تعريفاً فريداً، ويُستخدم لتعريف مرسل الإشعار المنبثق |
| الجلسة 0 | جلسة مخصّصة للخدمات منذ Windows Vista. لا تملك واجهة مستخدم تفاعلية (الفصل 10)3 |
1. الخلاصة أولاً
- أساس الإقامة في العلبة ما يزال
System.Windows.Forms.NotifyIcon. لن تظهر الأيقونة ما لم تُعيَّنIconويُضبطVisible = true(القيمة الافتراضيةfalse). يمكن أن يصل نص تلميح الأداةTextإلى 127 حرفاً في .NET 6 وما بعده (63 حرفاً قبل ذلك).45 - لا يوجد في WPF أي عنصر تحكّم مكافئ لـ NotifyIcon. ينص جدول المقارنة الرسمي صراحة على «لا يوجد عنصر تحكّم مكافئ». تفعيل كل من
UseWPFوUseWindowsFormsفي ملف csproj واستخدام NotifyIcon الخاص بـ WinForms هو الحد الأدنى للإعداد.67 - «إبقاء التطبيق في العلبة حتى عند النقر على زر الإغلاق» يُنفَّذ بضبط
e.Cancel = trueفيFormClosingواستدعاءHide(). قدّموا دائماً وسيلة خروج منفصلة وصريحة (عنصر «إنهاء» في قائمة العلبة)، وتحقّقوا منCloseReasonحتى لا تعيقوا أبداً إيقاف تشغيل النظام.8 - واجهة الإشعارات المنبثقة واضحة التوصية اعتباراً من عام 2026. بغض النظر عما إذا بنيتم تطبيق WinForms أو WPF أو تطبيقاً غير مُعبَّأ، فإن
AppNotificationManagerالخاص بـ Windows App SDK (حزمة NuGet: Microsoft.WindowsAppSDK) هو الموصى به. أماWindows.UI.Notificationsفهي «للصيانة فقط»، ومكوّن الإشعارات في Community Toolkit أُرشف.910 - تعمل AppNotification الخاصة بـ Windows App SDK حتى من ملف exe عادي بلا هوية حزمة. تتولّى
AppNotificationManager.Default.Register()تلقائياً تسجيل خادم COM والمعالجة المكافئة لـ AUMID، لذا لم تعد بحاجة لإعداد اختصار في قائمة ابدأ كما كان مطلوباً في الأجيال الأقدم. مع ذلك، الإشعارات من العمليات المرفوعة الصلاحيات (مسؤول) غير مدعومة.111213 - الإشعار المنبثق ليس وسيلة «مضمونة الوصول» إلى المستخدم. أثناء تفعيل عدم الإزعاج (Focus Assist) لا تظهر اللافتة وتذهب مباشرة إلى مركز الإشعارات، ويمكن للمستخدم إيقاف الإشعارات كلياً من الإعدادات. يجب ألا يفترض تصميمكم أن «الإشعار المنبثق قد ظهر» كشرط مسبق لتدفق عمل الأعمال.1415
- لا يمكن لخدمة Windows إرسال إشعارات منبثقة (الجلسة 0 لا تحتوي على واجهة مستخدم). النهج القياسي هو وضع وكيل مقيم في العلبة ضمن جلسة المستخدم والتنسيق معه عبر IPC.3
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 18، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. أساسيات الإقامة في العلبة عبر NotifyIcon
المكوّن الذي يضع أيقونة في منطقة الإشعارات عند الطرف الأيمن من شريط المهام هو System.Windows.Forms.NotifyIcon. تستخدمه التطبيقات التي تستمر في العمل في الخلفية مدخلاً لعرض الحالة وقائمة التشغيل.4
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 = "أداة مراقبة الطلبات ── قيد التشغيل", // ツールチップ(.NET 6+で最大127文字)
ContextMenuStrip = menu,
Visible = true // 既定はfalse。trueにして初めて表示される
};
_trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}
النقاط التي ينبغي تثبيتها ثلاث.
- لا تظهر الأيقونة إلا باجتماع
IconوVisible = true. القيمة الافتراضية لـVisibleهيfalse.4 - لـ
Textحد طول. من .NET 6 فما بعده الحد 127 حرفاً، وفي .NET 5 وما قبله و.NET Framework 63 حرفاً، وإذا تجاوز وقعArgumentException. التنفيذ الذي يحشو الحالة في تلميح الأداة أكثر من اللازم يصير سبباً لسقوط مفاجئ باستثناء.516 - تُربَط قائمة النقر الأيمن بـ
ContextMenuStrip. كثيراً ما تكون أيقونة العلبة المدخل الوحيد الظاهر للمستخدم، لذا أعدّوا «فتح» و«إنهاء» كحد أدنى.17
وثمة أمر آخر: أيقونة العلبة مدخل للإشعار، وهي في الوقت نفسه عرض حالة ظاهر باستمرار. يمكن تبديل 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;
}
// 「終了」メニュー経由、またはOSシャットダウン等はそのまま閉じる
base.OnFormClosing(e);
}
private void ExitApplication()
{
_exitRequested = true;
_trayIcon.Visible = false;
_trayIcon.Dispose();
Close();
}
يقع FormClosing قبل إغلاق النموذج، ويمكن إلغاء الإغلاق بـ e.Cancel = true. تحقّقوا عندئذ حتماً من CloseReason. السلوك الصحيح إلغاء الإغلاق فقط عند UserClosing (عملية المستخدم)، وعدم إلغاء الإغلاق الناشئ عن نظام التشغيل مثل WindowsShutDown.818
في WPF الفكرة نفسها، ونمط ضبط e.Cancel = true في Window.Closing ثم Hide() وارد كما هو في الوثائق الرسمية.19
يُلاحظ أن التطبيق المقيم بطبيعته يجاور مشكلة «التشغيل المزدوج يعالج الحدث نفسه مرتين». منع التشغيل المتعدد مرتَّب في «منع التشغيل المتعدد لتطبيقات Windows ── Mutex مسمّى والتفعيل عند التشغيل المزدوج». أما طريقة بناء محتوى التطبيق المقيم (حلقة المراقبة أو المعالجة الدورية) فأساسها «لماذا نستخدم .NET Generic Host وBackgroundService في تطبيقات سطح المكتب».
4. الإقامة في العلبة في تطبيقات WPF
لا يوجد في WPF عنصر تحكّم مكافئ لـ NotifyIcon. هذا نقص في المواصفات مذكور صراحة في جدول المقارنة الرسمي «عناصر تحكّم WinForms وما يكافئها في WPF» بعبارة «No equivalent control».6
الخياران الواقعيان اثنان.
| الخيار | الموجز | مؤشر الحكم |
|---|---|---|
| الجمع مع NotifyIcon في WinForms | تفعيل UseWPF + UseWindowsForms معاً في csproj |
بلا تبعية إضافية. يكفي إن كانت الأيقونة والقائمة فقط |
| طرف ثالث (مثل H.NotifyIcon) | تنفيذ أيقونة علبة موجَّه لـ WPF/WinUI | عندما تريدون الكتابة بـ XAML، أو نوافذ منبثقة ثرية. ضعوا في الاعتبار أنه غير رسمي من Microsoft |
الأول مجرد إيراد الخاصيتين معاً في ملف مشروع بأسلوب SDK.7
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
يمكن استخدام System.Windows.Forms.NotifyIcon بـ new عادي من Application في WPF. تصير القائمة وحدها WinForms (ContextMenuStrip)، لكن لقائمة علبة هذا القدر لا يشكّل مشكلة عملية في العادة.
5. ثلاثة مطبات لأيقونة العلبة
5.1. الأيقونة تدخل افتراضياً في «الأيقونات المخفية» ── لا يمكن فرض العرض
طلب إبقاء أيقونتكم ظاهرة دائماً في شريط المهام يظهر حتماً، لكن لا يمكن تحقيقه من البرنامج. تنص الوثائق الرسمية على أن الأيقونة تُضاف افتراضياً إلى منطقة الفيض (الأيقونات المخفية)، وأن من يقرّر الترقية إلى منطقة الإشعارات (العرض الدائم) هو المستخدم وحده.1
في Windows 11 يشغّل المستخدم التبديل لكل تطبيق من «الإعدادات ← التخصيص ← شريط المهام ← أيقونات علبة النظام الأخرى». إذا كان العرض الدائم مهماً تشغيلياً، فالتعامل الصحيح كتابة «فعّلوا هذا الإعداد» في دليل التثبيت أو التعليمات.
اكتبوا في الدليل المسار الكامل للقائمة حتى يمكن الإرشاد عبر الهاتف. مثال النص كما يلي.
- انقروا بالزر الأيمن في موضع فارغ من شريط المهام، واختاروا «إعدادات شريط المهام».
- في الشاشة المفتوحة افتحوا «أيقونات علبة النظام الأخرى».
- ابحثوا عن هذا التطبيق في القائمة، وشغّلوا المفتاح.
※ إذا لم تظهر الأيقونة، اضغطوا زر «^» (شكل الجبل) في يمين شريط المهام فتظهر الأيقونات المخفية مؤقتاً.
5.2. إعادة تشغيل Explorer تخفي الأيقونة ── TaskbarCreated
عندما يعيد Explorer بناء شريط المهام بعد انهيار أو إعادة تشغيل، تختفي أيقونات العلبة المسجَّلة. يبث الغلاف رسالة TaskbarCreated إلى كل النوافذ ذات المستوى الأعلى عند إنشاء شريط المهام، واستقبالها ثم إعادة تسجيل الأيقونة قاعدة على مستوى Win32.20
يعالج NotifyIcon في WinForms هذه الرسالة داخلياً ويعيد التسجيل تلقائياً، لذا لا يلزم الوعي بها عادة. عند استدعاء Shell_NotifyIcon بأنفسكم عبر P/Invoke (أو عند صيانة تطبيق أصلي يملك أيقونة علبة)، تحقّقوا من وجود معالجة إعادة التسجيل هذه. عرض «بعد إعادة تشغيل Explorer تختفي الأيقونة وحدها والعملية حية» يكاد يكون هذا.
5.3. نسيان Dispose يُبقي الأيقونة
على مستوى Win32 يلزم حذف أيقونة العلبة صراحة (NIM_DELETE) عندما تصير غير لازمة، وفي WinForms يتولّى ذلك NotifyIcon.Dispose().1 احرصوا على مرور Visible = false ثم Dispose() حتماً في مسار الإنهاء. إذا لم تُحذف لأن العملية انتهت بشكل غير طبيعي مثلاً، تبقى أيقونة شبح حتى يمرّر مؤشر الفأرة فوقها؛ مشهد يراه مطوّر التطبيقات المقيمة مرة على الأقل.
كما في كود الفصل 3، الشكل الآمن إنهاء أيقونة العلبة في معالجة قائمة «إنهاء» ثم استدعاء Close().
6. الإشعارات المنبثقة ── أي واجهة تُستخدم
الإشعار المنبثق إشعار يظهر كلافته في أسفل يمين الشاشة ويبقى في مركز الإشعارات. طريقة إخراجه من تطبيق سطح المكتب تعاقبت أجيالها على مدى عشر سنوات، وتبقى عيّنات قديمة كثيرة في نتائج البحث. الترتيب اعتباراً من 2026 كما يلي.9
| الواجهة | 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 تُعرض كـ Toast، وفي Windows 11 عرض مؤقت لا يبقى في مركز الإشعارات | جائزة للاستخدام البسيط. انتبهوا لتغيّر السلوك حسب نظام التشغيل |
تكميلان اثنان.
ShowBalloonTipفي خانة «واجهة قديمة لكنها ما تزال حية». في Windows 10 يُرسم البالون كـ Toast ويبقى في مركز الإشعارات حتى يُغلق. أما في Windows 11 فيصير عرضاً مؤقتاً أقرب إلى البالون التقليدي، وبعد اختفائه لا يبقى في مركز الإشعارات. تحديد مدة العرض (timeout) يُتجاهل حالياً ويتبع إعدادات إمكانية الوصول في جانب نظام التشغيل. يكفي لإشعار خفيف بتغيّر حالة تطبيق مقيم في العلبة، لكن اذكروا أنه لا ينفع في Windows 11 إن أردتم إبقاء سجلاً في مركز الإشعارات.21- في الطريقة الكلاسيكية لاستخدام
Windows.UI.Notificationsالخام من تطبيق سطح مكتب قيد شهير: لن يظهر Toast ما لم يُسجَّل اختصار بـ AUMID (AppUserModelID) في قائمة ابدأ.22 ما أزال هذا العناء هو AppNotification في الفصل التالي.
يمكن رسم خط وظيفي لمدى «جائز للاستخدام البسيط» في الجدول. إذا استُوفيت الخمسة التالية كلها يكفي ShowBalloonTip، ولا حاجة لزيادة تبعية Windows App SDK.
- التطبيق يملك أيقونة علبة أصلاً (البالون إشعار تابع لأيقونة العلبة، فهذا شرط)
- الإشعار لا يحتاج أزرار تشغيل أو حقل إدخال. يكفي أن يقدّم نافذة التطبيق إلى الأمام عند النقر
- لا حاجة لإبقاء سجل في مركز الإشعارات. في Windows 11 لا يبقى، فلا يصلح لغرض «المراجعة لاحقاً»21
- لا حاجة لشريط تقدّم أو تحديث محتوى إشعار معروض
- الإشعار مساعدة «إن لُوحظ أمكنت الاستجابة أسرع»، والعمل يدور حتى إن فات
في المقابل، إذا انطبق أي واحد مما يلي فاستخدموا AppNotification في الفصل 7 بلا تردّد.
| المتطلّب | السبب |
|---|---|
| تريدون وضع زر (مثل «فتح السجل») أو حقل إدخال في الإشعار | البالون عنوان ونص وأيقونة فقط، ولا يملك عناصر تشغيل |
| تريدون البقاء في مركز الإشعارات حتى في Windows 11 | البالون لا يبقى في Windows 1121 |
| تريدون تحديث تقدّم معالجة طويلة في إشعار واحد | شريط التقدّم وUpdateAsync وظيفة جانب AppNotification (الفصل 8) |
| تريدون أيضاً التعامل مع العودة من حالة لم يكن التطبيق فيها مشغَّلاً | البالون لا يوجد ما لم يكن التطبيق مشغَّلاً وأيقونة العلبة ظاهرة. AppNotification تستطيع تشغيل العملية بنقر الإشعار (الفصل 7) |
| تريدون لاحقاً حذف إشعارات قديمة من الكود أو استبدالها | الإدارة بـ Tag / Group وظيفة جانب AppNotification (الفصل 8) |
مؤشر الحيرة واحد: «هل تريدون أن يشغّل المستخدم شيئاً تجاه ذلك الإشعار». إن كان للقراءة فقط فبالون، وإن كان للضغط فـ AppNotification.
7. الحد الأدنى لتنفيذ Windows App SDK AppNotification
واجهة إشعارات تطبيقات Windows App SDK تعمل حتى من ملف exe عادي غير معبّأ، بلا معرّف حزمة.11 غير أن الأمر ليس «أدخلوا NuGet وانتهى»، بل لكل من جانب التطوير وجانب التوزيع مقدّمات. ننظر إليها بالترتيب.
7.1. إعداد جانب المشروع ── NuGet وcsproj
المقدّمة الرسمية ثلاث نقاط: أن يكون المشروع WPF أو WinForms على .NET 6 فما بعده، وأن يكون المشروع مُعدّاً لاستدعاء واجهات WinRT، وأن يكون مُعدّاً لاستخدام Windows App SDK.12
ما يُفعل أمران. أولاً أضيفوا حزمة NuGet Microsoft.WindowsAppSDK (من Visual Studio: «أدوات ← مدير حزم NuGet ← إدارة حزم NuGet للحل»). تضم هذه الحزمة حزماً فرعية لمكوّنات Windows App SDK، وتوصي الوثائق الرسمية أيضاً بإدخال الحزمة الرئيسة أولاً.2
ثم حرّروا csproj. بإضافة حصة Windows App SDK إلى مثال WPF+WinForms في الفصل 4 يصير الشكل التالي.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<!-- لتستدعي WinRT API ضمّن إصدار Windows في TFM -->
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<!-- تطبيق غير مُعبَّأ (ليس MSIX): هيّئ زمن التشغيل تلقائياً عند البدء -->
<WindowsPackageType>None</WindowsPackageType>
</PropertyGroup>
<ItemGroup>
<!-- الإصدار هو ما اخترته في مدير حزم NuGet -->
<PackageReference Include="Microsoft.WindowsAppSDK" Version="(الإصدار المستقر المختار)" />
</ItemGroup>
</Project>
<WindowsPackageType>None</WindowsPackageType> هي النقطة الحاسمة. التطبيق غير المُعبَّأ يجب أن يهيّئ زمن التشغيل قبل استخدام ميزات Windows App SDK، وكتابة هذه الخاصية تفعّل التهيئة التلقائية عند التشغيل (auto-initialization). عند البناء تُضاف إلى المشروع MddBootstrapAutoInitializer.cs وWindowsAppSDK-VersionInfo.cs. فقط عند الحاجة إلى تحكّم أدق تستدعون واجهة التمهيد مباشرة بدل التهيئة التلقائية.2
7.2. إدخال زمن التشغيل لدى جهة التوزيع
هنا ما يسهل إغفاله. في حالة التطبيق غير المُعبَّأ، مسؤولية إيصال زمن تشغيل Windows App SDK إلى جهاز المستخدم تقع على جهة التوزيع.23 توزيع exe وحده لا يكفي للتشغيل.
كشرط، يحتاج التطبيق غير المُعبَّأ أيضاً إلى حزمة Visual C++ القابلة لإعادة التوزيع.23 طريقتا الإدخال اثنتان.
| الطريقة | المحتوى | الحالات المناسبة |
|---|---|---|
| تشغيل المثبّت | تشغيل WindowsAppRuntimeInstall.exe الرسمي. يوجد مثبّت منفصل لكل من x86 / x64 / Arm64، ويضع حزمة معمارية ذلك الاسم فقط23 |
عادة هذه |
| إدخال حزمة MSIX مباشرة | تضمين حزمة MSIX في إعدادكم أو MSI ووضعها23 | عندما تريدون التحكّم الدقيق في الوضع |
المثبّت بلا واجهة، لذا يُستدعى متسلسلاً (سلسلة) من مثبّتكم. تتوفّر خيارات للتشغيل بلا مراقبة.23
:: 画面表示・テキスト出力を一切出さずに導入する
WindowsAppRuntimeInstall.exe --quiet
:: 使用中のWindows App SDKプロセスを終了させたうえで強制更新する(1.1以降)
WindowsAppRuntimeInstall.exe --force
ثلاث نقاط ينبغي تثبيتها عند الإدخال.23
- وجود امتيازات المدير يغيّر النتيجة. إذا شُغّل مرتفعاً وكان مستخدم التشغيل يملك امتيازات مدير، يُسجَّل للنظام كله (بما في ذلك المستخدمين الذين سيُضافون لاحقاً). وإلا صار الإدخال لمستخدم التشغيل فقط. قيمة العودة
0x80070005هي بالضبط حالة «غير مرتفع / بلا امتيازات مدير». - يُحكم بالنجاح أو الفشل من قيمة العودة.
0x0نجاح، و0x80073d06فشل إدخال حزمة واحدة أو أكثر. عند السلسلة من مثبّتكم انظروا هذه القيمة حتماً. - زمن التشغيل الذي يُدخل في جهاز التطوير يُوافَق مع إصدار حزمة NuGet المشار إليها وقناة الإصدار.2 ليس نادراً أن يكون سبب «يظهر في جهاز التطوير ولا يظهر لدى جهة التوزيع» هنا.
عند التوزيع معبّأ بـ MSIX يمكن معاملة توزيع زمن التشغيل كتبعية للحزمة، لكن يلزم بدل ذلك ذكر مفعّل COM في Package.appxmanifest (راجعوا نهاية 7.3).
7.3. الحد الأدنى للتنفيذ
using System;
using System.Windows.Forms;
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
internal static class ToastActivation
{
private static readonly object Gate = new();
private static Action? _pending;
private static bool _uiReady;
// 起動時に一度だけ。ハンドラー登録 → Register() の順序が重要
public static void Initialize()
{
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
// トーストのクリックやボタン押下がここに届く(引数はargs.Argumentsで判別)
if (!args.Arguments.TryGetValue("action", out var action) || action != "openLog")
{
return;
}
// NotificationInvoked はバックグラウンドスレッドで発生する。
// UIに触る処理は必ずUIスレッドへ渡すこと
Dispatch(ShowLogWindow);
};
AppNotificationManager.Default.Register();
}
// MainForm の OnShown から呼ぶ。フォームができる前に届いた分をここで流す
public static void MarkUiReady()
{
Action? pending;
lock (Gate)
{
_uiReady = true;
pending = _pending;
_pending = null;
}
// OnShown は UI スレッドなので、そのまま実行してよい
pending?.Invoke();
}
private static void Dispatch(Action action)
{
lock (Gate)
{
if (!_uiReady)
{
// トーストのクリックがアプリの起動そのものだった場合、ここへ来た
// 時点ではまだフォームが1つも無い。預かっておいて MarkUiReady が流す
_pending = action;
return;
}
}
MainWindow()?.Invoke(action);
// WPFなら: Application.Current.Dispatcher.Invoke(action);
}
private static Form? MainWindow() =>
Application.OpenForms.Count > 0 ? Application.OpenForms[0] : null;
private static void ShowLogWindow() { /* ログウィンドウを開く */ }
}
// MainForm 側。起動のきっかけがトーストのクリックだった場合はここで拾う
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
ToastActivation.MarkUiReady();
}
ToastActivation.Initialize();
// 通知を出す
var notification = new AppNotificationBuilder()
.AddText("اكتمل استيراد الطلبات")
.AddText("12 جديداً، خطأ واحد")
.AddButton(new AppNotificationButton("فتح السجلّ")
.AddArgument("action", "openLog"))
.BuildNotification();
AppNotificationManager.Default.Show(notification);
الملاحظات العملية كما يلي.
- اشتركوا في
NotificationInvokedقبلRegister(). إن أخطأ الترتيب فات الحدث عندما يُشغَّل التطبيق بنقر Toast.12 - إذا كان النقر هو ما شغّل التطبيق نفسه، فلا يوجد نموذج واحد بعد عند عمل المعالج. كتابة «انظر
Application.OpenFormsوإن كانnullفلا تفعل شيئاً» ترمي تشغيل هذا المسار صامتاً. كما أعلاه، احتفظوا به مرة ثم مرّروه بعد ظهور النموذج. حتى إن رُتّب الترتيب واستُقبل الحدث، الرمي هنا يصل إلى النتيجة نفسها. - يقع
NotificationInvokedعلى خيط خلفية. تنص العيّنة الرسمية أيضاً على «يقع على خيط خلفية، فمرّروا تحديثات واجهة المستخدم إلى خيط واجهة المستخدم». كتابة فتح نافذة أو لمس عناصر التحكّم مباشرة من المعالج تصادف استثناءات تتعلّق بالخيوط أو تجمّداً غير مفهوم. في WPF مرّروا إلى خيط واجهة المستخدم بـApplication.Current.Dispatcher.Invoke(...)، وفي WinForms بـControl.Invoke(...).12 - قبل إنهاء التطبيق استدعوا
AppNotificationManager.Default.Unregister(). تنص الوثائق الرسمية على وجوب تنظيف الموارد بـ Unregister قبل الإنهاء حتى يُشغَّل التطبيق بشكل صحيح عند نقر الإشعارات لاحقاً. أدرجوه إلى جانبNotifyIcon.Dispose()في معالجة قائمة «إنهاء» في الفصل 3 (عند إلغاء تثبيت تطبيق لن يستخدم وظيفة الإشعار بعد الآن مثلاً، تتوفّر على حدةUnregisterAll()التي تمحو معلومات التسجيل كلها).24 - في التطبيق غير المُعبَّأ تتولّى
Register()تلقائياً تسجيل خادم COM والمعالجة المكافئة لـ AUMID. وضع الاختصار وتسجيل السجل اللذان لزما في الجيل القديم (WinRT الخام) غير لازمين. عند التعبئة بـ MSIX يلزم بالعكس ذكر مفعّل COM في Package.appxmanifest.12 - يلزم الانتباه عند اختيار توزيع self-contained يضمّن زمن تشغيل Windows App SDK. تعتمد
AppNotificationManagerعلى حزمة Singleton في Windows App SDK، وتُرشد الوثائق رسمياً إلى اعتبارات عند الاستدعاء من تطبيق self-contained. تحقّقوا قبل تقرير طريقة التوزيع.25 - الإشعار من عملية مرتفعة إلى مدير غير مدعوم. تفشل
Showبهدوء بلا استثناء، فيصعب الانتباه إلى السبب. في التطبيقات التي تحتاج ارتفاعاً، ادرسوا فصلاً يترك الإشعار لعملية غير مرتفعة (تناولنا فكرة هذا الفصل في «طريقة كتابة عملية تفصل «المعالجة التي تحتاج امتيازات مدير فقط» في تطبيقات Windows»).13 - عندما يُنقر Toast، إن لم يكن التطبيق مشغَّلاً تُشغَّل عملية جديدة. في التطبيق المقيم في العلبة يلزم الجمع مع منع التشغيل المتعدد (راجعوا الفصل 3) وتصميم يمرّر المعالجة إلى المثيل القائم.
8. استخدام متقدّم ── Toast بشريط تقدّم وتنظيف الإشعارات
8.1. تحديث التقدّم داخل Toast ── UpdateAsync
تقدّم معالجة طويلة مثل «جارٍ استيراد 120 سجل طلب» لا يُعاد إخراج الإشعار مراراً، بل يُعاد كتابة شريط التقدّم داخل Toast واحد؛ هذا هو البناء الصحيح. في AppNotification توجد آلية ربط بيانات وتحديث لهذا الغرض.26
البناء ثلاث مراحل. أولاً اجعلوا كل قيمة في شريط التقدّم عنصر نائب بطرق Bind، واعرضوا مع تعريف الإشعار بـ Tag (وGroup إن لزم). بعد ذلك يكفي تمرير AppNotificationProgressData إلى UpdateAsync فيُعاد كتابة محتوى Toast المعروض وحده.
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);
رقم التسلسل عدد صحيح غير صفري يبيّن ترتيب التحديث، وعند وصول عدة تحديثات متقدّمة ومتأخرة يُعرض الأكبر. آلية حتى لا يُرجع بقيمة قديمة حتى إن أُلقيت تحديثات على التوازي من معالجة غير متزامنة، لذا زيدوه رتيباً.27
8.2. التمييز بين «التحديث» و«الاستبدال»
يمكن أيضاً تحديث المظهر بـ«استبدال» يعيد Show لإشعار جديد بنفس Tag، لكن السلوك يختلف.26
| الاستبدال (إعادة Show بنفس Tag) | التحديث (UpdateAsync) | |
|---|---|---|
| الموضع داخل مركز الإشعارات | ينتقل إلى الأمام | يبقى في مكانه |
| ما يمكن تغييره | كل شيء بما في ذلك التخطيط | شريط التقدّم المربوط والنص الأول فقط |
| النافذة المنبثقة | يمكن إعادة عرضها | لا تُعاد (يُعاد الكتابة بهدوء) |
| إذا كان المستخدم قد أغلقه | يُرسل دائماً | يفشل |
توصية الوثائق الرسمية واضحة أيضاً: المسار الوسط تحديث، والاكتمال استبدال. لا حاجة لإعلام بمنبثقة في كل مرة صار التقدّم من 50% إلى 65%، أما «اكتمل الاستيراد (3 أخطاء)» فنريد أن يصل حتى إن كان المستخدم قد أغلق إشعار التقدّم. الشكل العملي الكلاسيكي عند الاكتمال استبدال بتخطيط آخر يُخرج شريط التقدّم ويضع زراً (مثل «عرض محتوى الخطأ»).
8.3. تنظيف الإشعارات القديمة المتبقية في مركز الإشعارات
تتراكم Toast في مركز الإشعارات حتى يحذفها المستخدم. إذا أخرج تطبيق مقيم عدة إشعارات يومياً، امتلأ مركز الإشعارات بإشعارات التطبيق القديمة وطُمر المهم.
إذا أُلحقت Tag / Group يمكن حذف إشعارات تطبيقكم من الكود (بتعيين وسم، أو مجموعة، أو الكل). يمكن أيضاً ضبط أجل صلاحية، لذا يُستحسن تقرير قواعد تنظيف مثل «سلسلة التقدّم تُحذف عند الاكتمال» و«سلسلة المعلومات أجلها قصير» مع تصميم إخراج الإشعار.28
9. إدخال حالات «عدم وصول» الإشعار في التصميم
الإشعار المنبثق ليس وسيلة نقل مؤكدة. مسارات عدم العرض أو عدم الملاحظة موجودة كمسار سليم.
| الحالة | السلوك |
|---|---|
| تفعيل عدم الإزعاج (Focus Assist / Do not disturb) | لا تُعرض اللافتة، ويذهب الإشعار مباشرة إلى مركز الإشعارات |
| إيقاف الإشعارات لكل تطبيق أو للنظام كله | لا يُعرض |
| AppNotification من عملية مرتفعة إلى مدير | غير مدعوم. لا يُعرض |
| أثناء تشغيل تطبيق بملء الشاشة ونحوه | قد يُكبت بحكم نظام التشغيل |
عدم الإزعاج يتحكّم فيه المستخدم أو سياسة المجموعة: في Windows 10 «وضع التركيز (Focus assist)»، وفي Windows 11 مفتاح «عدم الإزعاج» في «الإعدادات ← النظام ← الإشعارات». توجد حتى مواصفة تفعيله تلقائياً لمدة ساعة بعد ترقية نظام التشغيل.14 عمل وضع التركيز ليس «عدم إخراج إشعار» بل «كبت عرض اللافتة وإرساله إلى مركز الإشعارات»، لذا إن فتح المستخدم مركز الإشعارات لاحقاً رآه.15
مواضع التحقّق أسرع في الميدان إن حُملت بمسار القائمة الكامل. لأن «الإشعار لا يظهر» كثيراً ما يُرشد عبر الهاتف أو الدردشة.
| ما يُراد التحقّق منه | أين يُنظر (Windows 11) |
|---|---|
| ألا يكون عدم الإزعاج مفعّلاً | الإعدادات ← النظام ← الإشعارات ← مفتاح «عدم الإزعاج» |
| ألا تكون شروط التفعيل التلقائي لعدم الإزعاج مضبوطة | إعداد شروط التفعيل التلقائي لعدم الإزعاج في شاشة «الإشعارات» نفسها |
| ألا تكون إشعارات تطبيقكم موقوفة | مرّروا شاشة «الإشعارات» نفسها نزولاً وابحثوا عن تطبيقكم في القائمة لكل تطبيق |
| هل اللافتة وحدها لا تظهر، أم الإشعار نفسه لم يصل | انقروا التاريخ والوقت في يمين شريط المهام وافتحوا مركز الإشعارات، وانظروا هل تراكمت إشعارات |
| ألا تكون أيقونة العلبة مخفية فقط | الإعدادات ← التخصيص ← شريط المهام ← أيقونات علبة النظام الأخرى (الفصل 5.1) |
في Windows 10 ما يعادل عدم الإزعاج هو «الإعدادات ← النظام ← وضع التركيز». التحقّق أولاً مما إذا وصل إلى مركز الإشعارات أقصر طريق للفصل: إن وصل فكبت لافتة في جانب نظام التشغيل (عدم الإزعاج ونحوه)، وإن لم يصل فاشكّوا في الإرسال نفسه من جانب التطبيق.
دلالة ذلك على التصميم بسيطة.
- استخدموا Toast قناة مساعدة «إن لُوحظ أمكنت الاستجابة أسرع»، واجعلوا المعلومات اللازمة للعمل قابلة للتأكيد حتماً في شاشة أو قائمة داخل التطبيق. لا تجعلوا «كان يفترض أن يظهر Toast» مقدّمة لتدفق العمل.
- للإشعارات التي لا بد من مقاطعتها (مثل تحذير إيقاف طارئ) توجد آلية تخترق عدم الإزعاج بإذن المستخدم عبر
AppNotificationScenario.Urgent(إشعار مهم)29، لكن الإسراف يجعل المستخدم يوقف إشعارات التطبيق كلها. ينبغي حصرها فيما هو طارئ فعلاً.
للحديث الأوسع عن كيفية عرض خلل جهاز خارجي ونحوه على الشاشة، راجعوا أيضاً «أفضل ممارسات تأكيد حالة الأجهزة الخارجية وعرضها».
10. عندما تريدون الإشعار من خدمة Windows
مطلب «عندما تكتشف خدمة مقيمة خللاً، نريد إعلام المستخدم المسجَّل دخوله بـ Toast» يظهر طبيعياً، لكن لا يمكن إخراج Toast مباشرة من الخدمة. منذ Windows Vista تعمل الخدمات في جلسة مخصّصة اسمها الجلسة 0، ولا تملك واجهة مستخدم للتفاعل مع المستخدم.3
النمط القياسي تكوين يفصل واجهة المستخدم إلى عملية في جانب جلسة المستخدم.
[セッション0] [ユーザーセッション]
Windowsサービス ←─ IPC ─→ トレイ常駐エージェント
(監視・業務処理) 名前付きパイプ等 (NotifyIcon + AppNotification)
- تتفرّغ الخدمة للمراقبة ومعالجة العمل، وترسل الأحداث المراد الإشعار بها إلى الوكيل عبر IPC.
- الوكيل تطبيق مقيم في العلبة يُشغَّل عند تسجيل دخول المستخدم (الفصول 2 إلى 8 من هذا المقال نفسها)، ويعرض الحدث المستلم بـ Toast أو تغيّر الأيقونة.
- اختيار وسيلة IPC مرتَّب في «كيف نختار الاتصال بين عمليات Windows»، وطريقة بناء جانب الخدمة في «كيفية صنع خدمة Windows وتشغيلها»، وآلية الجلسة نفسها في «كيف نفهم فصل جلسات Windows ── Session 0 وRDP والتشغيل المتزامن لعدة مستخدمين».
في بيئة يسجّل فيها عدة مستخدمين الدخول معاً (RDP أو جهاز مشترك) سيعمل وكيل واحد لكل جلسة. إذا قرّرتم أولاً كمتطلّب «من ينبغي إشعاره» (الجميع أم مستخدم معيّن فقط) لم يمل التصميم.
11. الخلاصة
- أساس الإقامة في العلبة
NotifyIcon. تُعرض بـIcon+Visible = true، وحدText127 حرفاً (.NET 6+)، وفي مسار الإنهاء يُمرَّرDispose()حتماً. تُستخدم الأيقونة والتلميح أيضاً عرضاً دائماً لـ«الحالة الحالية». - «الإبقاء في العلبة حتى عند الإغلاق بـ ×» يُنفَّذ بإلغاء
FormClosing، لكن يُلغى فقط عندCloseReason.UserClosing، ويُعدّ دائماً قائمة «إنهاء» صريحة. - العرض الدائم للأيقونة لا يقرّره إلا المستخدم. اختفاء الأيقونة عند إعادة تشغيل Explorer يُعالَج بإعادة التسجيل عند
TaskbarCreated(WinForms تلقائي). - التنفيذ الجديد للإشعارات المنبثقة خيار واحد:
AppNotificationManagerفي Windows App SDK. يعمل حتى من exe غير مُعبَّأ بلا معرّف حزمة، وRegister()تؤتمت التسجيل المُضني. إشعارات Community Toolkit مؤرشفة، وWinRT الخام صيانة فقط. للإشعار الخفيف الذي لا يحتاج زراً ولا سجلاً ولا تقدّماً، يبقى حكم الاكتفاء بـShowBalloonTip(الفصل 6). - في التطبيق غير المُعبَّأ، إيصال زمن تشغيل Windows App SDK إلى جهة التوزيع مسؤولية جهة التوزيع. اكتبوا
<WindowsPackageType>None</WindowsPackageType>في csproj، وأدرجوا سلسلةWindowsAppRuntimeInstall.exeمن المثبّت في تصميم التوزيع (الفصل 7.2). - مسار المعالجة الطويلة يُعاد كتابة شريط التقدّم في Toast واحد بهدوء عبر
UpdateAsync، والاكتمال يُستبدل بمنبثقة؛ هذا النمط القياسي. أرفقواTag/Groupوأدرجوا حذف الإشعارات القديمة في التصميم. - Toast قناة مساعدة قد لا تصل بسبب عدم الإزعاج أو إيقاف الإشعارات. صمّموا المعلومات اللازمة بحيث يمكن تأكيدها داخل التطبيق، وانتبهوا إلى أن الإشعار من عملية مرتفعة غير مدعوم.
- الإشعار من الخدمة يُحقَّق بتكوين فصل: وكيل مقيم في العلبة في جانب جلسة المستخدم + IPC.
مقالات ذات صلة
- منع التشغيل المتعدد لتطبيقات Windows ── Mutex مسمّى والتفعيل عند التشغيل المزدوج
- كيفية صنع خدمة Windows وتشغيلها ── من التمييز عن جدولة المهام حتى تحويل BackgroundService إلى خدمة
- كيف نختار الاتصال بين عمليات Windows ── جدول قرار الأنابيب المسمّاة / TCP / gRPC / الذاكرة المشتركة / COM
- كيف نفهم فصل جلسات Windows ── Session 0 وRDP والتشغيل المتزامن لعدة مستخدمين
- لماذا نستخدم .NET Generic Host وBackgroundService في تطبيقات سطح المكتب
- طريقة كتابة عملية تفصل «المعالجة التي تحتاج امتيازات مدير فقط» في تطبيقات Windows
- أفضل ممارسات تأكيد حالة الأجهزة الخارجية وعرضها - تصميم لا يكتفي بـ«متصل»
مجالات الاستشارة ذات الصلة
تتولّى شركة كومورا سوفت ذ.م.م. تصميم وتنفيذ أدوات مراقبة مقيمة في العلبة ووكلاء إشعار، ودراسة تكوين أنظمة مقيمة مجتمعة مع خدمة Windows، والتحقيق في أعطال التطبيقات المقيمة القائمة.
روابط مرجعية
-
Microsoft Learn، Notification Area (Windows Shell). حول إضافة الأيقونة افتراضياً إلى منطقة الفيض، وكون المستخدم وحده من يقرّر الترقية إلى منطقة الإشعارات، ولزوم حذف الأيقونة غير اللازمة بـ NIM_DELETE. ↩ ↩2 ↩3
-
Microsoft Learn، Use the Windows App SDK in an existing project. حول كون تطبيقات سطح المكتب بـ WPF وWinForms وWin32 غير مُعبَّأة افتراضياً، وتوصية إدخال حزمة NuGet
Microsoft.WindowsAppSDKالرئيسة أولاً، ولزوم تهيئة زمن التشغيل قبل استخدام ميزات Windows App SDK في التطبيق غير المُعبَّأ وتفعيل التهيئة التلقائية عند التشغيل عبر<WindowsPackageType>None</WindowsPackageType>، وإضافةMddBootstrapAutoInitializer.csوWindowsAppSDK-VersionInfo.csعند البناء، ووجوب موافقة زمن التشغيل المدخَل في جهاز التطوير مع إصدار حزمة NuGet وقناة الإصدار. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn، Service Changes for Windows Vista / Interactive Services. حول تخصيص الجلسة 0 للخدمات وعدم دعمها للتفاعل مع المستخدم، وتوصية صنع عملية في جلسة المستخدم عبر CreateProcessAsUser والربط عبر IPC للعرض على المستخدم. ↩ ↩2 ↩3
-
Microsoft Learn، NotifyIcon Class / NotifyIcon.Visible Property. حول كونه مكوّناً يعرض أيقونة في منطقة الإشعارات، ولزوم تعيين خاصية Icon، وكون القيمة الافتراضية لـ Visible هي false. ↩ ↩2 ↩3
-
Microsoft Learn، NotifyIcon.Text Property. حول الحد الأعلى لنص تلميح الأداة، ووقوع ArgumentException عند التجاوز. ↩ ↩2
-
Microsoft Learn، Windows Forms Controls and Equivalent WPF Controls. حول عدم وجود عنصر تحكّم في WPF مكافئ لـ NotifyIcon. ↩ ↩2
-
Microsoft Learn، MSBuild properties for .NET desktop projects. حول إمكان الجمع بين UseWPF وUseWindowsForms. ↩ ↩2
-
Microsoft Learn، Form.FormClosing Event. حول وقوعه قبل إغلاق النموذج، وإمكان إلغاء الإغلاق بخاصية Cancel. ↩ ↩2
-
Microsoft Learn، Windows notifications overview. جدول الواجهات الموصى بها حسب نوع التطبيق. حول توصية Windows App SDK لـ WinForms/WPF/Win32 غير المُعبَّأ، وموقع Maintenance only لـ Windows.UI.Notifications. ↩ ↩2
-
Microsoft Learn، Windows Community Toolkit — Notifications (archive). حول أرشفة مكوّن Notifications واستبداله بإشعارات تطبيقات Windows App SDK. ↩
-
Microsoft Learn، Use the Windows App SDK in an existing project. حول توافق واجهة إشعارات تطبيقات Windows App SDK مع المُعبَّأ وغير المُعبَّأ، وعدم حاجتها إلى معرّف حزمة. ↩ ↩2
-
Microsoft Learn، Quickstart: App notifications in the Windows App SDK. حول استخدام AppNotificationManager/AppNotificationBuilder، والتسجيل التلقائي عبر Register() في التطبيق غير المُعبَّأ، ووجوب الاشتراك في NotificationInvoked قبل Register()، وذكر البيان في التطبيق المُعبَّأ. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn، App notifications overview. حول عدم دعم إرسال الإشعار من تطبيق مرتفع إلى مدير. ↩ ↩2
-
Microsoft Learn، Notifications don’t appear or aren’t displayed. حول كبت الإشعار بعدم الإزعاج (Quiet Hours / Focus Assist)، والتفعيل التلقائي لمدة ساعة بعد ترقية نظام التشغيل، وإجراءات التحقّق عندما لا يظهر الإشعار. ↩ ↩2
-
Microsoft Learn، Focus assist. حول كون وضع التركيز آلية تكبت عرض لافتة الإشعار وترسله مباشرة إلى مركز الإشعارات. ↩ ↩2
-
Microsoft Learn، NotifyIcon.Text maximum text length increased. حول التغيير الكاسر في .NET 6 الذي وسّع الحد الأقصى لـ Text من 63 حرفاً إلى 127. ↩
-
Microsoft Learn، FormClosingEventArgs Class. حول إمكان تمييز سبب إغلاق النموذج عبر CloseReason. ↩
-
Microsoft Learn، How to close a window or dialog box (WPF). حول نمط الإخفاء في WPF بضبط e.Cancel = true في حدث Closing ثم Hide(). ↩
-
Microsoft Learn، The Taskbar. حول بث رسالة TaskbarCreated عند إنشاء شريط المهام، ووجوب إعادة تسجيل أيقونة العلبة عند استقبالها. ↩
-
Microsoft Learn، Shell_NotifyIconW function. حول عرض إشعار البالون في Windows 10 كـ Toast (لافتة) وبقائه في مركز الإشعارات، وصيرورته عرضاً مؤقتاً في Windows 11 لا يبقى في مركز الإشعارات، واتباع مدة العرض إعدادات إمكانية الوصول. ↩ ↩2 ↩3
-
Microsoft Learn، Send a local toast notification from a C# app (Win32/クラシックAPI). حول لزوم تسجيل اختصار بـ AUMID في قائمة ابدأ عند استخدام ToastNotificationManager الخام. ↩
-
Microsoft Learn، Windows App SDK deployment guide for framework-dependent apps packaged with external location or unpackaged. حول تحمّل مطوّر التطبيق غير المُعبَّأ مسؤولية توزيع زمن تشغيل Windows App SDK على المستخدم، ولزوم حزمة Visual C++ القابلة لإعادة التوزيع، ووجود طريقتين (مثبّت ووضع حزمة MSIX مباشرة)، وانقسام المثبّت حسب x86 وx64 وArm64 ووضع حزمة معمارية ذلك الاسم فقط، وخياري
--quietو--force، والإدخال للنظام كله عند الارتفاع وامتيازات المدير وإلا لمستخدم التشغيل فقط، ومعاني قيم العودة0x0و0x80073d06و0x80070005، وإمكان السلسلة من إعدادكم عبر ShellExecute. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn، AppNotificationManager.Register Method. حول وجوب استدعاء Unregister قبل إنهاء التطبيق لتنظيف الموارد حتى يتم تشغيل الإشعار لاحقاً بشكل صحيح، ووجوب محو معلومات التسجيل بـ UnregisterAll إذا لن تُستخدم وظيفة الإشعار بعد ذلك. ↩
-
Microsoft Learn، AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture. حول اعتماد صنف AppNotificationManager على حزمة Singleton في Windows App SDK، ووجود اعتبارات عند الاستدعاء من تطبيق بتوزيع self-contained. ↩
-
Microsoft Learn، App notification progress bar and data binding. حول ربط البيانات بطرق Bind، والتحديث عبر AppNotificationProgressData وUpdateAsync، والفرق بين التحديث والاستبدال (الموضع داخل مركز الإشعارات، ونطاق ما يمكن تغييره، وإعادة عرض المنبثقة، والسلوك بعد إغلاق المستخدم)، ووجوب دراسة الاستبدال عند الاكتمال. ↩ ↩2
-
Microsoft Learn، AppNotificationProgressData(UInt32) Constructor. حول كون رقم التسلسل عدداً صحيحاً غير صفري يحدّد ترتيب التحديث، وعرض بيانات الرقم الأكبر عند استقبال عدة بيانات تحديث. ↩
-
Microsoft Learn، Remove app notifications. حول إلحاق وسم ومجموعة، وحذف الإشعارات المعروضة، وضبط أجل الصلاحية. ↩
-
Microsoft Learn، App notification content. حول تقدّم الإشعار المهم scenario=”urgent” على إعداد عدم الإزعاج بإذن المستخدم. ↩
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
CI/CD عمليّ لتطبيقات WinForms / WPF ── أتمتة البناء والتوقيع والتوزيع عبر GitHub Actions
دليل عمليّ لبناء CI/CD لتطبيقات WinForms / WPF عبر GitHub Actions. يشمل YAML الأدنى للبناء والاختبار على windows-latest، وترقيم الإصدار ا...
السكون والإسبات وModern Standby والتطبيقات طويلة التشغيل ── منع «التوقّف في منتصف الليل» بالتصميم
نرتّب أسباب وصول تطبيقات Windows طويلة التشغيل إلى حالة «توقّفت عندما نظرت صباحاً»، انطلاقاً من الفرق بين سكون S3 والإسبات وModern Standb...
الطباعة وإخراج PDF في تطبيقات أعمال Windows ── التمييز بين System.Drawing.Printing وWPF ومكتبات التقارير
نرتّب بجدول قرار حسب المتطلبات طباعة WinForms عبر PrintDocument، وطباعة FlowDocument/FixedDocument في WPF، وخيارات إخراج PDF. ونشرح التحك...
دمج مصادقة Entra ID في تطبيقات WinForms/WPF ── التشكيل العملي لـ MSAL.NET ووسيط WAM
نرتّب خطوات دمج مصادقة Entra ID في تطبيقات WinForms/WPF لسطح المكتب. نشرح مفهوم العميل العامّ، وتسجيل التطبيق، وAcquireTokenSilent في MSA...
لماذا نستخدم Generic Host و BackgroundService في تطبيق سطح المكتب
تنظيم البدء والمعالجة الدوريّة والإيقاف والسجلات والإعدادات و DI في أدوات Windows والتطبيقات المقيمة باستخدام Generic Host و BackgroundSe...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- كيف يمكنني إنشاء تطبيق مقيم في علبة النظام باستخدام 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 أصبح في وضع الصيانة فقط. إذا كان الكود الحالي لديكم يعمل بالفعل بأحد هذين الخيارين، فلا داعي للتسرّع في إعادة كتابته، لكن لا يوجد سبب لاختيارهما في عمل جديد.
- ماذا يجب أن أتحقّق منه عندما لا يظهر الإشعار المنبثق؟
- ابدأوا بإعدادات نظام التشغيل. إذا كانت ميزة «عدم الإزعاج» (Focus Assist) مفعّلة، فإن الإشعارات تتخطى شكل اللافتة وتذهب مباشرة إلى مركز الإشعارات، كما يمكن إيقاف الإشعارات لكل تطبيق على حدة أو على مستوى النظام بالكامل من تطبيق الإعدادات. على مستوى التنفيذ، السببان الشائعان هما أن AppNotification غير مدعومة عند إرسالها من عملية مرفوعة الصلاحيات (مسؤول)، وأن Windows.UI.Notifications الخام لا تعرض شيئاً عند استدعائها من تطبيق سطح مكتب بلا AUMID.
- هل يمكن لخدمة Windows إرسال إشعارات منبثقة؟
- لا، لا يمكنها ذلك. منذ Windows Vista تعمل الخدمات ضمن الجلسة رقم 0 (Session 0) ولا تملك القدرة على عرض واجهة مستخدم للتفاعل مع المستخدم. إذا احتجتم لإخطار المستخدم، فإن النمط القياسي هو وضع عملية وكيل مقيمة في علبة النظام ضمن جلسة المستخدم، وجعلها تتواصل مع الخدمة عبر آلية اتصال بين العمليات (IPC) مثل الأنابيب المسمّاة، على أن يتولّى الوكيل نفسه مسؤولية عرض الإشعار.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.