بالنسبة لجميع تطبيقات الويب لعرض البيانات تقريبًا، يعد تنظيم طريقة عرض البيانات وتجنب المشاعر المربكة للمستخدمين أحد الأهداف الرئيسية. من المؤكد أن عرض 20 سجلاً في كل صفحة أمر مقبول، ولكن عرض 10000 سجل في كل صفحة يمكن أن يسبب إزعاجًا للمستخدمين بسهولة. يعد تقسيم البيانات إلى صفحات متعددة للعرض، أي ترحيل البيانات، الطريقة الأكثر شيوعًا لحل مثل هذه المشكلات.
1. نظرة عامة
يوفر ASP.NET نفسه عنصر تحكم واحدًا فقط يدعم ترحيل البيانات، وهو عنصر تحكم ترحيل البيانات DataGrid، ومع ذلك، فهو أكثر ملاءمة للاستخدام في بيئات الإنترانت، ولا يبدو أن الوظائف التي يوفرها عنصر تحكم ترحيل البيانات DataGrid يكون كافيًا لإنشاء تطبيق ويب مرن. أحد الأسباب هو أن عنصر التحكم DataGrid يفرض قيودًا على المكان الذي يمكن لمصممي الويب وضع عناصر تحكم الترحيل فيه وعلى مظهر عناصر تحكم الترحيل على سبيل المثال، لا يسمح عنصر التحكم DataGrid بالموضع الرأسي لعناصر تحكم الترحيل. عنصر التحكم الآخر الذي يمكنه الاستفادة من تقنية الترحيل هو Repeater. يمكن لمطوري الويب استخدام عنصر تحكم Repeater لتكوين طريقة عرض البيانات بسرعة، لكن وظيفة الترحيل تتطلب من المطورين تنفيذها بأنفسهم. تتغير مصادر البيانات باستمرار، وتختلف طرق عرض البيانات بشكل كبير، ومن الواضح أن تخصيص عناصر التحكم في الترحيل لهذه الظروف المتغيرة سيساعد بشكل كبير في توفير الوقت.
لا يوفر التحكم الشامل الممتاز في البيانات وظائف الترحيل المنتظمة فحسب، بل يمكنه أيضًا:
⑴ قم بتوفير أزرار التنقل الخاصة بصفحات "الصفحة الرئيسية" و"الصفحة السابقة" و"الصفحة التالية" و"الصفحة الأخيرة".
⑵ اضبط حالته وفقًا لحالة عرض البيانات، أي أنه يتمتع بحساسية للبيانات. إذا تم تعيين عنصر تحكم الترحيل لعرض 10 سجلات لكل صفحة، ولكن هناك في الواقع 9 سجلات فقط، فلا ينبغي عرض عنصر تحكم الترحيل عند تقسيم البيانات إلى صفحات متعددة للعرض، "الصفحة الرئيسية" و"الأعلى". الصفحة الأولى لا ينبغي أن يتم عرض زر "الصفحة"، ولا يجب أن يتم عرض زري "الصفحة التالية" و"الصفحة الأخيرة" في الصفحة الأخيرة.
⑶ لا يمكن الاعتماد على عناصر تحكم محددة في عرض البيانات.
⑷ القدرة على التكيف مع مختلف مصادر البيانات الحالية والمستقبلية.
⑸ ينبغي تكوين وضع العرض بسهولة ودمجه بسهولة في التطبيقات المختلفة.
⑹ عندما يكون الترحيل جاهزًا، قم بتذكير عناصر التحكم الأخرى.
⑺ حتى مصممي الويب عديمي الخبرة يجب أن يكونوا قادرين على استخدامه دون صعوبة.
⑻ توفير بيانات السمات حول معلومات الترحيل.
توجد حاليًا بعض الضوابط التجارية في السوق التي توفر الوظائف المذكورة أعلاه، ولكنها جميعها باهظة الثمن. بالنسبة للعديد من المطورين، يعد إنشاء عنصر تحكم عالمي في الترحيل بنفسك هو الخيار المثالي.
يوضح الشكل 1 واجهة التشغيل لعنصر تحكم الترحيل العالمي في هذه المقالة، حيث يكون عنصر التحكم المستخدم للعرض هو عنصر تحكم مكرر. يتكون عنصر التحكم في الترحيل من نوعين من المكونات: أربعة أزرار تنقل ومجموعة من روابط أرقام الصفحات.
يمكن للمستخدمين بسهولة تغيير عناصر التحكم في العرض وتغيير مظهر عنصر تحكم الترحيل نفسه. على سبيل المثال، يتم استبدال عنصر تحكم العرض الذي يتعاون مع عنصر تحكم الترحيل بعنصر تحكم DataGrid، ويتم عرض رابط رقم الصفحة وأزرار التنقل الأربعة في صفين.
يدعم ASP.NET ثلاث طرق لإنشاء عناصر تحكم ويب مخصصة: عناصر تحكم المستخدم، وعناصر التحكم المركبة، وعناصر التحكم المخصصة. اسم النوع الثالث من عنصر التحكم، عنصر التحكم المخصص، مضلل بسهولة. في الواقع، ينبغي اعتبار كافة عناصر التحكم الثلاثة عناصر تحكم مخصصة. الفرق بين عناصر التحكم المركبة وما يسمى بعناصر التحكم المخصصة من Microsoft هو أن الأول يتطلب طريقة CreateChildControls() تسمح طريقة CreateChildControls() لعنصر التحكم بإعادة رسم نفسه بناءً على أحداث معينة. بالنسبة لجهاز النداء العام الخاص بهذه المقالة، سوف نستخدم عنصر تحكم مركب.
يوضح مخطط تسلسل UML التالي الآلية العامة للتحكم في الترحيل العالمي.
على الرغم من أن هدفنا هو جعل التحكم الشامل في الترحيل مستقلاً عن عنصر التحكم الذي يمثل البيانات، فمن الواضح أنه يجب أن تكون هناك طريقة ما للتحكم في الترحيل للوصول إلى البيانات. يوفر كل عنصر تحكم يرث من فئة التحكم حدث DataBinding. نقوم بتسجيل جهاز النداء نفسه كمستمع لحدث DataBinding، حتى يتمكن جهاز النداء من التعرف على البيانات وتعديلها. نظرًا لأن كافة عناصر التحكم التي ترث من فئة التحكم تحتوي على حدث DataBinding هذا، فإن عنصر تحكم النداء يحقق هدف عدم الاعتماد على عنصر تحكم عرض بيانات محدد - بمعنى آخر، يمكن ربط عنصر تحكم النداء بجميع عناصر التحكم المشتقة من فئة التحكم. أي أنه يمكن ربطه بجميع عناصر تحكم الويب تقريبًا.
2. الوظائف الأساسية
عندما يقوم عنصر تحكم العرض التقديمي بتشغيل حدث DataBinding، يمكن لعنصر تحكم الترحيل الحصول على خاصية DataSource. لسوء الحظ، لا توفر Microsoft واجهات تنفذها جميع فئات ربط البيانات، مثل IdataSourceProvider، وليس كل عناصر التحكم التي ترث من فئة Control أو WebControl تحتوي على خاصية DataSource، لذلك ليس هناك أي فائدة في الترقية إلى فئة Control، وهي الطريقة الوحيدة الممكنة الطريقة هي تشغيل خاصية DataSoruce مباشرة من خلال Reflection API. قبل مناقشة أساليب معالج الأحداث، تجدر الإشارة إلى أنه من أجل تسجيل معالج الأحداث، يجب عليك أولاً الحصول على مرجع لعنصر تحكم العرض التقديمي. يكشف عنصر تحكم الترحيل عن خاصية سلسلة بسيطة BindToControl:
السلسلة العامة BindToControl
{
يحصل
{
إذا (_bindcontrol == فارغة)
throw new NullReferenceException("قبل استخدام عنصر تحكم الترحيل، يرجى الارتباط بعنصر التحكم عن طريق تعيين خاصية BindToControl.");
إرجاع _bindcontrol؛}
مجموعة {_ربط التحكم = القيمة؛}
}
هذه الطريقة مهمة جدًا، لذا من الأفضل إرسال رسالة أكثر وضوحًا بدلاً من طرح NullReferenceException القياسي. في أسلوب OnInit للتحكم في الترحيل، نقوم بحل الإشارة إلى عنصر تحكم العرض التقديمي. يجب أن يستخدم هذا المثال معالج الأحداث OnInit (بدلاً من المنشئ) للتأكد من أن صفحة aspx المترجمة في JIT تحتوي على مجموعة BindToControl.
تجاوز محمي باطل OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
base.OnInit(e);
...
}
تكتمل عملية البحث في عنصر تحكم العرض التقديمي من خلال البحث في عنصر التحكم الأصلي لعنصر تحكم الترحيل، حيث يكون الأصل هو الصفحة نفسها. ومن الخطير استخدام الأصل بهذه الطريقة، على سبيل المثال، إذا كان عنصر تحكم الترحيل مضمنًا في عنصر تحكم آخر، مثل عنصر تحكم الجدول، فسيكون المرجع الأصل مرجعًا لعنصر تحكم الجدول. نظرًا لأن أسلوب FindControl يبحث فقط في مجموعة التحكم الحالية، فمن المستحيل البحث ما لم يكن عنصر تحكم العرض التقديمي موجودًا في المجموعة. الأسلوب الأكثر أمانًا هو البحث بشكل متكرر عبر مجموعة عناصر التحكم حتى يتم العثور على عنصر التحكم الهدف.
بعد العثور على BoundControl، نقوم بتسجيل عنصر تحكم الترحيل كمستمع لحدث DataBinding. نظرًا لأن التحكم في الترحيل يعمل على مصدر بيانات، فمن المهم أن يكون معالج الحدث هذا هو الأخير في سلسلة الاستدعاء. ومع ذلك، طالما أن عنصر تحكم العرض التقديمي يسجل معالج حدث DataBinding في معالج حدث OnInit (السلوك الافتراضي)، فلن تكون هناك مشكلة عندما يقوم عنصر تحكم الترحيل بتشغيل مصدر البيانات.
معالج الأحداث DataBound مسؤول عن الحصول على خاصية DataSource لعنصر تحكم العرض التقديمي.
الفراغ الخاص BoundControl_DataBound (مرسل الكائن، System.EventArgs e)
{
إذا عاد (HasParentControlCalledDataBinding)؛
اكتب type = sender.GetType();
_datasource = type.GetProperty("DataSource");
إذا (_datasource == فارغة)
throw new NotSupportedException("يتطلب عنصر تحكم الترحيل أن يحتوي عنصر تحكم العرض التقديمي على مصدر بيانات.");
بيانات الكائن = _datasource.GetGetMethod().Invoc(sender,null);
_builder = محولات[data.GetType()];
إذا (_builder == فارغ)
throw new NullReferenceException("لم يتم تثبيت المحول المناسب للتعامل مع نوع مصدر البيانات التالي:"+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
BindParent();
RaiseEvent(DataUpdate,this);
}
في DataBound، نحاول الحصول على خاصية DataSource من خلال Reflection API ثم نعيد مرجعًا إلى مصدر البيانات الفعلي. والآن بعد أن أصبح مصدر البيانات معروفًا، يجب أن يعرف عنصر تحكم الترحيل أيضًا كيفية العمل على مصدر البيانات. من أجل جعل التحكم في الترحيل لا يعتمد على عنصر تحكم عرض تقديمي محدد، تكون المشكلة أكثر تعقيدًا. ومع ذلك، فإن جعل التحكم في الترحيل يعتمد على مصدر بيانات محدد يبطل هدف تصميم عنصر تحكم مرن في الترحيل. نحن بحاجة إلى استخدام بنية مكون إضافي للتأكد من أن عنصر تحكم الترحيل يمكنه التعامل مع مصادر البيانات المختلفة، سواء كان مصدر البيانات المقدم من .NET أو مصدر بيانات مخصص.
من أجل توفير بنية قوية وقابلة للتوصيل، سنقوم ببناء حل باستخدام نمط [GoF] Builder.
الشكل 4:
تحدد واجهة IDataSourceAdapter العناصر الأساسية المطلوبة للتحكم في الترحيل لتشغيل البيانات، وهو ما يعادل "المكونات الإضافية".
معرف الواجهة العامة DataSourceAdapter
{
كثافة العمليات إجمالي الكونت {الحصول على؛}
كائن GetPagedData(int start,int end);
}
تقوم الخاصية TotalCount بإرجاع إجمالي عدد العناصر الموجودة في مصدر البيانات قبل معالجة البيانات، بينما يقوم الأسلوب GetPagedData بإرجاع مجموعة فرعية من البيانات الأصلية على سبيل المثال: بافتراض أن مصدر البيانات عبارة عن صفيف يحتوي على 20 عنصرًا، يتم عرض عنصر تحكم الترحيل البيانات كـ 10 عناصر لكل صفحة، ثم المجموعة الفرعية من العناصر في الصفحة الأولى هي عناصر المصفوفة 0-9، والمجموعة الفرعية من العناصر في الصفحة الثانية هي عناصر المصفوفة 10-19. يوفر DataViewAdapter مكونًا إضافيًا من نوع DataView:
فئة داخلية DataViewAdapter:IDataSourceAdapter
{
DataView _view الخاص؛
DataViewAdapter الداخلي (عرض DataView)
{
_view = view;
}
إجمالي عدد العمليات العام
{
الحصول على {return (_view == null) 0 : _view.Table.Rows.Count؛}
}
الكائن العام GetPagedData (بداية int، نهاية int)
{
جدول DataTable = _view.Table.Clone();
for (int i = start;i<=end && i<= TotalCount;i++)
{
table.ImportRow(_view[i-1].Row);
}
طاولة العودة
}
}
يقوم DataViewAdapter بتطبيق أسلوب GetPagedData الخاص بـ IDataSourceAdapter، والذي يقوم باستنساخ DataTable الأصلي واستيراد البيانات الموجودة في DataTable الأصلي إلى DataTable الجديد. تم تعيين رؤية هذه الفئة عن قصد على المستوى الداخلي لإخفاء تفاصيل التنفيذ عن مطوري الويب وتوفير واجهة أبسط من خلال فئة Builder.
فئة مجردة عامة AdaptorBuilder
{
كائن خاص _ مصدر
خاص باطل CheckForNull ()
{
if (_source == null) throw new NullReferenceException("يجب توفير مصدر بيانات قانوني");
}
مصدر الكائن الظاهري العام
{
يحصل
{
CheckForNull();
إرجاع _ المصدر؛}
تعيين
{
_ المصدر = القيمة؛
CheckForNull();
}
}
ملخص عام محول IDataSourceAdapter {get؛}
}
توفر فئة مجردة AdaptorBuilder واجهة أكثر قابلية للإدارة لنوع IdataSourceAdapter. نظرًا لزيادة مستوى التجريد، لم يعد يتعين علينا استخدام IdataSourceAdapter مباشرة. في نفس الوقت، يوفر AdaptorBuilder أيضًا إرشادات لإجراء المعالجة المسبقة قبل ترحيل البيانات. بالإضافة إلى ذلك، يجعل هذا المنشئ أيضًا فئة التنفيذ الفعلية، مثل DataViewAdapter، شفافة لمستخدمي عنصر تحكم الترحيل:
public class DataTableAdapterBuilder:AdapterBuilder
{
Private DataViewAdapter _adapter;
Private DataViewAdapter ViewAdapter
{
يحصل
{
إذا (_adapter == فارغ)
{
جدول DataTable = (DataTable)Source؛
_adapter = new DataViewAdapter(table.DefaultView);
}
محول العودة؛
}
}
التجاوز العام لمحول IDataSourceAdapter
{
يحصل
{
إرجاع محول العرض؛
}
}
}
الفئة العامة DataViewAdapterBuilder:AdapterBuilder
{
Private DataViewAdapter _adapter;
Private DataViewAdapter ViewAdapter
{
يحصل
{ // تأخير الإنشاء
إذا (_adapter == فارغ)
{
_adapter = new DataViewAdapter((DataView)Source);
}
محول العودة؛
}
}
التجاوز العام لمحول IDataSourceAdapter
{
الحصول على {عودة ViewAdapter؛}
}
}
يرتبط نوع DataView ونوع DataTable ارتباطًا وثيقًا لدرجة أنه قد يكون من المنطقي إنشاء DataAdapter عام. في الواقع، يكفي مجرد إضافة مُنشئ آخر يتعامل مع DataTable. لسوء الحظ، عندما يحتاج المستخدمون إلى وظائف مختلفة للتعامل مع DataTable، يجب استبدال الفئة بأكملها أو توريثها. إذا قمنا بإنشاء منشئ جديد يستخدم نفس IdataSourceAdapter، فسيتمتع المستخدم بمزيد من الحرية في اختيار كيفية تنفيذ المحول.
في عنصر تحكم الترحيل، يتم إكمال عملية البحث عن فئة المنشئ المناسبة بواسطة مجموعة آمنة من النوع.
الفئة العامة محول مجموعة:DictionaryBase
{
سلسلة خاصة GetKey (اكتب المفتاح)
{
مفتاح الإرجاع. الاسم الكامل؛
}
مجموعة المحولات العامة () {}
إضافة publicvoid (مفتاح النوع، قيمة AdapterBuilder)
{
Dictionary.Add(GetKey(key),value);
}
يحتوي publicbool على (اكتب المفتاح)
{
return Dictionary.Contains(GetKey(key));
}
إزالة publicvoid (اكتب المفتاح)
{
Dictionary.Remove(GetKey(key));
}
هذا المحول العام [اكتب المفتاح]
{
الحصول على {return (AdapterBuilder)Dictionary[GetKey(key)];}
مجموعة{قاموس[GetKey(مفتاح)]=قيمة;}
}
}
يعتمد AdaptorCollection على نوع DataSource، ويتم تقديم DataSource بذكاء من خلال BoundControl_DataBound. مفتاح الفهرس المستخدم هنا هو طريقة Type.FullName، والتي تضمن تفرد مفتاح الفهرس لكل نوع، وفي الوقت نفسه، تقوم أيضًا بتعيين مسؤولية التأكد من وجود منشئ واحد فقط لكل نوع لـ AdaptorCollection. أضف بحث Builder إلى الأسلوب BoundControl_DataBound وستكون النتائج كما يلي:
محولات AdaptorCollection العامة
{
الحصول على {return _adapters؛}
}
منطقي خاص HasParentControlCalledDataBinding
{
الحصول على {return _builder ! = null؛}
}
باطلة خاصة BoundControl_DataBound(object sender,System.EventArgs e)
{
إذا عاد (HasParentControlCalledDataBinding)؛
اكتب type = sender.GetType();
_datasource = type.GetProperty("DataSource");
إذا (_datasource == فارغة)
throw new NotSupportedException("يتطلب عنصر تحكم الترحيل أن يحتوي عنصر تحكم العرض التقديمي على مصدر بيانات.");
بيانات الكائن = _datasource.GetGetMethod().Invoc(sender,null);
_builder = محولات[data.GetType()];
إذا (_builder == فارغ)
throw new NullReferenceException("لم يتم تثبيت المحول المناسب للتعامل مع نوع مصدر البيانات التالي:"+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
BindParent();
RaiseEvent(DataUpdate,this);
}
يستخدم الأسلوب BoundControl_DataBound HasParentControlCalledDataBinding للتحقق مما إذا كان قد تم إنشاء المنشئ. إذا كان الأمر كذلك، فلن يقوم بعد ذلك بإجراء عملية البحث عن المنشئ المناسب. تتم تهيئة جدول المحولات في المُنشئ:
public Pager()
{
SelectedPager=new System.Web.UI.WebControls.Style();
UnselectedPager = new System.Web.UI.WebControls.Style();
_adapters = new AdapterCollection();
_adapters.Add(typeof(DataTable),new DataTableAdapterBuilder());
_adapters.Add(typeof(DataView),new DataViewAdapterBuilder());
}
الطريقة الأخيرة التي سيتم تنفيذها هي BindParent، والتي تُستخدم لمعالجة البيانات وإرجاعها.
BindParent () باطلة خاصة
{
_datasource.GetSetMethod().Invoc(BoundControl,
كائن جديد[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});
}
هذه الطريقة بسيطة جدًا، لأن معالجة البيانات تتم بالفعل بواسطة المحول. بعد اكتمال هذه العملية، سنستخدم Reflection API مرة أخرى، ولكن هذه المرة لتعيين خاصية DataSource الخاصة بعنصر تحكم العرض التقديمي.
3. تصميم الواجهة
حتى الآن، تم تنفيذ الوظائف الأساسية للتحكم في الترحيل تقريبًا، ولكن إذا كان هناك نقص في طرق العرض المناسبة، فلن يكون التحكم في الترحيل مفيدًا جدًا.
من أجل فصل طريقة العرض بشكل فعال عن منطق البرنامج، فإن أفضل طريقة هي استخدام القوالب، أو لنكون أكثر تحديدًا، استخدم واجهة Itemplate. في الواقع، تدرك Microsoft بوضوح قوة القوالب وتستخدمها في كل مكان تقريبًا، حتى في محلل الصفحة نفسه. لسوء الحظ، القوالب ليست مفهومًا بسيطًا كما يعتقد البعض، ويستغرق الأمر بعض الوقت لفهم جوهرها حقًا، ولحسن الحظ، هناك الكثير من المعلومات في هذا المجال، لذلك لن أخوض في التفاصيل هنا. وبالعودة إلى التحكم في الترحيل، فهو يحتوي على أربعة أزرار: الصفحة الرئيسية، الصفحة السابقة، الصفحة التالية، الصفحة الأخيرة، وبالطبع رقم كل صفحة. يتم تحديد أزرار التنقل الأربعة من فئة ImageButton بدلاً من فئة LinkButton. من منظور تصميم الويب الاحترافي، من الواضح أن الأزرار الرسومية أكثر فائدة من الروابط الرتيبة.
ImageButton العام FirstButton {الحصول على {return First؛}}
ImageButton العام LastButton {الحصول على {return Last؛}}
عامة ImageButton PreviousButton {احصل على {return السابق؛}}
public ImageButton NextButton{get {return Next;}}
يتم إنشاء أرقام الصفحات ديناميكيًا لأنها تعتمد على عدد السجلات في مصدر البيانات وعدد السجلات المعروضة في كل صفحة. ستتم إضافة رقم الصفحة إلى اللوحة، ويمكن لمصممي الويب استخدام اللوحة لتحديد مكان عرض رقم الصفحة. سيتم مناقشة عملية إنشاء أرقام الصفحات بالتفصيل لاحقًا، نحتاج الآن إلى توفير قالب للتحكم في الترحيل حتى يتمكن المستخدمون من تخصيص مظهر عنصر التحكم في الترحيل.
[حاوية القالب(typeof(LayoutContainer))]
تخطيط ITemplate العام
{
الحصول على {العودة (_layout؛}
مجموعة {_تخطيط = القيمة؛}
}
فئة عامة LayoutContainer:Control,INamingContainer
{
publicLayoutContainer()
{this.ID = "الصفحة"؛}
}
توفر فئة LayoutContainer حاوية للقوالب. بشكل عام، من الجيد دائمًا إضافة معرف مخصص إلى حاوية القالب، مما سيتجنب المشكلات عند التعامل مع الأحداث وإجراء استدعاءات الصفحة. يصف مخطط UML التالي آلية العرض الخاصة بعنصر تحكم الترحيل.
الشكل 5:
الخطوة الأولى في إنشاء قالب هي تحديد التخطيط في صفحة aspx:
<التخطيط>
<asp:ImageButton id = "First" Runat = "server" imageUrl = "play2L_dis.gif"
AlternateText="الصفحة الرئيسية"></asp:ImageButton>
<asp:ImageButton id = "السابق" Runat = "الخادم" imageUrl = "play2L.gif"
AlternateText="الصفحة السابقة"></asp:ImageButton>
<asp:ImageButton id = "التالي" Runat = "الخادم" imageUrl = "play2.gif"
AlternateText="الصفحة التالية"></asp:ImageButton>
<asp:ImageButton id = "الأخير" Runat = "الخادم" imageUrl = "play2_dis.gif"
AlternateText="الصفحة الأخيرة"></asp:ImageButton>
<asp:Panel id="Pager" Runat="server"></asp:Panel>
</LAYOUT>
لا يحتوي مثال التخطيط هذا على أي عناصر تنسيق، مثل الجداول وما إلى ذلك. بالطبع، يمكن (ويجب) للتطبيقات الفعلية إضافة عناصر تنسيق، يرجى الاطلاع على المزيد من الإرشادات لاحقًا.
توفر واجهة Itemplate طريقة واحدة فقط InstantiateIn، والتي تقوم بتحليل القالب وربط الحاوية.
InstantiateTemplate () الفراغ الخاص
{
_container = new LayoutContainer();
Layout.InstantiateIn(_container);
First = (ImageButton)_container.FindControl("First");
السابق = (ImageButton)_container.FindControl("السابق");
Next = (ImageButton)_container.FindControl("Next");
Last = (ImageButton)_container.FindControl("Last");
هولدر = (لوحة)_container.FindControl("بيجر");
this.First.Click += new System.Web.UI.ImageClickEventHandler(this.First_Click);
this.Last.Click += new System.Web.UI.ImageClickEventHandler(this.Last_Click);
this.Next.Click += new System.Web.UI.ImageClickEventHandler(this.Next_Click);
this.Previous.Click += new System.Web.UI.ImageClickEventHandler(this.Previous_Click);
}
أول ما يفعله أسلوب InstatiateTemplate الخاص بعنصر التحكم هو إنشاء مثيل للقالب، أي استدعاء Layout.InstantiateIn(_container). الحاوية هي في الواقع نوع من التحكم، واستخدامها مشابه لعناصر التحكم الأخرى. تستخدم طريقة InstantiateTemplate هذه الميزة للعثور على أزرار التنقل الأربعة واللوحة المستخدمة للاحتفاظ برقم الصفحة. يتم العثور على أزرار التنقل من خلال معرفاتها، وهذا قيد صغير على عناصر التحكم في الترحيل: يجب أن تحتوي أزرار التنقل على معرفات محددة، وهي الأول والسابق والتالي والأخير بالإضافة إلى ذلك، يجب أن يكون معرف اللوحة هو جهاز النداء، وإلا فلن يكون كذلك وجد. لسوء الحظ، يبدو أن هذا هو النهج الأفضل لآلية العرض التي اخترناها؛ ولكن نأمل، مع التوثيق المناسب، أن هذا القيد البسيط لن يسبب أي مشاكل. البديل الآخر هو السماح لكل زر بأن يرث من فئة ImageButton، وبالتالي تحديد نوع جديد نظرًا لأن كل زر هو نوع مختلف، يمكن تنفيذ بحث متكرر في الحاوية للعثور على أزرار محددة مختلفة، مما يلغي الحاجة إلى استخدام معرف الزر؛ يصف.
بعد العثور على الأزرار الأربعة، قم بربط معالجات الأحداث المناسبة بها. يجب اتخاذ قرار مهم هنا، وهو متى يتم استدعاء InstantiateTemplate. بشكل عام، يجب استدعاء هذا النوع من الأساليب في أسلوب CreateChildControls، لأن الغرض الرئيسي من أسلوب CreateChildControls هو هذا النوع من المهام المتمثلة في إنشاء عناصر تحكم فرعية. نظرًا لأن عنصر تحكم الترحيل لا يعدل أبدًا عناصر التحكم الفرعية الخاصة به، فهو لا يحتاج إلى الوظيفة التي توفرها CreateChildControls لتعديل حالة العرض بناءً على بعض الأحداث. كلما تم عرض عنصر تحكم الطفل بشكل أسرع، كان ذلك أفضل، لذا فإن المكان المثالي لاستدعاء أسلوب InstantiateTemplate هو حدث OnInit.
تجاوز محمي باطل OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
InstantiateTemplate();
Controls.Add(_container);
base.OnInit(e);
}
بالإضافة إلى استدعاء الأسلوب InstantiateTemplate، يكون للأسلوب OnInit أيضًا مهمة هامة أخرى تتمثل في إضافة الحاوية إلى عنصر تحكم الترحيل. إذا لم تقم بإضافة الحاوية إلى مجموعة التحكم الخاصة بجهاز النداء، فلن يتم عرض القالب لأنه لن يتم استدعاء أسلوب العرض أبدًا.
يمكن أيضًا تعريف القوالب برمجيًا من خلال تنفيذ واجهة Itemplate. بالإضافة إلى كونها إجراءً لزيادة المرونة، يمكن أن توفر هذه الميزة أيضًا قالبًا افتراضيًا للاستخدام عندما لا يوفر المستخدم قالبًا من خلال صفحة aspx.
الفئة العامة DefaultPagerLayout:ITemplate
{
ImageButton الخاص التالي؛
ImageButton الخاص أولاً؛
ImageButton الخاص الأخير؛
ImageButton الخاص السابق؛
جهاز النداء الخاص
باللوحة العامة؛
{
التالي = زر ImageButton الجديد ()؛
First = new ImageButton();
Last = new ImageButton();
السابق = زر ImageButton الجديد ()؛
Pager = new Panel();
Next.ID="Next"; Next.AlternateText="الصفحة التالية";Next.ImageUrl="play2.gif";
First.ID="First"; First.AlternateText="Home";First.ImageUrl="play2L_dis.gif";
Last.ID = "Last"; Last.AlternateText = "الصفحة الأخيرة";
Previous.ID="السابق"; Previous.AlternateText="الصفحة السابقة";Previous.ImageUrl="play2L.gif";
Pager.ID = "بيجر"؛
}
الفراغ العام InstantiateIn (التحكم في التحكم)
{
control.Controls.Clear();
جدول الجدول = جدول جديد ()؛
table.BorderWidth = Unit.Pixel(0);
table.CellSpacing= 1;
table.CellPadding =0;
صف TableRow = جديد TableRow();
Row.VerticalAlign = VerticalAlign.Top;
table.Rows.Add(row);
خلية TableCell = جديدة TableCell();
cell.HorizontalAlign = HorizontalAlign.Right;
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(First);
cell.Controls.Add(Previous);
Row.Cells.Add(cell);
cell = new TableCell();
cell.HorizontalAlign= HorizontalAlign.Center;
cell.Controls.Add(Pager);
Row.Cells.Add(cell);
cell = new TableCell();
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(Next);
cell.Controls.Add(Last);
Row.Cells.Add(cell)
;
}
}
يوفر DefaultPagerLayout كافة عناصر التنقل برمجيًا ويضيفها إلى صفحة aspx، ولكن هذه المرة يتم تنسيق عناصر التنقل باستخدام جداول HTML القياسية. الآن، إذا لم يقدم المستخدم قالب العرض التقديمي، فسيقوم البرنامج تلقائيًا بتوفير القالب الافتراضي.
[TemplateContainer(typeof(LayoutContainer))]
تخطيط ITemplate العام
{
الحصول على {return (_layout == null) جديد DefaultPagerLayout():_layout;}
مجموعة {_تخطيط = القيمة؛}
}
دعونا نلقي نظرة على عملية إنشاء رقم كل صفحة. يحتاج عنصر تحكم الترحيل أولاً إلى تحديد بعض قيم الخصائص، واستخدام قيم الخصائص هذه لتحديد عدد أرقام الصفحات المختلفة التي سيتم إنشاؤها.
الصفحة الحالية العامة
{
يحصل
{
string cur = (string)ViewState["CurrentPage"];
العودة (cur == string.Empty || cur ==null) 1 : int.Parse(cur);
}
تعيين
{
ViewState["CurrentPage"] = value.ToString();}
}
كثافة العمليات العامة PagersToShow
{
الحصول على {عودة _results؛}
مجموعة {_النتائج = القيمة؛}
}
كثافة العمليات العامة ResultsToShow
{
الحصول على {return _resultsperpage؛}
مجموعة{_resultsperpage = القيمة؛}
}
يقوم CurrentPage فعليًا بحفظ الصفحة الحالية في حالة العرض الخاصة برقم الصفحة، وتسمح الخصائص المحددة بواسطة أسلوب PagersToShow للمستخدم بتحديد عدد الصفحات التي سيتم عرضها، بينما تسمح الخصائص المحددة بواسطة ResultsToShow للمستخدم بتحديد عدد السجلات التي سيتم عرضها. القيمة الافتراضية هي 10.
تقوم الدالة NumberofPagersToGenerate بإرجاع العدد الحالي من أرقام الصفحات التي يجب إنشاؤها.
تسلسل النداء الخاص
{
يحصل
{
إرجاع تحويل.ToInt32
(Math.Ceiling((double)CurrentPage/(double)PagersToShow));}
}
عدد العمليات الخاصة NumberOfPagersToGenerate
{
الحصول على {return PagerSequence*PagersToShow;}
}
كثافة العمليات الخاصة TotalPagesToShow
{
الحصول على{return Convert.ToInt32(Math.Ceiling((double)TotalResults/(double)_resultsperpage));}
}
إجمالي النتائج العامة
{
الحصول على {return _builder.Adapter.TotalCount؛}
}
يقوم الأسلوب TotalPagesToShow بإرجاع العدد الإجمالي للصفحات التي سيتم عرضها، والتي تم ضبطها بواسطة خاصية ResultsToShow المعينة مسبقًا الخاصة بالمستخدم.
على الرغم من أن ASP.NET يقوم بتعريف بعض الأنماط الافتراضية، إلا أنها قد لا تكون عملية جدًا لمستخدمي عناصر التحكم في الترحيل. يمكن للمستخدمين ضبط مظهر عنصر تحكم الترحيل من خلال الأنماط المخصصة.
النمط العام UnSelectedPagerStyle {get {return UnselectedPager;}}
النمط العام SelectedPagerStyle {get {return SelectedPager;}}
يوفر UnSelectedPagerStyle النمط المستخدم عند عدم تحديد رقم الصفحة، ويوفر SelectedPagerStyle النمط المستخدم عند تحديد رقم الصفحة.
GeneratePagers (التحكم في WebControl) باطلة خاصة
{
control.Controls.Clear();
int بيجر = (PagerSequence-1)* PagersToShow +1;
for (;pager<=NumberOfPagersToGenerate &&pager<=TotalPagesToShow;pager++)
{
LinkButton link = new LinkButton();
link.Text = pager.ToString();
link.ID = pager.ToString();
link.Click += new EventHandler(this.Pager_Click);
إذا (link.ID.Equals(CurrentPage.ToString()))
link.MergeStyle(SelectedPagerStyle);
آخر
link.MergeStyle(UnSelectedPagerStyle);
control.Controls.Add(link);
control.Controls.Add(new LiteralControl(" "));
}
}
GeneratePagers () باطلة خاصة
{
GeneratePagers(Holder);
}
يقوم الأسلوب GeneratePagers بإنشاء كافة أرقام الصفحات بشكل حيوي، وهي عبارة عن أزرار من النوع LinkButton. يتم تعيين سمات التسمية والمعرف لكل رقم صفحة من خلال حلقة، وفي الوقت نفسه، يرتبط حدث النقر بمعالج الحدث المناسب. وأخيرًا، تتم إضافة رقم الصفحة إلى عنصر تحكم الحاوية - في هذه الحالة، كائن اللوحة. يعمل معرف الزر على تحديد الزر الذي أدى إلى تشغيل حدث النقر. فيما يلي تعريف معالج الأحداث:
Pager_Click (object sender, System.EventArgs e) باطلة خاصة
{
زر LinkButton = (LinkButton) المرسل؛
CurrentPage = int.Parse(button.ID);
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvocer.Pager));
تحديث()؛
}
Next_Click (مرسل الكائن، System.Web.UI.ImageClickEventArgs e) باطل خاص
{
إذا (CurrentPage<TotalPagesToShow)
CurrentPage++;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvocer.Next));
تحديث()؛
}
الفراغ الخاص السابق_النقر (مرسل الكائن، System.Web.UI.ImageClickEventArgs e)
{
إذا (الصفحة الحالية > 1)
الصفحة الحالية--;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvocer.Previous));
تحديث()؛
}
الفراغ الخاص First_Click (مرسل الكائن، System.Web.UI.ImageClickEventArgs e)
{
الصفحة الحالية = 1؛
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvocer.First));
تحديث()؛
}
Last_Click باطل خاص (مرسل الكائن، System.Web.UI.ImageClickEventArgs e)
{
CurrentPage = TotalPagesToShow;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvocer.Last));
تحديث()؛
}
تتشابه معالجات الأحداث هذه من حيث أنها تقوم أولاً بتغيير الصفحة الحالية لعنصر تحكم الترحيل ثم تحديث عنصر التحكم المنضم.
تحديث باطل خاص ()
{
إذا عاد (!HasParentControlCalledDataBinding) ؛
ApplyDataSensitivityRules();
BindParent();
BoundControl.DataBind();
}
أولاً، يتحقق عنصر تحكم الترحيل مما إذا كان قد تمت تهيئة المحولات الضرورية عن طريق استدعاء الأسلوب HasParentControlCalledDataBinding. إذا كان الأمر كذلك، فقم بتطبيق القواعد المشار إليها مسبقًا لضبط عناصر التحكم تلقائيًا استنادًا إلى شروط عرض البيانات إلى عنصر التحكم الحالي، مما يجعل عنصر تحكم الترحيل يتصرف بشكل مختلف استنادًا إلى الشروط المختلفة للبيانات الموجودة في BoundControl. على الرغم من أنه يتم التحكم في هذه القواعد داخليًا عن طريق التحكم في الترحيل، إلا أنه يمكن نقلها بسهولة خارج التحكم باستخدام وضع الحالة [GoF] عند الضرورة.
المنطق العام IsDataSensitive
{
الحصول على {العودة _isdatasensitive؛}
مجموعة {_isdatasensitive = القيمة؛}
}
منطق خاص IsPagerVisible
{
الحصول على {return (TotalPagesToShow != 1) && IsDataSensitive;}
}
المنطق الخاص IsPreviousVisible
{
يحصل
{
العودة (!IsDataSensitive) صحيح:
(الصفحة الحالية != 1);
}
}
المنطق الخاص IsNextVisible
{
يحصل
{
العودة (!IsDataSensitive) صحيح:
(CurrentPage != TotalPagesToShow);
}
}
الفراغ الخاص ApplyDataSensitivityRules()
{
FirstButton.Visible = IsPreviousVisible;
PreviousButton.Visible = IsPreviousVisible;
LastButton.Visible = IsNextVisible;
NextButton.Visible = IsNextVisible;
إذا (IsPagerVisible) GeneratePagers();
}
تطبق الطريقة ApplyDataSensitivityRules قواعد محددة مسبقًا مثل IsPagerVisible وIsPreviousVisible وIsNextVisible. بشكل افتراضي، سيتم تمكين هذه القواعد لعنصر تحكم الترحيل، ولكن يمكن للمستخدم إيقاف تشغيلها عن طريق تعيين الخاصية IsDataSensitive.
حتى الآن، تم تصميم جزء العرض للتحكم في الترحيل بشكل أساسي. آخر عمل نهائي متبقي هو توفير العديد من معالجات الأحداث حتى يتمكن المستخدمون من إجراء التعديلات اللازمة عند حدوث أحداث مختلفة للتحكم في الترحيل.
المفوض العام void PageDelegate(object sender,PageChangedEventArgs e);
التعداد العام PagedEventInvocer{Next,Previous,First,Last,Pager}
الفئة العامة PageChangedEventArgs:EventArgs
{
صفحة جديدة خاصة؛
مُستدعي التعداد الخاص
public PageChangedEventArgs(int newpage):base();
{
this.newpage = newpage;
}
PageChangedEventArgs العامة (int newpage، استدعاء PagedEventInvocer)
{
this.newpage = newpage;
this.invocer = invocer;
}
كثافة العمليات العامة NewPage {get{return newpage;}}
التعداد العام EventInvocer {get {return invocer؛}}
}
نظرًا لأن عنصر التحكم في الترحيل يحتاج إلى إرجاع معلمات الأحداث المخصصة، فإننا نحدد فئة PageChangedEventArgs مخصصة. تقوم فئة PageChangedEventArgs بإرجاع نوع PagedEventInvocer، وهو عداد لعناصر التحكم التي قد تؤدي إلى تشغيل الحدث. من أجل التعامل مع معلمات الأحداث المخصصة، نحدد مفوضًا جديدًا، PageDelegate. يتم تعريف الحدث بالشكل التالي:
حدث عام PageDelegate PageChanged؛
الحدث العام EventHandler DataUpdate؛
عندما لا يحتوي الحدث على مستمع حدث مطابق، فسيطرح ASP.NET استثناءً. يحدد عنصر تحكم الترحيل أساليب RaiseEvent التالية.
RaiseEvent باطل خاص (EventHandler e، مرسل الكائنات)
{
this.RaiseEvent(e,this,null);
}
RaiseEvent باطل خاص (EventHandler e،object sender، PageChangedEventArgs args)
{
إذا (ه! = فارغة)
{
e(sender,args);
}
}
RaiseEvent باطل خاص (PageDelegate e، مرسل الكائن)
{
this.RaiseEvent(e,this,null);
}
RaiseEvent باطل خاص (PageDelegate e،object sender، PageChangedEventArgs args)
{
إذا (ه! = فارغة)
{
e(sender,args);
}
}
يمكن لمشغلات الأحداث الآن تشغيل الأحداث عن طريق استدعاء أساليب Raiseevent الفردية.
4. أمثلة التطبيق
في هذه المرحلة ، تم الانتهاء من تصميم التحكم في الترحيل ويمكن استخدامه رسميًا. لاستخدام التحكم في الترحيل ، ما عليك سوى ربطه بالتحكم في العرض التقديمي.
<ASP: معرف مكرر = "مكرر" Runat = "Server">
<قالب العنصر>
العمود 1:
<٪# convert.toString (databinder.eval (container.dataitem ، "column1")) ٪>
<br>
العمود 2:
<٪# convert.toString (databinder.eval (container.dataitem ، "column2")) ٪>
<br>
العمود 3:
<٪# convert.toString (databinder.eval (container.dataitem ، "column3")) ٪>
<br>
<ساعة>
</قالب العنصر>
</ASP: مكرر >
< CC1: معرف Pager = "Pager" resultStoshow = "2" Runat = "Server" bindToControl = "Repeater" >
< selectpagerstyle backcolor = "Yellow" />
</CC1:
تربط Pager > صفحة ASPX أعلاه عنصر تحكم في الترحيل ، ويقوم بتعيين عدد السجلات المعروضة في كل صفحة ، ولون رقم الصفحة المحدد ، ويستخدم التخطيط الافتراضي كما هو مبين في الشكل 1. فيما يلي مثال آخر ، والذي يربط عنصر التحكم بالترحيل إلى بيانات البيانات ، كما هو موضح في الشكل 2.
< ASP: DataGrid id = "Grid" Runat = "Server" ></ASP: DataGrid >
< CC1: PAGER ID = "PagerGrid" resultStoshow = "2" Runat = "Server" BindTocontrol = "Grid" >
< selectpagerstyle backColor = "Red" ></selectpagersstyle >
< التصميم >
< ASP: ImageButton id = "first" runat = "server" imageurl = "play2l_dis.gif" alwaysetext = "home" ></asp: imageButton >
< ASP: ImageButton id = "previour" runat = "server" imageurl = "play2l.gif" alwaysetext = "Previous Page" ></ASP: ImageButton >
< ASP: ImageButton id = "next" runat = "server" imageurl = "play2.gif" alwaysetext = "page التالي" ></asp: imageButton >
< ASP: ImageButton id = "last" runat = "server" imageurl = "play2_dis.gif" alwaysetext = "Last Page" ></ASP: ImageButton >
< ASP: Pann ID = "Pager" Runat = "Server" ></ASP: لوحة >
</التصميم >
</CC1:
يوضح الاختبار أن التحكم في الترحيل لا يعتمد على عناصر تحكم تقديمية محددة. المثال الكامل.
على الرغم من أن تعلم ضوابط الويب المخصصة ليس مهمة سهلة ، إلا أن فوائد إتقان هذه المهارة بديهية. الأوقات.
http://www.cnblogs.com/niit007/archive/2006/08/13/475501.html