سى++ بيلدر و الربط مع قواعد البيانات–الجزء الأول.

 

قواعد البيانات العلائقية أصبحت احدى اهم مجالات البرمجة الحديثة, فمع تعقد أشكال البيانات و العلاقة بينها و أحجامها الكبيرة, نمت الحاجة الى قواعد بيانات استعلامية سريعة, تعمل بطريقة الزبون/الخادم Client/Server.

و للتعامل مع هذه البيانات, يجب ربطها بالتطبيق الذى تقوم بتطويره لاستجلاب و تعديل البيانات و استخدامها بشكل أو اخر.

احدى اهم مميزات سى++ بيلدر هى قدرته الكبيرة على الربط قواعد البيانات العلائقية بسهولة و سرعة.

فى هذا المقال سوف نتحدث عن كيفية الربط بين السى++ بيلدر و قواعد البيانات العلائقية المختلفة مثل InterBase,SQL Server,Oracle و غيرهم.

قواعد بيانات مع سى++!!!!!!

تصلح لغة السى++ لجميع الاغراض, لكن عندما يقترب الأمر من برنامج يعتمد على قاعدة بيانات علائقية (موظفين, حسابات, مخازن… الخ) فان المبرمجين غالبا ما يتجهون الى لغات أخرى مثل الدلفى, و السى# و الفيجوال بيسك.

هناك حالة جفاء تاريخية بين لغة السى++ و قواعد البيانات,

من ناحية أخرى فان مبرمج السى++ (و منهم أنا) لا يفضل استخدام المحركات الجاهزة لقواعد البيانات, و عندما يحتاج الى حفظ بيانات معينة, فانه غالبا ما يحفظها فى ملفات Binary و ترتيبها فى منشأت structures ثم طبعها فى الملف أو حتى استخدام ملف نصى.

معظم بيئات تطوير لغة السى++ تفتقد القدرة على الربط مع قواعد البيانات, و بعضها يوفره و لكن بشكل معقد يستهلك الكثير من الوقت, و هو ما لا يتناسب مع مبرمج تطبيقات الأعمال الذى عادة يهمه انجاز العمل بشكل سريع و أمن. 

سى++ بيلدر مختلف تماما, فمع مكتبات قوية مثل Borland DataBase Engine و dbExpress و dbGo أصبح الربط مع قواعد البيانات سهلا و لا يتكلف سوى بضعة نقرات من الفأرة كما سوف نرى, كل ذلك مدمج مع قدرات لغة السى++ الرائعة.

متى يجب ان استخدم قواعد البيانات؟

كمبرمج سى++ سوف أفترض انك عادة لا ترى مزية اضافية من استخدام قاعدة بيانات علائقية و الربط معها, و أنك تفضل دائما حفظ البيانات فى شكل ملف ثنائى,

مثلا يمكنك ان تعرف منشأة تحفظ فيها البيانات التى تريدها

struct employee{

char name[250];

int age;

char adress[250];

char tel[10];

};

يمكنك بعد ذلك تصدير قاعدة البيانات تلك بشكل ملف ثنائى,

مثلا :

employee emp[100];    // قاعدة بيانات ل100 موظف

emp[0].name=”Fady”;

emp[0].age=27;

.

.

.

ثم يمكن بعد ذلك حفظ المصفوفة فى ملف :

 

for(int counter=0;counter<employee_count;counter++)

     fwrite((char*)&emp[counter],sizeof(employee),1,handle);

ما فعلناه بالأعلى هو مثال بسيط عن تصميم قاعدة بيانات بسيطة flat file database عن طريق السى++,

هذه الطريقة قد تنفع فى بعض الاحيان, حتى مع قواعد البيانات الكبيرة, لكن هل فكرت فى المشاكل الأتية :

– ماذا لو كنت تحتاج الى client/server database, حيث يتولى خادم قاعدة البيانات تنفيذ طلبات الزبائن سواء تعديل او حذف او عرض من قاعدة البيانات؟!

– ماذا لو كانت هناك عدة جداول بيانات كلها مرتبطة ببعضها البعض, ان حل هذه العلاقات برمجيا سوف يستهلك الكثير من الوقت و يصرفك عن حل المشكلة الاساسية.

– ماذا لو اردت البحث عن صف فى قاعدة بيانات ضخمة؟!

ان حل هذه المشاكل و غيرها ممكن برمجيا باستخدام قوة السى++, لا شك انك قادر على التعامل مع مثل هذه المشاكل بنفسك, و لكن هذا سوف يكلفك الكثير من الوقت و الجهد و الأخطاء و المزيد من الوقت لاصلاحها!

كمبرمج سى++ قد يعجبك هذا التحدى,

لكن مديرك على الأرجح لن يتفهم تلك المشاعر النبيلة التى تدفعك الى كتابة كل “بايت” فى برنامجك.

رغم ذلك, فانه فى بعض الأحيان, قد يكون من الأفضل استخدام قاعدة بيانات flat file, ما يعطى برنامجك مظهرا اكثر احترافية و سرعة اكبر , و هذا هو الفارق بينك كمبرمج سى++ و بين مبرمج الفيجوال بيسك و مبرمج السى#.

مثلا لو كنت تريد حفظ بعض الخيارات لبرنامجك, (لون الخلفية, لون النص, حجم الخط… الخ) فانه من الأفضل حفظها فى ملف flat file database.

لو كان برنامجك يستخدم قاعدة بيانات واحدة ليس فيها علاقات أو كانت قواعد البيانات ذات أهمية فرعية فى برنامجك, فانه من المبالغة حفظها بواسطة محرك قواعد بيانات.

قواعد البيانات التى يدعمها سى++ بيلدر :

يوفر السى++ بيلدر أدوات قوية تستطيع الربط مع معظم قواعد البيانات المعروفة, سواء التجارية منها مثل (MS SQL Server, Oracle, InterBase) أو حتى تلك مفتوحة المصدر مثل (MySQL).

اضافة الى ذلك, فان السى++ بيلدر يوفر قاعدة بيانات سريعة و صغيرة و مدمجة بداخل محرك BDE, هى Paradox, لو تذكرونها فانها منتج قديم و قوى من بورلاند و يعتبر الوريث الشرعى للdBase الشهيرة من شركة Ashton Tate, لكن مضافا اليها محرك SQL مدمج فى الBDE هو SQL Links, تستطيع فعل كل شىء بواسطة الParadox, لكنها لا تدعم بنية الClient/Server.

يمكنك استخدام paradox فى قاعدة بيانات محلية local database فهى تندمج داخل برنامجك و تريحك من عناء تثبيت محرك database خارجى.

لو احتاج الامر الى اكثر من ذلك, فيمكنك الانتقال الى قواعد بيانات أكثر ضخامة مثل MS SQL Server أو InterBase.

بما اننا نستخدم مترجم من شركة Embarcadero, و ان قاعدة البيانات InterBase من انتاج الشركة نفسها و يندمج بسرعة مع سى++ بيلدر, فاننا بحاجة الى التعرف اكثر على الInterBase.

 

قاعدة بيانات InterBase :

انتر بيس هى قاعدة بيانات من انتاج شركة امباركاديرو (بورلاند سابقا) كاملة المزايا, و تعتبر منافسا مهما للOracle و الMS SQL Server و غيرهم.

الانتربيس تشتهر بسرعتها و حجمها الصغير, فمثلا تثبيت حزمة الزبون لا يكلفك اكثر من عدة كيلوبايتات,

العيب الاساسى فى رايى الشخصى هو أدوات التعديل الخاصة بالانتربيس التى تبدو فقيرة الى حد ما و صعبة الاستخدام, عكس ادوات مايكروسوفت مثلا MS SQL Server Management Tool

فى نسختها المجانيةDeveloper edition  توفر انتربيس كل ما يتمناه المبرمج, بدون حد أقصى للبيانات (مثلما تفعل مايكروسوفت حيث تفرض حدا أقصى لحجم البيانات فى النسخة Express).

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

سوف نتعلم سويا كيف نربط السى++ بيلدر مع انتربيس و مع MS SQL Server.

تتوفر انتربيس على موقع امباركاديرو :

http://www.embarcadero.com/products/interbase

كما يمكن تنزيل Microsoft SQL Server من موقع مايكروسوفت :

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c243a5ae-4bd1-4e3d-94b8-5a0f62bf7796

 

فى الجزء القادم سوف نتحدث عن كيفية ربط السى بيلدر مع قواعد البيانات. فالى اللقاء.

 

 

Advertisements

مقدمة فى السى++ بيلدر الجزء السادس

  1. سوف نتحدث فى هذا الدرس عن كيفية استخدام الخيوط Threads فى السى++ بيلدر,
    يمكن انشاء الThreads عبر استخدام الAPI الخاصة بالويندوز, و هذه الطريقة صعبة و مملة و بالطبع سوف تكلفك الكثير من الوقت و الجهد,
    أو يمكنك استخدام مكتبات VCL التى توفر لنا طريقة سهلة و سريعة و أمنة لعمل برنامج متعدد الخيوط.
    مقدمة عن الخيوط Threads:
    الخيوط ببساطة هى سلسلة من التعليمات التى ينفذها الحاسوب,
    تتكون برامج الويندوز من خيط واحد على الأقل, و هو الخيط الرئيسى او الMain Thread و هو فى الحقيقة البرنامج الذى يتم تنفيذه,
    بالاضافة الى ذلك يمكننا الويندوز من اضافة خيوط فرعية لأداء مهام أخرى بالتوازى مع الخيط الرئيسى للبرنامج, فيصبح البرنامج بالتالى متعدد المهام بداخله.
    يستخدم السى ++ بيلدر الخيوط من خلال عدة مهام لا يلاحظها المبرمج, مثلا المكون TTimer يقوم بالعد عبر خيط خاص به, و كذلك مكونات Indy الخاصة بالانترنت تعمل من خلال خيط خاص بها, حتى لا يؤثر عمل هذه المكونات على خيط البرنامج الرئيسى.
    لكن ما حاجتنا نحن الى تنفيذ مهام فرعية بالتوازى مادمنا نستطيع فعل كل شىء من الخيط الرئيسى؟!
    مثلا تخيل انك تقوم بتصميم برنامج يقرأ بعض الأرقام من جهاز خارجى موصول بالحاسوب, و أن هذا الجهاز يأخذ بعض الوقت لتغذية الأرقام الى الحاسوب,
    معنى ذلك أن البرنامج الذى صممته سوف يتوقف الى ان يتلقى الارقام من الجهاز البطىء, ثم يقوم بالمعالجة الخاصة به, و فى خلال هذه الفترة سوف يتوقف عن الاستجابة لأحداث المستخدم, حتى البسيطة منها مثل تغيير حجم النافذة أو حتى غلقها!
    الأفضل هو تصميم خيط منفصل يقوم بالانتظار و القراءة من الجهاز الخارجى البطىء, ثم يقوم بتزويد الخيط الرئيسى بالمعلومات, و هو ما عليه الا اظهارها فى شكل متناسق للمستخدم (رسوم بيانية مثلا).
    نأتى الى موضوع برنامج اليوم, و هو الباحث عن الملفات,
    سوف نقوم بعمل برنامج بسيط للبحث عن ملف يحدده المستخدم,
    حتى الأن يبدو الامر سهلا و لكن لنتخيل اننا صممنا البرنامج بحيث يقوم الخيط الرئيسى بالبحث عن الملف,
    عملية البحث عن الملف قد تأخذ وقتا طويلا بحسب حجم وحدة التخزين و كمية الملفات الموجودة بها, ليس لنا دخل بهذا, فقد يصل الأمر مع الحواسيب البطيئة و كمية كبيرة من الملفات المتناثرة الى ساعة كاملة للبحث عن ملف معين.
    و بالطبع أثناء عملية البحث سوف يتوقف البرنامج الرئيسى عن الاستجابة لأحداث المستخدم,
    فقد يجد المستخدم الملف الذى اراده قبل انتهاء عملية البحث و يريد فتحه و التعامل معه و لكن فى هذه الحالة لن يسمح له برنامجنا بفتح ملفه قبل ان تنتهى عملية البحث, و هو بالطبع قصور كبير فى البرنامج.
    نعود الى برنامجنا البسيط الباحث عن الملفات.
    هناك حلين لهذه المشكلة :
    وضع عبارة Application->ProcessMessages() وسط كود عملية البحث, و هى تخبر البرنامج بتنفيذ الرسائل المتأخرة (من احداث المستخدم) ثم العودة,
    فى هذه الحالة سوف يستجيب البرنامج لأحداث المستخدم لكن استجابته سوف تكون بطيئة, لأنه لا ينفذ الا عند عبارة ProcessMessages().
    الحل الأخر و هو الأكثر احترافية استخدام الخيوط أو الThreads.
    سوف نصمم Thread خاص يقوم بعملية البحث عن الملفات, فيما يتفرغ الخيط الرئيسى للاستجابة لأحداث المستخدم كالنقر على ملف تم ايجاده مثلا, أو ايقاف عملية البحث.
    لنفتح بيئة التطوير سى++ بيلدر,
    افتح مشروع جديد باستخدام File->New->VCL Forms Application
    قم بتصميم الفورم الرئيسى كما فى الصورة :
    غير اسم الفورم الرئيسى الى Name=init.

    ضع TGroupBox و غير الCaption=Search Criteria و غير صفة الAlign الى alTop.
    ضع TLabeledEdit و غير اسمها Name الى search_file, سوف تجد الTLabeledEdit فى الTool Pallete تحت التبويب Additional.
    الTLabeledEdit هو مربع كتابة عادى, لكنه مزود بLabel فوقه لتسهيل عملية عنونة مربع الكتابة,
    لتغيير العنوان فى الObject Inspector, قم بتوسيع الEditLabel الذى سوف تجده فى التبويب Linkage. و غير الCaption=File To Search

    سوف نستخدم هذه المرة Component جديد هو TDriveComboBox و هو قائمة منسدلة تعرض الDrives الموجودة بالجهاز فيها, و هو مكون مأخوذ أساسا من الويندوز 3.1 و كان من الخصائص المميزة له,
    سوف تجد الTDriveComboBox فى الTool Pallete تحت تبويب اسمه Win3.1.

    قم باضافة زرين TButton واحد لبدء البحث بعنوان Caption=Search! و الثانى لايقاف البحث اسمه Caption=Stop!
    قم بتغيير خصائص زر البحث و اجعله Default=true حتى يقوم بالعمل مع الضغط على مفتاح Enter.
    و غير خصائص زر التوقف Cancel=true حتى يتوقف البحث عند الضغط على Esc.
    أضف قائمة TListBox و سوف نسميها Name=search_results و هى التى سوف تعرض نتائج البحث و اجعل الAlign=alClient.
    أضف كذلك TStatusBar و اجعل الAlign=alBottom, و سوف نسميه Name=status للتسهيل, و غير الصفة SimplePanelٍ الى true.
    سوف يعرض الStatusBar المجلد الذى يتم البحث فيه حاليا كما سوف يعرض ايضا حالة البحث.
    الأن جاء وقت اضافة خيط البحث أو Thread البحث.
    سوف نسميه TSearchThread.
    لاضافة الThread افتح قائمة File->New->Other…
    سوف يظهر المربع New Items, نشط التبويب C++ Builder Files, و اختر Thread Object ثم اضغط OK كما فى الصورة.

    سوف يظهر المربع New Thread Object,
    نقوم بكتابة اسم صف الThread
    TSearchThread
    كما هو موضع فى الصورة.

    سوف يقوم السى++ بيلدر باشتقاق الThread الخاص بنا من الصف TThread و بهذا نصبح جاهزين لمعالجة الThread الخاص بنا.
    قم بتسجيل المشروع و ضع الملف الذى يحتوى على الفورم الرئيسى init2.cpp.
    و الملف الذى يحتوى على كود الThread سجله باسم search_thread_2.cpp.
    الأن قم بفتح الملف search_thread_2.h

    لنضف بداخله متغيرين هم search_dr و هو الدرايف الذى سوف يتم البحث بداخله و الsearch_file و هو الملف الذى نريد البحث عنه.

    الأن نبدأ بكتابة كود البحث عن الملفات,
    سوف يتكون كود البحث عن الملفات من اجرائين فقط,
    الاجراء الأول هو GetFiles يقوم فقط بتجهيز المجلد الرئيسى عبر تحويل c مثلا الى c:\ و من ثم يقوم باستدعاء الاجراء الأخر هو FindFilesIn.
    الاجراء FindFilesIn سوف يقوم بالبحث عن الملف باستخدام الAPI FindFirstFile و FindNextFile, فاذا ما وجد ملف يقوم بمقارنة اسمه بsearch_file , اذا وجده متطابقا يقوم باضافته الى القائمة search_results.
    اذا وجده مجلد فانه يقوم باستدعاء نفسه مرة أخرى مضيفا اسمه الى المجلد حتى يتم البحث داخل المجلد الفرعى و هكذا دواليك ثم يعود مرة اخرى ليختبر الملف الذى بعده.
    فى النهاية سوف تكتمل عملية البحث فى جميع ارجاء القرص الذى اخترناه.
    الأن نضيف تعريف الاجرائين GetFiles و FindFilesIn الى الملف search_thread_2.h

    كود سي بلس بلس:

                void GetFiles(char dr);
    void FindFilesIn(const char *cur_dir_c);

    فى الملف search_thread_2.cpp نكتب كود ال GetFiles كالتالى :

    كود سي بلس بلس:


    void TSearchThread::GetFiles(char dr)
    {

    char str[100];
    char *current_dir;

    str[0]=dr;
    str[1]=':';
    str[2]=NULL;
    current_dir=(char*)malloc(4000);
    strcpy(current_dir,str);]


    FindFilesIn(current_dir);


    شرح الكود :
    قمنا بتعريف سلسلة أحرف str بطول 100 حرف, و مؤشر أحرف current_dir
    سوف نضيف العلامة : بعد حرف القرص c مثلا ليصبح c: ثم نضيف بعد ذلك NULL لاغلاق الحرفى.
    نقوم بنقل محتويات الحرفى str الى current_dir, ثم نستدعى كود البحث و بالتالى سوف تبدأ عملية البحث من c: (أو اى قرص اخر اختاره المستخدم) ثم تتابع مع المجلدات التى يحتويها.
    قبل ان نكتب الاجراء FindFilesIn سوف نقوم بكتابة اجرائين هم AddFile و هو الذى سوف يقوم باضافة الملف الذى وجدناه فى الsearch_results و كذلك الاجراء ShowSearchFolder و هو الذى سوف يقوم باظهار المجلد الذى نقوم بالبحث فيه حاليا.
    و سوف نقوم بتعريف متغيرين جديدين هم صفتين للTSearchThread filename و current_search_folder.
    الأول سوف يحمل اسم الملف الذى سوف نضيفه الى القائمة,
    و الثانى سوف يحمل المجلد الحالى الذى نقوم بالبحث فيه.

    كود سي بلس بلس:


    void __fastcall AddFile();

    كود سي بلس بلس:


    void __fastcall ShowSearchFolder();
    AnsiString filename;
    AnsiString current_search_folder;

    الأن نضيف معالجة الAddFile

    كود سي بلس بلس:

    void __fastcall TSearchThread::AddFile()
    {
    init->search_results->Items->Append(filename);
    }

    كما نرى يقوم باضافة الحرفى filename الى قائمة الملفات .
    و الأن نقوم باضافة معالجة current_search_folder :

    كود سي بلس بلس:

    void __fastcall TSearchThread::ShowSearchFolder()
    {
    init->status->SimpleText=current_search_folder;
    }

