المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : خطوة خطوة لإنشاء TNA (إيقونة بجنب الساعة) مع إضافة قائمة PopupMenu وتلميح منطادي


kachwahed
06-01-2009, 08:13 AM
بسم الله الرحمن الرحيم

بسم الله والحمد لله والصلاة والسلام على رسول الله، أما بعد: السلام عليكم ورحمة الله وبركاته.

في هذا الموضوع استخدام إحدى دوال ShellApi من أجل إنشاء إيقونة بجنب الساعة (TNA) واستدعاء أحداثها (النقر بالزر الأيسر أو الأيمن أو النقر المزدوج...)، ثم إضافة قائمة من النوع TPopupMenu وكيفية تركيبها في TNA، وأخيرا طريقة إنشاء تلميحات على شكل منطادي Hint BalloonTip.

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

في البداية مقدمة سريعة (لا بد منها):
ما هي TNA ؟
TNA اختصار لعبارة Task Notification Area، أترجمها: علبة شريط المهام (بالفرنسية: L'aire de notification de la barre des tâches)، هي العلبة (أو المنطقة) التي تظهر فيها الإيقونات إلى جانب ساعة الويندوز على الجانب الأيمن من شريط المهام، تماما مثل إيقونة التحكم بالصوت لدى ويندو.
يمكنك إضافة إضافة إيقونة برنامجك الى علبة النظام ، والرد على الأحداث التي يوقعها مؤشر الماوس على إيقونة علبة شريط المهام (status area).

كيف يتم إنشاء إيقونة في علبة شريط المهام ؟
توفر دوال ShellApi دالة تجعل من العملية سهلة جدا، تدعى هذه الدالة Shell_NotifyIcon.
المعرفة كما يلي:

function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL;
حيث IpData مؤشر على نوع معرف كما يلي:

cbSize: DWORD;
Wnd: HWND;
uID: UINT;
uFlags: UINT;
uCallbackMessage: UINT;
hIcon: HICON;
szTip: array [0..63] of AnsiChar;

سنشرح كل عنصر بشيء من التفصيل...

تتطلب Shell_NotifyIcon اثنين من الخصائص أو المعالم (البارامترات):

المَعلم الأول (dwMessage) ويحدد الإجراءات الواجب اتخاذها.
ويأخذ قيمة (أو أكثر) من القيم التالية :

NIM_ADD : إضافة إيقونة إلى مركز المنطقة.
NIM_DELETE : حذف إيقونة من مركز المنطقة.
NIM_MODIFY : يعدل إيقونة في مركز المنطقة.
هذه القيم معرفة في الوحدة ShellApi.pas التابعة لدوال API، متوفرة في المسار:
\Program Files\Borland\Delphi7\Source\Rtl\Win

ملاحظات:
لإضافة إيقونة في علبة النظام، نستدعي Shell_NotifyIcon باستعمال NIM_ADD.

وفي أي وقت أردت تغيير الإيقونة (ومحتوياتها) يمكنك استدعاء Shell_NotifyIcon باستعمال NIM_MODIFY.

وقبل أن تنهي عمل برنامجك عليك بإزالة الإيقونة من علبة شريط المهام (أو بالأحرى تحريرها من الذاكرة) عليك باستدعاء Shell_NotifyIcon باستعمال NIM_DELETE التي تحذف الإيقونة من علبة شريط المهام.

(سيتضح ذلك أكثرالمثال التطبيقي)

المَعلم الثاني (pnid) هو مؤشر على النوع TNotifyIconData، الذي يحفظ معلومات (الخصائص) عن الأيقونات.

يتضمن النوع TNotifyIconData العناصر التالية:

cbSize : تحدد حجم بيانات NOTIFYICONDATA.
نوعها: DWORD.

hWnd : مقبض الإطار المستعمل لتلقي تنبيهات الأحداث.
نوع البيانات: HWND.

uID: رمز تعريف للأيقونة التي في علبة النظام.
نوع البيانات: UINT.

uCallBackMessage : تحديد النوع النبيه الذي يرسله إطار البرنامج المعني، والذي يستخدم لاستقبال الرسائل.
نوع البيانات: UINT.

hIcon: مقبض (Handle) للإيقونة التي ستعرض في علبة شريط المهام.
نوع البيانات: HICON.

szTip: (مبدئيا) هو نص حرفي يستخدم كتلميح على الإيقونة.
نوع البيانات: AnsiChar طولها 64 بايت.

uFlags : مجموعة من الخصائص تشير إلى ما تحويه الخصائص السابقة.
نوع البيانات: UINT.
القيم التي تأخذها: توليفة (تركيبة) من الثوابت الآتية، التي تشير إلى الخصائص المضبوطة كما يجب والتي سوف تستخدم، وتأخذ uFlags القيم التالية:

NIF_ICON: تحقق تمرير هذه الخاصية يشير إلى أن قيمة hIcon سيكون فعلا أيقونة وبالتالي ستظهر في علبة شريط المهام.

NIF_MESSAGE: النجاح في اجتياز هذه القيمة يدل على أن عنصرuCallBackMessage سيستخدم كقيمة ترسل تنبيهات الأحداث.
NIF_TIP: تنبئ هذه القيمة عن وجود عنصر szTip الذي يستخدم كتلميح (Hint) على أيقونة علبة شريط المهام.

والآن نبدأ مع الأمثلة:

المثال الأول: عرض أيقونة البرنامج في علبة شريط المهام وإضافة حدث النقر عليها (في ستة خطوات)
الخطوة الأولى: نعرف الوحدة ShellApi مع الوحدات المستعملة في قائمة uses
interface

uses
...
ShellApi;
الخطوة الثانية: نعرف متغير من النوع TNotifyIconData مع باقي متغيرات البرنامج قبل عبارة
var
Form1: TForm1;
MyTNA: TNotifyIconData;

الخطوة الثالثة: نستعمل في هذا المثال الحدث OnCreate لإضافة أيقونة علبة شريط المهام مباشرة عند تشغيل البرنامج

procedure TForm1.FormCreate(Sender: TObject);
begin
with MyTNA do
begin
cbSize := SizeOf(TNotifyIconData);//حجز حجم المكون
uID := 0;//رقم تعريفي لهذه الإيقونة، نحتاجه إذا كانت لدينا عدة أيقونات
uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;//
Wnd := Handle;//يمكن تعويضها بـ Self.Handle أو بـ Form1.Handle ويجب ذلك عند استدعائها من وحدة أخرى
uCallbackMessage := WM_APP + 1;//هذه قيمة حرة نستعملها لتلقي رسائل الأحداث
hIcon := Application.Icon.Handle;//لا بد من استعمال مقبض أيقونة البرنامج ككل، وليس مقبض الإطار
szTip := 'Delphi 4 arab';//هذا تلميح عادي يظهر على الإيقونة
end;
Shell_NotifyIcon(NIM_ADD, @MyTNA);//بهذا الإجراء نقوم بإضافة الإيقونة إلى علبة الشريط
end;

ملاحظات:
1-القيمة WM_APP + 1 التي يأخذها المتغير uCallbackMessage عادة ما تخزن في ثابت باستعمال const، وتعرف في الجزء interface مباشرة بعد تعريف الوحدات قبل type، وذلك لأنها ستستعمل أكثر من مرة.
2- في العنصر hIcon يمكن استعمال أيقونة من الرسورس عوضا عن أيقونة البرنامج، باستخدام
LoadIcon(HInstance, 'MyIcon') عوضا عن:
Application.Icon.Handle
حيث تمثل MyIcon اسم أيقونة من الرسورس.

إلى هنا تظهر الإيقونة قرب ساعة الويندوز لكنها لا تختفي عند انتهاء البرنامج لذلك نضيف الأمر الآتي:
الخطوة الرابعة: نقوم بإزالة الإيقونة من علبة شريط المهام عند انتهاء البرنامج من الحدث OnDestroy لإطار البرنامج

procedure TForm1.FormDestroy(Sender: TObject);
begin
Shell_NotifyIcon(NIM_DELETE, @MyTNA);//بهذا الإجراء نقوم بحذف الإيقونة من علبة الشريط
end;
الخطوة الخامسة: سنضيف إجراء يتلقى رسائل الأحداث من الإيقونة TNA (مثل حدث النقر، تحريك الماوس،... ) ثم نعدل عليها
نعرف في الجزء private الإجراء التالي:
private
procedure WMTaskMsg(var Msg: TMessage); message WM_APP +1;


لاحظ: القيمة WM_APP +1 هي التي أسندناها لـ uCallbackMessage نستعملها لتلقي الأحداث عبر خاصية،
أي أن إيقونة TNA تتلقى الأحداث من هذا الإجراء.

إضغط على Ctrl+Shift+C ليأخذ دلفي إلى موضع كتابة الإجراء الآتي أو أكتبه بنفسك.

الخطوة السادسة : نقوم بإخفاء بيان البرنامج من شريط المهام ثم إخفاء إطار النموذج، لتبقى فقط إيقونة TNA في علبة الشريط.

procedure TForm1.WMTaskMsg(var Msg: TMessage);
begin

if Msg.LParam = WM_LBUTTONDOWN then//إذا تلقت الأيقونة ضغط بالزر الأيسر ...
begin
ShowWindow(Application.Handle, SW_HIDE);//فسيتم إجراء منطقي (بولياني) لإخفاء البرنامج من شريط المهام
ShowWindow(Handle, SW_HIDE);//وسيتم نفس الإجراء السابق لإخفاء إطار النموذج (الفورم) من سطح المكتب
end;

if Msg.LParam = WM_RBUTTONDOWN then//إذا تلقت الأيقونة ضغط بالزر الأيمن ...
begin
ShowWindow(Application.Handle, SW_SHOW);//فسيتم نفس الإجراء السابق لإظهار البرنامج في شريط المهام
ShowWindow(Handle, SW_SHOW);//وسيتم نفس الإجراء السابق لإظهار النموذج على سطح المكتب
end;

end;
ملاحظات:
1- Msg.LParam هي المسؤولة عن تلقي الأحداث، وتتلقى الأحداث التالية:
WM_MOUSEMOVE: تحريك مؤشر الماوس

WM_LBUTTONDOWN: الضغط على زر الماوس الأيسر
WM_LBUTTONUP: تحرير زر الماوس الأيسر
WM_LBUTTONDBLCLK: نقر مزدوج بزر الماوس الأيسر
ونفس الأحداث بالنسبة لزر الماوس الأيمن (أظنها واضحة ;)):
WM_RBUTTONDOWN:
WM_RBUTTONUP:
WM_RBUTTONDBLCLK:
تنبيه: لأجل هذه الأحداث لم نستعمل else بعد if، أما case of فهي المناسبة في هذه الحالة وسنستخدمها لاحقا.
2- Msg.WParam هي المسؤولة عن التحقق من رقم uID للأيقونة التي تلقت الحدث، وبالتالي:
يمكن إضافة شرط if قبل الشرطين السابقين من أجل معرفة فيما إذا كان الحدث من الأيقونة التي أنشأناها أم أنه جاء من أيقونة أخرى، وذلك بالتحقق من رقم uID الذي أعطيناه القيمة 0 عند الإنشاء، كما يلي:


