PDA

View Full Version : آموزش: ایجاد کامپوننت های اعتبارسنجی سفارشی (C# و VB.NET)



alireza_s_84
چهارشنبه 05 خرداد 1389, 00:32 صبح
سلام خیلی وقت پیش بود کامپوننتی با نام FarsiFieldValidator ایجاد کردم که توی همین سایت قرار دادم.
http://barnamenevis.org/forum/showthread.php?t=181788
جزئیات و کاربرد کامپوننت رو توی همون تاپیک دنبال کنید در این تاپیک قصد دارم نحوه ایجاد کامپوننت های اعتبار سنجی رو آموزش بدم و سورس اون کامپوننت رو اینجا قرار میدم.
مقدمه:
سالها پیش طراحان وب برای اعتبار سنجی ورودی های کاربران از دو روش استفاده میکردند . یک روش این بود که بعد از سابمیت شدن فرم داده ها در سمت سرور اعتبارسنجی میشدند و اگر ایرادی داشتند دوباره با بارگزاری صفحه کاربر رو متوجه میکردند. روش دیگه اعتبارسنجی همزمان سمت سرور و سمت کلاینت بود . وظیفه اعتبار سنجی سمت کلاینت بر عهده جاوا اسکریپت همیشه در صحنه بود.
بعد از پیدایش و ظهور دات نت مایکروسافت با ترفندی زیرکانه این اعتبار سنجی رو که کار بسیار سخت و طاقت فرسایی هم بود رو بسیار راحت و کاربردی کرد. کسایی که ASP کلاسیک کار کرده باشن خوب میفهمن که من چی میگم شما تصور کن خودت بشینی کدهای جاوا برای اعتبارسنجی بنویسی(همه هم تکراری هستن) بعد در سمت سرور هی کنترلها رو چک کنی حالا اگر فیلدها زیاد باشن که دیگه حسابی وقتت گرفته ست.
چارچوب دات نت با ایجاد کلاس BaseValidator این بار بزرگ رو از روی دوش برنامه نویسان ASP.Net برداشت تا به جنبه های دیگر برنامه نویسی بپردازند.
کلاس BaseValidator:
این کلاس کلاس پایه تمام کنترلهای اعتبار سنجی دات است. کنترلهای اعتبارسنجی دات نت رو همه میشناسید و نیازی به توضیح من نیست. این کنترلها کلا از یک فایل جاوا اسکریپت 30 کیلوبایتی برای اعتبار سنجی سمت کلاینت بهره میگیرن حال شما چه یک کنترل اعتبار سنجی داشته باشید چه 300 تا این فایل بارگزاری میشه.
کلاس BaseValidator یک کلاس تجریدی (برابر با MustInherit در زبان VB و abstract در زبان #C) است. و تنها نیاز به پیاده سازی متد EvaluateIsValid داره.
شما توی این متد باید اعتبار سنجی رو انجام بدین و دو مقدار برگشت بدین true اگر داده معتبر است و در غیر اینصورت false.
این کلاس از کلاس Label مشتق شده و واسط IValidator رو پیاده سازی میکنه. چیز دیگه ای به ذهنم نمیرسه توضیح بدم(یعنی چیزی نمونده:بامزه:)
سناریو برای ایجاد یک کنترل جدید:
مدتها بود فیلدهایی داشتم که تنها باید حروف فارسی رو قبول میکردند و نیاز مبرمی داشتم که این فیلدها رو اعتبارسنجی کنم. اینکار رو با یک کد جاوا اسکریپت انجام میدادم و در سمت سرور هم با یک تابع تا اینکه تصمیم گرفتم این سناریو رو تبدیل به یک کنترل کنم.
فیلدهای فارسی از سه حالت خارج نیست:
1) تنها باید حروف قبول بکنند(مثل نام و نام خانوادگی)
2) تنها حروف و عدد و خط فاصله و خط زیر قبول کنند(مثل نشانی)
3) فیلدهای کامنت و پست مطالب که باید تمامی حروف فارسی و اعداد و علائم رو قبول کنند.
بر این اساس ما کنترلی ایجاد خواهیم کرد تا کار اعتبارسنجی این سه فیلد رو برای ما انجام بده.
شروع کار:
من هر دو مثال VB و #C رو میذارم تا دوستان مشکلی نداشته باشند(من خودم این کامپوننت رو با VB نوشتم ولی بعد تبدیل به #C کردم)
تعریف یک کلاس مشتق شده:

namespace Softcam
{
[ToolboxData("<{0}:FarsiValidator runat=server></{0}:FarsiValidator>"), DefaultProperty("Text")]
public class FarsiValidator : BaseValidator
{
}
}

کد VB:

<DefaultProperty("Text"), ToolboxData("<{0}:FarsiValidator runat=server></{0}:FarsiValidator>")> _
Public Class FarsiValidator
Inherits BaseValidator
End Class

مرحله بعد تعریف یک خصوصیت برای کنترل ماست تا نوع اعتبارسنجی رو بر اساس سناریو انجام بده:

private ValidateType _ValidateType;
[DefaultValue(2), Category("Behavior"), Bindable(true)]
public ValidateType ValidationType
{
get
{
return this._ValidateType;
}
set
{
this._ValidateType = value;
}
}

کد VB:

Private _ValidateType As ValidateType
<Bindable(True), DefaultValue(2), Category("Behavior")> _
Public Property ValidationType As ValidateType
Get
Return Me._ValidateType
End Get
Set(ByVal value As ValidateType)
Me._ValidateType = value
End Set
End Property

مرحله بعد تعریف یک نوع شمارشی برای نگهداری نوع اعتبارسنجی ماست که در خصوصیت فوق استفاده کردیم:

public enum ValidateType
{
FarsiAddress,
FarsiCaracters,
FarsiFields
}

کد VB:


Public Enum ValidateType
' Fields
FarsiAddress = 0
FarsiCaracters = 1
FarsiFields = 2
End Enum


خوب تا اینجا موارد لازم رو تعریف کردیم ما برای اعتبار سنجی سمت کلاینت از یک فایل جاوا اسکریپت حدود 2 کیلوبایت(1.87 دقیقا) استفاده میکنیم که باید بصورت ریسورس به کامپوننت ما اضافه بشه و در حین بارگزاری کامپوننت هم در صفحه رجیستر بشه .
کلا برای اضافه کردن هر فایلی به منبع شما باید از قاعده زیر پیروی کنید:
بر روی فابل موردنظرتون توی SolutionExplorer کلیک راست کنید و Properties رو انتخاب کنید بعد توی قسمت Build Action گزینه Embedded Resource رو انتخاب کنید. اینطوری فایل شما بصورت ریسورس با کامپوننت کامپایل میشه.
بعد نوبت معرفی این وب ریسورس توی کلاستون هست که از فرمول زیر پیروی میکنه:

[assembly: WebResource([Assembly of project].[Folder containing resource].[Filename of resource] ,[MimeType])]
[Assembly of project]: فضای نام پروژه یا namespace
[Folder containing]: اگر فایل شما توی پوشه قرار داره اسم پوشه و اگر توی خود ریشه پروژه قرار داره نیازی به این قسمت نیست.
[Filename of resource]: که میشه نام فایل شما با پسوندش.
[MimeType]: هم میشه نوع MimeType فایل که برای فایلهای مختلف متفاوته و میتونید لیست اون رو اینجا ببینید:
http://en.wikipedia.org/wiki/Internet_media_type
خب فایل ما که توی ریشه پروژه قرار داره و اسمش هم هست FarsiValidatorScript.js پس با توجه به توضیحات فوق کد زیر رو به قبل از فضای نام پروژه اضافه میکنیم:

[assembly: WebResource("Softcam.FarsiValidatorScript.js", "application/x-javascript")]

دوستان VB کار هم کد زیر رو میتونن قبل از تعریف کلاس قرار بدن:

<Assembly: WebResource("Softcam.FarsiValidatorScript.js", "application/x-javascript")>

خب نوبت به پیاده سازی نحوه اعتبارسنجی میرسه. برای داشتن حروف دو راه پیشرو داریم:
1) یک متغیر که حروف فارسی رو برای ما نگه داره
2) یک آرایه یا لیست که کد هگز کارکترها رو برای ما نگه داره.
اوایل که کامپوننت نوشته شد از روش اول استفاده کردم که کلی ایراد داشت و مهترینش مشکل با اعداد فارسی و همچنین حروف فارسی و عربی در کیبوردهای تصحیح شده بود.
تا اینکه ازروش دوم استفاده کردم و کد حروف و کارکترها رو توی لیست نگهداری کردم و مشکلات حل شد پس روش دوم رو توضیح میدم. سه تا متیغر نیاز داریم به ازای سه حالت اعتبار سنجی:

یک متغیر برای نگهداری کد کارکتر حروف
یک متغیر برای نگهداری کد اعداد
یک متغیر برای نگهداری سایر علائم

مرحله بعد پر کردن این سه متغیر با کد حروفه:

private HashSet<int> CodeBase = new HashSet<int>(new int[] { 0x0627, 0x0628, 0x067E, 0x062A, 0x062B, 0x062C, 0x0686, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0698, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x0642, 0x06A9, 0x0643, 0x06AF, 0x0644, 0x0645, 0x0646, 0x0648, 0x0647, 0x0649, 0x06CC, 0x064A, 0x0622, 0x0626, 0x0621, 0x0624, 0x0651, 0x020 });
private HashSet<int> NumberBase = new HashSet<int>(new int[] { 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x05F, 0x0640, 0x02D });
private HashSet<int> CharsBase = new HashSet<int>(new int[] { 0x00BB, 0x00AB, 0x061F, 0x003C, 0x003D, 0x003E, 0x003A, 0x066C, 0x005D, 0x005B, 0x005C, 0x0640, 0x02D, 0x0028, 0x0029, 0x066A, 0x002B, 0x002E, 0x002F, 0x002A, 0x007D, 0x007B, 0x061B, 0x0021, 0x007C, 0x007E });

کد VB:

Private CharsBase As HashSet(Of Integer) = New HashSet(Of Integer)(New Integer() { &HBB, &HAB, &H61F, 60, &H3D, &H3E, &H3A, &H66C, &H5D, &H5B, &H5C, &H640, &H2D, 40, &H29, &H66A, &H2B, &H2E, &H2F, &H2A, &H7D, &H7B, &H61B, &H21, &H7C, &H7E })
Private CodeBase As HashSet(Of Integer) = New HashSet(Of Integer)(New Integer() { &H627, &H628, &H67E, &H62A, &H62B, &H62C, &H686, &H62D, &H62E, &H62F, &H630, &H631, &H632, &H698, &H633, &H634, &H635, &H636, &H637, &H638, &H639, &H63A, &H641, &H642, &H6A9, &H643, &H6AF, &H644, &H645, &H646, &H648, &H647, &H649, &H6CC, &H64A, &H622, &H626, &H621, &H624, &H651, &H20 })
Private NumberBase As HashSet(Of Integer) = New HashSet(Of Integer)(New Integer() { &H6F0, &H6F1, &H6F2, &H6F3, &H6F4, &H6F5, &H6F6, &H6F7, &H6F8, &H6F9, &H660, &H661, &H662, &H663, &H664, &H665, &H666, &H667, &H668, &H669, &H30, &H31, 50, &H33, &H34, &H35, &H36, &H37, &H38, &H39, &H5F, &H640, &H2D })

خب فقط یک تغییر کوچک توی خصوصیت ValidationType که قبلا تعریف کردیم میدم تا ست لازم برای اعتبار سنجی رو به ما بده:

[DefaultValue(2), Category("Behavior"), Bindable(true)]
public ValidateType ValidationType
{
get
{
return this._ValidateType;
}
set
{
this._ValidateType = value;
switch (this._ValidateType)
{
case ValidateType.FarsiAddress:
CodeBase.UnionWith(NumberBase);
break;

case ValidateType.FarsiCaracters:
CodeBase.UnionWith(NumberBase);
CodeBase.UnionWith(CharsBase);
break;
}
}
}

کد VB:

<Bindable(True), DefaultValue(2), Category("Behavior")> _
Public Property ValidationType As ValidateType
Get
Return Me._ValidateType
End Get
Set(ByVal value As ValidateType)
Me._ValidateType = value
Select Case Me._ValidateType
Case ValidateType.FarsiAddress
Me.CodeBase.UnionWith(Me.NumberBase)
Exit Select
Case ValidateType.FarsiCaracters
Me.CodeBase.UnionWith(Me.NumberBase)
Me.CodeBase.UnionWith(Me.CharsBase)
Exit Select
End Select
End Set
End Property