بالطبع, سوف يتبادر الى ذهنك سؤالين هنا:
– لماذا لم نقوم بتمرير الحرفى filename و current_search_folder بدلا من وضعهم كمتغيرين فى الTSearchThread؟
– لماذا أضفنا ال__fastcall ؟
الحقيقة أنه يمكن كتابة الاجرائين بطريقة عادية,
و لكن هناك مشكلة و هى الSafety او الThread Safety.
لنتخيل معا, بما ان خيط البحث يتم تنفيذه بشكل متوازى مع الخيط الرئيسى, ماذا لو قام خيط البحث باضافة ملف جديد الى قائمة الsearch_results فى الوقت نفسه الذى يقوم فيه الخيط الرئيسى بأخذ اسم الملف لفتح مثلا؟
فى هذه الحالة سوف يتوقف البرنامج برسالة Acess Violation, لا يمكن أبدا أن يستخدم المكون من خيطين فى الوقت نفسه.
لذا فاننا لن نستدعى الاجراء AddFile بطريقة عادية, بل سوف نستدعيه بطريقة Synchronize, أى اننا سوف نقوم بتنسيق عمله مع الخيط الرئيسى MainThread.
تقوم الدالة Synchronize بتنسيق عمل الاجراء داخل الخيط الرئيسى و ليس الخيط الفرعى الذى تنتمى اليه الدالة أو الMethod.
و استخدامها يكون كالتالى :

كود سي بلس بلس:

Synchronize(AddFile)

الحقيقة أن الدالة Synchronize لها بعض الحدود, فهى لا تستطيع سوى تنسيق اجراء void يمرر اليه متغيرات void كما يجب ان يكون الاجراء __fastcall.
__fastcall هى اضافة من السى++ بيلدر على لغة السى++ القياسية, و هى تقوم بتمرير المتغيرات داخل الregisters الخاصة بالمعالج و ليس من خلال الStack Memory.
يجب أن تستدعى جميع الطرق التى تعالج الأحداث بواسطة الاضافة __fastcall.
الأن لنبدأ بكتابة الطريقة FindFilesIn

كود سي بلس بلس:


void TSearchThread::FindFilesIn(const char *cur_dir_c)
{
WIN32_FIND_DATA find_data;
bool more_files=true;
HANDLE handle_f;
char search_dir[4000];
char *cur_dir;
bool real_folder;
char cmp_str[5];
char file_str[4000];
char fn_c[4000];
cur_dir=(char*)malloc(4000);
memset(cur_dir,'',4000);
strcpy(search_dir,"");
memset(search_dir,'',sizeof(search_dir));
strcpy(search_dir,cur_dir_c);
strcpy(cur_dir,cur_dir_c);
strcat(cur_dir,"\\*.*");

current_search_folder=cur_dir;
Synchronize(ShowSearchFolder);

handle_f=FindFirstFile(cur_dir,&find_data);

if(strcmp(find_data.cFileName,".")==0 || strcmp(find_data.cFileName,"..")==0)
real_folder=false;
else
real_folder=true;

if(find_data.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY && real_folder)
{
strcpy(search_dir,cur_dir_c);
memset(last_folder,'',4000);
strcpy(last_folder,search_dir);
strcat(search_dir,"\\");
strcat(search_dir,find_data.cFileName);
FindFilesIn(search_dir);

}

strcpy(file_str,find_data.cFileName);

if(stricmp(file_str,search_file.c_str())==0)
{

strcpy(fn_c,cur_dir_c);
strcat(fn_c,"\\");
strcat(fn_c,find_data.cFileName);
filename=fn_c;

Synchronize(AddFile);
}


while(more_files)
{
if(FindNextFile(handle_f,&find_data)==false)
more_files=false;



if(strcmp(find_data.cFileName,".")==0 || strcmp(find_data.cFileName,"..")==0)
real_folder=false;
else
real_folder=true;

if(find_data.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY && real_folder)
{
memset(last_folder,'',4000);
strcpy(search_dir,cur_dir_c);
strcpy(last_folder,search_dir);
strcat(search_dir,"\\");

strcat(search_dir,find_data.cFileName);
FindFilesIn(search_dir);

}

strcpy(file_str,find_data.cFileName);
cmp_str[0]=file_str[strlen(file_str)-3];
cmp_str[1]=file_str[strlen(file_str)-2];
cmp_str[2]=file_str[strlen(file_str)-1];
cmp_str[3]=NULL;

if(stricmp(file_str,search_file.c_str())==0)
{

strcpy(fn_c,cur_dir_c);
strcat(fn_c,"\\");
strcat(fn_c,find_data.cFileName);
filename=fn_c;

Synchronize(AddFile);

}



}

free(cur_dir);
FindClose(handle_f);
}

شرح الكود :
فى البداية نقوم بتزويد الفورم الرئيسى باسم المجلد الذى نبحث فيه حاليا من خلال استدعاء الدالة ShowSearchFolder :

كود سي بلس بلس:

            Synchronize(ShowSearchFolder);

يعتمد كود البجث على الAPI FindFirstFile و FindNextFile و هم دالتين الويندوز المختصين بعرض الملفات الموجودة(سواء كانت ملفات حقيقية أو مجلدات) فى مجلد معين,

كود سي بلس بلس:

            handle_f=FindFirstFile(cur_dir,&find_data);

يجب ان تبدأ عملية البحث عبر الدالة FindFirstFile و هى التى تقوم بارجاع الHandle الخاص بعملية البحث, و تقوم كذلك بارجاع اول ملف من المجلد المطلوب, ثم نقوم باستخدام الدالة الأخرى FindNextFile مستخدمين نفس الHandle الذى أنشأته الدالة الأولى.

كود سي بلس بلس:

                        if(FindNextFile(handle_f,&find_data)==false)

الدالة FindFirstFile تأخذ القيم cur_dir و هو المجلد مصحوبا برغبات البحث, فمثلا للبحث عن جميع الملفات فى مجلد الc: تكون قيمة cur_dir=”c:\\*.*”;
تقوم الدالة بوضع النتائج فى منشأه STRUCT من نوع خاص بالويندوز اسمها WIN32_FIND_DATA و هى التى قمنا بتعريفها فى بداية الاجراء.
و من خلال حلقة تكرارية مرهونة بارجاع الدالة FindNextFile القيمة false فى حالة انتهاء الملفات الموجودة فى المجلد.
بعد ان نستخلص اسم الملف و نوعه, نقوم باختباره, فقد يكون الملف حقيقى, أو قد يكون مجلد داخلى, و هنا يجب ان نبحث بداخله. و المجلد قد يكون مجلد حقيقى أو مجلد وهمى ذو النقطتين “..” أو النقطة “.” و هم النقطتين الذين يعرضهم الدوس عند تنفيذ امر Dir و التى تعنى الرجوع الى المجلد الأعلى و النقطة تعنى الرجوع الى المجلد الرئيسى Root.

نقوم اذا بمقارنة اسم الملف فاذا كان نقطة او نقطتين فان علينا تجاهله لأنه لا يمثل لنا شيئا :

كود سي بلس بلس:



if(strcmp(find_data.cFileName,".")==0 || strcmp(find_data.cFileName,"..")==0)
real_folder=false;
else
real_folder=true;

بعذ ذلك علينا أن نختبر الملف هل هو مجلد أم هو ملف حقيقى,
نقوم بمقارنته عبر القيمة الموجودة فى WIN32_FIND_DATA و هى dwFileAttributes. و فى حالة كون الملف هو مجلد تقوم قيمتها FILE_ATTRIBUTRE_DIRECTORY, و يجب كذلك أن تكون real_folder و هى القيمة التى عرفناها عندما قمنا بمقارنة اسم الملف بالنقطة و النقتطين.

كود سي بلس بلس:


if(find_data.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY && real_folder)
{
memset(last_folder,'',4000);
strcpy(search_dir,cur_dir_c);
strcpy(last_folder,search_dir);
strcat(search_dir,"\\");

strcat(search_dir,find_data.cFileName);
FindFilesIn(search_dir);

}

اذا ما كان الملف مجلد فرعى, و “حقيقي” علينا ان نبحث بداخله,
نقوم باضافة اسمه الى السلسلة الحرفية search_dir ثم نقوم باستدعاء الدالة من داخل نفسها مرة أخرى, للبحث فى المجلد الفرعى ثم العودة لاستكمال البحث فى المجلد الحالى, و هكذا دواليك, الى ان يتم البحث فى المجلدات كلها.
اذا كان الملف حقيقى فاننا نقارنه باسم الملف الذى نريد البحث عنه, فاذا كان متطابقا, نقوم باضفته الى القائمة search_results,

كود سي بلس بلس:

if(stricmp(file_str,search_file.c_str())==0)
{
strcpy(fn_c,cur_dir_c);
strcat(fn_c,"\\");
strcat(fn_c,find_data.cFileName);
filename=fn_c;
Synchronize(AddFile);
}

طبعا يتم ذلك من خلال استخدام Synchronize كما اسلفنا حتى ننسق عمله مع الخيط الرئيسى.
تقوم الحلقة while بعمل التكرار طالما كان هناك ملفات, و ينتهى عملها مع ارجاع دالة الFindNextFile القيمة false و التى تعنى أن عملية البحث قد انتهت و لا ملفات جديدة, هنا نخرج من الحلقة و نقوم باغلاق الhandle الخاص بالبحث و تحرير الذاكرة من المؤشر cur_dir.