وإذا أردنا أن تتلقى الأحداث أيقونة أخرى نعطيها رقم uID مختلف عن السابق -1 مثلا- ثم نكتب if Msg.WParam = 1 وهكذا...
في مثالنا هذا لا توجد إلا أيقونة واحدة ولذلك لم ندرج شرط التحقق من uID.


الخطوة السادسة: نضيف إجراء يقوم بتلقي حدث تصغير البرنامج ثم نعدل عليه.
في الحقيقة يمكن لهذا الإجراء أن يتلقى أوامر تصغير، تكبير، إسترجاع، من أجل التعديل على أي منها، سنقتصر في هذه الخطوة على تلقي إجراء التصغير فقط.
نضيف الإجراء التالي مباشرة تحت الإجراء السابق في قسم private كالتالي:

private
procedure WMTaskMsg(var Msg: TMessage); message WM_APP +1;
procedure WMSyscommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;

ثم نستخدم Ctrl+Shift+C ليأخذنا دلفي إلى موضع كتابة الإجراء الآتي كما فعلنا مع الإجراء السابق.


procedure TForm1.WMSyscommand(var msg: TWmSysCommand);
begin
if msg.CmdType and $FFF0 = SC_MINIMIZE then//إذا تلقى الحدث = تصغير البرنامج ...
begin
ShowWindow(Handle, SW_HIDE);//سيتم إجراء إخفاء النموذج من سطح المكتب
ShowWindow(Application.Handle, SW_HIDE);//وسيتم نفس الإجراء لإخفاء البرنامج من على شريط المهام
end//وإن لم يتلقى حدث تصغير البرنامج (تلقى حدث آخر)
else inherited;//فسيرث الإجراء الأصلي له (يعني إذا تلقى تكبير النموذج فسيقوم بتكبيره كما في الأصل)
end;
ملاحظات:
سنقوم لاحقا بإنشاء إجراءات (precedure) تقوم بإظهار وإخفاء النموذج، وأخرى تقوم بعرض الأيقونة...، ثم نستدعي كل منها متى اقتضت الحاجة.

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

