المتابعون للمدونة

الأحد، 11 يونيو 2017

TimeSpanPiker


مشروع أداة اختيار المدة الزمنية
TimeSpanPiker 



توجد نسختان إحداهما بلغة فيجوال بيزيك دوت نت، والأخرى بلغة سي شارب.. يمكن تحميل من هذا الرابط. 

Download Source Code in C# & VB.NET:

مؤخرا، أردت تصميم مربع حوار يتيح للمستخدم إدخال مدة زمنية.. كانت أمامي هذه الخيارات:

1- استخدام مربع نص عادي TexBox وكتابة الكود اللازم للسماح بكتابة الأرقام فقط والعلامة العشرية والسالب.. يتطلب هذا أيضا معالجة مشكلة اللصق Paste لأنها قد تؤدي إلى لصق نص لا يصلح كرقم.
2- استخدام الأداة NumericUpDown وهي تسمح بكتابة الأرقام فقط.. هذا حل مريح، لكن فيه مشكلة صغيرة، أن المدة الزمنية يجب أن تعرض كرقم واحد يتكون من الثواني وكسر الثواني، وهذا يعني أن المستخدم لكي يدخل المدة الزمنية 33 دقيقة و 12 ثانية ونصف، يجب أن يجري عملية حسابية لتحويلها إلى ثواني (نفس المشكلة موجودة في الحل رقم 1).. أيضا عند ضغط زر الزيادة أو زر الإنقاص، يتم إضافة معدل الزيادة على العدد كله، ولا توجد وسيلة لزيادة الجزء العشري بمفرده والجزء الصحيح بمفرده كنوع من التسهيل على المستخدم.
3- استخدام مربع النص المقنن MaskedTextBox وجعله يعرض صيغة المدة الزمنية فقط.. هذا حل جيد، لكن ينقصه زرا الزيادة والإنقاص.
4- مزج الحلين 3 و 4 معا، وهذا ما فعله المبرمج في المشروع Subtitle Edit (الذي تكلمنا عنه في عهدة منشورات سابقة) حيث أنشأ أداة اسمها TimeUpDown عبارة عن الأداة NumericUpDown لكن وضع MaskedTextBox فوقها بحيث يخفي مربع النص الخاص بها، ويظل زر الإنقاص وزر الزيادة ظاهرين.. لكن هناك بعض المشاكل في هذه الأداة، فمعدل الزيادة الخاصة بها هو الميلي ثانية فقط، وصعب التحكم في زيادة كل جزء على حدة إلا بالكتابة اليدوية.
5- استخدام أداة DateTimePiker المخصصة لاختيار التاريخ والوقت، مع ضبط صيغة تنسيقها لعرض جزء الوقت فقط، وعرض زري الزيادة والإنقاص الخاصين بالأداة.. هذه الأداة تسمح بتحديد كل جزء من الوقت على حدة، وزيادة قيمته بالأزرار.. هذا الحل استخدمت شبيها به في المشروع الذي شرحته في كتاب المدخل العملي السريع، وكان مناسبا هناك لإدخال وقت الموعد، لكن المشكلة هنا أننا نتعامل مع مدة زمنية وليس وقتا.. المدة الزمنية يمكن أن تكون موجبة أو سالبة ونتعامل معها برمجيا باستخدام السجل TimeSpan بينما يتم التعامل مع الوقت باستخدام السجل DateTime ولا يمكن أن يكون سالبا (لهذا لا تسمح الأداة DateTimePiker بإدخال قيم سالبة).. وحينما جربت هذه الطريقة اضطررت إلى وضع مربع اختيار CheckBox بجوار أداة اختيار التاريخ، فإذا تم اختياره كان معنى هذا أن المدة الزمنية سالبة.. وقد عرضت مربع الاختيار في شكل زر (بوضع القيمة Button في الخاصية Appearance) وبرمجته ليعرض العلامة + إذا لم يكن مختارا و - إذا تم اختياره.
لكن ظلت هناك مشكلة أن الأداة DateTimePiker تحدد تلقائيا الجزء الذي يمثل أكبر جزء من التاريخ المعروض (في حالتنا هذه جزء الساعات).. هذا منطقي عند اختيار وقت، لكن ما أريده هو تحديد جزء الثواني، ولم تعط ميكروسوفت أي طريقة برمجية لاختيار الجزء المحدد.. وكان الحل الوحيد هو استخدام الفئة SendKeys في حدث عرض النموذج Shown لمحاكاة ضغط السهم الأيمن من لوحة المفاتيح مرتين، وبهذا يتم تحديد جزء الثواني أولا.
 
