WPF الأساسيات : ربط البيانات DataBinding في WPF - نسخة قابلة للطباعة +- منتدى فيجوال بيسك لكل العرب | منتدى المبرمجين العرب (http://vb4arb.com/vb) +-- قسم : قسم لغات البرمجة الاخرى (http://vb4arb.com/vb/forumdisplay.php?fid=4) +--- قسم : قسم البرمجة بتقنية WPF (http://vb4arb.com/vb/forumdisplay.php?fid=86) +--- الموضوع : WPF الأساسيات : ربط البيانات DataBinding في WPF (/showthread.php?tid=4133) |
WPF الأساسيات : ربط البيانات DataBinding في WPF - Islam Ibrahim - 17-09-12 توضح هذه المقالة خطوة بخطوة كيف يمكنك ربط مصادر البيانات Data Sources مع أدوات تحكم WPF Controls - WPF وصفيًا من خلال لغة XAML., أو يدويًا (إجرائيًا) من خلال الكود, هذه المقالة تركّز بشكل أكبر على إيضاح مبادئ ربط البيانات في WPF بشكل عام. ولا تُناقش كيفية استخدام مصادر البيانات بشكل خاص, محتويات المقالة كالتالي:
ربط البيانات Data Binding هو آلية تسمح بتكوين ارتباط بين مصادر البيانات مع واجهة الاستخدام المرئية Graphical User Interface, مصادر البيانات قد تختلف حسب طبيعة التطبيق, عادةً مصادر البيانات تقيم في إحدى طبقات التطبيق والتي تسمّى Business Logic Layer, هذا الارتباط يُمكن أن يكون أُحادي الاتجاه أو ثنائي الاتجاه, في الوضع الأول يمكن فقط لأحد الطرفين (مصدر البيانات أو عنصر تحكم واجهة الاستخدام) التأثير على الطرف الآخر, وفي الوضع الثاني يكون التأثير بينهما متبادلاُ. في WPF عادةً ما يُشير المُصطلح Data Source إلى كائن في بيئة الدوت نت( .NET object ) هذا الكائن قد يكون مجموعة Collection مثل Lists أو Arrays , ملف XML, أو كائن خدمة ويب Web service object, أو كائن يشير إلى جدول في قاعدة بيانات Data table, أو كائن POCO بسيط ((Plain Old CLR Object, أو حتى عنصر تحكّم من عناصر WPF مثل Button . [SIZE="3"]نظام التبعيّة Dependency System في WPF, ومفاهيم ربط البيانات الأساسية[/SIZE] أضاف WPF نوعًا جديدًا من الخصائص, وهي خصائص التبعيّة Dependency Properties, والتي تم إدخالها لتبسيط المهام التي من أجلها تم تصميم WPF, مثل تنسيق المظهر Styling, ربط البيانات التلقائي Automatic Data Binding, التحريك Animation, والمزيد, قد تبدو هذه الإضافة غامضةُ بعض الشيء بالنسبة لك, لأنها ستُعقّد نوعًا ما الانطباع الذي أخذته عن كائنات Microsoft.NET الكلاسيكيّة: مجرّد حقول Fields, وإجرءات Methods وخصائص Properties بسيطة, لكن بمُجرّد أن تفهم المشاكل التي يحُلها نظام التبعيّة , ستُرحب كثيرًا بهذه الإضافة. خصائص التبعيًة Dependency Properties تعتمد في الحصول على القيم الخاصة بها على مًوفّر القيمة Value Provider, وقد تعتمد على أكثر من مُوفّر واحد, وقد يكون هذا المُوفّر إما تحريكًا Animation يقوم تلقائيًا بتغيير قيمة تلك الخاصيّة, أو عنصر أبوي Parent Element ينشر قيم خصائصه نحو العناصر الأبناء Child Elements, وهكذا. من أهم ميزات خصائص التبعيّة هو اعتمادها على النظام المبني على التنبيه عند حدوث تغيّر في القيم التي تحملها Change Notifications. الدافع وراء هذه الإضافة هو تمكين الميزات الغنيّة التي يُقدّمها WPF مباشرةً من خلال لغة تمييز التطبيقات القابلة للامتداد XAML, وما يجعل WPF يعتمد بشكل كبير على استخدام لغة التمييز هذه هو مبالغة نظام WPF في استخدام الخصائص, على سبيل المثال, عنصر التحكّم Button يحتوي على 111 خاصيّة (منها 98 موروثة من النوع Control وفئاته القاعدية), يُمكن تعيين قيم الخصائص لعناصر WPF بسهولة من خلال XAML (بطريقة مباشرة أو باستخدام أدوات التصميم مثل مُصمّم WPF المُضمن مع Visual Studio) , ولكن من دون اللجوء لكتابة بعض الكود الإضافي لن يكون من المُمكن الحصول على النتيجة المُنتظرة فقط بمجرد إسناد بعض القيم من خلال XAML, فلا تتوّقع أنك ستحصل على ميزات خصائص التبعيّة مثل الربط التلقائي باستخدام الخصائص العادية CLR Properties فقط. كيفية تعريف خصائص التبعيّة في الواقع كمطوّر WPF لست مُجبرًا على الإلمام بجميع التفاصيل الدقيقة لكيفية تعريف وكتابة خصائص التبعيّة, إلا إذا كنت تخطّط لكتابة أدوات تحكّم خاصة بك Custom Controls, وبشكل عام فإنك تحتاج فقط لفهم كيفية عملها, ومع مرور الوقت ستتمنّى لو كانت جميع الخصائص هي خصائص تبعيّة! لغات عائلة Microsoft.NET في الواقع لا تتفهّم خصائص التبعيّة, لهذا فإن واجهة برمجة WPF - WPF APIs هي المسؤولة عن إدارة تلك الخصائص, ولهذا السبب عمليًا ستحتاج لاستخدام بعض الفئات الأساسيّة Base Classes الموجودة في WPF APIs من أجل كتابة خصائص التبعيّة, المثال التالي هو أبسط شكل لما يُمكن أن تكون عليه خاصية التبعيّة, وهو عبارة عن تعريف للخاصية Content الموجودة في عنصر التحكّم Button والعناصر الأخرى الموروثة من النوع ContentControl. PHP كود : public class ContentControl : Control, IAddChild PHP كود : Public Class ContentControl الحقل المُشترك ContentProperty هو الذي يُمثّل خاصية التبعيّة, وهو من النوع System.Windows.DependencyProperty, وبالإجماع, جميع الحقول التي تُمثّل خصائص التبعيّة, تكون عامة public, ومُشتركة static, تنتهي تسميتها بالكلمة Property, منصّة WPF الأساسيّة تتطلب اتباع هذا الأسلوب في التعريف للعديد من الأغراض مثل أدوات الترجمة Localization, وأثناء القيام بتحميل ومُعالجة XAM, فإن WPF يخضع في معالجته لهذا الأسلوب. وفي العادة يتم إنشاء خصائص التبعيّة باستدعاء الإجراء DependencyProperty.Register والذي يتطلب المُدخلات التالية: اسم الخاصًية متبوعًا بالكلمة Propertyكما في المثال السابق(ContentProperty), نوع الخاصيّة (object), ونوع الفئة الحاوية على الخاصيًة (ContentControl), يُمكن اختياريًا باستخدام أحد ال Overloads للإجراء Register السابق تحديد كيفية مُعالجة WPF لتلك الخاصيّة, وكذلك الإجراءات CallBacks التي سيتم استدعاءؤُها عند تغيّر قيمة الخاصيّة, وكذا بعض المعلومات الأخرى. في التعريف السابق تم استدعاء الإجراء Register لإسناد القيمة null للخاصية Content, وتمرير مُفوّض Delegate يشير إلى الإجراء OnContentChanged من أجل التنبيه عند تغيّر قيمة الخاصية Content. ثم بعد ذلك تأتي الخاصيّة Content والتي يتم من خلال إجرائيها get و set استدعاء الإجرائين GetValue و SetValue على التوالي, والموروثة من النوع System.Windows.DependencyObject, الفئة المنخفضة المستوى, والتي ترث منها جميع الفئات التي تحتوي على خصائص التبعيّة, الإجراء GetValue يقوم باسترداد القيمة التي تمّ تخزينها باستخدام الإجراء SetValue, وفي حال لم يتم استدعاء هذا الإجراء (SetValue) مُسبقًا, سيتم استرداد القيمة الافتراضية والتي تمّ تمريرها للإجراء DependencyProperty.Register. وفي الواقع لست بحاجة لتعريف الخاصية Content, لأنه من المُمكن استدعاء الإجرائين GetValue و SetValue فهما إجراءان عامّان, ولكن استخدام الخصائص يجعل استهلاك خصائص التبعيّة مألوفًا لدى مستخدم هذه الخصائص, ومن جهة أخرى فهي تُمكّن من إسناد الفيم لها من خلال XAML. وقد تستغرب لما لا يستخدم الإجراءان Get/SetValue ال Generics, والسبب مبدئيًا هو ظهور مفاهيم خصائص التبعيّة قبل ظهور Generics. من جهة أخرى قد تستغرب لما يتم تعريف حقول خصائص التبعيّة على أنها مشتركة static, وحتى تفهم ذلك تخيّل العكس, ماذا لو تم تعريفها على أنها Instance Fields عندها ستحتاج من أجل كل عنصر أو مثيل Instance من Button حوالي 111 حقلاً أي 111 قيمة, ومن كل مثيل من Label حوالي 104 حقلاً أي 104 قيمة, وهكذا... عندها سيزيد استهلاك الذاكرة بشكل مريع! لكن خصائص التبعيّة جاءت لتحل هذه المشكلة, حيث أن الإجرائين Get/SetValue يستخدمان أسلوب فعّال واقتصادي لمنع هذا الاستهلاك الفضيع للذاكرة, حيث هناك من 89 خاصية تبعيّة من بين 111 خاصيّة للنوع Button, و 82 من بين 104 للنوع Label وهذا سيُحسّن بشكل كبير كميّة استهلاك الذاكرة, لا تسن أن الحقول المشتركة Static Fields يتم حفظ قيمها على مستوى النوع وليس على مستوى مثيل ذلك النوع Per-type values . فوائد خصائص التبعيّة تتجاوز تحسين استهلاك موارد النظام من الذاكرة, وتتعدّى ذلك إلى عزل المطوّر عن الكثير من الأعمال الشاقّة مثل التحقّق من وصول المسارات Threads Access, أو مطالبة العنصر الأبوي Parent Element الذي يحتوي على عنصر تحكّم ما بإعادة رسم نفسه, على سبيل المثال, تغيير قيمة الخاصيًة Background للعنصر Button, يحتاج لمطالبة هذا الأخير بإعادة رسم نفسه, وهذا مًمكن من خلال تمرير القيمة FrameworkPropertyMetaDataOptions.AffectRender للإجراء DependencyProperty.Register, بالإضافة إلى كل ذلك, هناك الفوائد الثلاثة الأخرى التي سيرد ذكرها فيما يلي. 1 - التنبيه بالتغيّرات Change Notification عندما يحدث تغيّر في قيمة خاصية التبعيّة, يقوم WPF بتشغيل مجموعة من الإجراءات, بناءًا على كيفية تعريف تلك الخاصية, قد يكون هذا الإجراء إعادة رسم عنصر معيّن في واجهة الاستخدام, أو إحداث تغيّر في تصميم واجهة الاستخدام, تحديث ربط البيانات, والمزيد. وإن من أهم ما يُميّز نظام التنبيه بالتغيّرات هذا هو مُشغّلات الخصائص Property Triggers, والتي تسمح لك بتشغيل إجرائك الخاص عند حدوث التغيّر دون الحاجة لكتابة كود إجرائي, على سبيل المثال, قد تريد تغيير لون خلفية عنصر تحكّم Button عند مرور مؤشر الماوس فوقه, واسترداد القيمة السابقة عند مغادرة المؤشر, مبدئيًا ستحتاج للقيام بذلك إلى إرفاق إجرائين للحدثين MouseEnter و MouseLeave, PHP كود : <Button MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave" /> PHP كود : <Button> ,كما لاحظت فإنه ليس بوُسعك إسناد مُشغّل بشكل مُباشر إلى عناصر WPF, بل ستحتاج لتعريف مظهر Style جديد وتعريف هذا المُشغّل بداخله ومن ثم تعيين هذا المظهر إلى العنصر الهدف كما في المثال السابق. 2 - وراثة الخصائص Property Inheritance وراثة الخصائص أو "وراثة قيم الخصائص" غير مُرتبطة مطلقًا بمفاهيم الوراثة في البرمجة المُوجهّة بالكائنات OOP, بل تعني انتقال قيمة خاصيّة معينّة من عنصر إلى آخر في شجرة العناصر Elements Tree, على سبيل المثال عندما تقوم بتحديد قيمة خاصيّة التبعيّة FontSize للإطار Window فإن هذه القيمة يتم توريثها لجميع العناصر الموجودة ضمن هذا الإطار مثل عناصر تحكّم Button, Label, إلى غير ذلك, جرّب لمثال التالي: PHP كود : <Window x:Class="WPFDependencyOverview.MainWindow" لكن هذه ليست قاعدة في كل الحالات, فهناك بعض الحالات التي لا يمكن فيها لعنصر أبوي أن يُورّث قيم أحد خصائصه لأحد عناصره الأبناء, في هذه الحالة إما أن العنصر الإبن يأخذ قيمته من مُوفّر القيمة ذي الأولوية الأكبر من العنصر الأبوي, كما في المثال السابق حيث أن عنصر TextBlock لم يتأثر بحجم الخط الذي تمّ تعيينه للإطار لأنه قد تم تعيين قيمته المحليّة Local Value إلى 36 بكسل والتي لها أولوية أعلى من العنصر الأبوي (Window), بينما تأثر كلّ من Button و TextBox بالقيمة الأبوية 18 بكسل, وإما أن الخاصيّة نفسها لم يتم تكوينها بالشكل الذي يجعلها قادرةُ على توريث قيمتها. في العادة يُمكن الحصول على هذا السلوك (توريث القيمة) من خلال تمرير القيمة FrameworkPropertyMetaDataOptions.Inherits إلى الإجراء DependencyProperty.Register . 3 - إمكانيّة تعدّد مُوفّري القيمة Multiple Value Providers Support تمّ تسمية خصائص التبعيّة Dependency Properties بهذا الإسم لانها تعتمد في الحصول على قيمهتا على مجموعة من موفّري القيمة Value Providers يعتمد WPF على آلية مُعقّدة بعض الشيء , وتتمّ هذه الآلية وفق ترتيب معيّن وفي مراحل. يمُّر WPF حاليًا على 5 مراحل من أجل تحديد تلك القيمة النهائيّة التي ستحملها الخاصيّة, يُمكن تلخيص تلك المراحل فيما يلي: المرحلة الأولى: تحديد القيمة الابتدائية Base Value: يشترك أغلب مُوفّري القيمة في عملية احتساب القيمة الابتدائية لخاصيّة مُحدّدة وفق ترتيب مُعيّن يخضع فيه هذا الترتيب لمبدأ الأسبقية (ما يُسمّى بـ Property Precedence), حيث أن مُوفّر القيمة ذي أعلى أسبقية هو الأحق بأن يُعطي قيمته لتلك للخاصية, يُمكن اختصار هذا الترتيب في القائمة التالية للقيم الناتجة من المُوفّر ذو الأسبقية العُليا إلى المُوفّر ذي الأسبقية السُفلى.
إذا لم يكن بوسعك معرفة مُوفّر القيمة الحالي لخاصيًة مُحدّدة, يمكنك استخدام الإجراء المُشترك DependencyPropertyHelper.GetValueSource, كوسيلة مُساعدة لذلك, هذا الإجراء يُعيد بعض البيانت المفيدة, من بينها قيمة من النوع BaseValueSource Enum والتي تُحدد مصدر القيمة الفعلي, يُمكنك مراجعة MSDN Library لمعرفة المزيد حول هذا الإجراء. المرحلة الثانية: اختبار القيمة Value Evaluating إذا كانت القيمة العائدة من المرحلة السابقة هي عبارة عن تعبير Expression (كائن مُشتق من النوع System.Windows.Expression) , سيقوم WPF باختبار ذلك التعبير للحصول على قيمة حقيقية, ال Expressions تستخدم في الغالب من أجل ربط البيانات Data Binding وهو ما سنتحدّث عنه في تكملة هذا المقال. المرحلة الثالثة: تطبيق التحريكات Apply Animations إذا كان هناك تحريك Animation يتم تطبيقه حاليًا على العنصر, فإن لهذا التحريك القدرة على تغيير قيمة الخاصيّة لهذا العنصر, لهذا من المُمكن للتحريك أن يُلغي مفعول كافة مُوفرّي القيمة السابقين في الخطوة الأولى ويُلغي كذلك القيمة الناتجة عن الخطوة الثانية. المرحلة الرابعة: تطبيق القيمة الاضطراريّة Apply Corece Value بعد الحصول على آخر قيمة "من المرحلة السابقة", يقوم WPF بتمرير تلك القيمة إلى المُفوّض CoerceValueCallback (إن تمّ تسجيله عند تعريف خاصيّة التبعيّة من خلال الإجراء DependencyPropery.Register), هذا المُفوّض مسؤول عن إرجاع قيمة جديدة قد تكون مُغايرة كليًا للقيمة الأخيرة بناءًا على منطق Logic مُحدّد, على سبيل المثال أداة ProgressBar تستخدم هذا المُفوّض لتقييد قيمة التقدّم Progress Value إلى أدنى قيمة لها Minimum إذا كانت الفيمة الأخيرة أقل من Minimum, أو إلى أعلى قيمة Maximum إذا كانت القيمة الأخيرة أكبر من Maximum (يُمكن تشبيه هذه الخطوة بـ Validation). المرحلة الخامسة: التحقّق من صلاحية القيمة Validation يقوم WPF بتمرير آخر قيمة تمّ تحصيلها من المرحلة السابقة إلى المُفوّض ValueValidateCallback (إن تمّ تسجيله عند تعريف خاصيّة التبعيّة من خلال الإجراء DependencyPropery.Register), هذا المُفوّض يجب أن يُعيد true إن كانت القيمة صالحة, أو يُعيد false إن لم تكن كذلك, الأمر الذي سيُسبّب إطلاق استثناء Exception. تعابير الربط Binding Expressions يستخدم WPF لتحقيق ربط البيانات الفئة System.Windows.Data.Binding والتي تعمل على وصل خاصيّتين مع بعضهما البعض ويبقي هذا الاتصال مغتوحًا بينهما, يُمكنك إنشاء هذا الاتصال مرّة واحدة وسيتولّى WPF إبقاهء على مدار عمر التطبيق, ويحتاج WPF لإنشاء الربط إلى أربع مُكونات أساسية:
لنفترض على سبيل المثال أنك تريد ربط الخاصية Content لعنصر Label ليقوم بعرض النص الذي يتم إدخاله داخل عنصر TextBox, ولنفترض أن اسم عنصر Label هو lblResult, وأن اسم عنصر TextBox هو txtBox, وعليه ستكون مُكونات الربط الأساسيّة وفق ما سبق كما يلي:
يُتبع ... WPF الأساسيات : ربط البيانات DataBinding في WPF - Islam Ibrahim - 17-09-12 تابع - الربط عنصرًا إلى عنصر Element To Element Binding أبسط سيناريو يُمكن به إنجاز عملية الربط هو حينما يكون الكائن المَصدر أحد عناصر WPF, وتكون الخاصيّة المَصدر عبارة عن Dependency Property, وهذا لأن خصائص التبعيّة مبنيّة أساسًا على دعم التنبيه بالتغيّرات Change Notification كما ورد سابقًا, ولهذا عندما تُغيّر من قيمة الخاصية المَصدر في الكائن المَصدر فإن الخاصيّة الهدف في الكائن الهدف تتغيّر تلقائيًا, وهذا ماتريده بالضبط, ولا تحتاج لإتمام ذلك أيّة مُتطلبات إضافية! وحتى تفهم كيف يُمكن ربط عنصر إلى آخر, لاحظ الصورة التالية التي تحتوي على عنصري تحكّم من عناصر WPF, شريط تمرير Silder, وأداة عرض النّص TextBlock, عندما تقوم بتحريك مُؤشِّر الشريط نحو اليمين يتزايد حجم النّص, وإذا قمت بتحريكه نحو الاتجاه المعاكس يتناقص حجم النّص. من الواضح أنه يُمكنك بسهولة إنجاز هذا السلوك من خلال الكود, من خلال مُعالجة الحدث Slider.ValueChanged ونسخ قيمة موضع المُؤشِّر الحالية وتعيين حجم الخط استنادًا إلى تلك القيمة, ولكن استخدام الربط عنصرًا إلى عنصر سيجعل ذلك أسهل! قبل البدء, لست بحاجة لإجراء أيّة تعديلات على الكائن المَصدر (شريط التمرير), كلّ ما تحتاجه فقط هو ضبط مجال القِيم كما تفعل في العادة. الربط سيكون بداخل العنصر TextBlock, وبدل أن تقوم بإسناد قيمة نصية للخاصيّة FontSize ستستخدم تعبير الربط Binding Expression كما يلي: PHP كود : <StackPanel> <Slider Name="sliderFontSize" Margin="3" Minimum="1" Maximum="32" Value="12" TickFrequency="1" TickPlacement="TopLeft"> </Slider> <TextBlock Margin="10" Text="Simple Text" Name="lblSampleText" FontSize="{Binding ElementName=sliderFontSize, Path=Value}"> </TextBlock> </StackPanel> تعابير الربط تستخدم امتدادات التمييز Markup Extensions (ولهذا تم إحاطتُها بـ {}) ,ويبتدأ تعبير الربط بالكلمة Binding لأنك هنا تقوم بإنشاء كائن من الفئة System.Windows.Data.Binding Class, ورغم ذلك يُمكنك تكوين كائن الربط هذا بعدّة طُرق, وفي مثل هذه الحالة نحتاج لإعداد خاصيّتين لهذا الكائن: الخاصيّة ElementName لتحديد اسم العنصر المَصدر, والخاصيّة Path لتحديد الخاصيّة المَصدر. لاحظ أن اسم الخاصيّة Path تم استخدامه هنا بدل الاسم Property لأن هذا المسار قد يكون إما اسم خاصيّة, وإما مُفهرِسًا Indexer, يُمكنك استخدام النقطة "." للفصل بين أسماء خصائص متداخلة مثل: Property1.Property2.Property3. إنشاء الربط إجرائيًا يُفضّل دائمًا عند إجراء عمليات الربط أن تستخدم XAML فهو أسهل وأكثر مرونة, ولكن ليس هناك ما يمنعك من إجراء الربط إجرائيًا من خلال الكود, وفي المثال التالي توضيح لكيفية إنشاء الربط المُستخدم في المثال السابق من خلال الكود: PHP كود : Binding binding = new Binding() { ElementName = "sliderFontSize", Path = new PropertyPath("Value"),}; lblSampleText.SetBinding(TextBlock.FontSizeProperty, binding); يًمكنك كذلك إلغاء هذا الربط ومسحه إجرائيًا من خلال إجراءات الفئة BindingOperations, الإجراء ClearBinding والذي يأخذ وسيطة من النوع DependencyProperty الذي يحمل الربط الذي تحاول مسحه, بينما الإجراء ClearAllBindings يقوم بمسح كافة الارتباطات لعنصر ما. PHP كود : BindingOperations.ClearAllBindings(lblSampleText); إن استخدام الربط من خلال XAML أكثر شيوعًا من الطريقة الإجرائية, لأنه أسهل وبتطلّب عملاً أقل, ولكن هناك حالتان خاصّتان قد تحتاج فيهما إلى استخدام الطريقة الإجرائية, الأولى عندما تحتاج إلى تكوين الربط بناءًا على شروط مُعيّنة, على سبيل المثال قد تحتاج إلى إجراء بعض عمليات التحقّق مثلاً من مُتغيّرات النظام قبل إنشاء الربط, وفي هذه الحالة يُمكنك تقليل حجم الكود بتعريف كائنات الربط مبدئيًا والاحتفاظ بها في موارد الإطار Window Resources من خلال XAML, وتكوين تلك الكائنات إجرائيًا بالشكل المطلوب كما سلف. وأما الحالة الثانيّة وهي عندما تريد مسح الربط والنخلّص منه, فلا يكفي ان تقوم بإسناد قيمة جديدة إلى الخاصية التي تريد مسحها بغرض التخلّص من الربط, فهذا لن يُؤثّر في الأمر شيئًا, بل ستحتاج لاستخدام الإجراء ClearBinding أو الإجراء ClearAllBindings كما سلف. أوضاع الربط والتحديث Data Binding/Updating Modes كما رأيت حتى الآن فإن مسار تغيّر الخصائص كان دائمًا من العنصر المَصدر إلى العنصر الهدف, ولكن من المُمكن بشكل ما أو بآخر أن تتغيّر قيمة الخاصية الهدف مُباشرةّ, ومن الجيّد في هذه الحالة أن ينعكس هذا التغيّر نحو العنصر المَصدر أيضًا, ففي المثال السابق قد تقوم من خلال الكود بتغيير قيمة الخاصية FontSize , عندها سستوقّع للحظة أن شريط التمرير سيقوم بتحديث نفسه تلقائيًا للقيمة ولكن هيهات! يُمكن حلّ هذه المسألة بساطة باستخدام الخاصية Mode للكائن Binding, حيث يُمكن التحكّم من خلالها في أوضاع مسار تغيّر البيانات, وهي كالتالي:
الصورة التالية تُلخّص الأوضاع السابقة: عندما تتعامل مع الربط ثنائي الاتجاه TwoWay, أو أحادي الاتجاه نحو المَصدر OneWayToSource, فإنك قد ترغب أحيانًا في التحكّم في الكيفية التي يتمّ بها تحديث الهدف, على سبيل المثال, عتدما يقوم المستخدم بإدخال البيانات في عنصر TextBox, قد ترغب في أن يحدث التغيير فقط عندما يقوم المستخدم بالانتقال إلى عنصر التحكم التالي بواسطة المفتاح Tab, أو كلّما قام المستخدم بكتابة حرف أو تغيير حرف في عنصر TextBox, أو أن تتحكّم بنفسك من خلال الكود في الوقت الذي ترغب فيه بإجراء التحديث. الخاصية UpdateSourceTrigger من نوع الترقيم UpdateSourceTrigger Enum والموجودة في الفئة Binding تُتيح لك التحكم في تلك الكيفية من خلال أوضاع هي كالتالي:
كما يوجد هناك وضع افتراضي بالنسبة لعملية الربط فإن هناك أيصًا وضعًا افتراضيًا بالنسبة لعملية التحديث, على سبيل المثال, فالخاصيّة TextBox.Text افتراضيًا يكون وضع التحديث الخاص بها هو LostFocus . خيارات الربط Data Binding Options حتى الآن جميع الأمثلة السابقة تُركّز على الربط بين عنصرين من عناصر WPF, ولكن في أغلب الأحيان في التطبيقات التي تعتمد على قواعد البيانات Data-Driven Applications مصادر البيانات ليست عناصر WPF (ليست أدوات تحكّم واجهة الاستخدام), بل تكون كما ورد في مقدمّة هذا المقال – كائنات غير مرئية – وتحتاج لتمثيلها من خلال عناصر WPF المرئية, ويجب أن يكون متاحًا الوصول إلى خصائص تلك الكائنات , بحيث تكون تلك الخصائص عامّة Public. عند إجراء الربط مع كائنات ليست من عناصر WPF يجب عليك استبعاد استخدام الخاصية ElementName للفئة Binding, وإنما عليك استخدام أحد البدائل التالية:
الخاصيّة Source يُمكنك التعامل مع الخاصيّة Source بشكل مُباشر, كلّ ما تحتاجه فقط هو أن يكون الكائن مَصدر البيانات جاهزًا للارتباط به, أبسط طريقة يُمكنك من خلالها استخدام هذه الخاصيّة هو أن تقوم بإسناد كائن ستاتيكي لها, قد يكون هذا الكائنن من كتابتك, او احد كائنات مكتبة Microsoft.NET, كما يُوضّح المثال التالي: PHP كود : <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"/> PHP كود : <Window.Resources> <FontFamily x:Key="MyFont">Calibri</FontFamily> </Window.Resources> PHP كود : <TextBlock Margin="10" Text="Simple Text" FontFamily="{Binding Source{StaticResource MyFont}}" Name="lblSampleText"> الخاصيّة RelativeSource هناك طريقة أخرى لتحديد الكائن مَصدر البيانات, عبر الخاصيّة Binding.RelativeSource, والتي تُشير إلى ذلك الكائن حسب علاقته مع الكائن الهدف, هذه الخاصيّة من النوع RelativeSource, والذي هو في الأصل مُلحق ترميز (موروث من النوع MarkupExtension), وفيما يلي بعض الاستخدامات الشائعة لهذه الخاصيّة:
PHP كود : {Binding RelativeSource={RelativeSource Self}}
PHP كود : {Binding RelativeSource={RelativeSource TemplatedParent}}
PHP كود : {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type desiredType}}}
PHP كود : {Binding RelativeSource={RelativeSource FindAncestor, AncestorLevel=n, AncestorType={x:Type desiredType}}} استخدام الخاصيّة RelativeSource مُفيد عند بناء قوالب الأدوات Control Templates , كما أن استخدام الخاصية Self مُفيد أيضًا في الكثير من الأحيان عندما تريد ربط خاصيّة لعنصر مع خاصيّة أخرى لنفس العنصر دون الحاجة لذكر اسم ذلك العنصر في تعبير الربط, المثال التالي سيُعطيك فكرة عن كيفية استخدامها, حيث سيتمّ ربط الخاصية ToolTip لعنصر TextBlock مع الخاصية Text لنفس العنصر, وبهذا سيتم عرض قيمة الخاصيّة Text ضمن ToolTip كلّما مررت فوق العنصر: PHP كود : <TextBlock Text="Some data" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}" /> الخاصيّة DataContext في بعض الأحيان, قد يكون لديك عدة عناصر WPF مرتبطة بنفس الكائن المَصدر, على سبيل المثال, افترض أن الكائن المصَدر سيكون كائنًا من النوع Person ويحتوي على الخصائص التالية: FName, LName, Age, وتريد أن تربط كل خاصيّة منها مع TextBox , وعليه سيكون لديك 3 عناصر TextBox ولتكن بداخل عنصر أبوي من نوع StackPanel, كما يُوضّح المثال التالي, في هذه الحالة قد تقوم بكتابة تعابير ربط طويلة نوعًا ما لكل عنصر TextBox لأنك ستقوم بتحديد الخاصية Source في تعابير الربط تلك, بالإضافة إلى المسار طبعًا: PHP كود : <StackPanel> <TextBlock Text="{Binding Source=myPerson, Path=FName}" /> <TextBlock Text="{Binding Source=myPerson, Path=LName}" /> <TextBlock Text="{Binding Source=myPerson, Path=Age}" /> </StackPanel> PHP كود : <StackPanel DataContext="{Binding Source=myPerson}"> PHP كود : <StackPanel DataContext="{Binding Source=myPerson}"> <TextBlock Text="{Binding Path=FName}" /> <TextBlock Text="{Binding Path=LName}" /> <TextBlock Text="{Binding Path=Age}" /> </StackPanel> RE: WPF الأساسيات : ربط البيانات DataBinding في WPF - farhat ali - 23-07-18 بارك الله فيك |