كود سي بلس بلس:



free(cur_dir);
FindClose(handle_f);]

تحياتى لكم هذا هو الجزء الأول من المقال, و نكمله بعد قليل ان شاء الله.
أرجو عدم اضافة أى رد الا بعد اكتمال الموضوع حتى نحافظ على التسلسل.

 

الأن بقى لنا ان نقوم بمعالجة الفورم الرئيسى و الذى سوف يقوم بمهمتين اساسيتين :
– الأولى تشغيل و ايقاف الخيط الفرعى TSearchThread لبدء و ايقاف عملية البحث.
– الثانية هى فتح الملف الذى وجدناه سواء تشغيله أو فتح المجلد الذى ينتمى اليه فى صفحة الWindows Explorer.
نضيف فى بداية الملف init2.h الخاص بالفورم الرئيسى عبارة
#include “search_thread_2.h”
و نقوم بتعريف مؤشر TSearchThread لاستخدامه فى البحث بعد عبارة Tinit *init,

كود سي بلس بلس:

TSearchThread *now_search;

نضغط على زر Search! مرتين لتوليد الحدث Button1Click
و نكتبه كالتالى :

كود سي بلس بلس:


void __fastcall Tinit::Button1Click(TObject *Sender)
{
now_search=new TSearchThread(this);

search_results->Items->Clear();
now_search->search_dr=drive->Drive;
now_search->search_file=search_file->Text;
now_search->Resume();
}now_search=new TSearchThread(this);

شرح الكود :
نعرف خيط بحث جديد عبر عبارة new, ثم نقوم بتنظيف قائمة الsearch_resutls
نقوم بتغذية الخيط now_search الذى حجزناه بقيم البحث و هى الdrive و الsearch_file
الdrive نقوم باستخلاصه من قائمة الTDriveComboBox من خلال الصفة Drive من نوع char.
و بالطريقة نفسها نقوم باستخلاص اسم الملف الذى نريد البحث عنه من الTEditLabel search_file.
الأن لبدء تشغيل الخيط now_search, نعطيه الأمر Resume().
لايقافة نعطيه الأمر Suspend().
الأن نعالج أمر ايقاف البحث,
نقوم بالضغط مرتين على الزر Stop! لتوليد الحدث Button2Click

كود سي بلس بلس:


void __fastcall Tinit::Button2Click(TObject *Sender)
{
try{
now_search->Suspend();
status->SimpleText="Search Stopped!";
}
catch(...) {}
}

كما نرى, قمنا باستخدام الأمر Suspend() لايقاف عملية البحث عبر ايقاف الخيط now_search.
و نكتب فى الstatus ان البحث توقف.
لاحظ اننا وضعنا الأمر وسط try{} و ذلك تحسبا لأى خطأ قد يحدث (مثل توقف الThread لأى سبب أخر فى هذه الحالة لا يمكن ايقافه مرة اخرى).
الأن نقوم باضافة PopupMenu خاصة بالseach_results و تحتوى على عنصرين
Open
Explore
الأول يقوم بتشغيل الملف (سواء كان ملف تنفيذى أو صورة او مستند).
الثانى يقوم بفتح المجلد الموجود به الملف.

ثم نقوم باضافة القائمة الى الsearch_results عبر تغيير الخاصية PopupMenu و اختيار
PopupMenu1
سوف يقوم البرنامج بفتح الملف فى حالة الضغط عليه مرتين أو من خلال اختيار open.
اضغط على TListBox search_resutls و من الObject Inspector اختر الحدث DblClick
و بعد توليد الحدث نعالجة كالتالى :

كود سي بلس بلس:

void __fastcall Tinit::search_resultsDblClick(TObject *Sender)
{
AnsiString fn,file_path;
fn=search_results->Items->Strings[search_results->ItemIndex].c_str();
file_path=ExtractFilePath(fn);
fn=ExtractFileName(fn);
chdir(file_path.c_str());
// MessageBox(this->Handle,fn.c_str(),file_path.c_str(),NULL);
ShellExecute(NULL,"open",fn.c_str(),NULL,file_path.c_str(),SW_SHOW);
}

سوف نستخدم الAPI ShellExecute و هى دالة الويندوز الخاصة بفتح الملفات الخارجية, و هى تأخذ القيم الأتية:
الأولى هى الhandle الخاصة بالبرنامج الرئيسى, لا داعى لها هنا لذا سوف نجعلها NULL.
القيمة الثانية هى سلسلة حرفية تحوى أمر الفتح, و قد تكون open او explore او print,
Open لفتح الملف و تشغيله
Explore لفتح المجلد الموجود به الملف و هو ما سنستخدمه لاحقا.
Print لطبع الملف (طبعا اذا ما كان صالحا للطباعة).
القيمة الثالثة هى اسم الملف فقط دون الPath اى مثلا readme.txt.
الثالثة هى سلسلة حرفية بها البارامترز التى سوف تمرر الى الملف, لا داعى لها هنا بالطبع.
الرابعة هى سلسلة حرفية تمثل الPath للملف, أى المجلد الذى يحتوى الملف.
الخامسة هى قيمة int تمثل فتح الملف فى شكل كبير او عادى او Minimized, قد تكون SW_SHOW أو SW_MINIMIZE أو SW_MAXIMIZE. سوف نجعلها هنا SW_SHOW لفتح الملف بصورته الافتراضية.
كما نعرف فاننا نحصل على اسم الملف كاملا و الPath, و لكننا يجب ان نمرره فى شكل مجزء الى دالة الويند
وز ShellExecute, لذا فان مكتبات الVCL تزودنا بدالتين غاية فى الروعة تحسبا لمثل هذه الأمور,
ExtractFilePath و هى تقوم باستخراج المسار للملف
فمثلا


CODE


c:\\C++ Builder Projects\SearchFileProject\readme.txt



سوف ترجع لنا


CODE


c:\\C++ Builder Projects\SearchFileProject\



الدالة المقابلة هى ExtractFileName, كما خمنت سوف ترجع لنا اسم الملف وحده ففى الحالة السابقة سوف تعطينا :
Readme.txt
الأن بقى امرا سهلا, و هو معالجة الحدث Explore.
نفتح الPopupMenu و نضغط مرتين على Explore, لتوليد الحدث و نكتبه كالتالى :

كود سي بلس بلس:


void __fastcall Tinit::Explore1Click(TObject *Sender)
{
AnsiString fn,file_path;
fn=search_results->Items->Strings[search_results->ItemIndex].c_str();
file_path=ExtractFilePath(fn);
fn=ExtractFileName(fn);
chdir(file_path.c_str());
// MessageBox(this->Handle,fn.c_str(),file_path.c_str(),NULL);
ShellExecute(NULL,"explore",NULL,NULL,file_path.c_str(),SW_SHOW);
}

كل ما فعلناه هو تغيير الأمر الذى مررناه الى shellExecute من open الى explore.
الأن نعالج الحدث Open من قائمة الPopupMenu لنستدعى الحدث الذى ولدناه قبل ذلك search_resultsDblClick

كود سي بلس بلس:


void __fastcall Tinit::Open1Click(TObject *Sender)
{
search_resultsDblClick(Sender);
}

تحياتى لكم و أرجو ان يكون الموضوع قد نال اعجابكم.
ملفات المشروع مرفقة و الملف التنفيذى مرفق.
المشروع تمت كتابته على C++ Builder 2007 و يمكن ترجمته على اى نسخة اعلى.
يمكنكم الأن اضافة الردود.
و اللى اللقاء مع الجزء القادم.

مقدمة فى السى++ بيلدر الجزء الخامس

 

  1. فى الدرس الماضى تحدثنا عن المكونات الشائعة المعتادة و السهلة الاستخدام.
    فى هذا الدرس سوف نستكمل تعلم استخدام المكونات الشائعة فى السى++ بيلدر و لكننا سوف نتعرض لمكونات اكثر تعقيدا فى التعامل معها :
    الصفوف التى سوف نتعامل معها هى :
    TTreeView : أداة عرض البيانات بطريقة الشجرة.
    TListView : أداة القوائم المتقدمة.
    TCheckListBox : قائمة مربعا الCheck
    TOpenDialog : مربع حوار فتح ملف.
    TSaveDialog : مربع حوار تسجيل ملف.
    TProgressBar : أداة عرض التقدم.
    TTrackBar : المنزلق.
    TToolBar : شريط أزرار الوصول السريع.
    TImageList : حافظة الصور.
    ابدأ مشروع جديد من النوع العادى VCL Forms Application.
    كما تعلمنا فى الدرس الماضى, قم بتغيير اسم المشروع الى Controls2.
    و صمم الفورم كما فى الصورة :

    فى اقصى اليسار ضع القائمة الشجرية TtreeView و اعطها اسم Name= tree للتسهيل.
    فى المنتصف قائمة TlistView و اعطها الاسم listview للتسهيل.
    فى اليمين قائمة TcheckListBox و اعطها الاسم check_list للتسهيل.
    أضف بعد ذلك حافظة الصور TimageList.
    حافظة الصور تقوم بتخزين عدة صور من اى نوع Ico,bmp,jpg داخل الResource الخاصة بالمشروع, و يمكن ربطها بسهولة مع المكونات المختلفة الموجودة فى المشروع,و بالتالى فانها توفر جهدا كبيرا كما أنها ضرورية فى حالة الToolBar.
    لاضافة صور داخل الحافظة اسحبها الى الفورم الرئيسى, ثم اضغط على الIcon الخاصة بها مرتينDblClick.
    سوف يظهر لك المربع التالي:

    لاضافة الصور اضغط على زر Add و اختر الصور المراد اضافتها.
    كل صورة سوف تأخذ رقم يعبر عنها, و سوف نرى بعد ذلك كيفية استخدام هذه المجموعة من الصور.
    قم باضافة مجموعة صور بسيطة تعبر عن الNew و الOpen و الSave. ثم اضغط OK.
    الأن حان الوقت لربط عنصر الlistview مع قائمة الصور.
    من الObject Inspector غير الصفة LargeImages الموجودة فى Linkage الى ImageList1.
    قم باضافة بقية المكونات Ttoolbar , TtrackBar, TprogressBar كما فى الشكل.
    بنفس الطريقة, قم بربط الToolBar1 مع الImageList1 عبر تغيير الصفة Images الى ImageList1
    لاضافة زر داخل الToolBar يكفى الضغط بزر الفأرة الأيمن عليها ثم اختر New Button.
    سوف يتخذ الButton الجديد تلقائيا الصورة التالية فى الImageList. فاذا كنت ترغب بتغيير الصورة الخاصة بهذا الزر, قم بتغيير الصفة ImageIndex الى الرقم المطلوب, حيث يعبر الImageIndex عن رقم الصورة داخل الImageIndex.
    الأن أصبحنا مستعدين لكتابة الكود الخاص بالبرنامج.
    نحتاج أن يبدأ البرنامج بحشو القوائم بقيم افتراضية للاختيار منها.
    أنسب طريقة لذلك هو التعامل مع الحدث FormShow حيث عند ظهور الفورم الرئيسى, يقوم البرنامج بحشو القوائم.
    فى البداية سوف نكتب الاجراءات الخاصة بملأ القوائم :
    فى الملف Unit1.h أضف السطر التالى تحت الpublic:
    void FillTree();
    لتعريف الطريقة Method FillTree()
    اذهب الى Unit1.cpp و اكتب الاجراء FillTree كما الاتى :

    كود سي بلس بلس:


    void TForm1::FillTree()
    {
    TTreeNode *node_1;
    TTreeNode *node_2;

    tree->Items->Add(NULL,"Node1");

    node_1 = tree->Items->Item[0];
    tree->Items->AddChild(node_1,"CHILD1");
    node_2=tree->Items->Item[1];

    tree->Items->AddChild(node_2,"Item1");
    tree->Items->AddChild(node_2,"Item2");
    node_2=tree->Items->Item[tree->Items->Count-1];
    tree->Items->AddChild(node_2,"Child Item1");
    tree->Items->AddChild(node_2,"Child Item2");

    }

    الشرح:
    لاضافة عناصر للشجرة يجب استخدام الصف TtreeNode.
    يمثل الصف TtreeNode نقطة داخل الشجرة و التى يمكن اضافة عناصر تحت تبويبها عبر الطريقة AddChild. و يمكننا اعادة استخدام الTreeNode لاضافة عناصر اخرى داخل قائمة الشجرة.
    سوف نبدأ باضافة العنصر “الفرع” الأم Node1 عبر tree->Items->Add(NULL,”Node1″);
    و من ثم سوف نضيف فروع للNode1
    أول فرع نسميه Child1. لاضافة هذا الفرع يجب جلب الفرع الام Node1 الى الTreeNode الذى عرفناه node_1
    node_1 = tree->Items->Item[0];
    حيث الItem[0] هو بالطبع الNode1.
    و من ثم نضيف الفروع تحت التبويب Node1 عبر

    كود سي بلس بلس:

    tree->Items->AddChild(node_1,"CHILD1");

    و بنفس الطريقة نعرف node_2 على انه العنصر الثانى فى القائمة الشجرة الا و هو CHILD1

    كود سي بلس بلس:

    node_2=tree->Items->Item[1];

    و بنفس الطريقة نضيف له الفروع Item1 , Item2.
    و لاضافة عناصر متفرعة من Item2 سوف نقوم باعادة استخدام الnode_2
    node_2=tree->Items->Item[tree->Items->Count-1];
    لاحظ اننا هذه المرة قمنا بجلب رقم الItem2 عبر Count-1 بدلا من تتبع رقمه.
    و باستخدام نفس الطريقة AddChild نقوم باضافة فرعين اضافيين تحت التبويب Item2 هما Child Item1 , Child Item2

    كود سي بلس بلس:

    tree->Items->AddChild(node_2,"Child Item1");
    tree->Items->AddChild(node_2,"Child Item2");

    لاحظ أن عناصر القائمة الشجرية تعامل كأنها قائمة عادية يتم الوصول لأى عنصر بداخلها عبر Items بصرف النظر عن كون العنصر رئيسى أو فرعى.
    لمعرفة ما اذا كان العنصر الحالى يحتوى على Node اى ان تحته عناصر اخرى يمكن استخدام الطريقة HasChildren التى ترجع true فى حالة كونه Node و false فى حالة كونه عنصر فرعى ليس تحته عناصر اخرى.
    مثال :

    كود سي بلس بلس:

    tree->Items->Item[0]->HasChildren

    الأن ننتقل الى الخطوة التالية و هى ملأ الListView
    سوف تجد ان الطريقة مشابهة لعنصر الTTreeView و لكننا سوف نعتمد هذه المرة على الصف TlistItem بدلا من الTTreeNode
    نضيف تعريف الMethod FillListView()
    فى الملف Unit1.h نضيف السطر :

    كود سي بلس بلس:

         void FillListView();