في الحقيقة كل هذه الحلول لم ترحني، وقررت أن أبحث إن كان أحد قد كتب مشروع دوت نت لإنشاء أداة خاصة لاختيار المدة الزمنية، وقد وجدت هذا المشروع:
وهي أداة جيدة مبنية باستخدام مربع نص عادي يعرض النص 00:00:00، والفكرة هنا هي عدم المساح للمستخدم إلا بتحديد جزء واحد فقط من أجزاء التاريخ، والكتابة فوقه (مع السماح بكتابة الأرقام فقط).
ستؤدي هذه الأداة الغرض في معظم الحالات، لكني وجدت فيها عدة عيوب:
- عدم عرض جزء أجزاء من الثانية.. أحتاج في المشروع إلى زيادة مدة عرض الترجمة ثانية ونصف مثلا.. والطريقة المكتوب بها الأداة لا تجعل من السهل تعديلها لإضافة جزء كسر الثانية، ولا تعديل صيغة عرض المدة الزمنية.
- لا تسمح الأداة بالمدد السالبة.
- ليس فيها زر الإنقاص وزر الزيادة.
- من عيوب مربع النص في دوت نت أنه لا يسمح بتغيير قيمة التبطين (الهوامش الداخلية) Padding ولم يعجبني اقتراب النص المعروض من حواف مربع النص.
- كما أن المبرمج بذل جدها كبيرا لعرض التنسيق وقراءته يدويا (بحساب مواضع بداية ونهاية كل جزء في الصيغة)، متجاهلا حقيقة أن ميكروسوفت قد قامت بجهد أعقد من هذا بكثير وقدمته لنا جاهزا من خلال الوسيلتين TimeSpan.ToString و TimeSpan.TryParseExact
(لتفاصيل أكثر انظر فصل الوقت والتاريخ في كتاب إطار العمل، وصيغ تنسيق المدة الزمنية في ملاحق الكتاب) 
لكل هذا قررت أن أستفيد من فكرة مربع النص الذي يحدد فقط جزءا معينا من الصيغة، وأكتب الأداة TimeSpanPicker الخاصة بي التي تحقق المزايا التالية:
- هناك هوامش داخلية جيدة للأداة، لأني أزلت إطار مربع النص، وجعلت بينه وبين حواف أداة المستخدم هوامش جيدة، ومنحت مربع النص وأداة المستخدم نفس لون الخلفية، وبهذا يبدو للعين كأنهما أداة واحدة.. لكن هذا يجعل بإمكان المستخدم ضغط حواف أداة المستخدم بالفأرة وهذا يجعل المؤشر Focus يخرج من مربع النص، لهذا استخدمت الحدث GotFocus الخاص بأداة المستخدم، لنقل المؤشر منها إلى مربع النص كلما تم ضغطها:
private void TimePicker_GotFocus(object sender, EventArgs e) {
   TimeBox.Focus();
}
لاحظوا أن الحدث GotFocus لا يظهر في نافذة الخصائص (عند جعلها تعرض الأحداث)، لهذا لا بد من تسجيل معالج هذا الحدث يدويا في حدث تحميل أداة المستخدم Load:
  this.GotFocus += TimePicker_GotFocus;
