الشبكة العربية لمطوري الألعاب

خبير مدير وسام البهنسي مشاركة 1

اكتشفت خطأ عويصاً نوعاً وآمل أن أجد أحداً قادراً على مساعدتي في هذا الموقف.
الموقف كالآتي:

لدي WinForm بسيطة تماثل في عملها أية نافذة Windows Explorer لتصفح الملفات. أي أنها تحتوي على عنصر ListView يتم سرد الملفات به. يمكن تغيير اسم أي ملف عن طريق اختياره ثم الضغط عليه مرة واحدة (أو باستخدام زر F2 كذلك)، حيث سيتحول عندها الاسم إلى text box يمكنك كتابة الاسم الجديد فيه، كما أنه يمكنك عمل copy/cut/paste للنص المحتوى في هذا الصندوق المؤقت..

طبعاً من ضمن الوظائف الأساسية في الـ Windows Explorer هي نسخ الملفات ولصقها في أماكن اخرى. في حالة الـ WinForm المذكورة أعلاه، فقد وضعت Menu Strip بها الأوامر الرئيسية (نسخ، لصق، تغيير الاسم) مع اختصارات للوحة المفاتيح (Ctrl+C و Ctrl+V و F2).

المشكلة في برنامجي كالآتي:
عندما يبدأ المستخدم بتغيير اسم الملف (يضغط F2 مثلاً) ثم يضغط Ctrl+C لنسخ اسم الملف، فإن الـ WinForm لدي تستقبل الاختصار وتقوم بنسخ الملف ككل بدلاً من نسخ نص اسم الملف فقط. مما يعني أنه كنتيجة، عندما يقوم المستخدم بعمل paste بعدها، سيفاجأ بأنه قد حصل على نسخة من الملف بدلاً على نسخة من نص اسمه...

السؤال، ما هي الطريقة الصحيحة لتفادي هذا الموقف؟

لقد وضعت برنامج #C بسيط يظهر هذه المشكلة تماماً كما شرحتها:
http://www.inframez.com/temp/shortcuts_problem.zip


شكراً،

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مشرف مؤيد مارديني مشاركة 2

"نصف" المشكلة محلول و هو النصف الذي سنحدد فيه إن كان المستخدم موجود في وضع التصفح فيقوم البرنامج بنسخ الملف المختار، أو موجود في وضع الـRename فيقوم البرنامج بنسخ الاسم بدلاً من الملف، يمكننا تنفيذ ذلك بسهولة عن طريق تعريف متغير تتغير قيمته عند استدعاء الحدث AfterLabelEdit و الحدث BeforeLabelEdit من قبل الـListView، ثم فحص قيمة ذلك المتغير عند عمل Copy أو Paste.
يبقى النصف الثاني من المشكلة، و هو تحديد النص المحدد Selected من اسم الملف في وضع الـRename لنسخه أو استبداله، و لا توفر لنا الـListView القياسية ذلك حسب معلوماتي، و أعتقد أن أسهل طريقة للقيام بذلك هو عمل Custom ListView خاصة بك، هل حاولت ذلك؟

مع التحية،

Moayad Mardini,
MSDN Forums Moderator

خبير مدير وسام البهنسي مشاركة 3

وفي Dec 3, 2007 06:30، أعرب مؤيد مارديني عن رأيه بالموقف كالآتي:

يبقى النصف الثاني من المشكلة، و هو تحديد النص المحدد Selected من اسم الملف في وضع الـRename لنسخه أو استبداله، و لا توفر لنا الـListView القياسية ذلك حسب معلوماتي، و أعتقد أن أسهل طريقة للقيام بذلك هو عمل Custom ListView خاصة بك، هل حاولت ذلك؟


لا أعتقد أن هذا هو بالفعل أسهل حل. أوافقك في الجزء الأول، أما بالنسبة للجزء الثاني فإن الحل الذي خطر ببالي هو فصل الـ handler الخاص بالـ menu commands بين الحدثين BeginLabelEdit و EndLabelEdit، مما يعطي فرصة لصندوق النص أن يتصرف بشكل طبيعي مرة أخرى.
لقد وجدت شخصاً آخر واجه مكشلتي إياها، لكني لم أجد جواباً شافياً بعد:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=230499&SiteID=1

مشكلة غبية فعلاً☺

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مشرف مؤيد مارديني مشاركة 4

في Dec 4, 2007 03:43، قال وسام البهنسي بهدوء وتؤدة:

أما بالنسبة للجزء الثاني فإن الحل الذي خطر ببالي هو فصل الـ handler الخاص بالـ menu commands بين الحدثين BeginLabelEdit و EndLabelEdit، مما يعطي فرصة لصندوق النص أن يتصرف بشكل طبيعي مرة أخرى.


بالرغم من سهولة الحل إلا أنه ليس كاملاً، فقد يرغب المستخدم باستخدام الأمر Copy/Paste من القائمة لنسخ/لصق نص في وضع الـRenaming، ليجد أنه لا يعمل في هذه الحالة، و هكذا علينا أن نجبره على استخدام Ctrl+C/V أو الـContext Menu للتعامل مع النص.

Moayad Mardini,
MSDN Forums Moderator

خبير مشرف مؤيد مارديني مشاركة 5

حللتها، حللتها!
ها هو رابط تحميل المشروع بعد التعديل :
http://www.zshare.net/download/53710120bb53d1/

فكرة الحل :
-إزالة الخاصية ShortcutKeys لكل أمر موجود في القائمة.
-ضبط الخاصية ShortcutKeyDisplayString لكل أمر موجود في القائمة إلى القيمة التي كانت موجودة في الخاصية ShortcutKeys له (و هذا سيجعل كل أمر من أوامر القائمة "يبدو" و كأن له Shortcut Key في حين أنه ليس كذلك).
-إضافة Event handler للحدث KeyDown للـListView.
-استخدام الـKeyEventArgs لفحص الأزرار التي يقوم المستخدم بضغطها (فمثلاً إن ضغط C أثناء ضغط Ctrl و لم يكن في وضع إعادة التسمية و كان أحد عناصر القائمة مختاراً يقوم بنسخه).
-جعل كل أمر من أوامر القائمة (Copy مثلاً) يحاكي ضغط الأزرار المناسبة لهذا الأمر (مثلاً ضغط Ctrl+C) و ذلك عن طريق استخدام SendKeys.Send("^(C)");

Moayad Mardini,
MSDN Forums Moderator

خبير مدير وسام البهنسي مشاركة 6

شكراً جزيلاً مؤيد! سأجرب حلك قريباً (ربما على متن الطائرة) وأرد لك النتائج...

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مدير وسام البهنسي مشاركة 7

حسناً، كصلاحية فإنك حلك صالح، لكني وجدت حلاً آخر يتطلب عدداً أقل من التغييرات.
ببساطة، دع كل شيء على ما هو، وفقط أضف الكود الآتي إلى الـ form التي تحوي التحكم:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
   return labelEditing ? false : base.ProcessCmdKey(ref msg,keyData);
}

طبعاً المتغير labelEditing هو متغير يتم وضعه إلى true عند نداء BeginLabelEdit ويوضع إلى false عند EndLabelEdit...

على أنني كنت آمل في حل أبسط أيضاً (مثلاً تغيير Property اسمها FixShortcutProblem إلى true☺

جزيل الشكر،

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مشرف مؤيد مارديني مشاركة 8

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

شكراً،

Moayad Mardini,
MSDN Forums Moderator