و فى الملف Unit1.cpp نضيف التالى :

كود سي بلس بلس:


void TForm1::FillListView()
{
TListItem *pList;

pList=listview->Items->Add();
pList->Caption="ListItem1";
pList->ImageIndex=0;

pList=listview->Items->Add();
pList->Caption="ListItem2";
pList->ImageIndex=1;

pList=listview->Items->Add();
pList->Caption="ListItem3";
pList->ImageIndex=2;

}

الشرح :
نقوم بتعريف pList من الصف TlistView ثم نقوم بتهيئته من المكون listview على انه Item جديد تمت اضافته الى المكون.
نعطيه عنوانا ListItem1 عبر العبارة :

كود سي بلس بلس:

pList->Caption="ListItem1";

ثم نقوم بتخصيص الصورة عبر :

كود سي بلس بلس:

pList->ImageIndex=0;

و بنفس الطريقة نقوم باعادة استخدام الpList لاضافة ListItem2 و ListItem3.
بقى ان نقوم بملأ الTCheckListBox.
فى الملف Unit1.h.

كود سي بلس بلس:

void FillCheckList();

و فى Unit1.cpp

كود سي بلس بلس:


void TForm1::FillCheckList()
{
check_list->Items->Append("Check 1");
check_list->Items->Append("Check 2");
check_list->Items->Append("Check 3");
check_list->Items->Append("Check 4");
}

كما رأينا فان اضافة سطور الى الTCheckListBox مطابق لطريقة اضافته فى الTListBox.
و هى عبارة عن قائمة و لكن من الCheckBoxes و نقوم منها بمعرفة الChecks التى قام المستخدم باختيارها.
الأن بقى استدعاء الدوال التى كتبناها عند بداية ظهور الفورم الرئيسى.
من الEvents الخاص بالForm1
قم بتوليد الحدث FormShow :

كود سي بلس بلس:


void __fastcall TForm1::FormShow(TObject *Sender)
{
FillTree();
FillListView();
FillCheckList();
}

الخطوة التالية هى تعلم كيفية استخراج المعلومات من المكونات التى استخدمناها.
نبدأ بأسهل مكون و هو الTTrackBar التى اضفناها و معها سوف نعرف استخدام الTProgressBar.
نقوم بتوليد الحدث OnChange للTTrackBar من الEvents الخاصة بها حيث يقوم بالتنفيذ عند تغيير موقع الTrackBar.

كود سي بلس بلس:


void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
ProgressBar1->Position=TrackBar1->Position;
}

نقوم بتحريك الProgressBar من خلال الصفة Position و مساواتها مع قيمة الPosition للTrackBar.حيث يعتبر كل منهم عكس الأخر فواحد يقرأ التغير من المستخدم و الأخر يقوم بعرض القيمة المخصصة اليه.
الأن لنأتى الى الTTreeView و قراءة اختيار المستخدم لاحدى عناصرها.
سوف نختار الحدث OnDblClick الذى يتولد عند الضغط مرتين على احدى عناصر الشجرة.

كود سي بلس بلس:



void __fastcall TForm1::treeDblClick(TObject *Sender)
{

MessageBox(this->Handle,tree->Selected->Text.c_str(),"Controls2",NULL);

}

كما نرى ان استدعاء عنوان العنصر الذى اخترناه سهل من خلال Selected حيث ترجع لنا الSelected العنصر TtreeNode الذى تم اختياره.
و لكننا سوف نواجه مشكلة بسيطة.
اذا ما ضغط المستخدم على احدى الفروع الرئيسية بهدف فتح العناصر الموجودة تحته سوف يظهر له البرنامج اسم الشجرة الرئيسية, ليس هذا هو المطلوب مع معظم التطبيقات حيث تكون الفروع الرئيسية هى الCategories التى تقسم العناصر الى عدة ابواب لتسهيل الاختيار, انما يتم الاختيار فقط من الفروع النهائية و لا يمكن الاختيار من الفروع الرئيسية.
الحل بسيط و هو اختبار العنصر الذى اختاره المستخدم عبر العبارة HasChildren و التى ترجع قيمة صحيحة true اذا ما كان رئيسى و false فى حالة اذا ما كان فرع نهائى.
يصبح الكود كالتالى :

كود سي بلس بلس:


void __fastcall TForm1::treeDblClick(TObject *Sender)
{
if(!tree->Selected->HasChildren)
MessageBox(this->Handle,tree->Selected->Text.c_str(),"Controls2",NULL);

}

الأن نعالج احداث الlistview.
نولد الحدث OnDblClick ايضا و نكتبه كالتالى :

كود سي بلس بلس:


void __fastcall TForm1::listviewDblClick(TObject *Sender)
{
MessageBox(this->Handle,listview->Items->Item[listview->ItemIndex]->Caption.c_str(),"Control2",NULL);

}

استخدمنا هنا المصفوفة Items و التى تحتوى على الTListItem الموجودة فى القائمة, و نقوم بالاشارة الى العنصر المختار عبر الصفة ItemIndex, و نقوم بأخذ عنوانه من الصفة Caption و من ثم اظهارها فى الMessageBox.
الأن نقوم بمعالجة حدث الزر البسيط Submit و الذى سوف يقوم باظهار العناصر المختارة من الTCheckListBox.
نضغط على الزر لتوليد الحدث OnClick.

كود سي بلس بلس:


void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString str;
str="The following items are checked :\n";

for(int i=0;i<check_list->Items->Count;i++)
{
if(check_list->Checked[i])
str=str+check_list->Items->Strings[i]+"\n";
}

MessageBox(this->Handle,str.c_str(),"Control2",NULL);
}

نقوم بعمل حلقة تكرارية لاختبار عناصر القائمة التى اختارها المستخدم و نقوم باختبار كل عنصر على حدة عبر الصفة Checked[], فاذا ما كانت true نقوم باضافة اسمه الى الAnsiString str و بعدها نقوم بعرض الحرفى الناتج فى MessageBox.
الأن جاء دور معالجة الToolBar و التى سوف نتعلم من خلالها ايضا استخدام مربع فتح الملفات الشائع.
نضغط مرتين على الزر الذى يحمل ايقونة ملف جديد New File و نكتب المعالجة كالتالى :

كود سي بلس بلس:


void __fastcall TForm1::ToolButton1Click(TObject *Sender)
{
MessageBox(this->Handle,"New File!","Control2",NULL);
}


الأن نعالج حدث الضغط على الزر Open.

كود سي بلس بلس:



void __fastcall TForm1::ToolButton2Click(TObject *Sender)
{
TOpenDialog *open_dlg=new TOpenDialog(this);

open_dlg->Filter="Text Files (*.txt) |*.txt";

if(open_dlg->Execute())
MessageBox(this->Handle,open_dlg->FileName.c_str(),"Open!",NULL);

delete open_dlg;
}

هنا استخدمنا الصف TopenDialog و الذى يمثل مربع فتح الملف الشائع,
نعرف الFilter للملفات كالتالى حيث يمثل الFilter ما بعد العلامة | :
open_dlg->Filter=”Text Files (*.txt) |*.txt”;
نظهر اسم الملف الذى اختاره المستخدم فى الرسالة MessageBox
يتم اظهار المربع الحوارى من خلال Execute() و الذى يرجع القيمة true فى حالة قيام المستخدم بفتح الملف بالفعل, أما فى حالة عدم اختيار ملف أى اختيار Cancel فسوف يرجع القيمة false.
لمعالجة حدث الضغط على زر الحفظ Save فى الToolBar, نستخدم نفس الطريقة مع الOpen, و لكن من الصف TsaveDialog حيث يمثل مربع الحفظ الشائع.

كود سي بلس بلس:


void __fastcall TForm1::ToolButton3Click(TObject *Sender)
{
TSaveDialog *save_dlg=new TSaveDialog(this);

save_dlg->Filter="Text Files (*.txt) |*.txt";

if(save_dlg->Execute())
MessageBox(this->Handle,save_dlg->FileName.c_str(),"Save!",NULL);

delete save_dlg;
}

الفارق ان مربع الSave لا يقوم فيه المستخدم باختيار ملف معين لفتحه, بل يقوم باختيار موقع و كتابة اسم الملف و الضغط على زر Save.
بذلك نكون قد انهينا الجزء الخامس من دروس السى++ بيلدر.
ان شاء الله فى الدرس القادم سوف نتحدث عن معالجة الThreads فى السى++ بيلدر و التى اصبحت احدى ضروريات البرمجة الحديثة.
تنويه : ملفات المشروع مرفقة فى الملف Controls2.rar