Dr.MOT
06-01-2009, 12:01 PM
ما شاء الله تبارك الله ,,

تبدو درسا غاية في الروعه ,,

بارك الله فيك أخي ,,

جآري الإطلاع . . ولي عودة إن واجهت أ أشكال ,,

شكرا !

kachwahed
06-01-2009, 12:20 PM
العفو أخي
شرفتني بمرورك
وشجعتني بشكرك.
شكرا كثيرا.
للتنبيه فقط: نحن نستعمل Delphi 7 وليس فيه مكون BalloonTip الذي يوجد في Delphi 2009 ويمكن استعماله مباشرة.
وهذا شكل المثال النهائي الشامل:
http://img123.imageshack.us/img123/8100/tnaballoontipyn5.png

AL-MOB4RM3G
06-01-2009, 01:16 PM
نعم ما شاء الله عليك اخي...
الدرس غاية في الروعة وسيتم التجربة بعد قليل ان شاء الله...

بارك الله فيكم اخي ولا تحرمنا من جديدك

nabilkeb
06-01-2009, 04:09 PM
بارك الله فيك أخي على الموضوع:) . جاري الإطلاع و التجريب ان شاء الله ....

me&delphi
06-01-2009, 11:14 PM
شرح ممتاز أخي الكريم بارك الله فيك

مبتدأ جدا
07-01-2009, 02:59 AM
درس مهم ... ستتم التجربة بإذن الله..
بارك الله فيك ((كن بخير))

ThePrince
07-01-2009, 11:00 AM
درس جميل وأشكرك على الشرح الوافي والجميل وهذا ما يدل على تميزك في هذة اللغة
بارك الله فيك أخي الكريم وبأنتظار بقية الدرس :)

nabilkeb
07-01-2009, 11:20 AM
السلام عليكم و رحمة الله و بركاته:
- في العنصر hicon يمكن استعمال أيقونة من الرسورس عوضا عن أيقونة البرنامج، باستخدام
loadicon(hinstance, 'myicon') عوضا عن:
Application.icon.handle
حيث تمثل myicon اسم أيقونة من الرسورس. ما نجح معي الأمر .

