اشتراک‌ها
چهار پروژه با Blazor

- Countries & Cities
- Purchase Orders
- Birthday Reminders
- Birthday Reminders - SQLite 

چهار پروژه با Blazor
مطالب
یکپارچه سازی CKEditor با Lightbox
در یک پروژه من احتیاج داشتم تا عکس هایی که کاربر از طریق ckeditor آپلود می‌کنه ، به صورت خودکار با lightbox  یکپارچه شه و به صورت گالری عکس نمایش داده شود.
همان طور که می‌دونید مکانیزم عملکرد اکثر پلاگین‌های lightbox  به صورت زیر است:
<a href="orginal size of image directory"><img src="thumb(smaller size) image directory" /></a>

خب معلوم میشه که آدرس عکسی که به صورت کوچیک)اصطلاحا بند انگشتی) به نمایش در میاد باید داخل تگ img ، وآدرس عکس با سایز اصلی ، داخل تگ a قرار بگیره.

پس تقریبا معلوم شد که چه باید بکنیم.

ابتدا باید عکسی که آپلود شده را به صورت خودکار به اندازه کوچکتر تغییر ابعاد داده و سپس آدرس عکس تغییر ابعاد داده شده ، را به عنوان آدرس عکس آپلود شده به ckeditor باز گردانیم  که در شکل زیر آن را نشان داده ام:

ولی  ما فقط  آدرس  عکس تغییر ابعاد داده شده متعلق به تگ img  را ، به ckeditor  داده ایم و برای اینکه با lightbox  به خوبی کار کند، احتیاج داریم که آدرس عکس با سایز اصلی را داخل تگ  a  قرار دهیم، که در ckeditor می‌توانیم از قسمت پیوندها این کار را انجام دهیم، و همه‌ی این عملیات باید به صورت خودکار انجام شود.


خب تا اینجا خلاصه ای از آنچه باید انجام شود را گفتم.

در کل منطقی که باید پیاده شود بدین گونه است:

1-  پیاده سازی ckeditor
2-  فعال کردن قسمت آپلود عکس ckeditor
3-  آپلود کردن عکس و تغییر اندازه آن به کمک  کلاس WebImage
4-  برگرداندن آدرس عکس‌های آپلود شده به ckeditor و روش‌های پیاده سازی آن

نکات:
الف) من در اینجا از ASP.NET MVC استفاده می‌کنم .شما  نیز می‌توانید منطق پیاده سازی شده را به راحتی و با کمی تغییرات به پروژه خود اعمال کنید.
ب) کدهایی که من نوشتم 100% بهینه نیستند و کاملا هم بدون اشکال نیستند ولی  نیازهای شما را برآورده می‌کند.
ج)آدرس دادن فایل‌های من کاملا صحیح نیستند و بهتر است شما از T4MVC استفاده کنید.

مرحله اول: دانلود و پیاده سازی ckeditor در صفحه

ابتدا به سایت ckeditor.com مراجعه  و از قسمت دانلود، آن را دانلود کرده و سپس از حالت فشرده درآورده، در فولدری مثلا به نام Scripts در پروژه‌ی خود بریزید.
همان طور که می‌دانید ckeditor با یک textarea  یکپارچه میشود ، که من ساده‌ترین آن را برای شما شرح می‌دهم.
اول از همه شما باید یک فایل جاوا اسکریپت متعلق به ckeditor  را در صفحه ای که از آن می‌خواهید استفاده کنید به صفحه ضمیمه کنید. نام این فایل ckeditor.js هست که در فایل دانلودی در داخل پوشه‌ی ckeditor دیده میشود.
 
<script src="/Scripts/ckeditor/ckeditor.js" type="text/javascript"></script>
 نکته: بهتر است که این ارجاع را قبل از بسته شدن تگ body انجام دهید یعنی درست قبل از </body>.
خب فرض کنید که یک textarea به شکل زیر داریم. برای اینکه این textarea  با ckeditor  یکپارچه شود کافیست که class آن را بر روی ckeditor  قرار دهید.
<textarea id="content" name="content" class="ckeditor" ></textarea>
نکته: حتما برای textarea خود id را نیز تعیین کنید ، چون در غیر این صورت  ckeditor کار نخواهد کرد.

