public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return (now.Day % 3 == 0) && (now.Hour == 0 && now.Minute == 1 && now.Second == 1); }
بازخوردهای پروژهها
پر نکردن فیلد های PDF با استفاده از iTextSharp
من با استفاده از این مثال اینجا یک PDF درست کردم بعد هر بار تلاش برای تغییر دادههای Text Box میکنم یا خالی ذخیره میکند.
using System; using System.Diagnostics; using System.IO; using iTextSharp.text; using iTextSharp.text.pdf; namespace Delete { class Program { //روش صحیح ثبت و معرفی فونت در این کتابخانه public static iTextSharp.text.Font GetTahoma() { var fontName = "Tahoma"; if (!FontFactory.IsRegistered(fontName)) { var fontPath = Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf"; FontFactory.Register(fontPath); } return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } static void Main(string[] args) { string fileNameExisting = @"name.pdf"; string fileNameNew = @"newform.pdf"; using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open)) using (var newFileStream = new FileStream(fileNameNew, FileMode.Create)) { var pdfReader = new PdfReader(existingFileStream); using (var stamper = new PdfStamper(pdfReader, newFileStream)) { //نکته مهم جهت کار با اطلاعات فارسی //در غیراینصورت شاهد ثبت اطلاعات نخواهید بود stamper.AcroFields.AddSubstitutionFont(GetTahoma().BaseFont); //form.Fields.Keys = تمام فیلدهای موجود در فرم var form = stamper.AcroFields; //مقدار دهی فیلدهای فرم form.SetField("name3", "مقدار1"); form.SetField("name2", "مقدار2"); // "Yes" and "Off" are valid values here //form.SetField("Check Box 1", "Yes"); // "" and "Off" are valid values here //form.SetField("Option Button 1", ""); // نحوه مقدار دهی لیست //form.SetListOption("ListBox1", new[] { "1مقدار یک", "مقدار دو1" }, null); //form.SetField("ListBox1", null); // به این ترتیب فرم دیگر توسط کاربر قابل ویرایش نخواهد بود //stamper.PartialFormFlattening --> جهت غیرقابل ویرایش نمودن فیلدی مشخص stamper.FormFlattening = true; stamper.Close(); pdfReader.Close(); } } //Process.Start("newform.pdf"); } } }
که اگر بخوام این مشکل را بر طرف بشه کافی:
به جای این کد
PdfStamper pdfStamper = new PdfStamper(pdfReader, stream);
PdfStamper stamper = new PdfStamper(pdfReader, stream, '\0', true);
pdfStamper.FormFlattening = false;
من برای ایجاد هدر سفارشی میخواستم از این مثال استفاده کنم تا فیلد هایی را پر کند از مثالهای مشابه اون نتیجه دلخواه رو نگرفتم
مطالب
OpenCVSharp #1
معرفی OpenCV
پردازش تصاویر علمی است برای پیاده سازی الگوریتمهای مختلفی بر روی تصاویر دیجیتال؛ برای مثال تشخیص خودکار شمارهی پلاک خودروهای وارد شدهی به محدودهی طرح ترافیک، تا تشخیص چهرهی افراد، در گوشیهای همراه. پردازش تصاویر، در صنایع مختلف، علوم پزشکی و همچنین نظامی، کاربردهای بسیاری دارند.
برای انجام این کار، کتابخانههای بسیار زیادی طراحی شدهاند؛ اما در این بین OpenCV جایگاه خاصی دارد. این کتابخانهی بسیار مشهور سورس باز، جهت پردازش تصاویر در سیستم عاملهای مختلفی مانند Windows, Mac, Linux, Android و iOS بکار میرود.
محصور کنندههای OpenCV مخصوص دات نت
تا امروز محصور کنندههای زیادی جهت استفادهی از کتابخانهی OpenCV در دات نت طراحی شدهاند که تعدادی از مهمترینهای آنها به شرح زیر هستند:
الف) Emgu CV
این کتابخانه، یکی از مشهورترین محصور کنندههای OpenCV است و دارای مجوزی دوگانه میباشد. برای کارهای سورس باز، مجوز GPL دارد (یعنی باید کارتان را سورس باز کنید) و برای کارهای تجاری باید مجوز آنرا بخرید. البته باید توجه داشت که مجوز کتابخانهی اصلی OpenCV از نوع BSD است و این محدودیتها را ندارد.
ب) OpenCvSharp
کتابخانهی OpenCvSharp دارای مجوز BSD است (همانند کتابخانهی اصلی OpenCV) و محدودیتی برای استفاده ندارد. هر دو نوع مدل برنامه نویسی OpenCV را که شامل متدهای C و ++C آناست، پشتیبانی میکند و در طراحی آن سعی شدهاست که بیشترین نزدیکی به طراحی اصلی OpenCV وجود داشته باشد. همچنین این کتابخانه چندسکویی بوده و با Mono لینوکسی نیز سازگار است و از دات نت 2 به بعد را نیز پشتیبانی میکند. جامعهی کاربری آن فعال است و مدام به روز میشود.
ج) SharperCV
دیگر نگهداری نمیشود.
د) OpenCVDotNet
آخرین تاریخ به روز رسانی آن سال 2007 است.
ه) DirectCV
آخرین تاریخ به روز رسانی آن سال 2011 است.
در این بین یکی از بهترین انتخابها، کتابخانهی OpenCvSharp ژاپنی است. مجوز استفادهی از آن محدود نیست. به روز رسانی مرتب و منظمی دارد و API آن طوری طراحی شدهاست که به سادگی بتوانید مثالهای C و ++C کتابخانهی OpenCV را تبدیل به معادلهای #C کنید.
نصب OpenCvSharp
برای نصب کتابخانهی OpenCvSharp میتوان از بستههای نیوگت آن کمک گرفت. این کتابخانه به همراه دو بستهی نیوگت ارائه میشود.
اگر فرمان ذیل را صادر کنید
علاوه بر اسمبلیهای دات نتی OpenCVSharp، کتابخانهی native مربوط به OpenCV سازگار با نگارش ارائه شده را نیز دریافت خواهید کرد.
و اگر دستور ذیل را اجرا کنید:
به این معنا است که تنها اسمبلیهای دات نتی OpenCVSharp را دریافت میکنید. در این حالت نیاز است به سایت OpenCV مراجعه و بستههای کامپایل شدهی آنرا دریافت کنید. سپس فایلهای dll موجود در پوشهی opencv\build\x64\vc12\bin را برای مثال به پوشهی bin پروژهی خود کپی نمائید.
روش توصیه شدهی در اینجا، همان نصب بستهی نیوگت OpenCvSharp-AnyCPU است. به این ترتیب نگارشهای X86 و X64 کتابخانهی OpenCV سازگار با OpenCvSharp را نیز دریافت خواهید کرد.
نکتهای در مورد ارائهی نهایی پروژههای مبتنی بر OpenCV
OpenCV یک کتابخانهی native ویندوز است و دات نتی نیست . بنابراین DLLهای آن باید بسته به معماری CPU جاری، انتخاب شوند. یعنی اگر برنامهی دات نتی خود را در حالت Any CPU کامپایل میکنید، این برنامه در یک سیستم 64 بیتی، 64 بیتی رفتار میکند و در یک سیستم 32 بیتی، 32 بیتی. بنابراین باید دقت داشت که اگر سیستم جاری 64 بیتی است و میخواهید از اسمبلیهای X86 مربوط به OpenCV استفاده کنید، برنامه با پیام استثنای یافت نشدن OpenCV و BadImageFormatException کرش خواهد کرد. بستهی نیوگت OpenCvSharp-AnyCPU شامل هر دو معماری X86 و X64 است و هر دو سری DLLهای OpenCV را به همراه دارد.
همچنین OpenCV تحت ویندوز، توسط کامپایلر ویژوال ++C، کامپایل شدهاست. به همین جهت در این حالت، علاوه بر نصب دات نت، نیاز است VC++ redistributable packages را نیز بر روی کامپیوتر کلاینت نصب کرد.
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU اگر به پوشهی bin برنامهی خود مراجعه کنید، پوشهی جدید dll را نیز میتوان مشاهده کرد. داخل این پوشه، دو پوشهی X86 و X64 وجود دارند که حاوی DLLهای اصلی OpenCV میباشند. در این پوشهها اگر برای مثال فایلی به نام msvcp120.dll را یافتید، یعنی این نگارش از OpenCV نیاز به بستههای مخصوص VC++ 12 دارد.
رعایت این دو نکته بسیار مهم است؛ در غیر اینصورت برنامهی شما آغاز نخواهد شد.
اولین برنامهی OpenCVSharp
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU، مقدمات نصب OpenCV به پایان میرسد. در ادامه یک برنامهی کنسول جدید را ایجاد کرده و کدهای ذیل را به آن اضافه کنید:
این خروجی را دریافت خواهید کرد:
در این مثال یک تصویر 128*128 ایجاد شده و سپس با گرادیانی از رنگ خاکستری پر میشود. در ادامه یک پنجرهی native مخصوص OpenCV ایجاد شده و این تصویر در آن نمایش داده میشود.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
پردازش تصاویر علمی است برای پیاده سازی الگوریتمهای مختلفی بر روی تصاویر دیجیتال؛ برای مثال تشخیص خودکار شمارهی پلاک خودروهای وارد شدهی به محدودهی طرح ترافیک، تا تشخیص چهرهی افراد، در گوشیهای همراه. پردازش تصاویر، در صنایع مختلف، علوم پزشکی و همچنین نظامی، کاربردهای بسیاری دارند.
برای انجام این کار، کتابخانههای بسیار زیادی طراحی شدهاند؛ اما در این بین OpenCV جایگاه خاصی دارد. این کتابخانهی بسیار مشهور سورس باز، جهت پردازش تصاویر در سیستم عاملهای مختلفی مانند Windows, Mac, Linux, Android و iOS بکار میرود.
محصور کنندههای OpenCV مخصوص دات نت
تا امروز محصور کنندههای زیادی جهت استفادهی از کتابخانهی OpenCV در دات نت طراحی شدهاند که تعدادی از مهمترینهای آنها به شرح زیر هستند:
الف) Emgu CV
این کتابخانه، یکی از مشهورترین محصور کنندههای OpenCV است و دارای مجوزی دوگانه میباشد. برای کارهای سورس باز، مجوز GPL دارد (یعنی باید کارتان را سورس باز کنید) و برای کارهای تجاری باید مجوز آنرا بخرید. البته باید توجه داشت که مجوز کتابخانهی اصلی OpenCV از نوع BSD است و این محدودیتها را ندارد.
ب) OpenCvSharp
کتابخانهی OpenCvSharp دارای مجوز BSD است (همانند کتابخانهی اصلی OpenCV) و محدودیتی برای استفاده ندارد. هر دو نوع مدل برنامه نویسی OpenCV را که شامل متدهای C و ++C آناست، پشتیبانی میکند و در طراحی آن سعی شدهاست که بیشترین نزدیکی به طراحی اصلی OpenCV وجود داشته باشد. همچنین این کتابخانه چندسکویی بوده و با Mono لینوکسی نیز سازگار است و از دات نت 2 به بعد را نیز پشتیبانی میکند. جامعهی کاربری آن فعال است و مدام به روز میشود.
ج) SharperCV
دیگر نگهداری نمیشود.
د) OpenCVDotNet
آخرین تاریخ به روز رسانی آن سال 2007 است.
ه) DirectCV
آخرین تاریخ به روز رسانی آن سال 2011 است.
در این بین یکی از بهترین انتخابها، کتابخانهی OpenCvSharp ژاپنی است. مجوز استفادهی از آن محدود نیست. به روز رسانی مرتب و منظمی دارد و API آن طوری طراحی شدهاست که به سادگی بتوانید مثالهای C و ++C کتابخانهی OpenCV را تبدیل به معادلهای #C کنید.
نصب OpenCvSharp
برای نصب کتابخانهی OpenCvSharp میتوان از بستههای نیوگت آن کمک گرفت. این کتابخانه به همراه دو بستهی نیوگت ارائه میشود.
اگر فرمان ذیل را صادر کنید
PM> Install-Package OpenCvSharp-AnyCPU
و اگر دستور ذیل را اجرا کنید:
PM> Install-Package OpenCvSharp-WithoutDll
روش توصیه شدهی در اینجا، همان نصب بستهی نیوگت OpenCvSharp-AnyCPU است. به این ترتیب نگارشهای X86 و X64 کتابخانهی OpenCV سازگار با OpenCvSharp را نیز دریافت خواهید کرد.
نکتهای در مورد ارائهی نهایی پروژههای مبتنی بر OpenCV
OpenCV یک کتابخانهی native ویندوز است و دات نتی نیست . بنابراین DLLهای آن باید بسته به معماری CPU جاری، انتخاب شوند. یعنی اگر برنامهی دات نتی خود را در حالت Any CPU کامپایل میکنید، این برنامه در یک سیستم 64 بیتی، 64 بیتی رفتار میکند و در یک سیستم 32 بیتی، 32 بیتی. بنابراین باید دقت داشت که اگر سیستم جاری 64 بیتی است و میخواهید از اسمبلیهای X86 مربوط به OpenCV استفاده کنید، برنامه با پیام استثنای یافت نشدن OpenCV و BadImageFormatException کرش خواهد کرد. بستهی نیوگت OpenCvSharp-AnyCPU شامل هر دو معماری X86 و X64 است و هر دو سری DLLهای OpenCV را به همراه دارد.
همچنین OpenCV تحت ویندوز، توسط کامپایلر ویژوال ++C، کامپایل شدهاست. به همین جهت در این حالت، علاوه بر نصب دات نت، نیاز است VC++ redistributable packages را نیز بر روی کامپیوتر کلاینت نصب کرد.
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU اگر به پوشهی bin برنامهی خود مراجعه کنید، پوشهی جدید dll را نیز میتوان مشاهده کرد. داخل این پوشه، دو پوشهی X86 و X64 وجود دارند که حاوی DLLهای اصلی OpenCV میباشند. در این پوشهها اگر برای مثال فایلی به نام msvcp120.dll را یافتید، یعنی این نگارش از OpenCV نیاز به بستههای مخصوص VC++ 12 دارد.
رعایت این دو نکته بسیار مهم است؛ در غیر اینصورت برنامهی شما آغاز نخواهد شد.
اولین برنامهی OpenCVSharp
پس از نصب بستهی نیوگت OpenCvSharp-AnyCPU، مقدمات نصب OpenCV به پایان میرسد. در ادامه یک برنامهی کنسول جدید را ایجاد کرده و کدهای ذیل را به آن اضافه کنید:
using OpenCvSharp; namespace OpenCVSharpSample01 { class Program { static void Main(string[] args) { var img = Cv.CreateImage(new CvSize(128, 128), BitDepth.U8, 1); for (var y = 0; y < img.Height; y++) { for (var x = 0; x < img.Width; x++) { Cv.Set2D(img, y, x, x + y); } } Cv.NamedWindow("window"); Cv.ShowImage("window", img); Cv.WaitKey(); Cv.DestroyWindow("window"); Cv.ReleaseImage(img); } } }
در این مثال یک تصویر 128*128 ایجاد شده و سپس با گرادیانی از رنگ خاکستری پر میشود. در ادامه یک پنجرهی native مخصوص OpenCV ایجاد شده و این تصویر در آن نمایش داده میشود.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
در این مقاله قصد داریم قابلیت ایجاد قالب را در پروژههای ASP.NET MVC، فراهم کنیم تا ظاهر سایت یا به اصطلاح قالب سایت از طریق فایل کانفیگ تغییر کند. همانطور که میدانید معماری ASP.NET MVC براساس قراردادهای پیش فرض، قابل تعویض و تغییر طراحی شده است. یکی از این قراردادها، نحوهی پیدا کردن یک view برای کنترلر و اکشنهای آن است که به صورت زیر در ViewEngine تعریف شدهاست:
ViewEngine.ViewLocationFormats= "~/Views/{controller}/{action}.cshtml"
"~/Themes/{ThemeName}/Views/{controller}/{action}.cshtml"
public static void Themeable(this VirtualPathProviderViewEngine engine) { var ThemePath = "~/Themes"; var ThemeName = WebConfigurationManager.AppSettings["MvcTheme"]; if (string.IsNullOrEmpty(ThemeName)) return; var themeFolder = HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/", ThemePath, ThemeName)); if (!Directory.Exists(themeFolder)) throw new DirectoryNotFoundException(string.Format("Theme folder not exists: {0}/{1}}", ThemePath, ThemeName)); var newViewLocations = new[] { string.Format("{0}/{1}/Views/{2}/{3}.cshtml", ThemePath, ThemeName, "{1}", "{0}"), string.Format("{0}/{1}/Views/Shared/{2}.cshtml", ThemePath, ThemeName, "{0}"), // vb.net : // string.Format("{0}/{1}/Views/{2}/{3}.vbhtml", ThemePath, ThemeName, "{1}", "{0}"), // string.Format("{0}/{1}/Views/Shared/{2}.vbhtml", ThemePath, ThemeName, "{0}"), }; engine.ViewLocationFormats = newViewLocations; engine.PartialViewLocationFormats = newViewLocations; }
<appSettings> ... <add key="MvcTheme" value="Test1" /> </appSettings>
و در فایل Global.asax، قابلیت فراخوانی قالب را فعال کنید:
ViewEngines.Engines.OfType<RazorViewEngine>().Single().Themeable();
@{ // Layout = "~/Views/Shared/_Layout.cshtml"; Layout = "~/themes/test1/Views/Shared/_Layout.cshtml"; }
ابتدا مدل زیر را تعریف کنید:
public class ThemeBundle { public BundleType BundleType { get; set; } public string VirtualPath { get; set; } public string[] Urls { get; set; } } public enum BundleType { Style, Script }
توسط کد زیر bundleها را از محل پوشهی قالب، فراخوانی میکنیم:
public static void RegisterThemeBundels(BundleCollection bundles) { var ThemePath = "~/Themes"; var ThemeName = WebConfigurationManager.AppSettings["MvcTheme"]; var ThemeBundleFileName = "ThemeBundle.json"; List<ThemeBundle> list; try { JavaScriptSerializer jss = new JavaScriptSerializer(); var jsonaddress = System.Web.HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/{2}", ThemePath, ThemeName, ThemeBundleFileName)); var json = System.IO.File.ReadAllText(jsonaddress); list = jss.Deserialize<List<ThemeBundle>>(json); } catch (Exception ex) { throw new Exception(string.Format("Cannot read {0}. see more error in inner exception.", ThemeBundleFileName), ex); } foreach (var themeBundle in list) { switch (themeBundle.BundleType) { case BundleType.Script: bundles.Add(new ScriptBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; case BundleType.Style: bundles.Add(new StyleBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; default: throw new ArgumentOutOfRangeException(nameof(themeBundle.BundleType)); } } }
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { if (MvcTheme.ThemeName != null) { MvcTheme.RegisterThemeBundels(bundles); return; } bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate*")); ... } }
[ { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/jquery", "Urls": [ "~/themes/test1/js/jquery-1.10.2.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/jqueryval", "Urls": [ "~/themes/test1/js/jquery.validate.js", "~/themes/test1/js/jquery.validate.unobtrusive.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/modernizr", "Urls": [ "~/themes/test1/js/modernizr-2.6.2.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/bootstrap", "Urls": [ "~/themes/test1/js/bootstrap.js", "~/themes/test1/js/respond.js" ] }, { "BundleType": "Style", "VirtualPath": "~/themes/test1/css/css", "Urls": [ "~/themes/test1/css/bootstrap.css", "~/themes/test1/css/site.css" ] } ]
Themes ├───Test1 │ │ThemeBundle.json │ ├───Css │ ├───Fonts │ ├───Images │ ├───Js │ └───Views ├───Test2 │ │ThemeBundle.json │ ├───Css │ ├───Fonts │ ├───Images │ ├───Js │ └───Views
کد کامل و نهایی :
public static class MvcTheme { public static string ThemeName { get; } public static string ThemePath { get; set; } private const string AppSettingName = "MvcTheme"; private const string ThemeBundleFileName = "ThemeBundle.json"; static MvcTheme() { ThemePath = "~/Themes"; ThemeName = WebConfigurationManager.AppSettings[AppSettingName]; } public static void Themeable(this VirtualPathProviderViewEngine engine) { if (string.IsNullOrEmpty(ThemeName)) return; var themeFolder = HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/", ThemePath, ThemeName)); if (!Directory.Exists(themeFolder)) throw new DirectoryNotFoundException(string.Format("Theme folder not exists: {0}/{1}}", ThemePath, ThemeName)); var newViewLocations = new[] { string.Format("{0}/{1}/Views/{2}/{3}.cshtml", ThemePath, ThemeName, "{1}", "{0}"), string.Format("{0}/{1}/Views/Shared/{2}.cshtml", ThemePath, ThemeName, "{0}"), // vb.net : // string.Format("{0}/{1}/Views/{2}/{3}.vbhtml", ThemePath, ThemeName, "{1}", "{0}"), // string.Format("{0}/{1}/Views/Shared/{2}.vbhtml", ThemePath, ThemeName, "{0}"), }; engine.ViewLocationFormats = newViewLocations; engine.PartialViewLocationFormats = newViewLocations; } public static void RegisterThemeBundels(BundleCollection bundles) { if(ThemeName == null) return; var list = ReadThemeBundles(); foreach (var themeBundle in list) { switch (themeBundle.BundleType) { case BundleType.Script: bundles.Add(new ScriptBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; case BundleType.Style: bundles.Add(new StyleBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; default: throw new ArgumentOutOfRangeException(nameof(themeBundle.BundleType)); } } } public static List<ThemeBundle> ReadThemeBundles() { try { JavaScriptSerializer jss = new JavaScriptSerializer(); var jsonaddress = System.Web.HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/{2}", ThemePath, ThemeName, ThemeBundleFileName)); var json = System.IO.File.ReadAllText(jsonaddress); var list = jss.Deserialize<List<ThemeBundle>>(json); return list; } catch (Exception ex) { throw new Exception(string.Format("Cannot read {0}. see more error in inner exception.", ThemeBundleFileName), ex); } } } public class ThemeBundle { public BundleType BundleType { get; set; } public string VirtualPath { get; set; } public string[] Urls { get; set; } } public enum BundleType { Style, Script }
- نکته یک: با حذف مقدار نام قالب در فایل کانفیگ، به راحتی به حالت پیش فرض asp.net mvc بر میگردید.
- نکته دو: نام bundle را حتما هم عمق با آدرس قالب تعریف کنید تا وقتی فایل css به پوشهی images یا فونت مجاور خود اشاره میکند، آدرس دهی معتبر باشد.
- نکته سه: اگر از RazorGenerator استفاده میکنید، در فایل RazorGeneratorMvcStart متد ()Themeable را بر روی engine آن صدا بزنید.
در ASP.Net، ما user-control سفارشی را جهت استفاده مجدد و مستقل در صفحات ASPX ایجاد میکنیم. هر user-control دارای properties عمومی، متدها و یا delegateهای خاص خود است و زمانی که user-control در یک صفحه وب جاسازی (embedded) یا فرخوانی (load) میشود بوسیله صفحه وب قابل استفاده است.
و در نهایت کد پیاده سازی نهایی صفحه ما بشکل زیر خواهد شد:
بعد از درج user-control در صفجه وب و فراخوانی آن، ممکن است نیاز باشد مثلاً باکلیک بر روی دکمهای از user-control متدی از صفحه اجرا شود. اما یک مشکل، زمانی که در حال ایجاد user-control هستید هیچ اطلاعی از صفحه ای که قرار است user-control در آن قرار بگیرد ندارید پس چگونه میتوانیم به متدهای آن دسترسی داشته باشیم؟!
در کلاس Delegate، متدی بنام DynamicInvoke وجود دارد که برای فراخوانی (Invoke) متد اشاره شده در delegate استفاده میشود. ما از این متد برای صدا زدن یک متد صفحه وبی که user-control در آن قرار دارد استفاده میکنیم.
مثال:
public partial class CustomUserCtrl : System.Web.UI.UserControl { private System.Delegate _delWithParam; private System.Delegate _delNoParam; // برای فراخوانی متدهایی از صفحه که دارای پارامتر هستند public Delegate PageMethodWithParamRef { set { _delWithParam = value; } } // برای فراخوانی متدهایی از صفحه که بدون پارامتر هستند public Delegate PageMethodWithNoParamRef { set { _delNoParam = value; } } protected void Page_Load(object sender, EventArgs e) { } protected void BtnMethodWithParam_Click(object sender, System.EventArgs e) { //Parameter to a method is being made ready object[] obj = new object[1]; obj[0] = “Parameter Value” as object; _delWithParam.DynamicInvoke(obj); } protected void BtnMethowWithoutParam_Click(object sender, System.EventArgs e) { //Invoke a method with no parameter _delNoParam.DynamicInvoke(); } }
فرض کنید در user-control بالا، دو دکمه وجود دارد که متد BtnMethodWithParam_Click را به رویداد کلیک یک دکمه، و متد BtnMethowWithoutParam_Click به رویداد کلیک دکمه دیگر منتسب میکنیم، سپس دو عامل خصوصی (Private) را تعریف میکنیم و متد DynamicInvoke این عاملهای خصوصی را در متدهای BtnMethodWithParam_Click و BtnMethowWithoutParam_Click فراخوانی میکنیم حال کافیست عاملهایی در صفحه تعریف کنیم که این عاملها به متدهای مورد نظر صفحه اشاره کنند و این عاملهای صفحه را در عاملهای عمومی user-control قرار دهیم.
در ادامه به پیاده سازی صفحه میپردازیم:
ابتدا دو عامل تعریف میکنیم:
public partial class _Default : System.Web.UI.Page { delegate void DelMethodWithParam(string strParam); delegate void DelMethodWithoutParam();
در رویداد Page_Load، یک وهله از هر کدام از عاملهای بالا که به متد (توجه: امضاء متدها با امضاء عاملها یکسان است) مورد نظر ما در صفحه اشاره میکند ایجاد میکنیم:
protected void Page_Load(object sender, EventArgs e) { DelMethodWithParam delParam = new DelMethodWithParam(MethodWithParam); // عامل صفحه را به عامل عمومی تعریف شده در یوزر کنترل تخصیص میدهیم this.UserCtrl.PageMethodWithParamRef = delParam; DelMethodWithoutParam delNoParam = new DelMethodWithoutParam(MethodWithNoParam); // عامل صفحه را به عامل عمومی تعریف شده در یوزر کنترل تخصیص میدهیم this.UserCtrl.PageMethodWithNoParamRef = delNoParam; }
در زیر متدهایی خصوصی که در صفحه وجود دارند و قرار است با کلیک بر روی دکمههای user-control فراخوانی شوند را مشاهده میکنید:
// متد دارای پارامتری که قرار است در کنترل فراخوانی شود private void MethodWithParam(string strParam) { Response.Write(“It has parameter: ” + strParam); } // متد بدون پارامتری که قرار است در کنترل فراخوانی شود private void MethodWithNoParam() { Response.Write(“It has no parameter.”); }
public partial class _Default : System.Web.UI.Page { delegate void DelMethodWithParam(string strParam); delegate void DelMethodWithoutParam(); protected void Page_Load(object sender, EventArgs e) { DelMethodWithParam delParam = new DelMethodWithParam(MethodWithParam); // عامل صفحه را به عامل عمومی تعریف شده در یوزر کنترل تخصیص میدهیم this.UserCtrl.PageMethodWithParamRef = delParam; DelMethodWithoutParam delNoParam = new DelMethodWithoutParam(MethodWithNoParam); // عامل صفحه را به عامل عمومی تعریف شده در یوزر کنترل تخصیص میدهیم this.UserCtrl.PageMethodWithNoParamRef = delNoParam; } // متد دارای پارامتری که قرار است در کنترل فراخوانی شود private void MethodWithParam(string strParam) { Response.Write(“It has parameter: ” + strParam); } // متد بدون پارامتری که قرار است در کنترل فراخوانی شود private void MethodWithNoParam() { Response.Write(“It has no parameter.”); } }
شاید PDF را بشود تنها فرمت گزارشگیری دانست که همهجا و در تمام سیستم عاملها پشتیبانی میشود. از ویندوز تا لینوکس از وب تا WPF تا سیلورلایت تا همه جا و از همه مهمتر اینکه خروجی آن دقیقا همان چیزی است که کاربر نهایی میخواهد: من میخوام اون چیزی رو که میبینم، دقیقا همان را، بدون کم و کاست و با همان صفحه بندی، بتوانم چاپ کنم.
برای تولید PDF میشود از کتابخانهی iTextSharp استفاده کرد اما برای نمایش آن حداقل در ویندوز بهترین راه حل استفاده از COM Components شرکت Adobe است که به همراه برنامه رایگان Adobe PDF reader ارائه میشود. در ادامه نحوهی استفاده از این Active-X را بررسی خواهیم کرد.
نمایش PDF در WPF
در تمام حالتها هدف این است که به نحوی به اکتیوایکس شرکت Adobe دسترسی پیدا کنیم؛ یا با اضافه کردن آن به پروژه یا استفاده از امکانات یکپارچه مرورگرها. در WPF از زمان ارائه سرویس پک یک دات نت سه و نیم (به بعد)، کنترل مرورگر وب هم به جمع کنترلهای قابل استفاده در آن اضافه شده است. در اینجا به سادگی چند سطر زیر میشود یک فایل PDF را در WPF نمایش داد:
<Window x:Class="WpfAppTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Pdf Report" Height="495" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Width="703">
<Grid>
<WebBrowser x:Name="WebBrowser1"/>
</Grid>
</Window>
WebBrowser1.Source = new Uri(PdfFilePath);
نمایش PDF در WinForms
اکتیوایکس نمایش دهنده PDF شرکت Adobe اساسا در فایل ذیل قرار گرفته است:
C:\Program Files\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
الف) در VS.NET از طریق منوی Tools گزینهی Choose toolbox items ، برگهی Com components را انتخاب کنید.
ب) سپس گزینهی Adobe PDF reader که به همان مسیر dll فوق اشاره میکند را انتخاب نمائید و بر روی دکمهی OK کلیک کنید.
ج) اکنون این کنترل جدید را بر روی فرم برنامه قرار دهید. به صورت خودکار COMReference های متناظر به پروژه اضافه میشوند.
اکنون نحوهی استفاده از این شیء COM به همراه آزاد سازی منابع مرتبط به شرح زیر خواهند بود:
using System.Windows.Forms;
namespace WindowsFormsAppTests
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
this.FormClosing += Form1_FormClosing;
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
axAcroPDF1.Dispose();
}
void Form1_Load(object sender, System.EventArgs e)
{
axAcroPDF1.LoadFile(PdfFilePath);
axAcroPDF1.setShowToolbar(true);
axAcroPDF1.Show();
}
}
}
نمایش PDF در Silverlight
در Silverlight هم از نسخهی 4 به بعد کنترل WebBrowser همانند آنچه که در WPF موجود است، اضافه شده است؛ اما این کنترل فقط در حالت اجرای در خارج از مرورگر برنامه Silverlight در دسترس میباشد. بنابراین روش دیگری را باید انتخاب کرد. این روش بر اساس تعامل سیلورلایت با کدهای HTML صفحه کار میکند. یک IFrame مخفی را در صفحه بالای شیء مرتبط با سیلورلایت قرار خواهیم داد. سپس در سیلورلایت Src این IFrame را به مسیر فایل PDF تنظیم میکنیم و همین. به این ترتیب فایل PDF نمایش داده میشود.
این IFrame به صورت زیر در همان صفحهی aspx ایی که object مرتبط با Silverlight نمایش داده میشود قرار میگیرد:
<iframe id="pdfFrame" style="visibility:hidden; position:absolute"><b>No Content</b></iframe>
<div id="silverlightControlHost">
var iFrame = HtmlPage.Document.GetElementById("pdfFrame");
var gt = pdfHost.TransformToVisual(Application.Current.RootVisual);
var offset = gt.Transform(new Point(0, 0));
var controlLeft = (int)offset.X;
var controlTop = (int)offset.Y;
iFrame.SetStyleAttribute("left", string.Format("{0}px", controlLeft));
iFrame.SetStyleAttribute("top", string.Format("{0}px", controlTop));
iFrame.SetStyleAttribute("visibility", "visible");
iFrame.SetStyleAttribute("height", string.Format("{0}px", pdfHost.ActualHeight));
iFrame.SetStyleAttribute("width", string.Format("{0}px", pdfHost.ActualWidth));
iFrame.SetStyleAttribute("z-index", "1000");
iFrame.SetProperty("src", "ShowPdf.aspx?file=" + fileName);
متاسفانه کاربران زیادی وجود دارند که هنوز درک صحیحی از جامعیت دادههای ارجاعی (referential Integrity) ندارند. نمیدانند که relationship چیزی جز قید کلید خارجی (foreign key) نیست. در ادامه مفاهیم زیر را در حد آشنایی توضیح خواهم داد:
توسط دستور create table به دو شکل میتوانیم بر روی ستونها قید (کلید اولیه، check، کلید خارجی، کلید یونیک...) تعریف نمود:
نکته: بطور پیش فرض برای کلید خارجی اعمال update و delete روی وضعیت no action تنظیم شده است. به این معنا که اگر سعی کنیم کلید اولیه جدول مرجع را بروز رسانی یا حذف کنیم ممانعت به عمل خواهد آمد. برای رفع این مشکل هم میتوانید از طریق design اقدام کنید و هم در هنگام ساخت جدول توسط DDL (همانطور که در دستورات فوق مشاهده میشود).
کلید خارجی ترکیبی
زمانی که در جدول والد (parent) کلید اولیه ترکیبی باشد، هر جدولی که بخواهد به کلید جدول والد ارجاعی داشته باشد باید از ترکیب دو ستون برای ساخت کلید خارجی استفاده کند.
فرض کنید جدول parent به این صورت است (ترکیب دو ستون col1 و col2 کلید اولیه است)
در اینجا چون ترکیب دو ستون کلید اولیه هست باید توسط "قید جدولی" اقدام به تعریف کلید کرد
خود ارجاعی و multiple cascading path
فرض کنید بخشهای مختلف یک سازمان که بصورت چارت است را توسط جدول پیاده سازی کردیم. ستونهای جدول به این شرح هستند:
تصویر نمودار جدول chart
حالا فرض کنید میخواهیم اطلاعات نامه هایی که بین بخشها رد و بدل میشود را در یک جدول ذخیره کنیم. جدول دارای ستونهای زیر خواهد بود:
نمودار جدول نامهها و چارت:
نکته ای که در اینجا وجود دارد این است که اگر کلید جدول chart بروز شود آنگاه SQL Server از دو راه میتواند جدول letters را بروز رسانی کند، به این علت پیغام خطایی با عنوان multiple cascading paths صادر میشود. برای رفع این مشکل باید از trigger کمک گرفت.
جدول اتصال (junction table)
برای پیاده سازی رابطه N-N از جدول واسط کمک گرفته میشود. برای این منظور رابطه N-N را باید به دو رابطه 1-N تجزیه کرد.
فرض کنید یک جدول مربوط به خلبانان و جدول دیگر مربوط به مسیرهای پروازی (مثل مسیر ایران-ترکیه، ایران-عربستان...) است. یک خلبان ممکن است در چند مسیر پروازی هواپیما را هدایت کرده باشد و یا بالعکس یک مسیر پروازی ممکن است توسط N خلبان طی شده باشد.
برای پیاده سازی اینگونه سیستم هایی باید یک جدول ایجاد نمود که دارای دو کلید خارجی باشد یکی آنها به جدول خلبانان و دیگری به مسیرهای پروازی مرتبط است.
میتوان ترکیب دو کلید خارجی جدول واسط را کلید اولیه در نظر گرفت.
پس خواهیم داشت:
و نمودار آن:
رابطه یک به یک
زمانی که نمونههای محدودی از یک موجودیت دارای مقدار برای یکسری خصیصه هستند بهتر است جدول به دو جدول تجزیه شود تا فضای اضافی صرف جدول نشود. مثلا در مدرسه تنها 10 درصد دانش آموزان جزء تیم فوتبال هستند حال اگر بخواهیم اطلاعات مربوط به تیم فوتبال مثل تعداد گل زده، تعداد بازی ... در جدول اصلی ذخیره کنیم برای 90 درصد دانش آموزان مقداری نخواهیم داشت. برای حل این مساله ارتباط یک به یک پیشنهاد میشود.
توجه داشته باشید که ستون std_code هم کلید اولیه هست و هم کلید خارجی که به جدول student ارجاع داده شده است.
نتیجه گیری
یک ستون همزمان میتواند کلید اولیه باشد و هم کلید خارجی (مثلا در ارتباط یک به یک)
همانطور که کلید اولیه ترکیبی داریم به همان شکل هم کلید خارجی ترکیبی داریم.
یک جدول میتواند به خودش ارجاع دهد که به آن اصطلاحا self-referencing میگویند
relationship چیزی جز کلید خارجی نیست و کلید خارجی نیز چیزی جز یک قید برای جامعیت دادهها نیست
جامعیت داده ارجاعی را میتوان توسط trigger پیاده سازی کرد
اگر SQL Server بیش از یک مسیر برای تغییر جدول child داشته باشد با مشکل مواجه خواهید شد
- کلید خارجی ترکیبی (composite foreign key)
- خود ارجاعی (self referencing)
- اعمال تغییرات به صورت آبشاری (cascade)
- چندین مسیر برای اعمال (multiple cascading path)
- جدول اتصال (junction table)- ارتباط یک به یک
توسط دستور create table به دو شکل میتوانیم بر روی ستونها قید (کلید اولیه، check، کلید خارجی، کلید یونیک...) تعریف نمود:
- قید ستونی
- قید جدولی
syntax مربوط به قید کلید خارجی در مدل ستونی به صورت زیر است:
<column_constraint> ::= [ CONSTRAINT constraint_name ] { ... | [ FOREIGN KEY ] REFERENCES [ schema_name . ] referenced_table_name [ ( ref_column ) ] [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] [ NOT FOR REPLICATION ] ... }
کلید خارجی ترکیبی
زمانی که در جدول والد (parent) کلید اولیه ترکیبی باشد، هر جدولی که بخواهد به کلید جدول والد ارجاعی داشته باشد باید از ترکیب دو ستون برای ساخت کلید خارجی استفاده کند.
فرض کنید جدول parent به این صورت است (ترکیب دو ستون col1 و col2 کلید اولیه است)
create table parent ( col1 int not null, col2 int not null, col3 char(1) null, -- Composite Primary Key primary key(col1, col2) );
و جدول child که دارای قید کلید خارجی ترکیبی به نام fk_comp است و به جدول parent ارجاع داده است:
create table child ( col0 int primary key, col1 int null, col2 int null, -- Composite Foreing Key Constraint constraint fk_comp foreign key (col1, col2) references parent(col1, col2) );
در این DDL هم از قید جدولی برای تعریف کلید خارجی ترکیبی استفاده شده است.
نمودار این دو جدول:
پس به عنوان نتیجه گیری، هرگاه جدول اصلی دارای کلید ترکیبی بود در جداول child نیز باید از کلید خارجی ترکیبی برای ایجاد relationship استفاده نمود.
اما این دو جدول را به یک شیوه دیگر نیز میتوان طراحی نمود. در جدول parent ترکیب دو ستون col1 و col2 را منحصربفرد (unique) گرفته و ستونی دیگر (مثلا از نوع identity) را به عنوان کلید اولیه در نظر گرفت (یا یک ستون از نوع محاسباتی تعریف کرده و آن را کلید قرار داد)
create table parent ( col0 int not null primary key identity, col1 int not null, col2 int not null, col3 char(1) null, -- Composite Unique Key unique(col1, col2) ); create table child ( col0 int primary key, col1 int null references parent );
فرض کنید بخشهای مختلف یک سازمان که بصورت چارت است را توسط جدول پیاده سازی کردیم. ستونهای جدول به این شرح هستند:
- کد بخش
- نام بخش
- کد بخش بالایی
create table chart ( chart_nbr int not null primary key, parent_nbr int null references chart, chart_name varchar(5) null );
حالا فرض کنید میخواهیم اطلاعات نامه هایی که بین بخشها رد و بدل میشود را در یک جدول ذخیره کنیم. جدول دارای ستونهای زیر خواهد بود:
- شماره نامه
- کد بخش فرستنده
- کد بخش گیرنده
create table letters ( letter_nbr int primary key, sec_sender int not null references chart, sec_reciver int not null references chart );
نمودار جدول نامهها و چارت:
نکته ای که در اینجا وجود دارد این است که اگر کلید جدول chart بروز شود آنگاه SQL Server از دو راه میتواند جدول letters را بروز رسانی کند، به این علت پیغام خطایی با عنوان multiple cascading paths صادر میشود. برای رفع این مشکل باید از trigger کمک گرفت.
جدول اتصال (junction table)
برای پیاده سازی رابطه N-N از جدول واسط کمک گرفته میشود. برای این منظور رابطه N-N را باید به دو رابطه 1-N تجزیه کرد.
فرض کنید یک جدول مربوط به خلبانان و جدول دیگر مربوط به مسیرهای پروازی (مثل مسیر ایران-ترکیه، ایران-عربستان...) است. یک خلبان ممکن است در چند مسیر پروازی هواپیما را هدایت کرده باشد و یا بالعکس یک مسیر پروازی ممکن است توسط N خلبان طی شده باشد.
برای پیاده سازی اینگونه سیستم هایی باید یک جدول ایجاد نمود که دارای دو کلید خارجی باشد یکی آنها به جدول خلبانان و دیگری به مسیرهای پروازی مرتبط است.
میتوان ترکیب دو کلید خارجی جدول واسط را کلید اولیه در نظر گرفت.
پس خواهیم داشت:
create table pilot ( pilot_code int primary key, pilot_name varchar(20) ); create table paths ( path_code int primary key, path_name varchar(20) ); create table junction ( pilot_code int references pilot, path_code int references paths, primary key (pilot_code, path_code) );
و نمودار آن:
رابطه یک به یک
زمانی که نمونههای محدودی از یک موجودیت دارای مقدار برای یکسری خصیصه هستند بهتر است جدول به دو جدول تجزیه شود تا فضای اضافی صرف جدول نشود. مثلا در مدرسه تنها 10 درصد دانش آموزان جزء تیم فوتبال هستند حال اگر بخواهیم اطلاعات مربوط به تیم فوتبال مثل تعداد گل زده، تعداد بازی ... در جدول اصلی ذخیره کنیم برای 90 درصد دانش آموزان مقداری نخواهیم داشت. برای حل این مساله ارتباط یک به یک پیشنهاد میشود.
create table student ( std_code int primary key, std_name varchar(25) not null ); create table football ( std_code int primary key constraint one_to_one_fk references student, std_cnt_goal int not null default (0) );
توجه داشته باشید که ستون std_code هم کلید اولیه هست و هم کلید خارجی که به جدول student ارجاع داده شده است.
نتیجه گیری
یک ستون همزمان میتواند کلید اولیه باشد و هم کلید خارجی (مثلا در ارتباط یک به یک)
همانطور که کلید اولیه ترکیبی داریم به همان شکل هم کلید خارجی ترکیبی داریم.
یک جدول میتواند به خودش ارجاع دهد که به آن اصطلاحا self-referencing میگویند
relationship چیزی جز کلید خارجی نیست و کلید خارجی نیز چیزی جز یک قید برای جامعیت دادهها نیست
جامعیت داده ارجاعی را میتوان توسط trigger پیاده سازی کرد
اگر SQL Server بیش از یک مسیر برای تغییر جدول child داشته باشد با مشکل مواجه خواهید شد
با استفاده از AutoComplete TextBoxes میتوان گوشهای از زندگی روزمرهی کاربران یک برنامه را سادهتر کرد. مشکل مهم dropDownList ها دریک برنامهی وب، عدم امکان تایپ قسمتی از متن مورد نظر و سپس نمایان شدن آیتمهای متناظر با آن در اسرع وقت میباشد. همچنین با تعداد بالای آیتمها هم حجم صفحه و زمان بارگذاری را افزایش میدهند. راه حلهای بسیار زیادی برای حل این مشکل وجود دارند و یکی از آنها ایجاد AutoComplete TextBoxes است. پلاگینهای متعددی هم جهت پیاده سازی این قابلیت نوشته شدهاند منجمله jQuery Autocomplete . این پلاگین دیگر توسط نویسندهی اصلی آن نگهداری نمیشود اما توسط برنامه نویسی دیگر در github ادامه یافته است. در ادامه نحوهی استفاده از این افزونه را در ASP.NET Webforms بررسی خواهیم کرد.
الف) دریافت افزونه
لطفا به آدرس GitHub ذکر شده مراجعه نمائید.
سپس برای مثال پوشهی js را به پروژه افزوده و فایلهای jquery-1.5.min.js ، jquery.autocomplete.js ، jquery.autocomplete.css و indicator.gif را در آن کپی کنید. فایل indicator.gif به همراه مجموعهی دریافتی ارائه نمیشود و یک آیکن loading معروف میتواند باشد.
علاوه بر آن یک فایل جدید custom.js را نیز جهت تعاریف سفارشی خودمان اضافه خواهیم کرد.
ب) افزودن تعاریف افزونه به صفحه
در ذیل نحوهی افزودن فایلهای فوق به یک master page نمایش داده شده است.
در اینجا از قابلیتهای جدید ScriptManager (موجود در سرویس پک یک دات نت سه و نیم و یا دات نت چهار) جهت یکی کردن اسکریپتها کمک گرفته شده است. به این صورت تعداد رفت و برگشتها به سرور بهجای سه مورد (تعداد فایلهای اسکریپت مورد استفاده)، یک مورد (نهایی یکی شده) خواهد بود و همچنین حاصل نهایی به صورت خودکار به شکلی فشرده شده به مرورگر تحویل داده شده، سرآیندهای کش شدن اطلاعات به آن اضافه میگردد (که در سایر حالات متداول اینگونه نیست)؛ به علاوه Url نهایی آن هم بر اساس hash فایلها تولید میشود. یعنی اگر محتوای یکی از این فایلها تغییر کرد، چون Url نهایی تغییر میکند، دیگر لازم نیست نگران کش شدن و به روز نشدن اسکریپتها در سمت کاربر باشیم.
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="AspNetjQueryAutocompleteTest.Site" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:PlaceHolder Runat="server">
<link href="<%= ResolveClientUrl("~/js/jquery.autocomplete.css")%>" rel="stylesheet" type="text/css" />
</asp:PlaceHolder>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference Path="~/js/jquery-1.5.min.js" />
<asp:ScriptReference Path="~/js/jquery.autocomplete.js" />
<asp:ScriptReference Path="~/js/custom.js" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
<div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
ج) افزودن یک صفحهی ساده به برنامه
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="default.aspx.cs" Inherits="AspNetjQueryAutocompleteTest._default" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:TextBox ID="txtShenas" runat="server" />
</asp:Content>
فرض کنید میخواهیم افزونهی ذکر شده را به TextBox استاندارد فوق اعمال کنیم. ID این TextBox در نهایت به شکل ContentPlaceHolder1_txtShenas رندر خواهد شد. البته در ASP.NET 4.0 با تنظیم ClientIDMode=Static میتوان ID انتخابی خود را به جای این ID خودکار درنظر گرفت و اعمال کرد. اهمیت این مساله در قسمت (ه) مشخص میگردد.
د) فراهم آوردن اطلاعات مورد استفاده توسط افزونهی AutoComplete به صورت پویا
مهمترین قسمت استفاده از این افزونه، تهیهی اطلاعاتی است که باید نمایش دهد. این اطلاعات باید به صورت فایلی که هر سطر آن حاوی یکی از آیتمهای مورد نظر است، تهیه گردد. برای این منظور میتوان از فایلهای ASHX یا همان Generic handlers استفاده کرد:
using System;
using System.Data.SqlClient;
using System.Text;
using System.Web;
namespace AspNetjQueryAutocompleteTest
{
public class AutoComplete : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string prefixText = context.Request.QueryString["q"];
var sb = new StringBuilder();
using (var conn = new SqlConnection())
{
//todo: این مورد باید از فایل کانفیگ خوانده شود
conn.ConnectionString = "Data Source=(local);Initial Catalog=MyDB;Integrated Security = true";
using (var cmd = new SqlCommand())
{
cmd.CommandText = @" select Field1 ,Field2 from tblData where Field1 like @SearchText + '%' ";
cmd.Parameters.AddWithValue("@SearchText", prefixText);
cmd.Connection = conn;
conn.Open();
using (var sdr = cmd.ExecuteReader())
{
if (sdr != null)
while (sdr.Read())
{
string field1 = sdr.GetValue(0) == DBNull.Value ? string.Empty : sdr.GetValue(0).ToString().Trim();
string field2 = sdr.GetValue(1) == DBNull.Value ? string.Empty : sdr.GetValue(1).ToString().Trim();
sb.AppendLine(field1 + "|" + field2);
}
}
}
}
context.Response.Write(sb.ToString());
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
در این مثال از ADO.NET کلاسیک استفاده شده است تا به عمد نحوهی تعریف پارامترها یکبار دیگر مرور گردند. اگر از LINQ to SQL یا Entity framework یا NHibernate و موارد مشابه استفاده میکنید، جای نگرانی نیست؛ زیرا کوئریهای SQL تولیدی توسط این ORMs به صورت پیش فرض از نوع پارامتری هستند (+).
در این مثال اطلاعات دو فیلد یک و دوی فرضی از جدولی با توجه به استفاده از like تعریف شده دریافت میگردد. به عبارتی همان متد StartsWith معروف LINQ بکارگرفته شده است.
به صورت خلاصه افزونه، کوئری استرینگ q را به این فایل ashx ارسال میکند. سپس کلیه آیتمهای شروع شده با مقدار دریافتی، از بانک اطلاعاتی دریافت شده و هر کدام قرارگرفته در یک سطر جدید بازگشت داده میشوند.
اگر دقت کرده باشید در قسمت sb.AppendLine ، با استفاده از "|" دو مقدار دریافتی از هم جدا شدهاند. عموما یک مقدار کفایت میکند (در 98 درصد موارد) ولی اگر نیاز بود تا توضیحاتی نیز نمایش داده شود از این روش نیز میتوان استفاده کرد. برای مثال یک مقدار خاص به همراه توضیحات آن به عنوان یک آیتم نمایش داده شده مد نظر است.
ه) اعمال نهایی افزونه به TextBox
در ادامه پیاده سازی فایل custom.js برای استفاده از امکانات فراهم شده در قسمتهای قبل ارائه گردیده است:
function formatItem(row) {
return row[0] + "<br/><span style='text-align:justify;' dir='rtl'>" + row[1] + "</span>";
}
$(document).ready(function () {
$("#ContentPlaceHolder1_txtShenas").autocomplete('AutoComplete.ashx', {
//Minimum number of characters a user has to type before the autocompleter activates
minChars: 0,
delay: 5,
//Only suggested values are valid
mustMatch: true,
//The number of items in the select box
max: 20,
//Fill the input while still selecting a value
autoFill: false,
//The comparison doesn't looks inside
matchContains: false,
formatItem: formatItem
});
});
پس از این مقدمات، اعمال افزونهی autocomplete به textBox ایی با id مساوی ContentPlaceHolder1_txtShenas ساده است. اطلاعات از فایل AutoComplete.ashx دریافت میگردد و تعدادی از خواص پیش فرض این افزونه در اینجا مقدار دهی شدهاند. لیست کامل آنها را در فایل jquery.autocomplete.js میتوان مشاهده کرد.
تنها نکتهی مهم آن استفاده از پارامتر اختیاری formatItem است. اگر در حین تهیهی AutoComplete.ashx خود تنها یک آیتم را در هر سطر نمایش میدهید و از "|" استفاده نکردهاید، نیازی به ذکر آن نیست. در این مثال ویژه، فیلد یک در یک سطر و فیلد دو در سطر دوم یک آیتم نمایش داده میشوند:
نظرات اشتراکها
تبدیلگر ایران سیستم به یونیکد
«نکتهی تکمیلی» که در نظرات فوق عنوان شد، با بانک اطلاعاتی شما هم کار میکند:
با این کدها:
دو نکته در اینجا مهم است:
الف) استفاده از درایور فاکس پرو
ب) code page استفاده شده 1256 است که باید در IranSystemConvertor تنظیم شود.
با این کدها:
// from http://www.microsoft.com/en-us/download/details.aspx?id=14839 var connectionString = "Provider=VFPOLEDB.1;Data Source=" + @"D:\path\JVJ100.DBF" + ";Password=;Collating Sequence=MACHINE"; using (var dbConnection = new OleDbConnection(connectionString)) { using (var dataAdapter = new OleDbDataAdapter("select FAMILY from JVJ100.DBF", dbConnection)) { using (var dataset = new DataSet()) { dataAdapter.Fill(dataset, "table1"); foreach (DataRow dataRow in dataset.Tables[0].Rows) { var familyIranSystem = dataRow[0] as string; var familyIranUnicode = ConvertTo.Unicode(familyIranSystem, 1256); if (!string.IsNullOrWhiteSpace(familyIranUnicode)) { } } } } }
دو نکته در اینجا مهم است:
الف) استفاده از درایور فاکس پرو
ب) code page استفاده شده 1256 است که باید در IranSystemConvertor تنظیم شود.
پیش از ادامهی نوشتار بهتر است توضیحاتی دربارهی قالبهای T4 داده شود. این قالبهای مصنوعی حاوی کدهایی که است که هدف آن صرفهجویی در نوشتن کد توسط برنامه نویس است. مثلاً در MVC شما یکبار قالبی برای صفحه Index خود تهیه میکنید که برای نمونه بجای ساخت جدول ساده، از گرید Kendo استفاده کند و همچنین دارای دکمه ویرایش و جزئیات باشد. از این پس هر بار که نیاز به ساخت یک نمای نوع لیست برای یک ActionResult داشته باشید فرم ساز MVC از قالب شما استفاده خواهد کرد. روشن است که خود Visual Studio نیز از T4 در ساخت بسیاری از فرمها و کلاسها بهره میبرد.
خبر خوب اینکه برای ساخت کلاسهای هر موجودیت در Entity Framework نیز از قالبهای T4 استفاده میشود و اینکه این قالبها در دسترس توسعهدهندگان برای ویرایش یا افزودن است.
افزونهی Tangible را دریافت کنید و سپس نصب کنید. این افزونه ظاهر نامفهوم قالبهای T4 را ساده و روشن میکند.
ما نیاز داریم که خود Visual Studio زحمت این سه کار را بکشد:
1- بالای هر کلاس موجودیت عبارت using System.Runtime.Serialization; را بنویسید.
2- صفت [DataContract] را پیش از تعریف کلاس بیفزاید.
3- صفت [DataMember] را پیش از تعریف هر ویژگی بیفزاید.
همانند شکل زیر روی فایل MyNewsModel.tt دوکلیک کنید تا محتوای آن در سمت چپ نشان داده شود. این محتوا باید ظاهری همانند شکل پیدا کرده باشد:
کد زیر را در محتوای فایل جستوجو کنید:
public string Property(EdmProperty edmProperty) { return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", Accessibility.ForProperty(edmProperty), _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); }
public string Property(EdmProperty edmProperty) { return string.Format( CultureInfo.InvariantCulture, "[DataMember]" + Environment.NewLine + "{0} {1} {2} {{ {3}get; {4}set; }}", Accessibility.ForProperty(edmProperty), _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); }
public string EntityClassOpening(EntityType entity) { return string.Format( CultureInfo.InvariantCulture, "{0} {1}partial class {2}{3}", Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); }
این کد را نیز به این صورت تغییر دهید:
public string EntityClassOpening(EntityType entity) { return string.Format( CultureInfo.InvariantCulture, "[DataContract]" + Environment.NewLine + "{0} {1}partial class {2}{3}", Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); }
public string UsingDirectives(bool inHeader, bool includeCollections = true) { return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string.Format( CultureInfo.InvariantCulture, "{0}using System;{1}" + "{2}", inHeader ? Environment.NewLine : "", includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", inHeader ? "" : Environment.NewLine) : ""; }
public string UsingDirectives(bool inHeader, bool includeCollections = true) { return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string.Format( CultureInfo.InvariantCulture, "using System.Runtime.Serialization;" + Environment.NewLine + "{0}using System;{1}" + "{2}", inHeader ? Environment.NewLine : "", includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", inHeader ? "" : Environment.NewLine) : ""; }
فایل MyNewsModel.tt را ذخیره کنید و از آن خارج شوید. بار دیگر هر کدام از کلاسهای tblNews و tblCategory را باز کنید. خواهید دید که به صورت خودکار تغییرات مد نظر ما به آن افزوده شده است. از این پس بدون هیچ دلواپسی بابت حذف صفتها، میتوانید هرچند بار که خواستید مدل خود را بههنگام کنید.
در بخش پسین دوباره به WCF بازخواهیم گشت و به تعریف روالهای مورد نیاز خواهیم پرداخت.