2- msg.wparam هي المسؤولة عن التحقق من رقم uid للأيقونة التي تلقت الحدث، وبالتالي:
يمكن إضافة شرط if قبل الشرطين السابقين من أجل معرفة فيما إذا كان الحدث من الأيقونة التي أنشأناها أم أنه جاء من أيقونة أخرى، وذلك بالتحقق من رقم uid الذي أعطيناه القيمة 0 عند الإنشاء، كما يلي:

وإذا أردنا أن تتلقى الأحداث أيقونة أخرى نعطيها رقم uid مختلف عن السابق -1 مثلا- ثم نكتب if msg.wparam = 1 وهكذا...
في مثالنا هذا لا توجد إلا أيقونة واحدة ولذلك لم ندرج شرط التحقق من uid. ممكن توضيخ و شرح أكثر؟

وشكرا جزيلا على هذه المشاركة و الدرس المفيد .

kachwahed
08-01-2009, 09:14 AM
وعليكم السلام ورحمة الله وبركاته

بداية فاتني تنبيه مهم، وهو أن المكون TBalloonHint الذي سنستخدم إن شاء الله لا يعمل إلا مع وجود Internet Explorer بإصدار 5.0 فما فوق.
وبخصوص الأيقونة من الرسورس للأخ الذي قال:
ما نجح معي الأمر .
عليك بتعويض 'MyIcon' بأيقونة من الرسورس بالكود التالي:

hIcon := LoadIcon(HInstance,MakeIntreSource(1));

طبعا بعد إضافة تعريف أيقونة الرسورس في الوحدة ({R MyIcon.res$})نفسها أو وحدة المشروع، وللاطلاع على كيفية استعمال الرسورس راجع موضوع الأخ DeltaAziz
http://www.delphi4arab.com/forum/showthread.php?t=42&highlight=Besm.wav
وموضوع الأخ merouane
http://www.delphi4arab.com/forum/showpost.php?p=1014&postcount=4

وفي المرفقات مثال عن ذلك (استعمال أيقونة من الرسورس).
ملاحظة:
يمكن استعمال إحدى أيقونات الويندوز (التنبيه والتحذير...) عوض الأيقونة hIcon، كالتالي:
hIcon := LoadIcon(0, IDI_APPLICATION);
hIcon := LoadIcon(0, IDI_QUESTION);
hIcon := LoadIcon(0, IDI_EXCLAMATION);
hIcon := LoadIcon(0, IDI_ERROR);
اختر واحدة منها عوض الكود السابق.

أما بخصوص التحقق من رقم uID فقال:
ممكن توضيح و شرح أكثر؟

وسأشرح:
ذكرنا أنه يمكن استخدام عدة أيقونات في علبة شريط المهام، لكن المشكلة أنك إذا استعملت عدة أيقونات كيف يعرف برنامجك أنك نقرت على الأيقونة الأولى أو الثانية أو الثالثة أو ....
الحل نستخدم قيمة مختلف لرقم uID لكل أيقونة
مثلا لأيقونة الأولى:
MyTNA.uID := 0;
للأيقونة الثانية:
MyTNA.uID := 1;
ثم نكتب في بداية الإجراء:
procedure TForm1.WMTaskMsg(var Msg: TMessage);
شرط التحقق التالي:
if Msg.WParam = 0 then
إي إذا نقرنا على الأيقونة الأولى...
ثم نكتب أوامر يقوم بها البرنامج عند النقر على الأيقونة الأولى بالزر الأيس كما في الشرح.
وإذا أردنا أن نكتب إجراء في حدث النقر على الأيقونة الثانية هذه المرة فنكتب شرط التحقق من أن الأيقونة الثانية هي التي تلقت حدث النقر، كما يلي:
if Msg.WParam = 1 then
أظن أن الفكرة اتضحت إن شاء الله ;).
أضفت شرط التحقق في الكود سورس أيضا.

mobile
08-01-2009, 12:30 PM
بسم الله الرحمن الرحيم
بارك الله فيك عمل في منتهى الروعة كنت ابحث عن هذا منذ بعيد

kachwahed
08-01-2009, 03:08 PM
السلام عليكم
يمكن تعويض الأمرين:
ShowWindow(Application.Handle, SW_HIDE);
ShowWindow(Handle, SW_HIDE);


مباشرة بالأمر:
Visible := False;

وكذلك فيما يخص لـ:
ShowWindow(Application.Handle, SW_SHOW);
ShowWindow(Handle, SW_SHOW);


تعوض بـ:
Visible := True;
والنتيجة هي نفسها.

ThePrince
10-01-2009, 12:37 PM
هناك طريقة أخرى في أنشاء أيقونة بجانب الساعة بأستخدام المكتبة CoolTrayIcon.pas
وهو موضح في السورس كود الموجود في المرفقات