مقدمة فى السى++ بيلدر الجزء الرابع

  1. هذه المرة سوف يكون الدرس سهلا و ممتعا, فهذا الدرس سوف يقدم طرق استخدام مكونات الVCL.
    تتمتع مكونات الVCL بسهولة بالغة لدى استخدامها, فقد صممت طرق استدعاء الدوال و قراءة و كتابة الصفات بحيث تصبح سهلة التخمين, يمكن لأى مبرمج أن يستخدم مكون جديد عليه دونما حتى النظر فى ملف المساعدة المرفق.استخدام المكونات بكفاءة هو احدى اهم مهارات المبرمج فى العصر الحالى, دعونا نكون واقعيين, الواجهة الأنيقة و المتجاوبة تعطى ايحاء بالاحترافية, فكم من برامج سيئة التصميم و لا تؤدى عملها بكفاءة و لكنها رغم ذلك تنال الاعجاب لأن واجهتها انيقة و سهلة الاستخدام, فى المقابل فان هناك برامج اعتمادية و جيدة التصميم و لكن المبرمج نسى ان يعتنى بالواجهة, ففشلت.
    الواجهة المحترفة تعطى انطباعا جيدا للمستخدم, و الحقيقة ان المبرمج القادر على انشاء واجهة انيقة و عصرية هو ذلك القادر على كتابة كود جيد فى الغالب.استخدام المكونات القياسية بكامل قدراتها سوف يوفر للمستخدم بيئة سهلة للتعامل مع برنامجك, و سهولة الاستخدام تعنى الكثير فى عالم البرمجة. بل ان معظم اصحاب العمل يقيمون البرامج من خلال واجهتها الرئيسية, فان اعجبته اعجبه البرنامج.
    يوفر لنا السى بيلدر طريقة سهلة لانشاء واجهة محترفة و انيقة فى وقت قليل نسبيا,فى هذا الدرس و فى الدرس القادم سوف نتعلم كيفية استخدام ادوات السى بيلدر لانشاء واجهة جذابة و سهلة, سوف نبدأ بالأدوات البسيطة الشائعة فى الويندوز, ثم سوف نرى فى الجزء التالى استخدام المكونات الأكثر تقدما.
    هذه المرة سوف نتعلم استخدام المكونات الاتية :
    TGroupBox : مربع تجميع المكونات
    Frame.TEdit : مربع الكتابة العادى ذو السطر الواحد.
    TBitBtn: زر التحكم المتقدم القادر على عرض الصور.
    TMemo : مربع الكتابة متعدد السطور.
    TSplitter : الفاصل المتحرك.
    TPageControl : مكون الصفحات المتعددة.TAnimate
    : مكون عرض الفيديو.
    TSpeedButton : الزر السريع القادر على اخذ الشكل المسطح.
    TListBox : مربع القائمة.
    TRadioGroup: مربع تجميع الRadio buttons
    .TRadioButton : زر الاختيار.
    TButton : الزر البسيط.
    TCheckBox: مربع الCheck.
    TStringGrid: جدول السلاسل الحرفية Grid.
    سوف ننشىء مشروعا جديدا باسم Controls1.
    فى صفحة الخصائص للفورم Form1 Object Inspector قم بتغيير الخاصية Caption و التى تعنى عنوان الفورم (الذى يظهر فى الشريط الازرق) الى VCL Controls.
    قبل وضع المكونات, سوف نقوم بتغيير خصائص مشروعنا ليبدو اكثر خصوصية و احترافية.
    قم باختيار Project ثم Options.قم باختيار صفحة الApplication و غير الTitle الى VCL Controls.تغيير ايقونة المشروع قم بالضغط على الزر Load Icon و اختر الايقونة المناسبة.
    اضغط OK لنبدأ فى اعداد المشروع.
    سوف يحتوى المشروع على قسمين, القسم الاول سوف يحتوى على مربع الMemo و مربع الEdit, أما القسم الثانى فسوف يحتوى على عدة صفحات باستخدام TPageControl. و سوف يمكننا التحكم فى حجم كل قسم تماما كفكرة الFrames فى صفحات الويب.
    ملحوظة :ماذا اذا اردت ان يتمتع مشروعك بشكل و احساس الWindows XP او الWindows Vista؟اذا اردت ذلك فيمكنك اضافة المكون TXPManifest فقط الى الفورم الرئيسى, سوف يقوم السى بيلدر تلقائيا باضافة ملف الManifest الى مشروعك. اذا لم تضيفه فسوف تظهر المكونات بالشكل التقليدى للويندوز 2000.
    قم باضافة TGroupBox فى الناحية اليسرى من الفورم, و غير عنوانه الى Editing, و غير الصفة align الى القيمة alLeft. اى حاذى الى اليسار. طبعا ذلك يتم من مربع الObject Inspector.
    أضف الTedit و الTMemo و الTBitBtn كما فى الشكل.
    بالنسبة للزر BitBtn1 قم بكتابة العنوان OK, لوضع صورة بجانب العنوان, قم بتغيير باختيارها من خلال الصفة Glyph و اختر منها الملف الذى سوف يحتوى على الصورة (يجب ان يكون من نوع Bitmap).
    لجعل الزر يعمل مع ضغط المفتاح enter و يظهر مظللا Default, قم بتغيير القيمة Default الى true.
    ملحوظة :القيمة Cancel تجعل الزر يعمل مع مفتاح الESC. لو كان الزر موجود فى مربع حوارى فانه يعمل كزر الOK فى حالة الDefault و زر الCancel فى حالة الESC.

    لتفريغ المربع Memo1 من محتوياته, قم بتغيير الصفة Lines و فتح الTStrings و تفريغها. حيث يعمل مربع الTMemo عن طريق المتغير Lines من نوع TStringList اى فى شكل قائمة.كما يمكن التعامل معه بشكل عادى من خلال الصفة Text من نوع AnsiString. و سوف يقوم الTMemo بتقسيم الText الى عدة سطور.لنظهر طريقة التعامل مع الاثنين, عند الضغط على مفتاح OK سوف يقوم البرنامج باضافة النص المكتوب فى الTEdit1 الى الTMemo1 فى سطر جديد.قم بالضغط مرتين على الزر TBitBtn1 لتوليد الحدث BitBtn1Click

    كود سي بلس بلس:



    void __fastcall TForm1::BitBtn1Click(TObject *Sender)
    {
    Memo1->Lines->Append(Edit1->Text);
    }

    ]
    سوف يقوم هذا الكود باضافة سطر جديد عبر طريقة Append من النص المكتوب فى Edit1.الان جرب البرنامج اكتب اى نص فى EditBox ثم اضغط على OK.الأن حان الوقت لتصميم القسم الثانى من الفورم.قم باضافة خط التقسيم المتحرك TSplitter و حاذى الى اليسار عن طريق الصفة Align=alLeft.
    الأن ضع المكون TPageControl و ضع الalign=alClient اى حاذى الى المكون الأب.قم بتجربة المشروع و تحريك الSplitter و سوف تجد ان المكون TGroupBox و TPageControl يغيرون احجامهم طبقا لحركة الSplitter.الأن قم باضافة الصفحات الأتية الى TPageControl عبر الضغط عليه Right Click ثم اختر New Page :أضف صفحات بالعناوين الأتية :VideoListBoxRadio ButtonCheck BoxString Gridفى الصفحة الاولى اضف TAnimate و زرين من نوع TSpeedButton . لجعل الTSpeedButton يبدو مسطحا غير الصفة Flat الى true و اختر صورة مناسبة من خلال الGlyph احداهن تعبر عن تشغيل الفيديو و الاخرى ايقاف الفيديو.قم باختيار اى ملف فيديو ليقوم الTAnimate بعرضه من خلال الصفة FileName و اختر بعدها الملف المناسب.الأن قم بمعالجة الحدثين لزرين الTSpeedButton.

    كود سي بلس بلس:


    void __fastcall TForm1::SpeedButton1Click(TObject *Sender){ Animate1->Play(0,Animate1->FrameCount,NULL);
    }//---------------------------------------------------------------------------
    void __fastcall TForm1::SpeedButton2Click(TObject *Sender){ Animate1->Stop();
    }

    ]
    كما نرى يعمل الTanimate من خلال الطريقة Play, و نمرر له ثلاثة متغيرات من نوع Int الاول يعبر عن الFrame البداية فى ملف الفيديو 0, و الثانى Frame النهاية و لمعرفة الصورة الاخيرة فى الملف نستخدم التابع FrameCount (يتكون ملف الفيديو من عدة صور متتابعة) , اما الثالث فهو يعبر عن عدد مرات التكرار قبل التوقف, بما اننا سوف نوقف تشغيل الفيديو بأنفسنا فسوف نعطيه القيمة NULL حيث سوف يظل الفيديو يعرض الى ان نضغط على زر التوقف.
    فى معالجة زر التوقف سوف نقوم فقط باعطاء الأمر Stop الى مكون الفيديو. فى الصفحة التالية سوف نتعرض لاستخدام الListBox, نضيف عنصر القائمة ListBox1 و تحته زرين من النوع البسيط Tbutton الاول عنوانه Add Item و الثانى Delete Item. عند الضغط مرتين على اى عنصر فى القائمة سوف يتم عرضه فى رسالةMessageBox نضيف المكونات فى الصفحة الثانية كما فى الصورة.
    نعالج الحدث لدى الضغط على زر الاضافة:

    كود سي بلس بلس:



    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    AnsiString str;
    str=InputBox("Controls","Enter New Item","Item");
    ListBox1->Items->Append(str);
    }

    ]
    هنا استخدمنا مربع الادخال InputBox و الذى يتيح للمستخدم ادخال نص عبر Dialog الادخال القياسى.يأخذ مربع الادخال ثلاثة متغيرات من فئة الAnsiString, الأول هو العنوان, الثانى هو الرسالة التى تظهر للمستخدم, الثالث هو الكلمة الافتراضية التى تظهر فى المربع و سوف نجعلها Item.
    فى العبارة التالية قمنا باضافة ما كتبه المستخدم الى مكون القائمة عبر استخدام الStringList الخاص به Items و الطريقة Append.
    حديث حول الTStringList:يعتبر صف الTStringList هو احدى اهم صفوف الVCL و اكثرها شيوعا و استخداما, نظرا لما يوفره من سهولة فى التعامل, الصف TstringList هو عبارة عن مصفوفة متتالية من الAnsiString, يمكن اضافة عنصر للمصفوفة من خلال Append(AnsiString) , كما يمكن حذف عنصر معين عبر Delete(int index(, و كذلك يمكن حذف المصفوفة كلها عبر Clear() , يمكننا كذلك الوصول لأى عنصر بداخل المصفوفة عبر Strings[int index], سعة المصفوفة (عدد عناصرها) يمكن معرفته من خلال المتغير int Count.تستخدم كل من مكونات الListBox و الMemo و كذلك الStringGrid صف الTStringList, و بالتالى ان اتقان التعامل معه يمكننا من استخدام هذه المكونات بكفاءة.الحدث التالى سوف نتعرض لحذف عنصر من المصفوفة, و معالجته كالتالى :

    كود سي بلس بلس:



    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
    if(ListBox1->ItemIndex>-1)ListBox1->Items->Delete(ListBox1->ItemIndex);
    }

    ]
    بكل بساطة قمنا بحذف العنصر عبر استخدام العبارة Delete, و للوصول الى العنصر المطلوب, استخدمنا ItemIndex الذى يحمل رقم العنصر الذى اختاره المستخدم.الأن علينا ان نعالج حدث الDblClick لدى الListBox1, سوف نختار الListBox, و من الObject Inspector نفتح صفحة معالجة الاحداث Events , و نختار الحدث OnDblClick,

    كود سي بلس بلس:



    void __fastcall TForm1::ListBox1DblClick(TObject *Sender)
    {MessageBox(this->Handle,ListBox1->Items->Strings[ListBox1->ItemIndex].c_str(),"VCL Controls",NULL);
    }

    سوف يقوم البرنامج بعرض العنصر المشار اليه فى رسالة ويندوز قياسية MessageBox.
    للوصول الى العنصر, يمكننا استخدام طريقة GetText و هى ابسط, أو يمكن الوصول اليه بالطريقة الصعبة كما فعلنا, فقمنا بفتح المصفوفة Strings من الTStringList الخاص بالقائمة و الوصول اليه عبر ItemIndex.
    الصفحة الثالثة : Radio Buttons
    عادة ما يتم تجميع ازرار الاختيار لأمر معين فى مجموعة واحدة, و لا يمكن اختيار أكثر من زر اختيار واحد من هؤلاء الذين ينتمون لنافذة أب واحدة (سواء كانت هذه النافذة فورم او كانت Group Box).
    نضع مكون من نوع TRadioGroup و هو يشبه الTGroupBox و لكنه مختص بتجميع ازرار الاختيار.
    نضع بداخله ثلاثة اختيارات و لتكن بيئة تطوير :C++ Builder, Visual c++ , Devcpp
    و سوف نغير الName الخاص بكل واحد لتمييزهم :
    الاول نسميه cbuilder
    الثانى visual_c
    الثالث dev_cpp
    نصمم الصفحة بهذا الشكل :

    كل ما علينا الان معالجة الحدث Submit.
    قبل المعالجة يجب ان نعرف ان زر الاختيار يحمل قيمه bool اى True او False, هذه القيمة توجد فى الصفة Checked.
    نضيف الكود بالشكل التالى :

    كود سي بلس بلس:

    void __fastcall TForm1::SubmitClick(TObject *Sender)
    {
    if(cbuilder->Checked)
    MessageBox(this->Handle,"C++ Builder choosen.\nThe fastest way to write professional c++ windows applications","Controls",MB_ICONINFORMATION);

    if(visual_c->Checked)
    MessageBox(this->Handle,"Visual C++ choosen.\nReliable, but it seems you have a lot of time!","Controls",MB_ICONINFORMATION);

    if(dev_cpp->Checked)
    MessageBox(this->Handle,"Dev C++ choosen.\nIt's free, that's only what we can say!","Controls",MB_ICONINFORMATION);
    }

كود سي بلس بلس:


[RIGHT][LEFT][/LEFT][/RIGHT]

هنا استخدمنا التابع MessageBox للرد على اختيار المستخدم.
الصفحة الرابعة : الCheckBox
فى هذه الصفحة سوف نستخدم ازار الCheck و هى تلك التى تضع بداخلها علامة “صح” لتعبر عن القيمة bool,
تتشابه ازرار الCheck مع زر الاختيار فيما عدا شىء واحد, و انه يمكن اختيار عدد منها فى فورم اب واحد.
سوف نصمم الصفحة بهذا الشكل :

الأن علينا معالجة الحدث Submit
و سوف نغير الName لكل منهم لسهولة التعامل :
الاول c_cpp
الثانى basic
الثالث pascal_opascal
الرابع cobol.

كود سي بلس بلس:

void __fastcall TForm1::Button3Click(TObject *Sender)
{
AnsiString str;

str="I can work on : ";

if(c_cpp->Checked)
str=str+"\nC/C++";

if(basic->Checked)
str=str+"\nBasic.";

if(pascal_opascal->Checked)
str=str+"\nPascal, Object Pascal.";

if(cobol->Checked)
str=str+"\nCOBOL.";


MessageBox(this->Handle,str.c_str(),"VCL Controls",MB_ICONINFORMATION);
}

بما انه يمكن اختيار عدة اختيارات, فقمنا بتعريف الحرفى str و اختبرنا علامة كل مربع Check فاذا كان Checked اضفنا العبارة الخاصة به,
و فى النهاية عرضنا الرسالة بواسطة MessageBox.
لاحظ : الثابت الخاص بالرسالة MessageBox MB_ICONINFORMATION يقوم باخبار الويندوز بعرض ايقونة المعلومات (I) فى الرسالة.
الصفحة الخامسة :TStringGrid.
فى الصفحة الخامسة و الاخيرة سوف نضع الجدول TStringGrid و الذى سوف تجده فى الToolPallete تحت التبويب Additional.
ضع الجدول و سمه grid, و غير الصفة Align=alClient
معنى ذلك انه سوف يقوم بتحجيم نفسه مع النافذة الاب (فى هذه الحالة الصفحة الموجود بها).
سوف نقوم بعمل جدول مشابه لذلك الموجود فى الاكسل, و سوف نقوم بترقيم الRows و الColums بارقام متتابعة,
يحمل الجدول نوعين من الخلايا, النوع الاول هو الFixed و هى الخلايا التى تحمل العناوين, و النوع الثانى هو الجدول العادى.
فى الObject Inspector سوف تجد قيمتين FixedCols و FixedRows , ضع 1 فى كل منهم اذا لم يكن موجودا,
معنى ذلك اننا سوف نخصص Row واحد للعناوين و Col واحد للعناوين.
لتمكين المستخدم من الكتابة داخل الجدول, سوف نقوم بفتح التبويت Options داخل الObject Inspector, و نغير القيمة goEditing الى true.
لتميكن المستخدم من تغيير احجام الخلايا غير القيمة RowSizing و ColSizing الى true.
الأن علينا أن نعد الجزء الثابت من الجدول.
سوف نقوم بكتابة اجراء جديد اسمه FillFixedCell.
للوصول الى اى خلية داخل الجدول, نستخدم الArray ذات البعدين Cells, و يتم التعامل معها كأى AnsiString.
فى البداية نتجه الى الملف controls1.h
و نقوم بتعريف الاجراء الجديد تحت public:

كود سي بلس بلس:

void FillFixedCells();

الأن نكتب الاجراء فى الملف controls1.cpp

كود سي بلس بلس:


void TForm1::FillFixedCells()
{
for(int co=1;co<grid->ColCount;co++)
{
grid->Cells[co][0]="C"+IntToStr(co);
}

for(int ro=1;ro<grid->RowCount;ro++)
{
grid->Cells[0][ro]="R"+IntToStr(ro);
}

grid->Cells[0][0]="GRID";
}

كما راينا, يمكن الوصول لأى خلية بواسطة الCells, كما يمكن معرفة عدد الRows من خلال RowCount, و عدد الCols من خلال ColCount.
و بهذا يمكننا عمل حلقة تكرارية تقوم بكتابة رقم الRow و الCol لكل خلية ثابتة, سوف يبقى عندنا الخلية 0,0 و هذه سوف نكتب فيها كلمة GRID.

لكن متى يتم ملىء الجدول؟؟!
بالطبع يجب ملىء العناوين فى بداية البرنامج, لذا سوف نعالج الحدث Show الخاص بForm1.
اختر الفورم و فى الObjectInspector اذهب الى التبويب Events, و اختر الحدث OnShow.
سوف يقوم باستدعاء الدالة FillFixedCells.

كود سي بلس بلس:


void __fastcall TForm1::FormShow(TObject *Sender)
{
FillFixedCells();
}

الأن نريد ان نمكن المستخدم من زيادة عدد الrows او الColumns فى الجدول,
سوف يمكنه ذلك من خلال قائمة PopUp.
اضف الى الفورم قائمة من نوع TPopupMenu.
سمها pop.
لنربطها بالجدول, اذهب الى الجدول و فى الObjectInspector ابحث عن القيمة PopupMenu تحت التبويب Linkage.
من القائمة المنسدلة اختر pop.
الان عدل القائمة بوب عبر الضغط عليها مرتين و ضع فيها اختيارين
Add Row
Add Column.
لاضافة row نستخدم القيمة RowCount و نزيدها(لاحظ انها نفس القيمة التى يمكن قراءة عدد الRows منها).
و كذلك بالنسبة للcolumns نستخدم الColCount.
الان نعالج الحدثين Add Row و Add Column كما يلى :

كود سي بلس بلس:

void __fastcall TForm1::AddColumn1Click(TObject *Sender)
{
grid->ColCount++;
FillFixedCells();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::AddRow1Click(TObject *Sender)
{
grid->RowCount++;
FillFixedCells();
}

بالطبع بعد اضافة اى صف او عمود يجب اعادة كتابة عناوين الجدول, لذا استدعينا الدالة FillFixedCells.
بقى شىء واحد,
ماذا ان اضفنا StatusBar فى الفورم, و سوف يقوم هذا الStatus بعرض التاريخ و الوقت.
من الToolPallete و التبويب Win32 اختر الTStatusBar.
سوف نستخدم الstatusbar فى شكلها البسيط لذا غير القيمة SimplePanel الى true من الObject Inspector.
و سمها status للتسهيل.

لعرض الوقت كل ثانية سوف نستخدم الTTimer.
من التبويب System فى الToolPallete اضف عنصر الميقاتى TTimer.

كما ترى الTimer له قيمتين اساسيتين,
الأول هى الInterval و هى تعبر عن الوقت بالمللى ثانية. (لضبطه على توليد الحدث كل ثانية نكتب 1000).
الثانى هو Enabled و هو يستخدم لايقافه و تشغيله.
يولد الTTimer الحدث OnTimer كلما انقضى الوقت المطلوب,
و لا يملك الTimer سوى الحدث OnTimer فقط
ملحوظة: يقوم الTTimer بالعد من خلال Thread خاص به, و بالتالى لا يؤثر على بقية اجزاء البرنامج.
الأن نعالج الحدث OnTimer :

كود سي بلس بلس:


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
AnsiString str;

str="Date : "+Date().DateString();
str=str+" ,Time : "+Time().TimeString();

status->SimpleText="VCL Controls ,"+str;
}

هنا قمنا باستخدام تابعى الVCL و هم Date() و Time(), الذين يرجعون التاريخ و الوقت فى شكل صف TDateTime الذى سبق شرحه.
لتحويل الTDateTime الى AnsiString نستخدم الطريقة DateString() و TimeString().
الان نقوم بتخصيص الstr الى SimpleText الخاص بالstatus box.
سوف يقوم الTimer بتوليد الحدث OnTimer كل ثانية و بهذا يتغير الوقت دوريا.
بهذا نكون انتهينا من الدرس الرابع.
سوف تجدون ملفات المشروع Control1 مع المرفقات.
فى الدرس القادم سوف نستخدم مكونات اكثر تعقيدا مثل الTreeList و الListView و غيرها.
الى اللقاء مع الدرس الخامس و اعتذر بشدة عن التأخير نظرا لانشغالى الشديد و سفرى.

