كيف يمكنني الخضوع لسلسلة واجهة المستخدم لتحديث واجهة المستخدم أثناء إجراء المعالجة المجمعة في تطبيق WinForm؟

StackOverflow https://stackoverflow.com/questions/106036

  •  01-07-2019
  •  | 
  •  

سؤال

لدي تطبيق WinForms مكتوب بلغة C# مع .NET 3.5.يدير عملية دفعية طويلة.أريد أن يقوم التطبيق بتحديث حالة ما تفعله العملية المجمعة.ما هي أفضل طريقة لتحديث واجهة المستخدم؟

هل كانت مفيدة؟

المحلول

يبدو BackgroundWorker وكأنه الكائن الذي تريده.

نصائح أخرى

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

ربما لا تكمن المشكلة في ضرورة الاستسلام لسلسلة رسائل واجهة المستخدم ولكن في قيامك بالمعالجة على سلسلة رسائل واجهة المستخدم مما يمنعها من التعامل مع الرسائل.يمكنك استخدام مكون عامل الخلفية لإجراء المعالجة الدفعية على سلسلة رسائل مختلفة دون حظر سلسلة رسائل واجهة المستخدم.

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

لعرض التحديثات، تعد أشرطة التقدم أو نص شريط الحالة من الأساليب الأكثر شيوعًا.

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

للتعرف على ما يقوله الأشخاص حول DoEvents، إليك وصفًا لما يمكن أن يحدث.

لنفترض أن لديك نموذجًا يحتوي على بيانات وأن حدثك الطويل الأمد يقوم بحفظه في قاعدة البيانات أو إنشاء تقرير بناءً عليه.تبدأ في حفظ التقرير أو إنشائه، ثم تقوم بشكل دوري باستدعاء DoEvents بحيث تستمر الشاشة في الرسم.

لسوء الحظ، لا تقوم الشاشة بالرسم فحسب، بل ستتفاعل أيضًا مع إجراءات المستخدم.وذلك لأن DoEvents يوقف ما تفعله الآن لمعالجة كافة رسائل Windows التي تنتظر معالجتها بواسطة تطبيق Winforms.تتضمن هذه الرسائل طلبات إعادة الرسم، بالإضافة إلى قيام أي مستخدم بالكتابة والنقر وما إلى ذلك.

لذلك، على سبيل المثال، أثناء قيامك بحفظ البيانات، يمكن للمستخدم القيام بأشياء مثل جعل التطبيق يعرض مربع حوار مشروط لا علاقة له على الإطلاق بالمهمة طويلة الأمد (على سبيل المثال، تعليمات->حول).أنت الآن تتفاعل مع إجراءات المستخدم الجديدة داخل المهمة طويلة المدى قيد التشغيل بالفعل.ستعود DoEvents عند انتهاء جميع الأحداث التي كانت تنتظرك عند استدعائها، ومن ثم ستستمر مهمتك الطويلة الأمد.

ماذا لو لم يغلق المستخدم مربع الحوار المشروط؟تنتظر مهمتك الطويلة الأمد إلى الأبد حتى يتم إغلاق مربع الحوار هذا.إذا كنت ملتزمًا بقاعدة بيانات وتحتفظ بمعاملة، فأنت الآن تحتفظ بمعاملة مفتوحة بينما يتناول المستخدم القهوة.إما أن تنتهي مهلة المعاملة الخاصة بك وتفقد عملك المستمر، أو أن المعاملة لا تنتهي مهلة المعاملة ومن المحتمل أن تؤدي إلى طريق مسدود للمستخدمين الآخرين لقاعدة البيانات.

ما يحدث هنا هو أن Application.DoEvents يعيد إدخال الكود الخاص بك.انظر تعريف ويكيبيديا هنا.لاحظ بعض النقاط من أعلى المقالة، لكي يتم إعادة إدخال الكود، يجب:

  • يجب ألا تحتوي على بيانات ثابتة (أو عالمية) غير ثابتة.
  • يجب أن تعمل فقط على البيانات المقدمة لها من قبل المتصل.
  • يجب ألا تعتمد على أقفال الموارد المفردة.
  • يجب عدم استدعاء برامج الكمبيوتر أو إجراءات عدم إعادة الدخول.

من المستبعد جدًا أن تعمل التعليمات البرمجية طويلة الأمد في تطبيق WinForms فقط على البيانات التي تم تمريرها إلى الطريقة بواسطة المتصل، ولا تحتوي على بيانات ثابتة، ولا تحمل أي أقفال، ولا تستدعي سوى طرق إعادة الدخول الأخرى.

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

استخدم مكون عامل الخلفية لتشغيل المعالجة المجمعة في سلسلة رسائل منفصلة، ​​ولن يؤثر هذا بعد ذلك على سلسلة رسائل واجهة المستخدم.

أريد أن أكرر ما أشار إليه المعلقون السابقون:يرجى تجنب DoEvents() كلما أمكن ذلك، لأن هذا غالبًا ما يكون شكلاً من أشكال "الاختراق" ويسبب كوابيس الصيانة.

إذا ذهبت إلى طريقBackgroundWorker (الذي أقترحه)، فسيتعين عليك التعامل مع مكالمات الترابط المتقاطع إلى واجهة المستخدم إذا كنت تريد استدعاء أي أساليب أو خصائص لعناصر التحكم، حيث إنها مرتبطة بمؤشر الترابط ويجب استدعاؤها فقط من الخيط الذي تم إنشاؤه عليه.استخدم Control.Invoc() و/أو Control.BeginInvoc() بالشكل المناسب.

إذا كنت تعمل في موضوع الخلفية/العامل، يمكنك الاتصال Control.Invoke على أحد عناصر تحكم واجهة المستخدم الخاصة بك لتشغيل مفوض في مؤشر ترابط واجهة المستخدم.

Control.Invoke متزامن (ينتظر حتى يعود المندوب).إذا كنت لا تريد الانتظار يمكنك استخدام .BeginInvoke() لانتظار الأمر فقط.

القيمة العائدة ل .BeginInvoke() يسمح لك بالتحقق من اكتمال الطريقة أو الانتظار حتى اكتمالها.

يستخدم Backgroundworker, وإذا كنت تحاول أيضًا تحديث مؤشر ترابط واجهة المستخدم الرسومية عن طريق معالجة ملف ProgressChanged حدث (مثل، ل ProgressBar)، تأكد من ضبطه أيضًا WorkerReportsProgress=true, أو سيتوقف مؤشر الترابط الذي يُبلغ عن التقدم في المرة الأولى التي يحاول فيها الاتصال ReportProgress...

يتم طرح استثناء، ولكن قد لا تراه إلا إذا قمت بتمكين "عند الرمي"، وسيُظهر الإخراج أن مؤشر الترابط قد تم الخروج منه.

Application.DoEvents() أو ربما قم بتشغيل الدفعة في موضوع منفصل؟

كان DoEvents() هو ما كنت أبحث عنه ولكني قمت أيضًا بالتصويت لصالح إجابات العاملين في الخلفية لأن هذا يبدو حلاً جيدًا وسأقوم بالتحقيق فيه أكثر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top