تا به اینجای کار توانستیم ckeditor را اجرا کنیم.
در مرحله بعد سراغ فعال سازی آپلود عکس در آن می‌رویم.

مرحله دوم: فعال سازی آپلود عکس در ckeditor

از شواهد این طور به نظر میاد که به صورت پیشفرض این امکان غیر فعال است و باید به صورت دستی آن را فعال کنیم.
 برای این کار کافیست که از مستندات ckeditor  کمک بگیریم.
خب یکی از راحت‌ترین این روش‌ها این است که قبل از بسته شدن تگ بادی فایل جاوا اسکریپت زیر را بنویسیم:
 
<script type="text/javascript">
        CKEDITOR.replace( 'content' , { filebrowserImageUploadUrl: '/Admin/UploadImage' } );
</script>
نکته اول: این خط کدی که نوشتیم مرحله اول را باطل می‌کنه یعنی احتیاج نبود که مرحله اول را طی کنیم و می‌توانستیم مستقیما از همین مرحله شروع کنیم.
توضیحات:
آرگومان اول CKEDITOR.replace که در اینجا content  است ، در واقع  id همان textarea ای هست که می‌خواهیم ckeditor  روی آن اعمال شود.
آرگومان دوم نام کنترلر و اکشن را برای آپلود فایل مشخص می‌کنه.(از منطق ASP.Net MVC استفاده کردم)
خب تا اینجا اگر تست بگیرید میبینید که قسمت زیر برایتان فعال شده.

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

برای این که ببینم که این فرم آپلود ckeditor ، چه پارامترهایی را به اکشن متد من ارسال می‌کنه ، از فایرباگ کمک گرفتم:

همان طور که می‌یبینید به خوبی آدرس post  شدن اطلاعات به اکشن متدی که من برایش مشخص کردم را فهمیده.
اما سه پارامتر دیگر نیز به اکشن متد ما ارسال می‌کنه: CKEditor و CKeditorFuncNum و langCod
برای اینکه با این پارامتر‌های ارسالی بیشتر آشنا شوید ، توصیه می‌کنم این صفحه را ببینید.
آنچه که از این پارامتر‌ها برای ما مهم هست ، من در اکشن متد تعریفی خود لحاظ کرده ام.

خب همون طور که یپش از این گفته بودم ما نیاز داریم که عکس آپلود شده را به ابعاد کوچکتر تغییر اندازه داده  و اصطلاحا از آن به عنوان تصویر بند انگشتی استفاده کنیم. آدرس این عکس کوچک شده ، همانیست که در آدرس تگ img  قرار می‌گیره و در ابتدا به کاربر نمایش داده میشه.
برای انجام این عمل من کلاسی را برای کار کردن با عکس برایتان معرفی می‌کنم،  به نام WebImage  که  در داخل فضای نامی System.Web.Helpers  قرار گرفته است.
از طریق این کلاس می‌توان کلیه عملیات دریافت فایل آپلود شده،  ویرایش ، تغییر اندازه ، چرخاندن ، بریدن و حتی watermark کردن و در نهایت ذخیره عکس را به آسانی انجام داد.
من کدهای متد UploadImage را برایتان قرار می‌دهم که زیاد هم بهینه نیست و سپس برایتان توضیح میدهم.