kachwahed
10-01-2009, 01:06 PM
شكرا كثيرا أخي ThePrince على المشاركة.
في الحقيقة هناك مكونات (أو مكتبات pas) كثيرة مجانية تقدم هذه الخدمة وبعضها يختلف عن بعض الآخر.
أما هذه التي قدمتها فهي حقيقة مميزة باحتوائها على المكون TBalloonHintIcon والعديد من الخصائص.
لكننا نريد أن نتعلم كيف ننشئ أيقونة بجنب الساعة برمجيا دون الاستعانة بمكونات أو مكتبات خارجية، وقد نبهنا لذلك في بداية الشرح.
ذلك لكي يتحكم المبرمج في استخدام هذا المكون بالشكل الذي يناسبه، إضافة إلى أن استعمال مكتبات خارجية يثقل البرنامج ونحن نريد أن نجعله أخف ما يمكن.
شرح تركيب المكون BalloonHint واستعمال مكون PopupMenu غدا إن شاء الله.
عموما شكرا على المكتبة كنت سأضع واحدة في ختام الموضوع.

musvc
10-01-2009, 07:02 PM
نحن نستعمل Delphi 7 وليس فيه مكون BalloonTip الذي يوجد في Delphi 2009

يوجد في نسخة Delphi 10 lite هذا المكون ايضا ً ( تستطيع تحميل البرنامج لأن حجمه 59 ميقا فقط ) وأنه افضل من الدلفي 7 في نضري

kachwahed
11-01-2009, 11:59 AM
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله

اليوم إن شاء الله نواصل ما بدأنا به، نبدأ بإنشاء مكون قائمة منسدلة (TPopupMenu) ونحملها على الأيقونة TNA، ثم نكتب الإجراءات اللازمة لإنشاء التلميح المنطادي TBalloonHint.

الخطوة الأولى إضافة مكون من النوع TPopupMenu:
إنطلاقا من المثال السابق: نضيف ما يلي:

من قائمة المكونات Standard نضع مكون من النوع TPopupMenu على النموذج Form1، وننشئ فيه ثلاث عناصر: عرض، إخفاء، إنهاء.
سنأتي إليه بعد قليل.

بعد أن استبدلنا الأمرين:
begin
ShowWindow(Application.Handle, SW_HIDE);
ShowWindow(Handle, SW_HIDE);
end;
مباشرة بالأمر: Visible := False
و:
begin
ShowWindow(Application.Handle, SW_SHOW);
ShowWindow(Handle, SW_SHOW);
end;

بالأمر: Visible := True
يصبح الإجراء TForm1.WMTaskMsg، بهذا الشكل:
procedure TForm1.WMTaskMsg(var Msg: TMessage);
begin
if Msg.LParam = WM_LBUTTONDOWN then
Visible := False;

