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

السبت، 26 أكتوبر 2013

رسم مستطيل حول الصف الحالي في DataGridView


س: كيف يمكن رسم مستطيل حول الصف الحالي في جدول العرض DataGridView؟ 



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

Dim Row = DataGridView1.CurrentRow ' الصف الحالي

If Row Is Nothing Then Return ' لا يوجد صف محدد حاليا

If Row.Index < > e.RowIndex Then Return

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

إلى هنا وكل شيء بسيط.. لكن هناك بعض اللمسات التي يجب وضعها حتى يظهر المستطيل بشكل صحيح:

- فمن الأفضل ألا يحتوي المستطيل على خانة رأس الصف Header.. لهذا سنطرح من عرض المستطيل عرض هذه الخانة، ويمكن معرفته باستخدام الخاصية DataGridView.RowHeadersWidth.

- ليست هناك مشكلة إن كان عرض الصف أكبر من عرض جدول العرض (في هذه الحالة يظهر المنزلق الأفقي)، فكائن الرسوم سيرسم المستطيل داخل حدود جدول العرض، حتى لو حاولت أن تعطيه عرضا كبيرا جدا.. لكن المشكلة تحدث في الحالة العكسية، حينما يكون عرض الصف أصغر من عرض جدول العرض، ففي هذه الحالة سيظهر المستطيل بامتداد جدول لعرض كله، وسيكون أكبر من عرض الصف!.. لحل هذه المشكلة علينا أن نطرح من عرض المستطيل الفارق بين عرض الصف وعرض جدول العرض.. لتنفيذ هذا، يجب أن نتأكد أن آخر عمود في الجدول (وهو العمود رقم DataGridView.Columns.Count - 1) معروض على الشاشة حاليا باستخدام الخاصية Displayed الخاصة بكائن العمود.. ثم نستخدم الوسيلة DataGridView.GetColumnDisplayRectangle لنحصل على المستطيل الذي يحمل أبعاد هذا العمود.. هذه الوسيلة تستقبل رقم العمود، ولها معامل ثان إذا جعلته True، فإنها تعيد أبعاد الجزء المعروض من العمود وتستبعد المساحة المختفية من العمود.. هذا هو ما نريده هنا:

Dim X1 = 0

Dim I = DataGridView1.Columns.Count - 1

If DataGridView1.Columns(I).Displayed Then

    Dim ColRect = DataGridView1.GetColumnDisplayRectangle(I, True)

    X1 = ColRect.Left

End If

القيمة X1 التي حصلنا عليها في الكود السابق، سنطرحها من عرض المستطيل الذي سنرسمه.

-      نظرا لأننا نتعامل مع جدول عرض يظهر من اليمين إلى اليسار، فسنحتاج أيضا إلى تعديل موضع الحافة اليسرى للمستطيل ليبدأ من الحافة اليسرى للعمود الأخير.. أي القيمة X1 التي حصلنا عليها في الكود السابق!

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

SystemInformation.VerticalScrollBarWidth

لكن قبل أن نطرح هذه القيمة، يجب أن نعرف أولا إن كان المنزلق الرأسي معروضا أم لا.. يمكننا أن نعرف هذا إذا مررنا على جميع صفوف الجدول، زنجمع ارتفاعاتها فإن كانت أكبر من ارتفاع جدول العرض، فهذا معناه أن هناك حاجة لعرض المنزلق الرأسي.. لكن علينا أيضا أن نفحص قيمة الخاصية DataGridView1.ScrollBars لنتأكد أن عرض المنزلق الرأسي مسموح به.. هذا هو الكود الذي يفعل هذا:

Dim X2 = 0

Dim RowsHeight = 0

If DataGridView1.ScrollBars = ScrollBars.Both OrElse

         DataGridView1.ScrollBars = ScrollBars.Vertical Then

    For Each R As DataGridViewRow In DataGridView1.Rows

                RowsHeight += R.Height

    Next

    If RowsHeight > DataGridView1.Height Then

        X2 = SystemInformation.VerticalScrollBarWidth + 4

    End If

End If

والآن دعنا نعرّف القلم الذي سنرسم به، وليكن لونه بنيا:

Dim pen As New Pen(Color.Brown)

Dim penWidth As Integer = 2

pen.Width = penWidth

والآن دعنا نحسب أبعاد المستطيل الذي سنرسمه.. لاحظ أنا سنأخذ سمك خط الرسم في حساباتنا:

Dim X = If(X1 = 0, Rect.Left + (penWidth \ 2), X1)

Dim Y As Integer = Rect.Top + (penWidth \ 2)

Dim W As Integer = Rect.Width - penWidth –

        DataGridView1.RowHeadersWidth - (X1 - X2)

Dim H As Integer = Rect.Height - penWidth

أخيرا لم يبقَ إلا أن نرسم المستطيل حول الصف:

e.Graphics.DrawRectangle(pen, X, Y, W, H)

 

لو جربت البرنامج الآن، فسترى المستطيل يظهر حول الصف الحالي.. لكنك كلما انتقلت من صف إلى آخر، رأيت أجزاء من المستطيل ما زالت حول الصف السابق، بينما قد لا يظهر المستطيل حول الصف الجديد!

نحتاج إذن إلى طريقة لمحو المستطيل تماما من الصف السابق..

يمكن فعل هذا في الحدث RowEnter، الذي ينطلق قبيل دخول صف جديدز. في هذا الحدث تشير الخاصية DataGridView.CurrentRow إلى الصف السابق، بينما تشير الخاصية e.RowIndex إلى رقم الصف الذي سيصير الصف الحالي.. كل ما سنفعله هو إنعاش كلا الصفين باستدعاء الوسيلة DataGridView.InvalidateRow، التي تستقبل رقم الصف المراد إنعاش رسمه.. هذا هو كود هذا الحدث:

Dim LastRow = DataGridView1.CurrentRow

If LastRow IsNot Nothing Then

    DataGridView1.InvalidateRow(LastRow.Index)

End If

DataGridView1.InvalidateRow(e.RowIndex)

يمكنك الآن تجربة الكود.. ستجده يعمل على ما يرام.

لكن تتبقى مشكلة واحدة فقط، تحدث عند تحريك المنزلق الأفقي (إن كان ظاهرا)، فهذا يؤدي إلى تكرار رسم الإطار، ما يجعل الحافة اليسرى له ترسم أكثر من مرة داخل خانات الصف مع استمرار التحرك.. نحتاج إذن إلى إنعاش المستطيل كلما تحرك المنزلق الأفقي.. يمكن فعل هذا في الحدث DataGridView.Scroll كالتالي:

If e.ScrollOrientation = ScrollOrientation.HorizontalScroll AndAlso

                     DataGridView1.CurrentRow IsNot Nothing Then

    DataGridView1.InvalidateRow(DataGridView1.CurrentRow.Index)

End If

ملحوظة:

الطريقة التي استخدمناها لمعرفة ظهور المنزلق الرأسي فيها مشكلة، أنها لا تأخذ في الاعتبار مساحة المنزلق الأفقي عن كان ظاهرا.. في الحقيقة أنا أستخدم طريقة مختلفة، فقد أنشأت أداة جديدة ترث الأداة DataGridView، وهذا أتاح لي استخدام الوسائل والخصائص المحمية Protected في فئة جدول العرض، ومنها الخاصية DataGridView.VerticalScrollBar التي تعيد كائن المنزلق الرأسي، ومن خلاله يمكن استخدام الخاصية Visible لمعرفة إن كان ظاهرا أم لا، كما يمكن استخدام الخاصية Width لمعرفة عرضه.


 

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

إرسال تعليق

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

صفحة الشاعر