public ActionResult UploadImage(string CKEditorFuncNum, string CKEditor, string langCode)
        {
            string message;  // message to display   when file upload successfully(optional)
            string thumbPath = "";  // the directory for thumb file that should resize
            var db = new MyDbContext();  // make new instance from my context
            // here logic to upload image
            // and get file path of the image
            var file = WebImage.GetImageFromRequest(); // get the uploaded file from request
            var ext = Path.GetExtension(file.FileName);  // get the path of file
            //get the file name without extension
            var fileName = Path.GetFileNameWithoutExtension(file.FileName);
            //add time to file name to avoid same name file overwrite, and then add extension to it
            fileName += DateTime.Now.ToFileTime() + ext;
            //choose the path for the original size of image
            var path = Path.Combine(Server.MapPath("~/Content/UploadedImages/Default"), fileName);
            file.Save(path);  //save the original size of the image
            db.Images.Add(new Image { RealName = file.FileName, FileName = fileName, UploadDate = DateTime.Now }); // save image info to db
            db.SaveChanges();  // submit changes to db
            string defaultPath = "/Content/UploadedImages/Default/" + fileName;  //path for original size of //images
            if (file.Width > 400)  // if width of image bigger than 400 px do resize
            {
                file.Resize(400, 400, true);  //resize the image , third argument is aspect ratio
                string thumbName = "Thumb-" + fileName;  // resized image name
                //path for resized image file
                string path2 = Server.MapPath("~/Content/UploadedImages/Thumbs/" + thumbName);
                thumbPath = "/Content/UploadedImages/Thumbs/" + thumbName;
                //save resized image file
                file.Save(path2);
            }
            else
            {
                thumbPath = defaultPath;  // if the size not bigger than 400px the thumb, path = default path
            }
            // passing message success/failure
            message = "Image was saved correctly";
            // since it is an ajax request it requires this string
            //java script that return files path to ckeditor
            string output = @"<script>window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");window.parent.document.getElementById('cke_145_textInput').value='" + defaultPath + "';window.parent.document.getElementById('cke_125_textInput').value=0;</script>";
            return Content(output);
        }


توضیحات:
ابتدا یه رشته  به نام message  در نظر گرفتم ، برای هنگامی که آپلود شد، ckeditor به کاربر نشان بده.
سپس منطق من به این صورت بوده که مسیری برای ذخیره سازی فایل‌های تغییر اندازه داده شده ، و نیز مسیری برای فایل‌های با اندازه اصلی در نظر گرفتم.
همچنین من در اینجا من از بانک اطلاعاتی برای ذخیره سازی اطلاعاتی از عکس استفاده کردم،  که در اینجا بحث اصلی ما نیست.
سپس به کمک WebImage.GetImageFromRequest فایل آپلود شده را دریافت کردم.این متد به اندازه کافی باهوش هست که بفهمد ، چه فایلی آپلود شده.
سپس پسوند فایل را از نام فایل جدا کردم، و تاریخ کنونی را به شکل رشته در آورده  و به انتهای نام عکس اضافه کرده تا از تکراری نبودن نام عکس‌ها مطمئن باشم.
سپس پسوند فایل را نیز دوباره به نام فایل اضافه کردم و به کمک متد Save کلاس WebImage عکس را ذخیره کردم.
سپس چک کردم که اگر عرض عکس بیشتر از 400  پیکسل هست ، آن  را تغییر اندازه  بده و ذخیره کنه، و در غیر این صورت آدرس عکسی که قرار بود تغییر اندازه داده بشه با آدرس عکس اصلی یکی میشه.


قسمت مهم:
نکته مهم اینه که ما آدرسهای عکس‌های آپلود شده را چگونه به ckeditor  برگردانیم.
همان طوری که در قسمت آخر هم مشاهده می‌کنید ، ما سه  دستور جاوا اسکریپت به مرورگر برگردوندیمم:
اولیش:

window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");

در حقیقت ما در اینجا ما از api‌های ckeditor و همچنین پارامترهای ارسالی از طرف ckeditor  استفاده کردیم ، تا قسمت آدرس عکس را با آدرس عکس تغییر اندازه داده شده و کوچک شده پر می‌کنیم.
سوال؟
حالا چگونه قسمت پیوند را پر کنیم؟ این  را دیگر من پیدا نکردم ، تا دست به دامن دوست  و یا شایدم دشمن قدیمیمون جاوا اسکریپت شدم.
اول رفتم به کمک فایرباگ دیدم که id  فیلد پیوند‌ها چیه؟


همان طور که معلومه  id این فیلد cke_145_textInput  هست و به کمک یه خط js می‌توان این فیلد را با آدرس عکس آپلود شده با سایز اصلی پر کرد.
اولش من این را نوشتم:

document.getElementsById("cke_145_textInput").value = defaultPath;

اما بازم js  شروع کرد به بدقلق شدن. هرچی توی کنسول دیباگش کردم خطای null بودن را میداد. بعد از یه ساعت سرو کله زدن و تقریبا ناامید شدن ، چشمم به قسمت اول کد api خود ادیتور که اولش را با window.parent شروع کرده  افتاد ، و من هم کد خودم را به شکل زیر تغییر دادم:

window.parent.document.getElementsById("cke_145_textInput").value = defaultPath;

موفقیت در این قسمت از کد باعث شد که من دست به کد‌تر شوم و مشکل border عکس ها، که به صورت  دیفالت در IE  یا همون دشمن همیشگی وجود داره ، را حل کنم ومقدار border را به صورت پیش فرض صفر کنم. 

window.parent.document.getElementsById('cke_125_textInput').value=0;

خب همه‌ی این دردسر‌ها را ما تحمل کردیم تا به ساده‌ترین شکل ممکن هر عکسی را که آپلود شد ، برای مکانیزم lightbox  آماده کنیم و به راحتی یه گالری عکس خوب داشته باشیم.
خب حتما می‌پرسید که از چه پلاگینی برای ایجاد lightbox  استفاده کنیم:
من به شخصه پلاگین colorbox  را پیشنهاد می‌دم.
با انجام یک سرچ ساده سایتش را پیدا کنید و با مستندات و ویژگی‌های آن آشنا شوید.
یک پیشنهاد:
برای انجام سلکت زدن برای عناصری که باید پلاگین colorbox  روی آنها اعمال شوند من سلکت زدن به شیوه‌ی زیر را پیشنهاد می‌کنم:

$(".container [href$='.jpg']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' });
$(".container [href$='.png']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' });
$(".container [href$='.gif']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' });

فرض کنید div ی که متن و عکس‌های ما را شامل میشه ، کلاسش container  باشه . با کمک [href$='.jpg']  می‌توان گفت هر لینکی که،  پسوند فایلی که به آن اشاره می‌کند، .jpg هست،  ویژگی colorbox  را به خود بگیرید.


یک پیشنهاد برای تشکیل گالری عکس:
همان طور که من در بالا اشاره کردم ، rel را بر روی  gal قرار دادم، تا هر تگی که ویژگی  rel را داشته باشد، تشکیل یک گروه برای گالری عکس را بدهد.
برای اینکه  بتوانیم این ویژگی را به عناصر مورد نظر خود اعمال کنیم ، بازم دست به دامان jQuery می‌شویم:

$(".container [href$='.jpg']").attr("rel", "gal");

خب مثل اینکه دیگر کار تمام شده و امیدوارم برای شما مفید بوده باشه.
موفق باشید... 

نظرات مطالب
React 16x - قسمت 10 - ترکیب کامپوننت‌ها - بخش 4 - یک تمرین
یک نکته تکمیلی:
یکی از مشکلات استفاده از JSON.parse(JSON.stringify(originalObject)) برای کپی کردن آبجکت‌ها این است که از آبجکت‌های circular پشتیبانی نمیکند؛ به عنوان مثال کد ساختار زیر را در نظر بگیرید:
const a = { x: 20, date: new Date() };
a.c = a;
استفاده از JSON.parse... خطای زیر را صادر خواهد کرد:
Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    --- property 'c' closes the circle
    at JSON.stringify (<anonymous>)
    at <anonymous>:1:17
یکی دیگر از مشکلات این است که برای پراپرتی‌هایی از نوع Date به صورت خودکار Date.prototype.toJSON صدا زده خواهد شد:
const a = { x: 20, date: new Date() };
JSON.parse(JSON.stringify(a))
{x: 20, date: '2023-01-04T15:17:02.957Z'}
برای رفع این دست از مشکلات میتوانید از تابع توکار structuredClone استفاده کنید:
structuredClone(a)
// {x: 20, date: Wed Jan 04 2023 15:17:02 GMT+0000 (Greenwich Mean Time), c: {…}}
البته این تابع یکسری مشکلات هم دارد: توابع، کلاس‌ها، DOM قابل clone نیستند. 
مطالب
برنامه نویسی اندروید با Xamarin.Android - قسمت اول
وقتی صحبت از ساخت برنامه‌های کاربردی iOS و Android می‌شود، بسیاری از افراد تنها گزینه را Objective-C یا Java می‌دانند. اما در این چند سال اکوسیستم‌هایی (مجموعه ای از ابزارها) برای ایجاد برنامه‌های کاربردی موبایل ظهور کرده‌اند و البته تمرکز آن‌ها بر روی Cross Platform بودن آن‌ها بوده است. هر کدام از آن‌ها قابلیت‌هایی را برای ما به ارمغان می‌آورند. البته بعضی فقط به ما امکان نوشتن کدهای Html و Java Script را می‌دهند و برخی دیگر از کدهای C++/C که کدهای low-level هستند، استفاده می‌کنند.
ما در اینجا قصد معرفی Xamarin را داریم. تنها پلتفرمی که تمام امکانات بومی موبایل را به همراه امکانات بسیار دیگری، برای ما فراهم می‌کند. این امکانات شامل موارد ذیل هستند:
1- اتصال کامل به SDK بومی: Xamarin شامل اتصالاتی برای استفاده از تمامی (تقریبا) امکانات iOS و Android می‌باشد. این اتصالات به صورت Strongly-typed هستند. به این معنا که برای بررسی و استفاده، آسان هست و همچنین در حین توسعه و کامپایل به خوبی صحت کد‌ها را چک می‌کند.
2- قابلیت ارتباط با Objective-C،Java، C،C : زامارین امکاناتی را برای فراخوانی مستقیم کتابخانه‌هایی که با Objective-C، Java، C و ++C نوشته شده‌اند، نیز فراهم کرده است. این یک امکان فوق العاده هست که شما بتوانید از تعداد بسیار زیاد کتابخانه‌های نوشته شده برای iOS و Android استفاده کنید.
3- استفاده از زبان مدرن #C: برنامه‌های Xamarin با #C نوشه می‌شوند که بهبود‌های قابل توجهی نسبت به زبان‌های Objective-C و Java داشته است. امکاناتی مانند عبارات لامبدا، LINQ، برنامه نویسی موازی و ....
4- مجموعه کلاس‌های فوق العاده: برنامه‌های Xamarin از Net BCL. که مجموعه‌ای عظیم و جامع از ویژگی‌های قدرتمند، مانند استفاده از XML، بانک اطلاعاتی، شبکه، IO و ...است، استفاده می‌کند که امکانات فوق العاده‌ای را برای توسعه دهندگان فراهم می‌نماید.
5- استفاد ه از یک IDE قدرتمند: برای Mac OS X شما Xamarin Studio  و برای ویندوز Xamarin Studio و Visual Studio را در اختیار دارید که برای یک توسعه دهنده‌ی نرم افزار چیزی را کم نگذاشته‌اند.
6- Cross Platform بودن: Xamarin برای سه پلتفرم مطرح موبایل، شامل iOS، Android و Windows Phone قابل استفاده می‌باشد و تقریبا 90 درصد از کدهای شما قابل استفاده‌ی مجدد در هر سه پلتفرم می‌باشد.
البته با ارائه‌ی Xamarin.Forms این میزان به 100درصد رسیده است!

نحوه‌ی نصب Xamarin:
می‌توانید Xamarin Studio و Xamarin For Visual Studio را از سایت Xamarin دانلود نموده و به راحتی نصب نمایید. برای آنکه بتوانید Xamarin را نصب و استفاده نمایید، لازم است که موارد زیر را نیز به روی سیستم خود داشته باشید:
1- Android SDK  
2- GTK#
3- Android NDK
4- Java SDK(JDK)
هر آنچه را که برای ادامه‌ی مسیر با ما لازم دارید، از ehsanavr.com دانلود نمایید.
 و البته نحوه‌ی نصب Xamarin به صورت کامل و همراه با تصاویر مربوطه نیز در آدرس زیر وجود دارد:

Emulator یا شبیه ساز اندروید: Xamarin یک شبیه ساز بسیار عالی برای تست برنامه‌های اندرویدی در اختیار ما قرار داده است که از Virtual Box استفاده می‌کند. می‌توانید این نرم افزار را با نام Xamarin Android Player از اینجا دانلود نمایید. بعد از نصب و اجرای آن شما باید Imageهای مربوط به هر نسخه‌ای را که میخواهید، دانلود کنید:

Xamarin Android Player


  
کمی درباره سطوح مختلف APIهای اندروید:
اندروید برای تشخیص سازگاری برنامه‌های اندروید، از سطوح مختلف APIها(API Levels) استفاده می‌کند. هر سطح از این APIها یک ورژن از اندروید را شامل می‌شوند. برای مثال Marshmallow که به اندروید 6 معروف می‌باشد، از API Level شماره 23 بهره می‌برد و Lollipop نسخه‌ی 5، شامل API Level شماره 21 و Lollipop 5.1 شامل API Level شماره 22 می‌باشد و الی آخر.
اهمیت دانستن این موضوع، به این دلیل می‌باشد که انتخاب API Level مناسب، ارتباط مستقیمی با موبایل هایی دارد که می‌توانند برنامه‌ی شما را اجرا کنند. می‌توانید لیست کامل API‌های موجود را از اینجا مشاهده نمایید:

برای هر برنامه‌ی اندروید نوشته شده، 3 تنظیم برای SDK مورد استفاده قرار می‌گیرد:
Target Framework: مشخص کننده‌ی نوع فریموورکی می‌باشد که برنامه با آن کامپایل می‌شود.
Minimum Android Version: مشخص کننده‌ی قدیمی‌ترین نسخه‌ی اندرویدی می‌باشد که می‌خواهید برنامه‌ی شما روی آن اجرا شود. این API Level در زمان اجرا استفاده می‌شود.
Target Android Version: نسخه‌ای را که برنامه‌ی شما بر روی آن اجرا می‌شود، مشخص می‌نماید. این API Level در زمان اجرا استفاده می‌شود. همیشه میزان این API Level باید برابر یا بیشتر از Target Framework باشد.
البته معمولا این سه تنظیمات را روی یک API Level تنظیم می‌کنند.
قبل از اینکه بخواهید API Level مورد نظر را انتخاب کنید، باید SDK مربوط به آن را دانلود و نصب نمایید. برای مدیریت نسخه‌های SDKهای نصب شده بر روی سیستم خود می‌توانید از Android SDK Manager که در فولدر SDK قرار دارد می‌توانید استفاده نمایید.


کمی درباره‌ی معماری Xamarin:
برنامه‌های نوشته شده در Xamarin.Android در محیط Mono اجرا می‌شوند و Mono در کنار ماشین مجازی زمان اجرای اندروید، اجرا می‌شود. این دو سیستم روی هسته‌ی لینوکس اجرا می‌شوند و APIهای مختلفی را در اختیار برنامه نویسان قرار می‌دهند. Mono با زبان C نوشته شده است. شما می‌توانید کلاس‌های NET. مانند: System، System.IO، System.Net را برای دسترسی به قابلیت‌های لینوکس مورد استفاده قرار بدهید.
در اندروید، بیشتر قابلیت‌های سیستم مانند صدا، گرافیک، OpenGL و قابلیت‌های تلفن، مستقیم در دسترس برنامه‌های بومی(Native) نیستند. آن‌ها فقط از طریق APIهای Android Runtime Java در دسترس هستند که در فضای نام Java.* یا Android.* قرار داردند. تصویر زیر این توضیحات را به خوبی نشان می‌دهد.
 

Xamarin

توسعه دهندگان Xamarin.Android به امکانات مختلفی از سیستم عامل با فراخوانی API‌های NET. دسترسی دارند و همچنین کلاس‌های موجود در فضای نام Android، پُلی برای استفاده از API‌های اندروید توسط برنامه نویسان Xamarin می‌باشد.
نکته‌ی مهم دیگر این است که Packageهای برنامه‌های نوشته شده با Xamarin ساختاری شبیه به برنامه‌های معمول اندرویدی دارد، البته همراه با موارد زیر:
1- اسمبلی‌های برنامه (شامل IL)
2- کتابخانه‌های بومی، که باید حتما برنامه‌های Xamarin.Android کتابخانه‌های زمان اجرای مناسب با معماری اندروید مانند:armeabi، armeabi-v7a، x86 را در اختیار داشته باشد.

در بخش بعد اولین برنامه‌ی اندرویدی خود را با Xamarin اجرا می‌نماییم.