if Msg.LParam = WM_RBUTTONDOWN then
Visible := True;
end;
الآن نغير في الإجراء بحيث إذا نقرنا على الأيقونة يظهر البرنامج وإذا نقرنا عليها مرة أخرى يختفي، ونستعمل هنا case...of:
(للتنبيه فقط: case of تستعمل في حالة تعدد الشروط لنفس المتغير، فمثلا عوض أن نقول "إذا كان متغير x يساوي 1 إذا إفعل a، وإذا كان المتغير x يساوي 2 إذا إفعل b...". نقول مباشرة: "من أجل x يساوي 1: إفعل a، 2: إفعل b...).
procedure TForm1.WMTaskMsg(var Msg: TMessage);
begin
case Msg.LParam of
WM_LBUTTONDOWN: Visible := not Visible;
WM_RBUTTONDOWN:;
end;
end;
يمكن تشغيل البرنامج على هذا الشكل وملاحظة النتيجة.

الآن نربط مكون TPopupMenu مع حدث النقر بالزر الأيمن WM_RBUTTONDOWN، ونستعمل لذلك متغير من النوع TPoint (أي إحداثيات (x,y)على سطح مكتب الويندوز).
نعرف هذا المتغير في هذا الإجراء (مباشرة قبل begin) كما يلي: var Pnt: TPoint
ثم نعطي هذا المتغير قيمة إحداثيي مؤشر الماوس باستعمال GetCursorPos.
وأخيرا نعرض القائمة المنسدلة PopupMenu1 في حدث النقر بالزر الأيمن باستعمال خاصية Popup(x,y)، فيصبح الإجراء كما يلي:

procedure TForm1.WMTaskMsg(var Msg: TMessage);
var Pnt: TPoint;//نعرف متغير من النوع إحداثيات
begin
//نجعل هذا المتغير يأخذ قيمة إحداثيات مؤشر الماوس
GetCursorPos(Pnt);

case Msg.LParam of
WM_LBUTTONDOWN: Visible := not Visible;
//نعرض القائمة المنسدلة في موضع المؤشر عندما ينقر بالزر الأيمن
WM_RBUTTONDOWN: PopupMenu1.Popup(Pnt.X, Pnt.Y);
end;

end;
الآن عند النقر على الأيقونة بالزر الأيمن تظهر القائمة المنسدلة PopupMenu1 بالعناصر التي شكلناها سابقا (عرض، إخفاء، إنهاء).
والآن سنضيف أحداث هذه العناصر (كل عنصر كلمة واحدة فقط !!!؟) كما التالي:

procedure TForm1.N1Click(Sender: TObject);
begin
//للعرض:
Show
end;
//للإخفاء:
procedure TForm1.N2Click(Sender: TObject);
begin
Hide
end;
//للانهاء:
procedure TForm1.N4Click(Sender: TObject);
begin
Close
end;
وانتهينا...

الخطوة الثانية: إضافة التمليح المنطادي BalloonHint
(هذه الخطوة تتطلب شيئا من التركيز)

أولا: لا بد من التنبيه كما أشرنا أن المكون BalloonHint هو إحدى المكونات الحديثة لويندوز xp (وكذلك ويندوز 2000 NT، وويندوز Me) أي أنها لا تعمل في نظام ويندوز 98، وكذلك لا بد من توفر Internet Explorer بإصدار 5.0 على الأقل.

ثانيا: لن نستعمل النوع TNotifyIconData لأنه لا يحتوي على مكون BallonHint بل سننشئ بأنفسنا نوع جديد سنسميه: TNewNotifyIconData ثم نضيف إليه التمليح المنطادي.

بدايةً نعرف في الحقل Interface بعد تعريف الأنواع type، التسجيل TTimeoutVersion الذي يخزن مدة ظهور التمليح (سنشرح ذلك لاحقا)، مباشرة بعده نعرف النوع الجديد TNewNotifyIconData له نفس معالم (البارامترات) النوع السابق TNotifyIconData، إضافة إلى ستة أخرى جديدة (وهي التي تتطلب الإصدار 5.0 لـ IE) هي كالآتي:

dwState: DWORD;
dwStateMask: DWORD;
szInfo: array [0..255] of Char;
TTimeoutVersion: TTimeoutVersion;
szInfoTitle: array [0..63] of Char;
dwInfoFlags: DWORD;
نشرح أهم الخصائص:
szInfo: متغير حرفي لتخزين رسالة لا تفوق 256 حرف للنص الذي سيظهر في التمليح.
TTimeoutVersion: هذا متغير من نوع تسجيل (record) الذي عرفناه من قبل ويحفظ إصدار التلميح، ومدة ظهور التمليح حسب الويندوز xp لا تقل عن 10 ثوان ولا تزيد عن 60 ثانية.
szInfoTitle: عنوان التمليح وحجمه 64 حرف.
dwInfoFlags: رمز التمليح (الأيقونة المستعملة (أو الشكل) في التلميح)، ويتخذ عدة أنواع (مثل أشكال MessageDlg).
في النهاية يظهر تعريف النوع كما يلي:
interface
...
type
TForm1 = class(TForm)
...
end;

TTimeoutVersion = record
case Integer of
0: (uTimeout: UINT);//إصدارات أقدم من ويندوز 2000
1: (uVersion: UINT);//إصدار ويندوز 2000 فما فوق
end;

TNewNotifyIconData = record
cbSize: DWORD;
Wnd: HWND;
uID: UINT;
uFlags: UINT;
uCallbackMessage: UINT;
hIcon: HICON;
szTip: array [0..127] of Char;
dwState: DWORD;
dwStateMask: DWORD;
szInfo: array [0..255] of Char;
TTimeoutVersion: TTimeoutVersion;
szInfoTitle: array [0..63] of Char;
dwInfoFlags: DWORD;
end;

var

//ولا ننسى أن نعرف أيقونتنا من هذا النوع الجديد (طبعا نحذف تعريف النوع القديم):
Form1: TForm1;
MyTNA: TNewNotifyIconData;


والآن لم يبق لنا سوى كتابة الإجراء الذي يعرض التلميح المنطادي، نبدأ بتعريف الإجراء في الحقل Private:

procedure ShowBalloonTips(TipInfo, TipTitle: string);
ثم نضغط على Ctrl+Alt+C ونكتب أوامر الإجراء على النحو التالي:
(يتلقى هذا الإجراء قيمتين نصيتين تمثلان عنوان التلميح ومضمونه)
procedure TForm1.ShowBalloonTips(TipInfo, TipTitle: string);
begin
with MyTNA do begin//إنشاء أيقونة
cbSize := SizeOf(MyTNA);//حجز حجم الأيقونة
uFlags := $10;//تعني كل الأحداث NIF_MESSAGE + NIF_ICON + NIF_TIP
StrPLCopy(szInfo, TipInfo, SizeOf(szInfo) - 1);//كتابة نص التلميح
MyTNA.TTimeoutVersion.uTimeout := 1000;//المدة أكتب أي قيمة فلن يؤثر ذلك، لأن الويندوز من يتحكم بوقتها
strPLCopy(szInfoTitle, TipTitle, SizeOf(szInfoTitle) - 1);//كتابة عنوان التلميح
dwInfoFlags := $00000001; //الشكل الذي سيأخذه التلميح شكل إعلام
end;
Shell_NotifyIcon(NIM_MODIFY, @MyTNA);//دالة تغيير الإيقونة إلى أيقونة ذات تلميح منطادي
end;

ملاحظات:
1- كتبنا هنا إجراء يقوم يتلقى نص رسالة التلميح وعنونها المكتوب فيها، ويعيد تغيير الأيقونة إلى أيقونة تحمل تليمح منطادي BalloonHint.
2- فيما يخص المتغير dwInfoFlags يمكنه أن يأخذ عدة قيم (مثل قيم mtWarning, mtError, mtInformation للرسائل MessageDlg) يمكن تخزينها في ثوابت معلن عنها مسبقا بحيث:

NIIF_INFO = $00000001; //تلميح إعلام
NIIF_WARNING = $00000002;//تلميح إنذار
NIIF_ERROR = $00000003;//تلميح خطأ
3- أما TTimeoutVersion يمكن إهماله تماما لأن قيمته يحددها الويندوز تلقائيا.

الخطوة الثالثة: نضع زر TButton ونكتب في حدث النقر عليه:
procedure TForm1.BtnShowHintClick(Sender: TObject);
begin
ShowBalloonTips('محتوى التلميح', عنوان التلميح');
end;
الخطوة الرابعة: التحكم في الأحداث التي يتلقاها التلميح
نريد أن نجعل البرنامج يعرض رسالة (ShowMessage) عند عرض التلميح، ورسالة أخرى عند النقر عليه ورسالة ثالثة عند انتهاء مدة التلميح.
نضيف إلى الإجراء الذي يتلقى الأحداث (الذي عرفناه سابقا) الأحداث التالية: ظهور التلميح، اختفاء التلميح، النقر على التلميح، كما يلي:
procedure TForm1.WMTaskMsg(var Msg: TMessage);
var Pnt: TPoint;
begin
GetCursorPos(Pnt);

case Msg.LParam of
WM_LBUTTONDOWN: Visible := not Visible;
WM_RBUTTONDOWN: PopupMenu1.Popup(Pnt.X, Pnt.Y);

WM_USER + 2: ShowMessage('ظهور التلميح');//تحدث عندما يظهر التلميح
WM_USER + 3: ShowMessage('اختفاء التلميح');//تحدث أيضا عند اختفاء الأيقونة لأن التلميح يختفي معها
WM_USER + 4: ShowMessage('انتهاء مدة التلميح');//تحدث عندما يرسل الويندوز حدث انتهاء مدة التلميح
WM_USER + 5: ShowMessage('النقر على التلميح');//تحدث عندما ينقر المستخدم على التلميح
end;

end;
ملاحظات:
1- WM_USER + i هي قيم معرفة لدى الدلفي في الوحدة Messages.pas.
2- عند النقر على الزر الذي يغلق التلميح (في أعلى يمين التلميح) يرسل الويندوز حدث انتهاء مدة التلميح WM_USER + 4 تذكر ذلك.
3- يمكن تخزين القيم WM_USER + i في ثوابت (باستعمال const) باستعمال أسماء مناسبة تدل عليها، مثل:
const
BALLOONSHOW = WM_USER + 2;
BALLOONHIDE = WM_USER + 3;
BALLOONTIMEOUT = WM_USER + 4;
BALLOONUSERCLICK = WM_USER + 5;
جرب الآن تشغيل البرنامج وانظر للنتيجة.
إلى هنا نكون قد أنشأنا برنامج يعرض أيقونة بقائمة منسدلة يمكنها تلقي أحداث المستعمل، وتعرض تلميح منطادي يتلقى هو الآخر أحداث المستعمل ويرد عليها.
وللحديث بقية سنأتي عليها إن شاء الله...

kachwahed
11-01-2009, 12:09 PM
نواصل...
خطوات إضافية: البرنامج مكتمل ويمكن إضافة خيارات أخرى، مثل جعل البرنامج يظهر في أحد المنطقتين دون الأخرى، عرض خيار للمستخدم فيما إذا كان يرغب بتصغير البرنامج عند غلقه...
الخطوة الأولى: يمكن أن نجعل البرنامج يظهر في أحد المكانين، إما في علبة شريط المهام أو في شريط المهام نفسه، ولأجل ذلك نقوم بما يلي:
أولا: من الأحسن أن نجعل إجراء منفرد يعرض الأيقونة، ثم نضيفه إلى FormCreate أو نترك هذا الأخير فارغا:
private
procedure ShowMyTNA;
نستعمل Ctrl+Alt+C ونكتب:
procedure TForm1.ShowMyTNA;
begin
with MyTNA do
begin
cbSize := SizeOf(TNotifyIconData);
MyTNA.uID := 0;
uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
Wnd := Handle;//
uCallbackMessage := WM_APP +1;
hIcon := Application.Icon.Handle;
szTip := 'Delphi 4 arab';
end;
Shell_NotifyIcon(NIM_ADD, @MyTNA);
end;
* يمكن إضافة زر يعرض الأيقونة إلى جنب ساعة الويندوز، ويخفيها من شريط المهام، ونجعله غير مفعل إذا كانت الأيقونة تظهر، ويفعل هو الآخر زر عرض التلميح، فنضع زر ونكتب في حدث النقر عليه:
procedure TForm1.BtnShowTNAClick(Sender: TObject);
begin
ShowMyTNA;//عرض الأيقونة
BtnShowTNA.Enabled := False;//تعطيل زر عرض الأيقونة
BtnShowHint.Enabled := True;//تفعيل زر عرض الأيقونة
ShowWindow(Application.Handle, SW_HIDE) //إخفاء البرنامج من شريط المهام
end;

ويلزم لأجل هذا التغيير، تغيير زر عرض النموذج الذي وضعناه في القائمة المنسدلة، بالشكل التالي:
procedure TForm1.N1Click(Sender: TObject);
begin
Show;//عرض النموذج
FormDestroy(Sender);//إخفاء الأيقونة، وذلك باستدعاء الأوامر التي تحدث عند إعدام النموذج
BtnShowTNA.Enabled := True;//تفعيل زر عرض الأيقونة
BtnShowHint.Enabled := False;//تعطيل زر عرض التلميح مرة أخرى
ShowWindow(Application.Handle, SW_SHOW)//عرض اسم البرنامج في شريط المهام
end;
الخطوة الثانية: إضافة مكون من النوع TCheckBox ثم نعدل في الإجراء الذي يتلقى الأحداث بإضافة حدث غلق النموذج ونغير فيه بما يلي:
procedure TForm1.WMSyscommand(var msg: TWmSysCommand);
begin
if msg.CmdType and $FFF0 = SC_MINIMIZE then
begin
Hide;
BtnShowTNA.Click;
end else
begin
if msg.CmdType and $FFF0 = SC_CLOSE then// إذا تلقى حدث غلق النموذج
if ChkBxCloseOnExit.Checked then//التحقق من أن الخيار "إخفاء عند الغلق" مفعل
begin
ShowMyTNA;//عرض الأيقونة
Hide;//إخفاء النموذج
end else Close//وإن لم يكن مفعل ...فيقوم الإجراء بغلق النموذج
else inherited;//وإن لم يتلقى الإجراء حدث تصغير ولا حدث الغلق...فسيرث الأحداث الأخرى
end
end;
آخر إضافة: يمكن إضافة تلميح منطادي عند محاولة غلق النموذج (في حين خيار "تصغير عند الغلق" مفعل) ينبه المستعمل على أن البرنامج لم يغلق، ولا يزال يشتغل (يضاف هذا الأمر في الإجراء الذي يتقلى أحداث التلميح):
...
if msg.CmdType and $FFF0 = SC_CLOSE then
if ChkBxCloseOnExit.Checked then
begin
ShowMyTNA;
ShowBalloonTips('لا يزال البرنامج مفتوحا', 'تنبيه');
Hide;
...
ملاحظات:
- inherited ترث الأحداث الأصلية، وفي حالتنا هذه غيرنا في حدث التصغير وفي حدث الغلق وبقي حدث تكبير النموذج وحدث تغيير حجم النموذج.
- أحيانا يفقد البرنامج التركيز (Focus) ولا يتلقى حينها أوامر لوحة المفاتيح (يحدث ذلك أحيانا عند تكبير النموذج) لاسترجاع التركير على البرنامج نستعمل الأمر BringToFront وأحيانا لا تنفعل فنستخدم:
SetForegroundWindow(Handle);

أتمنى أن يكون الموضوع نال إعجابكم وأشبع حاجتكم
وكما ذكرنا سابقا أنه توجد مكونات كثيرة تقوم بهذا العمل (كما أشار إلى ذلك بعض الأعضاء)، وأن الإصدارات الحديثة للدلفي تأتي معها مثل هذه المكونات، لكننا كما بينا أننا نريد أن نقوم بذلك بأنفسنا برمجيا لعدة أسباب منها: أن نتعلم كيفية التعامل دوال Api، والتحكم فيها وعرضها بالشكل الذي نريد، جعل البرنامج أكثر خفة بإضافة العناصر التي نحتاجها فقط (وهذا مهم بالنسبة لأي مبرمج محترف لأنه يعتبر 10 Kb إضافية لوزن البرنامج شيء ثقيل)...

المثال النهائي مع الكود سورس في المرفقات.

هذا والله سبحانه وتعالى أعلم، وصلى الله على سيدنا محمد.
والسلام عليكم ورحمة الله.

nabilkeb
13-01-2009, 11:08 PM
مشكووور أخي على المجهود المثمر و الجميل .... بارك الله فيك

ThePrince
03-02-2009, 11:26 AM
جزاك الله ألف خير ... درسك جميل وبصراحة أستفدت منة أشياء كثيرة بارك الله فيك أخي.

abo-ghadab
16-02-2009, 12:52 PM
السلام عليكم ورحمة الله وبركاته
--------------------------------
جزاكم الله خيرا أخوتي الكرام على هذه المعلومات

شكرا بكل معنى الكلمة

o15s19
12-03-2009, 06:48 PM
مشكور جدا لك أخي الكريم

المصمم الصغير
31-10-2010, 07:49 PM
هل يمكن اخى المبارك اعادة الدرس ولو بالفيديو للافادة اكثر لان يوجد هنا مبتدئين كثيرين ونسأل الله ان ينفعك بهذا العلم الكبير ونسأله واسع رحمته لك ولاهلك اجمعين
شكرا لك اخى بارك الله فيك
فى انتظارك بأذن الله

kachwahed
31-10-2010, 08:10 PM
آسف أخي، لدي انشغالات كثيرة حاليا...
الأمر لا يحتاج إلى فيديو، حاول إعادة القراءة شيئا فشيئا
إذا لم تتضح لك أي نقطة سأعيد شرحها بإذن الله
بالتوفيق.