چک میکنیم که نوع اعتبار سنجی ما چه فیلدی است بعد بر اساس اون با استفاده از متد UnionWith دو ست رو تبدیل به یک ست میکنیم.
مرحله بعد انجام عملیات اعتبار سنجی ماست:
همونطور که گفتم برای تعیین نتیجه اعتبارسنجی باید متد EvaluateIsValid کلاس BaseValidtor رو پیاده سازی کنیم ما با استفاده از متد GetControlValidationValue کلاس BaseValidtor مقدار کنترلی که باید اعتبار سنجی بشه رو بدست میاریم و بعد تک تک کارکترها رو چک میکنیم و اگر کارکتر غیر فارسی مشاهده شد False و در غیر اینصورت True برگشت میدیم:

protected override bool EvaluateIsValid()
{
string Value = this.GetControlValidationValue(this.ControlToValid ate);
return this.CheckString(Value);
}

کد VB:

Protected Overrides Function EvaluateIsValid() As Boolean
Dim Value As String = MyBase.GetControlValidationValue(MyBase.ControlToV alidate)
Return Me.CheckString(Value)
End Function

در نهایت تابع CheckString رو کار اصلی بررسی کارکترها رو انجام میده رو پیاده سازی میکنیم:

private bool CheckString(string value)
{
foreach (char ch in value)
{
if (!CodeBase.Contains((int)ch))
return false;
}
return true;
}

کد VB:

Private Function CheckString(ByVal value As String) As Boolean
Dim ch As Char
For Each ch In value
If Not Me.CodeBase.Contains(ch) Then
Return False
End If
Next
Return True
End Function

تا اینجا تمامی موارد لازم برای اعتبار سنجی پیاده سازی شد. فقط دو مورد دیگه مونده : لینک کردن فایل جاوا اسکریپت در صفحه و دیگری معرفی نوع اعتبارسنجی و تابع اعتبارسنجی سمت کلاینت(در کل میشه گفت ایجاد ارتباط بین کنترل و عملیات مد نظر در سمت کلاینت)
برای لینک کردن فایل جاوااسکریپت رویداد OnInit رو override میکنیم و فایل رو به صفحه لینک میکنیم:

protected override void OnInit(EventArgs e)
{
ScriptManager.RegisterClientScriptResource(this.Pa ge, typeof(FarsiValidator), "Softcam.FarsiValidatorScript.js");
base.OnInit(e);
}

کد VB:

Protected Overrides Sub OnInit(ByVal e As EventArgs)
ScriptManager.RegisterClientScriptResource(Me.Page , GetType(FarsiValidator), "Softcam.FarsiValidatorScript.js")
MyBase.OnInit(e)
End Sub

و در نهایت برای معرفی نوع و تابع اعتبارسنجی سمت کلاینت رویداد RegisterValidatorDeclaration رو سربارگزاری میکنیم(چی شد override):

protected override void RegisterValidatorDeclaration()
{
base.RegisterValidatorDeclaration();
ScriptManager.RegisterExpandoAttribute(this, this.ClientID, "evaluationfunction", "ValidateSpell", true);
ScriptManager.RegisterExpandoAttribute(this, this.ClientID, "ValidateType", this._ValidateType.ToString(), true);
}

کد VB:

Protected Overrides Sub RegisterValidatorDeclaration()
MyBase.RegisterValidatorDeclaration
ScriptManager.RegisterExpandoAttribute(Me, Me.ClientID, "evaluationfunction", "ValidateSpell", True)
ScriptManager.RegisterExpandoAttribute(Me, Me.ClientID, "ValidateType", Me._ValidateType.ToString, True)
End Sub

کار رویداد فوق در نتیجه نهایی توی کدهای HTML این خواهد بود:

var ctl00_cphBody_favLname = document.all ? document.all["ctl00_cphBody_favLname"] : document.getElementById("ctl00_cphBody_favLname");
ctl00_cphBody_favLname.evaluationfunction = "ValidateSpell";
ctl00_cphBody_favLname.ValidateType = "FarsiFields";
ctl00_cphBody_favLname.controltovalidate = "ctl00_cphBody_txtLname";
ctl00_cphBody_favLname.focusOnError = "t";
ctl00_cphBody_favLname.errormessage = "فقط حروف فارسی مجاز است.";
ctl00_cphBody_favLname.display = "Dynamic";

اینقدر واضح هست که فکر نکنم نیازی به توضیح داشته باشه.
اصل پروژه و کامپوننت رو میتونید از لینک http://barnamenevis.org/forum/showthread.php?t=181788 دریافت کنید.
سوالی بود در خدمت هستم