- تمتلك الأداة الخاصية Format التي تسمح للمبرمج باختيار الصيغة التي يريد بها عرض المدة الزمنية، بنفس قواعد إنشاء الصيغ الخاصة Custom Formats عند التعامل مع السجل TimeSpan في دوت نت.
- تمتلك الأداة الخاصية ShowSign وعند جعلها True تعرض الأداة العلامة + أو – قبل المدة الزمنية.. السبب في هذا أن ميكروسوفت عند برمجة السجل TimeSpan نسيت توفير صيغة خاصة لعرض المدد الزمنية السالبة!.. ويمكن تغيير إشارة المدة الزمنية بضغط الزر – أو + من لوحة المفاتيح مهما كان الجزء المحدد حاليا (كالثواني مثلا).. ونظرا لأن العلامة + موجودة فوق العلامة = وتحتاج لضغط الزر Shift، سمحت أيضا بضغط الزر = لتغيير الإشارة إلى + للتسهيل.
- تسمح الأداة بتحديد جزء واحد فقط من أجزاء الوقت، ويمكن اختيار الجزء الذي سيتم تحديده مبدئيا باستخدام الخاصية DefaultSelectedPart.. ويمكن للمبرمج تحديد أي جزء من الصيغة باستدعاء الوسيلة SelectPart.. ويمكن التحرك من جزء إلى آخر بضغطه بالفأرة أو ضغط سهم اليمين أو سهم اليسار أو الزرين Home أو End من لوحة المفاتيح.
- تستخدم الأداة الفئة UpDownButtons (التي كلمتكم عنها في منشور سابق) لعرض زري الزيادة والإنقاص، حيث يؤدي ضغطهما إلى زيادة قيمة الجزء المحدد أو إنقاصها.. ويمكن أداء نفس الغرض بضغط السهمين العلوي والسفلي من لوحة المفاتيح أو تحريك عجلة الفأرة Mouse Wheel للأمام والخلف.
ولكي يكون تنفيذ كل هذا سهلا، أنشأت فئة أخرى اسمها Part تمثل جزءا من أجزاء المدة الزمنية (الساعات، الإشارة، الدقائق، الثواني، أجزاء الثانية... إلخ)، بحيث تكون مسئولة عن عرض النص الخاص بهذا الجزء وقراءته وتغييره وتحديده وعدم السماح بأي أخطاء.. هذا يسمح بأن تحتوي الصيغة على حروف (كما تسمح بذلك الصيغ الخاصة بالسجل TimeSpan) لكن المستخدم لن يستطيع تحديد إلا الأجزاء الرقمية فقط، وبهذا لا يمكن أن يرتكب أي خطأ ولا يمكنه أن يمسح أي جزء من الصيغة، لأنه لن يستطيع أبدا تحديد أي جزء غير رقمي!.. على سبيل المثال، ستجدون في المشروع المرفق أن الأداة تعرض الصيغة:
 + 00 : 00 : 00 : 0.0
وهي صيغة تحتوي على مسافات فالصيغة القياسية هي:
+ 00:00:00:0.0
ستجدون كود هذه الفئة Part في المشروع المرفق.
وفي المشروع التجريبي، أضفت النموذجا DelyForm وهو يعمل كمربع حوار يسمح للمستخدم باختيار المدة الزمنية.. (ويمكن استخدامه أيضا لاختيار نسبة مئوية، ولكن هذا خارج نطاق اهتمامنا هنا.. وضعت الوظيفتين في نموذج واحد للاختصار).
أرجو أن تستفيدوا بالأداة ونموذج اختيار المدة الزمنية في مشاريعكم.. ولا تنسوني من دعائكم.. أرجو أيضا أن تدعو لأبي الراحل (أ. حمدي كامل غانم) ولصديقي الراحل (أ. حاتم يحيى طه) بالرحمة والمغفرة. 


 
VS.NET Windows Forms Custom Control to let user choose time or time span. There are two versions, VB.net & C#.
 
 

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحة الشاعر