مقدمة فى السى++ بيلدر الجزء الثالث

  1. رابط الجزء الأول
    http://www.vc4arab.com/showthread.php?t=5176
    رابط الجزء الثانى
    http://www.vc4arab.com/showthread.php?t=5283
    فى الجزء الأول تحدثنا و تعرفنا على السى++ بيلدر.
    فى الجزء الثانى تحدثنا عن التطبيقات متعددة الوثائق, و المربعات الحوارية.
    فى هذا الجزء سوف نتحدث عن نقطة قوية جدا فى السى++ بيلدر, و هى انشاء المكونات وقت التشغيل,
    توفر لنا مكونات الVCL سهولة بالغة لدى انشاء المكونات اثناء التصميم, و لكن هذه المكونات تكون ثابتة العدد.
    طيب ماذا اذا احتجنا الى انشاء تطبيق شبيه بمحرر شاشة الفيجوال بيسك او السى بيلدر؟
    مكونات VCL توفر لنا ايضا سهولة بالغة لدى انشائها فى وقت التشغيل. حتى ان الكثير من المبرمجين يفضلون هذه الطريقة.
    فى هذا الجزء سوف نقوم بعمل محرر شاشات مبسط و لكنه فعال.
    قم بعمل مشروع جديد من نوع VCL Forms Application من خلال
    File->New->VCL Forms Application
    ابدأ بانشاء القائمة الرئيسية عبر اضافة عنصر القائمة الى البرنامج و
    اضف قائمة File و تحتها “Save To File”.
    اضف قائمة باسم “Add”, و تحتها “Add Text Box” و “Add Button”.
    كما خمنت سوف يستطيع برنامجنا ان يتعامل مع نوعين من العناصر, عنصر الكتابة TEdit, و عنصر الزر TButton فقط. طبعا يمكن تمديد البرنامج ليشمل بقية الانواع بنفس المبدأ.
    اضف عناصر القائمة كما فى الصورة.
    هذا هو كل ما سنحتاجه من الموارد!! Resources
    اما الباقى فسوف نقوم بانشائه من خلال الكود.
    انقر على عنصر القائمة Add Text Box.
    هنا سوف يكون علينا انشاء عنصر كتابة جديد و اعطاءه ابعاد افتراضية ثم تركه للمستخدم كى يقوم بتحريكه فى جنبات الفورم بحرية لتحديد مكانه.

    كود سي بلس بلس:


    void __fastcall TForm1::AddTextBox1Click(TObject *Sender)
    {
    TEdit *new_edit=new TEdit(this);

    new_edit->Height=20;
    new_edit->Width=100;
    new_edit->Top=20;
    new_edit->Left=20;
    new_edit->DragMode=dmAutomatic;
    new_edit->Text="Text"+IntToStr(edit_count);
    new_edit->OnMouseDown=ControlShowPlace;
    edit_count++;

    new_edit->Parent=this;
    new_edit->Show();



    }

    فى البداية سوف نعرف مكون من نوع Tedit كمؤشر و نحجز له مكانا بواسطة new. ثم نبدأ بتعريف مكانه و ابعاده الافتراضية.
    و نكتب بداخله “Text” ثم رقم معبر عنه نأخذها من المتغير edit_count و الذى سوف نعرفه كمتغير global فى بداية البرنامج من خلال العبارة :

    كود سي بلس بلس:


    int edit_count=0;
    int button_count=0

    ;
    سوف يزيد المتغير edit_count مع كل اضافة لtext جديد.
    استخدمنا هنا اجراء جديد من الVCL هو IntToStr.
    كما هو واضح فان اجراء IntToStr يستخدم لتحويل المتغير int الى AnsiString. و تعريفه

    كود سي بلس بلس:

    AnsiString IntToStr(int num(

    فى العبارة التالية سوف نعرف المكون الأب لمربع الكتابة و هو بالطبع الفورم الاساسى و الذى سوف نشير اليه من خلال this.

    كود سي بلس بلس:

    new_edit->Parent=this;

    بقى ان نضيف قدرة الDrag&Drop الى المكون الجديد, لاعطاء المستخدم حرية تحريكه فى جنبات الفورم. و سوف نعطيها من خلال العبارة التالية :

    كود سي بلس بلس:

    new_edit->DragMode=dmAutomatic;

    الDragMode قد يتم اعطائها قيمة dmAutomatic و dmManual. الاوتوماتيك تشير الى اننا نرغب فى اعطاء المستخدم القدرة على تحريك المكون من خلال طريقة Drag&Drop.
    بقى ان نعرف كيف سيستجيب المكون الى الاحداث؟
    نحن انشأنا المكون, و لكننا لا نعرف بالضبط كيف سوف نعطيه القدرة على التفاعل مع المستخدم. فى هذا التطبيق نحتاج الى اعطاء المكون القدرة على اظهار موقعه الحالى بعد تحريكه.
    لذا سوف نربط الحدث OnMouseDown و الذى ينفذ لدى النقر على المكون باجراء سوف نكتبه نحن.

    كود سي بلس بلس:

    new_edit->OnMouseDown=ControlShowPlace;

فى النهاية لا يبقى سوى الخطوة الأخيرة و هى اظهار المكون.
new_edit->Show();
الأن علينا كتابة الاجراء ControlShowPlace.
لكى نستطيع ربطه بالحدث OnMouseDown يجب ان يحتوى الاجراء الذى سنكتبه على نفس التعريفات التى يولدها الحدث OnMouseDown الافتراضى.
فى الملف Unit1.h و تحت ال__published عرف الاجراء ControlShowPlace

كود سي بلس بلس:


void __fastcall ControlShowPlace(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);

بداية تعالوا نحلل المتغيرات التى فى التعريف :

كود سي بلس بلس:

Tobject *Sender 

كنت ترى هذا المؤشر فى كل الاحداث التى نكتبها, و لم نعرف ابدا ما هى اهميته او لماذا يشير, فى حالة الDesign Time Component لا اهمية حقيقية لهذا المؤشر,
لكن فى الRunTime تظهر اهميته.
هذا المؤشر يحمل الاشارة الى المكون الذى ولد هذا الحدث, و هو فى حالتنا هذه قد يكون احدى الTEdit او احدى الTButton, و فى كلتا الحالتين سوف نتعامل معه بنفس الطريقة.
الأن نكتب الاجراء كالتالى :

كود سي بلس بلس:


void __fastcall TForm1::ControlShowPlace(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
AnsiString ss;
if(Sender->ClassNameIs("TEdit"))
ss="Text Box - :\n";

if(Sender->ClassNameIs("TButton"))
ss="Button - :\n";
ss=ss+"Left : "+IntToStr(((TControl*)Sender)->Left);
ss=ss+"\nTop : "+IntToStr(((TControl*)Sender)->Top);

MessageBox(this->Handle,ss.c_str(),"Screen Editor",MB_ICONINFORMATION);

}

فى البداية عرفنا الحرفى ss و الذى سيحمل الرسالة التى تظهر للمستخدم لدى الضغط على المكون.
الخطوة التالية علينا معرفة هل المكون المولد للحدث من نوع Tedit ام من نوع Tbutton.
لمعرفة هذا يجب ان ندرس قليلا ما هو الTObject.
الTObject هو الفئة الأب لكل مكونات الVCL, كل الفئات الاخرى تنحدر من الفئة Tobject, و ينحدر منه الفئة Tpresistent و التى تتفرع الى فرعين,
الفرع الاول Tcomponent و هو الذى ينحدر منه Tcontrol و الفرع الثانى TinterfacePresistent و الذى تنحدر منه المكونات الرسومية مثل Timage.
فئة الTObject تستطيع ان تعطينا الفئة التى تنتمى اليها, و من خلالها يمكن معرفة نوع المكون الذى نتعامل معه.
و ذلك من خلال ClassNameIs و هو من نوع bool و يعطينا قيمة صحيحة اذا ما كان من النوع الذى حددناه.
و بهذه الطريقة يمكننا اضافة نوع المكون الى الرسالة التى سوف تظهر للمستخدم.
الأن نحتاج الى اضافة المكان,
و سوف نحتاج الى عمل Casting للSender الى الفئة Tcontrol, نظرا لأن الTObject لا يحمل متغيرات تحدد مكانه,

كود سي بلس بلس:

ss=ss+"Left : "+IntToStr(((TControl*)Sender)->Left);

فى النهاية, سوف نعرض المتغير الحرفى الذى ملأناه بالبيانات عبر الMessageBox.
الأن علينا اضافة قدرة السحب و الترك Drag&Drop الى الفورم الرئيسى, لكى يتسنى لنا تحريك المكونات.
من خلال احداث الForm1 الموجودة فى نافذة Object Inspector انقر مرتين على FormDragOver.
حدث الDragOver يقع عندما نقوم بعملية سحب عنصر ما داخل حدود النافذة. علينا ان نخبر الفورم بقبول العناصر التى يتم سحبها فوقه.
فى هذا الحدث سوف نكتب فقط Accept=true لكى نخبره بقبول السحب

كود سي بلس بلس:


void __fastcall TForm1::FormDragOver(TObject *Sender, TObject *Source, int X,
int Y, TDragState State, bool &Accept)
{
Accept=true;
}

الأن جاء وقت حدث الترك او DragDrop و هو الذى يولد لدى انتهاء عملية سحب العنصر. كل ما علينا هو تغيير مكان المكون الى مكانه الجديد بعد عملية السحب.

كود سي بلس بلس:


void __fastcall TForm1::FormDragDrop(TObject *Sender, TObject *Source, int X,
int Y)
{
((TControl*)Source)->Top=Y;
((TControl*)Source)->Left=X;
}

لاحظ هنا ان الSender هو Form1 و ليس العنصر, لأنه هو من ولد الحدث, أما الSource فهو العنصر الذى تم سحبه, و هو الذى سنستخدمه لتغيير المكان, لكن علينا اولا عمل Casting له لجعله فى صورة Tcontrol و الذى يقبل عملية التحريك.
طبعا الX و الY يمثلون مكان ترك المكون.
بالنسبة لعملية اضافة زر, فسوف تكون مشابهه تماما لعملية اضافة Text Box و لكن فقط سوف نغير فى الاسماء.

كود سي بلس بلس:


void __fastcall TForm1::AddButton1Click(TObject *Sender)
{
TButton *new_button=new TButton(this);

new_button->Height=20;
new_button->Width=100;
new_button->Top=20;
new_button->Left=20;
new_button->DragMode=dmAutomatic;
new_button->Caption="Button"+IntToStr(button_count);
//new_button->OnDblClick=ControlDblClick;
new_button->OnMouseDown=ControlShowPlace;
button_count++;

new_button->Parent=this;
new_button->Show();

}

و الأن المرحلة الأخيرة.
كيف اصل الى المكونات؟
لعلك تتسائل الأن, بعد توليد هذه المكونات كيف سوف اصل اليها؟؟ كيف سوف يمكننى تغيير خصائصها أو معرفة عددها و صفاتها؟
الأمر بسيط,
كل Parent Form اى كان يحتوى عدة مكونات بداخله, و يشير اليها عبر مصفوفة اسمها Components و بعدد CompenentCount.
معنى ذلك اننا نستطيع الوصول الى كل العناصر من خلال دائرة تكرارية بعدد الComponentCount.
تعالوا نفهم الطريقة من خلال معالجة الحدث Save To File.

كود سي بلس بلس:


void __fastcall TForm1::SaveToFile1Click(TObject *Sender)
{
int I;

TSaveDialog *save_dlg=new TSaveDialog(this);

TComponent *pComp;
AnsiString ss,fn;

save_dlg->Filter="Text File (*.txt) | *.txt";


if(save_dlg->Execute())
fn=save_dlg->FileName+".txt";
else
{
delete save_dlg;
return;
}

for (I = this->ComponentCount - 1; I >= 0; I--)

{
pComp = this->Components[I];
if(pComp->ClassNameIs("TEdit"))
{ ss=ss+"Text Box\n";
ss=ss+((TEdit*)pComp)->Text+"\n";
ss=ss+"Left="+IntToStr(((TControl*)pComp)->Left);
ss=ss+"\n";
ss=ss+"Top="+IntToStr(((TControl*)pComp)->Top);
ss=ss+"\n";
}

if(pComp->ClassNameIs("TButton"))
{ ss=ss+"Button\n";
ss=ss+((TButton*)pComp)->Caption+"\n";
ss=ss+"Left="+IntToStr(((TControl*)pComp)->Left);
ss=ss+"\n";
ss=ss+"Top="+IntToStr(((TControl*)pComp)->Top);
ss=ss+"\n";
}


}


ofstream fout(fn.c_str(),ios::out);
fout<<ss.c_str();

fout.close();

delete save_dlg;
}

بدأنا اولا بتحميل مربع حوارى الملفات الشائع, و الذى سوف يتيح للمستخدم اختيار اسم و مكان الملف الذى سوف نسجل عليه الشاشة التى صممناها.

كود سي بلس بلس:


TSaveDialog *save_dlg=new TSaveDialog(this);

save_dlg->Filter="Text File (*.txt) | *.txt";


if(save_dlg->Execute())
fn=save_dlg->FileName+".txt";
else
{
delete save_dlg;
return;
}

الأن قمنا بتعريف الFilter و ليكن *.txt
ثم قمنا بفتح المربع الحوارى بواسطة الأمر Execute. و قمنا بتخزين الاسم فى المتغير fn.
لاحظ اننا عرفنا كائن Tcomponent اسمهpComp و ذلك لكى نستخدمه فى تحميل المكونات بشكل متوالى.
TComponent *pComp;
الأن نقوم بعمل دورة تكرارية بعدد المكونات و كلما وجدنا عنصرا من نوع Tbutton أو Tedit نقوم بكتابة سكريبت بسيط يحتوى على اسمه و مكانه.

كود سي بلس بلس:

for (I = this->ComponentCount - 1; I >= 0; I--)

{
pComp = this->Components[I];
if(pComp->ClassNameIs("TEdit"))
{ ss=ss+"Text Box\n";
ss=ss+((TEdit*)pComp)->Text+"\n";
ss=ss+"Left="+IntToStr(((TControl*)pComp)->Left);
ss=ss+"\n";
ss=ss+"Top="+IntToStr(((TControl*)pComp)->Top);
ss=ss+"\n";
}

if(pComp->ClassNameIs("TButton"))
{ ss=ss+"Button\n";
ss=ss+((TButton*)pComp)->Caption+"\n";
ss=ss+"Left="+IntToStr(((TControl*)pComp)->Left);
ss=ss+"\n";
ss=ss+"Top="+IntToStr(((TControl*)pComp)->Top);
ss=ss+"\n";
}


}

قمنا هنا بتحميل المكون الحالى فى المؤشر pComp

كود سي بلس بلس:

  pComp = this->Components[I];

و بعدها اختبرناه لنعرف هل هو text ام button, و قمنا على هذا الاساس بكتابة السكريبت فى الحرفى ss.
لاحظ ان الدورة التكرارية for loop بدأت من الComponentCount الى ال0

كود سي بلس بلس:


for (I = this->ComponentCount - 1; I >= 0; I--)

يجب ان تكتب الدورة التكرارية بشكل تنازلى و ليس تصاعدى عند البحث عن المكونات.
فى المرحلة الاخيرة قمنا بكتابة السكريبت الذى ولدناه فى الملف عبر استخدام اداة fstream العادية.
يمكنك تحميل ملفات المشروع و الملف التنفيذى من خلال الملف Article3.rar.

مقدمة فى السى++ بيلدر الجزء الثانى

  1. فى الجزء الأول تحدثنا قليلا عن السى++ بيلدر و دخلنا سريعا الى تطوير برنامج صغير من خلاله و استعرضنا مقدمة سريعة عن التعامل مع الVCL.

    فى هذا الجزء نتحدث عن البرنامج متعدد الوثائق MDI و الDialogs ثم ندخل الى انشاء كائنات VCL فى وقت التنفيذ RunTime. لكن فى البداية دعونا نتحدث قليلا عن الVCL.

    الVCL هى اختصار للVisual Components Library, مكتبة المكونات المرئية, و هى تمثل للسى بيلدر ما تمثله الMFC للفيجوال سى++, و ما تمثله الOWL للبورلاند سى++,

    الVCL قد يعتبرها البعض تطورا طبيعيا للOWL التى كانت ترفق مع البورلاند سى++, و الذى توقف تطويره و استبدل بالسى بيلدر,

    الOWL كانت منافسا للMFC, و الحقيقة أن صفوف الOWL كانت متشابهة الى حد التطابق مع صفوف الMFC, لربما كان الاختلاف الوحيد هو فى اسماء الصفوف, لكن طريقة البرمجة كانت واحدة, و أذكر اننى اتقنت استخدام الOWL فى يوم واحد فقط لأننى كنت أعرف الMFC جيدا, و لم احتج الا الى ملف المساعدة المرفق مع بورلاند سى++ لمعرفة أسماء الصفوف و كيفية عملها لا أكثر.

    الVCL مختلفة قلبا و قالبا عن صفوف الOWL كما قلنا,

    ان طريقة العمل بها شبيه بما يقدمه الفيجوال بيسك من حيث السهولة, لكن الى هنا تنتهى المقارنة بينها و بين الفيجوال بيسك,

    الفيجوال بيسك جيد, و لكن دعونا نكون واقعيين, الفيجوال بيسك أداة برمجة تفتقر للجدية. لأن قدرات اللغة محدودة, هذه واحدة, و لأن سرعتها ليست على المستوى المطلوب.

    يعتمد الفيجوال بيسك على محرك يقوم بتشغيله, اسمه MSVBM, و سوف تجده اما مرفقا مع كل برامج الفيجوال بيسك, أو سوف تجده مضمنا داخل الملف التنفيذى.

    السى بيلدر و الدلفى من ناحية أخرى يترجم الى Native Code للويندوز, و هو لا يحتاج الى اى نوع من الماكينات الافتراضية تماما مثل الفيجوال سى++,

    أضف الى ذلك ان السى بيلدر هو مترجم سى++ حقيقى و كامل المزايا, يدعم كل ما تدعمه اللغة القياسية,

    أى ان قوة السى++ بين يديك تفعل بها ما تشاء, و أنت مازلت قادرا على فعل كل ما تستطيع السى++ فعله, و مازلت محتفظا بسرعة السى++,

    كل ما فعلته الVCL هو انها تولت عنك تنظيم الواجهة الرسومية, لتتفرغ على انجاز ما يجب عليك فعله حقا!

    هناك نقطة اخرى جيدة فى الVCL, ان تضمين و استخدام المكونات بل و انشائها أمر غاية فى البساطة و السهولة. و لذا, فان عدد كبير من المكتبات و المكونات متوفرة فعلا للسى بيلدر و الدلفى, من أول مكونات الReporting, الى الرسومات البيانية, الى مكونات الويب, و غير ذلك. و الكثير منها يأتى مرفقا مع السى بيلدر أو الدلفى, مثل Rave Report أداة التقارير سهلة الاستخدام و التى يمكنها تحويل تقريرك الى ملفات الPDF, و أداة ChartFX التى يمكنها انشاء الرسوم البيانية فى بعدين أو ثلاثة ابعاد. و طبعا لا ننسى الاداة Indy للشبكات.

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

    و لكن…!

    المفاجأة ان الVCL لم تكتب على السى++, الVCL مكتوبة على الاوبجكت باسكل Object Pascal, و هى اللغة المعتمدة فى الدلفى, هذه الحقيقة بالذات تؤلم مبرمجى السى++ نفسيا, و للأسف فان بورلاند وعدت باعادة كتابة الVCL على السى++, و لكنها لم تفعل, و بعد بيعها الى امباركادو, لا احد يعرف مازل الوعد قائما, أم لا.

    على كل حال فالاوبجكت باسكال هى لغة قوية أيضا, و سرعتها قريبة من السى++, و تدعم المؤشرات.

    بقى ان نذكر برامج مثل Skype, و Yahoo Messenger, و Help&Manual قد طورت باستخدام الVCL, و العديد من البرامج الأخرى. كما تعتبر الدلفى و السى بيلدر الخيار الأول لمن يريد تطوير برامج قواعد البيانات, فهى تتمتع بأدوات سريعة للولوج الى محركات قواعد البيانات الشهيرة, منها محركها الخاص Paradox ,و حتى محركات اوراكل و مايكروسوفت, و حديثا تم تمضين الدعم للمحركات المجانية مثل الMySQL.

    لندخل الى برنامجنا القادم, و الذى سوف يوضح لنا كيفية انشاء تطبيق متعدد الوثاق MDI.

    فى البداية اختر File->New->Other… لاختيار مشروع متعدد الوثائق.

    من الصندوق الحوارى اختر C++ Builder Projects ثم MDI Application و اضغط OK.

    سوف يقوم سى++ بيلدر بانشاء تطبيق MDI افترضى يحتوى على العناصر الاتية :

    الكائن MainForm من الصف TMainForm و هو صف منبثق من TForm.

    الكائن الثانى هو TMDIChild و هو يحمل الوثيقة. و قد قام C++ Builder بوضع كائن TMemo بداخله بشكل افتراضى و هو يمثل الTextBox متعدد السطور.

    الكائن TAbout و هو TForm من نوع Dialog و هو يحتوى على صندوق التعريف About.

    سوف تجد ان هذا هو شكل الMDiapp الافتراضى, و قد وضع سى++ بيلدر لك كل ما تحتاجه من اشرطة ادوات, و قوائم افتراضية.

    لنبدأ بتعديل القائمة الرئيسية,

    لتشغيل محرر القوائم اضغط مرتين على كائن القائمة.

    و أضف قائمة باسم Tools, و تحتها خيار Submit Dialog , Append Date.

    ثم قم باضافة مربع حوارى, عبر اختيار File->New->Form C++ Builder.

    هذا المربع الحوارى سوف يكون من النوع Dialog Modal, اى ان البرنامج الرئيسى سوف يتوقف لحين الانتهاء من العمل و اغلاقه, لهذا سوف نغير بعض صفاته.

    من الObject Inspector غير FormStyle الى fsStayOnTop, و هى قيمة ثابتة معرفة فى c++ builder تعنى ان هذا المربع يجب ان يظل فى المقدمة حتى يغلق.

    و غير BorderStyle الى bsDialog.

    الأن أضف العناصر التالية :

    و أضف الحدث لزرار Submit و هو مجرد اغلاق المربع الحوارى للعودة الى الMainForm.

    كود سي بلس بلس:


    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    Close();
    }

    نعود الى الMainForm و نضيف الحدث التالى لدى الضغط على القائمة Submit Dialog.

    لكن اولا علينا القيام ببعض الاجراءات.

    بشكل افتراضى, فان برنامج الVCL يعمل بطريقة استدعاء جميع الForms لدى البدء بالعمل, و من ثم اخفائها و اظهارها عند الحاجة, و بالطبع تكون مخفية لدى البدء بالعمل ماعدا الMainForm.

    ليس علينا تحميل الذاكرة بالDialog الذى انشأناه, بل نحن نريد تحميله فقط عند الحاجة اليه.

    لهذا اضغط على قائمة Project->Options.

    و من المربع الحوارى, اختر صفحة Forms.

    سوف تجد Form1 مندرج تحت قائمة AutoCreate Forms, قم بازاحته الى قائمة Available Forms.

    سوف نرى فيما بعد الفارق بين الAutoCreate و Avalaible. حيث سوف ننشىء المربع الحوارى الثانى من نوع AutoCreate.

    اضغط OK.

    الخطوة التالية هى تضمين Header الForm1 فى الMainForm.

    اتجه الى الاعلى و اضف العبارة

    #include “Unit1.h”

    الأن اصبحنا جاهزين لكتابة الحدث Submit Dialog

    و سوف نكتبه كالتالى :

    كود سي بلس بلس:



    void __fastcall TMainForm::SubmitText1Click(TObject *Sender)
    {
    TForm1 *submit_dialog=new TForm1(this);


    submit_dialog->ShowModal();
    MessageBox(this->Handle,submit_dialog->Edit1->Text.c_str(),"Submit",NULL);
    delete submit_dialog;
    }

    شرح الكود :

    TForm1 *submit_dialog=new Tform1(this);

    قمنا هنا بتعريف كائن من نوع Tform1 اسمه submit_dialog, و حجزنا له مكانا فى الذاكرة.

    submit_dialog->ShowModal();

    قمنا باستدعائه ليعمل بشكل Modal

    MessageBox(this->Handle,submit_dialog->Edit1->Text.c_str(),”Submit”,NULL);

    هذا الكود سوف ينفذ بعدما يغلق المستخدم المربع الحوارى عبر الضغط على Submit. و هنا سوف يعرض ما كتبه المستخدم فى مربع الTEdit فى رسالة.

    delete submit_dialog;

    بعد الانتهاء, نحرر الذاكرة من submit_dialog.

    الأن لنقوم بانشاء الForm الأخر, و سوف يكون اسمه Form2 افتراضيا (بالطبع تستطيع تغيير الأسماء الافتراضية الى اسماء معبرة عبر تغيير الصفة Name فى الObject Inspector).

    Form2 سوف يكون هذه المرة من النوع AutoCreate, هذا النوع اسهل فى التعامل معه لأنه موجود فى الذاكرة اصلا, فلا داعى لتعريف كائن من الصف الخاص به, فالكائن موجود و محمل بالذاكرة اصلا.

    بنفس الطريقة اختر File->New->C++ Builder Form.

    و صمم الفورم كالتالى :

    سوف يقوم هذا الفورم باضافة التاريخ الذى اخترناه الى الوثيقة التى نعمل عليها حاليا.

    لذا سوف نضيف الكائن من النوع TdateTimePicker.

    هذا الكائن سوف تجده فى صفحة الWin32 فى الToolPallete.

    و اذهب الى بداية ملف Main.cpp, و اضف

    كود سي بلس بلس:

    #include “Unit2.h”

    [/LEFT]

    ارجع الى الMainForm, و أضف الحدث التالى على اختيار عنصر القائمة Append Date.

    كود سي بلس بلس:


    void __fastcall TMainForm::ShowForm21Click(TObject *Sender)
    {
    Form2->Show();
    }

    كما نرى ان استدعاء الفورم سهل للغاية, فكل ما نحتاجه هو اظهاره فقط لأنه يعمل بالفعل, و لكنه خفى لا يظهر على الشاشة.

    لاحظ هنا ان عند اظهار هذا الفورم, فان الMainForm لازال يستطيع استقبال الاحداث فى الخلفية, لأنه Modeless و ليس Modal كالفورم السابق.

    دعنا الأن نضيف Method جديدة الى الMainForm, و هى التى سوف تقوم باضافة التاريخ, و لكن عبر امر من Form2.

    فى تعريفات الMainForm و تحت الpublic, عرف الMethod التى تدعى AppendDate(AnsiString date_str)

    كود سي بلس بلس:




    public:
    virtual __fastcall TMainForm(TComponent *Owner);
    void AppendDate(AnsiString date_str);

    و انتقل الى Main.cpp لكتابة المعالجة:

    كود سي بلس بلس:





    void TMainForm::AppendDate(AnsiString date_str)
    {
    ((TMDIChild*)this->ActiveMDIChild)->Memo1->Lines->Append(date_str);

    }

هنا قمنا بعمل Casting للChild الذى يعمل عليه المستخدم عبر استخدام ActiveMDIChild و التى ترجع لنا مؤشر الكائن النشط, و قمنا بتشكيله فى شكل ((TMDIChild لأننا نعرف بالتأكيد انه من هذا النوع, لذا فان عملية الCasting سوف تنجح بكل تأكيد, ثم قمنا باضافة سطر بالتاريخ الذى ارجعه لنا الForm2 عبر المتغير الحرفى date_str.[/RIGHT]

نلاحظ هنا طريقة عمل الMemo, و هى مختلفة قليلا عن طريقة عمل الTextBox, فالMemo هنا يعمل كما الList, فهو عبارة عن عدة سطور, قمنا باضافة سطر جديد عبرh Lines->Append(AnsiString)

الأن يبقى فقط ان نقوم باستدعاء الدالة AppendDate من خلال الForm2.

بالطبع اضف فى بدايته

كود سي بلس بلس:

#include "Main.h"

ثم فى حدث الضغط على زر “Add Date” :

كود سي بلس بلس:


void __fastcall TForm2::Button1Click(TObject *Sender)
{
MainForm->AppendDate(DateTimePicker1->DateTime.DateString());
Close();
}

بقى ان نشرح كيفية عمل DateTimePicker1
ترجع الDateTimePicker التاريخ او الوقت الذى اختاره المستخدم فى شكل صف TDateTime و هو صف VCL يستطيع معالجة الوقت و التاريخ.

قمنا هنا باستدعاء الMethod DateString() و التى سوف ترجع لنا التاريخ فى شكل AnsiString. لنستطيع عرضه.

كما ترى فان معظم العمل مع الVCL بديهى للغاية, و سهل, و سوف تستطيع الوصول الى جميع قدراتها و امكانيتها ببساطة, كل ما تحتاجه هو مساعدة بسيطة من الCode Completion او من ملف المساعدة المرفق مع السى بيلدر.

الى اللقاء فى الجزء الثالث.

مقدمة فى السى++ بيلدر

  1. يعتبر السى++ بيلدر احدى اشهر بيئات التطوير للسى++ فى الوقت الحالى, جنبا الى جنب مع فيجوال سى++,
    قدمت أول نسخة من السى++ بيلدر (و الشهير بسى بيلدر اختصارا) فى عام 1995 من بورلاند.
    ثم توالت النسخ وصولا الى سى++ بيلدر 6, ثم سى++ بيلدر 2006 النسخة التى حملت تغييرات كبيرة عن السابق و التى كانت جزءا من Borland Developer Studio,
    ثم فصلت بورلاند تطوير البورلاند ستديو الى فرع تابع لها اسمه “كود جير Code Gear”, و قدم C++ Builder 2007 الذى أدخل دعم ويندوز فيستا, ثم بيعت كود جير الى شركة امباركادو فى 2008, و قدمت بعد ذلك C++ Builder 2009 الذى يعتبر من اهم نسخ سى++ بيلدر و أكثرها ثباتا.
    و سوف احاول فى هذا الموضوع اعطاء مقدمة سريعة للبدء باستخدام السى++ بيلدر و الذى حتما يمثل خيارا قويا لمن يريد كتابة برامج تجارية و احترافية بالسى++.
    سى++ بيلدر و فيجوال سى++, ما الفرق؟!
    السى++ بيلدر ليس تقليدا أو بديلا للفيجوال سى++, بل هو بيئة تطوير مختلفة جذريا عن بيئة مايكروسوفت الشهيرة,
    يعتمد السى++ بيلدر على مكتبات الVCL و هى اختصار لVisual Components Library,
    و هى تمثل له ما تمثله الMFC للفيجوال سى++, الا ان مكتبات الVCL تختلف تماما عن مكتبات مايكروسوفت,
    بعكس الفيجوال سى++, فان السى++ بيلدر يعمل بمبدأ التطوير السريع للتطبيقات Rapid Application Development او ما يعرف ب RAD, حيث يمكن بناء البرنامج كمجموعة من الObjects و الEvents و الProperties, و هذا هو الجديد الذى تقدمه لنا مكتبات VCL و سى++ بيلدر, بعكس مكتبات الMFC و التى لا تدعم هذا التوجه.
    لذا فان الميزة الأساسية هنا أن السى++ بيلدر سوف يتيح لك انشاء واجهة التطبيق الخاص بك فى خطوات بسيطة و سهلة و سريعة, و حتما, فان الوقت الذى يتطلبه انشاء برنامج على السى++ بيلدر أقل بكثير من الوقت الذى تحتاجه لكتابة البرنامج نفسه مستخدما مكتبات الMFC.
    أى نسخة نستخدم؟
    أخر نسخة متوفرة من السى++ بيلدر هى CodeGear C++ Builder 2010, و منذ نسخة السابقة 2009 فان السى++ بيلدر أصبح أفضل من السابق لناحية الاعتمادية و السرعة.
    لكن حاليا و لمن يريد بداية سريعة و مجانية, يمكن تنزيل النسخة التجريبية من موقع الشركة www.codegear.com
    بداية سريعة :
    مع تشغيل السى++ بيلدر, سوف تظهر لك فى البداية صفحة الترحيب Welcome Page و التى على بعض اللينكات للمساعدة كما تحتوى كذلك على المشاريع التى قمت بفتحها مؤخرا.
    لنبدأ سريعا فى كتابة برنامج صغير,

    الأن اختر File->New->C++ VCL Forms Application لتبدأ مشروعك.
    لو كنت قادما من خلفية الفيجوال بيسك, فان الواجهة حتما الأن سوف تثلج صدرك!
    نلقى الضوء هنا على نافذتين مهمتين جدا :
    الTool Pallete تحتوى على الVCL Components التى تستخدمها, و فى الناحية الأخرى فان النافذة Object Inspector تحتوى على بوابتين, البوابة الأولى تمثل الProperties للكائن الحالى, و الثانية هى الEvents الخاصة به.

    الأن باستخدام الأدوات, قم بتصميم الشكل التالى :

    و كما نرى البرنامج بسيط يحتوى على مربعين Tedit و هم يمثلون عنصر الText Box كما هى التسمية المعتادة مع مايكروسوفت, و مربع يحمل نتيجة الجمع بينهم.
    اضغط مرتين على الزر “Execute” لتوليد الEvent Button1Click(TObject *Sender)
    و بداخلة اكتب الكود التالى :

    كود سي بلس بلس:


    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    float num1,num2;
    float result=0;

    try{
    num1=Edit1->Text.ToDouble();
    }
    catch(...) {num1=0;}

    try{
    num2=Edit2->Text.ToDouble();
    }
    catch(...) {num2=0;}

    result=num1+num2;

    Edit3->Text=FloatToStr(result);

    }

    شرح الكود :
    فى البداية عرفنا ثلاثة متغيرات num1, num2, result من نوع float لنقوم بالعمليات الحسابية.
    الجزء الثانى حملنا المتغير num1 بقيمة الEdit1 عبر طريقة ToDouble() لتحويل الString الى float.
    و نفس الشىء الى num2, ثم أظهرنا النتيجة فى Edit3
    و لكن ماهى دالة ToDouble()؟ و ماهى الFloatToStr ؟
    الحقيقة ان هذه الدوال خاصة بمتغير خاص بسى++ بيلدر اسمه AnsiString و هو متغير يحمل سلسلة الأحرف و نتعامل معه بشكل مشابه للString الموجود فى الفيجوال بيسك مثلا, و الCString فى الفيجوال سى++. و لكن الAnsiString أقوى كثيرا من الاثنين.
    الأن حولنا الرقم float الى AnsiString مرة أخرى عبر الدالة FloatToStr لنستطيع عرضه على الEdit3.
    تعالو نكتب الكود بطريقة اخرى لنفهم الAnsiString اكثر.

    كود سي بلس بلس:


    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    float num1,num2;
    float result=0;
    AnsiString s1,s2,s3;

    s1=Edit1->Text;
    try{
    num1=s1.ToDouble();
    }
    catch(...) {num1=0;}
    s2=Edit2->Text;
    try{
    num2=s2.ToDouble();
    }
    catch(...) {num2=0;}

    result=num1+num2;
    s3=FloatToStr(result);
    Edit3->Text=s3;

    }

    كما هو واضح, ان التعامل مع الAnsiString اسهل كثيرا من التعامل مع سلاسل الأحرف التقليدية فى السى++, و سوف يوفر عليك كثيرا من العناء, كما أنه أكثر امانا بالطبع.
    الأن جاء الوقت لنلقى نظرة أكثر تعمقا على VCL:
    فى الملف Unit1.h سوف تجد الكود التالى :

    كود سي بلس بلس:


    //$$---- Form HDR ----
    //---------------------------------------------------------------------------

    #ifndef Unit1H
    #define Unit1H
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    //---------------------------------------------------------------------------
    class TForm1 : public TForm
    {
    __published: // IDE-managed Components
    TEdit *Edit1;
    TLabel *Label1;
    TEdit *Edit2;
    TLabel *Label2;
    TEdit *Edit3;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
    private: // User declarations
    public: // User declarations
    __fastcall TForm1(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif

    هذا الجزء يحمل تعريف الTForm1, و هو الكلاس الذى سيعرف الفورم الخاص بالبرنامج, و هو تم توريثه من TForm الكلاس الخاص بالForms من VCL.
    class TForm1 : public TForm
    {
    فى الجزء التالى يعرف السى بيلدر الComponents التى وضعناها فى الفورم و يحجز لها مكانا فى الheap :

    كود سي بلس بلس:

    __published:	// IDE-managed Components
    TEdit *Edit1;
    TLabel *Label1;
    TEdit *Edit2;
    TLabel *Label2;
    TEdit *Edit3;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);

    ثم يعرف الButton1Click و هو الاجراء الذى سيحمل الكود المصاحب للحدث (الضغط على Button1.
    و هذا الكود تم توليده تلقائيا عند وضع الComponents على الفورم.
    لاحظ انه لا يمكن انشاء كائنات الVCL كstatic variables,
    فى مرحلة قادمة سوف نتعلم كيف نقوم بانشاء كائنات الVCL فى الRunTime.
    الأن لنلق نظرة على Unit1.cpp,

    كود سي بلس بلس:

     
    //$$---- Form CPP ----
    //---------------------------------------------------------------------------

    #include <vcl.h>
    #pragma hdrstop

    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    float num1,num2;
    float result=0;
    AnsiString s1,s2,s3;

    s1=Edit1->Text;
    try{
    num1=s1.ToDouble();
    }
    catch(...) {num1=0;}
    s2=Edit2->Text;
    try{
    num2=s2.ToDouble();
    }
    catch(...) {num2=0;}

    result=num1+num2;
    s3=FloatToStr(result);
    Edit3->Text=s3;

    }
    //---------------------------------------------------------------------------

    تم تعريف المؤشرForm1 من TForm , ثم كتابة كود الConstructor, و كود الحدث الخاص بbutton1.
    الأن لنلق نظرة على الملف Unit1.dfm و الذى يحمل الResource Code الخاص بالفورم, و الذى تستخدمه VCL لرسم الكائنات.
    object Form1: TForm1
    Left = 0
    Top = 0
    Caption = ‘Form1’
    ClientHeight = 315
    ClientWidth = 461
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = ‘Tahoma’
    Font.Style = []
    OldCreateOrder = False
    PixelsPerInch = 96
    TextHeight = 13
    object Label1: TLabel
    Left = 127
    Top = 41
    Width = 11
    Height = 16
    Caption = ‘+’
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = ‘Tahoma’
    Font.Style = [fsBold]
    ParentFont = False
    end
    object Label2: TLabel
    Left = 255
    Top = 41
    Width = 11
    Height = 16
    Caption = ‘=’
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = ‘Tahoma’
    Font.Style = [fsBold]
    ParentFont = False
    end
    object Edit1: TEdit
    Left = 16
    Top = 40
    Width = 105
    Height = 21
    TabOrder = 0
    end
    object Edit2: TEdit
    Left = 144
    Top = 40
    Width = 105
    Height = 21
    TabOrder = 1
    end
    object Edit3: TEdit
    Left = 272
    Top = 40
    Width = 105
    Height = 21
    TabOrder = 2
    end
    object Button1: TButton
    Left = 16
    Top = 80
    Width = 369
    Height = 49
    Caption = ‘Execute!’
    TabOrder = 3
    OnClick = Button1Click
    end
    end
    و لنلق نظرة الأن على مدخل البرنامج أو الكود الأساسى الذى يقوم بتشغيل البرنامج:

    كود سي بلس بلس:


    //$$---- EXE CPP ----
    //---------------------------------------------------------------------------

    #include <vcl.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    USEFORM("Unit1.cpp", Form1);
    //---------------------------------------------------------------------------
    WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
    try
    {
    Application->Initialize();
    Application->CreateForm(__classid(TForm1), &Form1);
    Application->Run();
    }
    catch (Exception &exception)
    {
    Application->ShowException(&exception);
    }
    catch (...)
    {
    try
    {
    throw Exception("");
    }
    catch (Exception &exception)
    {
    Application->ShowException(&exception);
    }
    }
    return 0;
    }
    //---------------------------------------------------------------------------

هنا مدخل البرنامج WinMain لمن يستخدم WinAPI, و يبدأ بتجهيز الApplication الذى ينتمى لفئة TApplication ثم تحميل Form1 و بدأ تنفيذ البرنامج,
و كما ترى أن البرنامج كله موضوع بين بلوك try, و بذلك عند حدوث اى خطأ فان البرنامج لا يتوقف فجأة و انما يستعرض الخطأ المشار اليه أولا.
فى الجزء التالى سوف نتحدث بشكل اكثر استفاضة عن انشاء الDialogs فى VCL و تحميلها, و كذلك سوف نتحدث عن انشاء الObjects فى خلال الRunTime.
الى اللقاء مع الجزء الثانى.