public abstract class DataErrorInfo :ObservableObject, IDataErrorInfo { [Browsable(false)] public string Error { get { var errors = ValidationHelper.GetErrors(this); return string.Join(Environment.NewLine, errors); } } public string this[string columnName] { get { var errors = ValidationHelper.ValidateProperty(this, columnName); return string.Join(Environment.NewLine, errors); } } }
<script src="~/scripts/jquery.filedrop.js" type="text/javascript"></script>
<div id="dropZone">فایل برنامه را به داخل این کادر بکشانید</div> <br> فایل یا فایلهای آپلود شده: <ul id="uploadResult"></ul>
.files { min-height: 42px; background: #CCC none repeat scroll 0% 0%; border-top: 1px solid #FFF; margin: 11px 0px; padding: 11px 13px; border-radius: 6px; } #dropZone.mouse-over { background-color: #1d4257; }
$('#dropZone').filedrop({ url: uploadAddress, paramname: 'files', maxFiles: 1, dragOver: function() { $('#dropZone').addClass('mouse-over'); }, dragLeave: function() { $('#dropZone').removeClass('mouse-over'); }, drop: function() { $('#dropZone').removeClass('mouse-over'); }, afterAll: function() { $('#dropZone').html('آپلود با موفقیت انجام شد'); }, uploadFinished: function(i, file, response, time) { $('#uploadResult').append('<li>' + file.name + '</li>'); } });
Url | آدرسی که قرار است فایلها به آن سمت ارسال شوند. |
Paramname | در سمت سرور باید فایلها را با استفاده از این نام پارامتر دریافت کنید. |
maxFiles | تعداد فایلهایی که میتوان با درگ و دراپ کردن روی آن به دست آورد. در بالا به یک فایل محدود شده است. |
dragOver | این رویداد زمانی اجرا خواهد شد که اشاره گر با حالت درگ کرده فایلها را به محل آپلود آورده است. |
dragLeave | موقعی که ماوس از محل آپلود خارج میشود |
drop | موقعی که شما فایلها را روی محل آپلود رها میکنید. |
afterAll | بعد از اینکه همه کارها تمام شد اجرا میشود.(آخرین رویداد) |
uploadFinished | کار آپلود به پایان رسیده است. در مثال بالا پس از پایان آپلود، نام فایل آپلود شده را به کاربر نشان دادهایم. |
نحوهی دریافت آن در سمت سرور, در یک اکشن متد به صورت زیر است:
[HttpPost] public virtual ActionResult UpdateApp(IEnumerable<HttpPostedFileBase>files) { foreach (HttpPostedFileBase file in files) { string filePath = Path.Combine(TempPath, file.FileName); file.SaveAs(filePath); } return Json(new {state = "success", message = "با موفقیت عملیات ارسال فایل انجام شد"}, JsonRequestBehavior.AllowGet); }
در اکشن متد بالا ما فایلها را از طریق نام پارامتر files که مشخص کرده بودیم، به عنوان یک لیست شمارشی دریافت میکنیم. کدها بالا برای سادهترین راه اندازی ممکن کفایت میکنند.
این موارد از اصلیترینها هستند که به کار میآیند. به غیر اینها یک سری خصوصیات اضافهتری هم برای آن وجود دارد.
fallback_id | اگر دوست دارید این آپلودر را نیر به یک آپلودر معمولی اتصال دهید از این شناسه استفاده کنید. |
withCredentials | با استفاده از کوکیها یک درخواست cross-origin ایجاد میکند. |
data | اگر دوست دارید به همراه فایلها اطلاعات دیگری هم به همراه آن
ارسال و پست شوند از این طریق اقدام نمایید. میتواند در قالب یک متغیر
باشد یا خروجی یک تابع.data: { param1: 'value1', param2: function(){ return calculated_data; } |
headers | برای ارسال مقدار اضافهتر در هدر درخواست به کار میرود و صدا زدن آن همانند کد data میباشد. |
error | در صورتیکه در فرایند آپلود خطایی رخ دهد، اجرا میگردد. نحوهی کدنویسی آن و بررسی خطاهای آن به شرح زیر است:error: function(err, file) { switch(err) { case 'BrowserNotSupported': alert('مرورگر از این فناوری پشتیبانی نمیکند') break; case 'TooManyFiles': // قصد آپلود همزمان فایلهای بیشتری از حد مجاز تعیین شده دارید break; case 'FileTooLarge': //حداقل حجم یکی از فایلها از حجم مجاز تعیین شده بیشتر است //برای دسترسی به نام آن فایل از کد زیر استفاده کنید //file.name break; case 'FileTypeNotAllowed': // نوع حداقل یکی از فایلها با نوعها مشخص شده ما یکی نیست break; case 'FileExtensionNotAllowed': // پسوند حداقل یکی از فایلها مورد تایید نیست break; default: break; } } |
allowedfiletypes | نوع فایلهای مجاز را تعیین میکند:allowedfiletypes: |
allowedfileextensions | پسوند فایل هایی که برای آپلود مجاز هستند را معرفی میکند.allowedfileextensions: |
maxfilesize | حداکثر حجم مجاز برای هر فایل که به مگابایت بیان میشود. |
docOver | این رویداد زمانی اجرا میشود که فایلهای درگ شده شما وارد محیط یا پنجره مرورگر میشود. |
uploadStarted | این رویداد زمانی اجرا میگردد که فرایند آپلود هر فایل به طور جداگانه در حال آغاز شدن است: متغیر i در کد زیر شامل اندیس فایلی است که آپلودش آغاز شده است و این اندیس از صفر آغاز میشود. متغیر file دسترسی شما را به اطلاعات یک فایل باز میکند مانند نام فایل. متغیر len تعداد فایل هایی را که کاربر در محل آپلود رها کرده است، باز میگرداند. function(i, file, len){ }, |
uploadFinished | با اتمام آپلود هر فایل، این رویداد فراخوانی میگردد. دو
پارامتر اول آن، همانند سابق هستند. پارامتر response خروجی json ایی را که در سمت
سرور برگرداندیم، به ما باز میگرداند. پارامتر بعدی، زمانی را که برای
آپلود طول کشیده است، بر میگرداند. function(i, file, response, time) { } |
progressUpdated | این رویداد برای نمایش پیشرفت یک آپلود مناسب است که آخرین پارامتر آن یک عدد صحیح از پیشرفت فایل را بر میگرداند.function(i, file, progress) { }, |
globalProgressUpdated | این رویداد میزان پیشرفت کلیه فایلها را به درصد باز میگرداند:function(progress) { $('#progress div') |
speedUpdated | سرعت آپلود هر فایل را با کیلوبیت بر ثانیه مشخص میکند.function(i, file, speed) { } |
rename | در صورتی که قصد تغییر نام فایل ارسالی را دارید میتوانید از این رویداد استفاده کنید. پارامتر name، نام اصلی فایل را بر میگرداند که میتوانید آن را دستکاری کنید و نام جدیدی را به عنوان خروجی برگردانید. نمونه کاربردی از این رویداد rename: function(name) { } |
beforeEach | این رویداد قبل از آپلود هر فایل آغاز میگردد و برگرداندن مقدار false در آن باعث جلوگیری و کنسل شدن آپلود آن فایل میگردد.function(file) { } |
beforeSend | پارامترهای اولی تکراری هستند ولی آخرین پارامتر یک
تابع done را میتوان به آن پاس کرد که قبل از اجرای کل عملیات آپلود صدا
زده میشود.function(file, i, done) { } |
PlUpload
DropZoneJS
این کتابخانه به نسبت DropFile امکانات بیشتری را دارد و در سایت اختصاصی آن مثالها و مستندات خوبی قرار گرفته است. در سادهترین حالت آن ابتدا فایل کتابخانه را صدا زده و سپس تگ فرم را به آن نسبت دهید:
<script src="https://rawgit.com/enyo/dropzone/master/dist/dropzone.js"></script> <form action="/upload-target" class="dropzone"></form>
Install-Package dropzone
با نصب این کتابخانه یک سری فایل CSS هم به سیستم اضافه میشود که میتوانید برای استایل دهی هر چه بیشتر از آن بهره ببرید. کد فرم را به شکل زیر تغییر دهید:
<form action="~/Home/SaveUploadedFile" method="post" enctype="multipart/form-data" class="dropzone" id="dropzoneForm" style="width: 50px; background: none; border: none;"> <div class="fallback"> <input name="file" type="file" multiple /> <input type="submit" value="Upload" /> </div> </form>
var myDropzone = new Dropzone("div#myId", { url: "/file/post"}); //============ OR ==================== $("div#myId").dropzone({ url: "/file/post" });
Dropzone.options.myId= { paramName: "file", //نام پارامتری که فایل از طریق آن انتقال میبابد maxFilesize: 2, // MB accept: function(file, done) { if (file.name == "justinbieber.jpg") { done("Naha, you don't."); } else { done(); } } };
یک نکته تکمیلی در مورد آپلود: در ASP.net به طور پیش فرض نهایت حجم فایل آپلودی 4 مگابایتی تعیین شده است که میتوانید آن را از طریق web.config تغییر دهید:
<configuration> <system.web> <httpRuntime maxRequestLength="1048576" /> </system.web> </configuration>
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="1073741824" /> </requestFiltering> </security> </system.webServer>
آموزش LINQ بخش دوم
// The Three Parts of a LINQ Query: // 1. منبع داده int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; // 2. ایجاد پرس و جو // numQuery is an IEnumerable<int> var numQuery = from num in numbers where (num % 2) == 0 select num; // 3. اجرای پرس و جو foreach (int num in numQuery) { Console.Write("{0,1} ", num); }
class Ingredient { public string Name { get; set; } public int Calories { get; set; } }
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100 }, new Ingredient {Name = "Milk", Calories = 150 }, new Ingredient {Name = "Flour", Calories = 50 }, new Ingredient {Name = "Butter", Calories = 200 } };
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100 }, new Ingredient {Name = "Milk", Calories = 150 }, new Ingredient {Name = "Flour", Calories = 50 }, new Ingredient {Name = "Butter", Calories = 200 } }; IEnumerable<string> highCalories = ingredients.Where(x => x.Calories >= 150) .OrderBy(x => x.Name) .Select(x => x.Name); foreach (var item in highCalories) { Console.WriteLine(item); }
Butter Milk Suger
عبارت Lambda نوشته شدهی در بخش Select مشخص میکند که خروجی بر اساس چه خصوصیتی از توالی ورودی باشد. در اینجا نام عناصر به صورت رشته در خروجی ظاهر میشوند.
سبک Query Expression (عبارتهای پرس و جو)
Query Expression یک گرامر زیبا و روان برای نوشتن پرس و جوها را ارائه میدهد. در مثال زیر از سبک Query Expression استفاده کردهایم:
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100}, new Ingredient {Name = "Milk", Calories = 150}, new Ingredient {Name = "Flour", Calories = 50}, new Ingredient {Name = "Butter", Calories = 200} }; IEnumerable<string> highCalories = from i in ingredients where i.Calories >= 150 orderby i.Name select i.Name; foreach (var item in highCalories) { Console.WriteLine(item); }
خروجی کد بالا با خروجی کد به سبک Fluent یکسان است:
Butter Milk Suger
همانطور که میبینید ترتیب عملیات همانند روش قبل است. عبارتهای پرس و جوی (from,where,orderby,select) به ترتیب با اصلاح توالی ورودی و تحویل آن به عبارت جستجوی بعدی کار را انجام میدهند.
عبارت جستجوی بالا با کلمهی کلیدی from آغاز شده است. هدف from دو چیز است:
1- مشخص کردن توالی ورودی (منبع داده)
2- معرفی متغیر Range (مشخص کردن عنصر مورد نظر در منبع داده)
متغیر Range همچون متغیر شمارنده در حلقه هاست.
در ادامه این سری آموزشی درباره متغیر Range بصورت کاملتری بحث خواهیم کرد.
function identity(arg: number): number { return arg; }
function identity(arg: any): any { return arg; }
function identity<T>(arg: T): T { return arg; }
- ارسال تمام آرگومانها که شامل آرگومان نوع داده هم میباشد
let output = identity<string>("myString"); // type of output will be 'string'
- روش دوم که شاید استفاده رایج از توابع جنریک هم هست، استفاده از امکان type argument inference میباشد.
let output = identity("myString"); // type of output will be 'string'
function loggingIdentity<T>(arg: T): T { console.log(arg.length); // Error: T doesn't have .length return arg; }
function loggingIdentity<T>(arg: T[]): T[] { console.log(arg.length); // Array has a .length, so no more error return arg; }
function loggingIdentity<T>(arg: Array<T>): Array<T> { console.log(arg.length); // Array has a .length, so no more error return arg; }
function identity<T>(arg: T): T { return arg; } let myIdentity: <T>(arg: T) => T = identity;
function identity<T>(arg: T): T { return arg; } let myIdentity: <U>(arg: U) => U = identity;
function identity<T>(arg: T): T { return arg; } let myIdentity: {<T>(arg: T): T} = identity;
interface GenericIdentityFn { <T>(arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn = identity;
interface GenericIdentityFn<T> { (arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn<number> = identity;
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; };
let stringNumeric = new GenericNumber<string>(); stringNumeric.zeroValue = ""; stringNumeric.add = function(x, y) { return x + y; }; alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); // Now we know it has a .length property, so no more error return arg; }
loggingIdentity(3); // Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3});
function find<T, U extends Findable<T>>(n: T, s: U) { // errors because type parameter used in constraint // ... } find (giraffe, myAnimals);
function find<T>(n: T, s: Findable<T>) { // ... } find(giraffe, myAnimals);
function create<T>(c: {new(): T; }): T { return new c(); }
class BeeKeeper { hasMask: boolean; } class ZooKeeper { nametag: string; } class Animal { numLegs: number; } class Bee extends Animal { keeper: BeeKeeper; } class Lion extends Animal { keeper: ZooKeeper; } function findKeeper<A extends Animal, K> (a: {new(): A; prototype: {keeper: K}}): K { return a.prototype.keeper; }
findKeeper(Lion).nametag; // typechecks!
کتابخانه simple-button-checks
Simple button checks is a simple plugin for transform checkbox inputs into html buttons for css customize. High performance, keyboard support and preserve original input click/change events. Demo
کتابخانه jquery-typeahead
معرفی Smart Unit Tests
<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>http://www.example.com/</loc> <lastmod>2005-01-01</lastmod> <changefreq>monthly</changefreq> <priority>0.8</priority> </url> </urlset>
که یک فایل XML متشکل از یک تگ urlset است و این تگ نیز حاوی یک یا چند تگ url میباشد. با توجه به تعاریف بالا به یک چنین کلاسی خواهیم رسید:
public enum ChangeFreq { Always, Hourly, Daily, Weekly, Monthly, Yearly, Never } [XmlElement("loc")] public string Url { get; set; } [XmlElement("lastmod")] public DateTime? LastModified { get; set; } public bool ShouldSerializeLastModified() { return LastModified.HasValue; } [XmlElement("changefreq")] public ChangeFreq? ChangeFrequency { get; set; } public bool ShouldSerializeChangeFrequency() { return ChangeFrequency.HasValue; } [XmlElement("priority")] public float? Priority { get; set; } public bool ShouldSerializePriority() { return Priority.HasValue; } }
دقت داشته باشید که چون پروپرتیهای LastModified ، ChangeFrequency و Priority از نوع Nullable تعریف شدهاند، پس باید کاری کنیم در صورتیکه این پروپرتیها نال بودند سریالیز نشوند. بدین منظور از تابع ShouldSerialize[MemberName] استفاده میشود. این تابع جزئی از دات نت است. کافی است بعد از ShouldSerialize نام پروپرتی را ذکر کنید. حال به کلاس دیگری نیاز داریم تا لیستی از کلاس فوق را دربر داشته باشد.
[XmlRoot("urlset",Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")] public class SiteMp { private readonly List<Location> _locations; public SiteMp() { _locations = new List<Location>(); } [XmlElement("url")] public List<Location> Locations { get { return _locations; } set { foreach (var location in value) { Add(location); } } } public void Add(Location location) { _locations.Add(location); } }
حال برای پردازش کلاس بالا لازم است ActionResultی را طراحی کنیم تا خروجی Response را به فرمت XML پردازش کند:
public class XmlResult : ActionResult { private readonly object _objectToSerialize; public XmlResult(object objectToSerialize) { _objectToSerialize = objectToSerialize; } public override void ExecuteResult(ControllerContext context) { if (_objectToSerialize == null) return; context.HttpContext.Response.Clear(); var xmlSerializer = new XmlSerializer(_objectToSerialize.GetType()); context.HttpContext.Response.ContentType = "text/xml"; xmlSerializer.Serialize(context.HttpContext.Response.Output, _objectToSerialize); } }
و در آخر یک کنترلر ساخته و به صورت زیر از آن استفاده میکنیم:
public class SiteMapController : Controller { // GET: SiteMap public ActionResult Index() { SiteMp siteMap = new SiteMp(); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/Index" }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/NewRequest", ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/FindRequest", ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/ContactUs/Index", ChangeFrequency = Location.ChangeFreq.Daily, LastModified = DateTime.UtcNow, Priority = 0.5f }); return new XmlResult(siteMap); }
اگر دقت کنید لینکهای ثابت باید به صورت دستی اضافه شوند. سناریویی را تصور کنید که لینکها زیاد باشند(جدای از لینک هایی که از دیتابیس لود میشوند) این کار کمی ناجور به نظر میرسد. در اینجا میخواهیم از طریق امکانات ،Reflection عمل اضافه کردن لینک به صورت خودکار انجام شود.
public class ControllerScanner { public static List<string> ScanAllControllers(HttpRequestBase requestBase) { Assembly asm = Assembly.GetAssembly(typeof(MvcApplication)); var controllerActionlist = asm.GetTypes() .Where(type => typeof (Controller).IsAssignableFrom(type)) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) .Where((returnType => returnType.ReturnType == (typeof(ViewResult)) || returnType.ReturnType==(typeof(ActionResult)))) .Select( x => new { Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name }) .OrderBy(x => x.Controller).ThenBy(x => x.Action).Distinct().ToList(); if (requestBase.Url == null) return null; var url = requestBase.Url.GetLeftPart(UriPartial.Authority); return controllerActionlist.Select(controller => $"{url}/{controller.Controller}/{controller.Action}").ToList(); } }
حال از کلاس بالا در کنترلر SiteMap به صورت زیر استفاده میکنیم :
public class SiteMapController : Controller { // GET: SiteMap public ActionResult Index() { var siteMap = new SiteMap(); var controllers = ControllerScanner.ScanAllControllers(Request); foreach (var controller in controllers) { siteMap.Add(new Location { Url = controller, ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); } return new XmlResult(siteMap); } }
در آخر نیز سطر زیر را به سیستم مسیریابی اضافه نمایید تا در صورت درخواست فایل sitemap.xml اکشن Index از کنترلر SiteMap فراخوانی شود.
routes.MapRoute( "SiteMap", // Route name "sitemap.xml", // URL with parameters new { controller = "Sitemap", action = "Index", name = UrlParameter.Optional, area = "" } );
استفاده از StructureMap به عنوان یک IoC Container
x.For<UnitOfWorkScopeBase<TrackingModelUnitOfWork>>() .HybridHttpOrThreadLocalScoped() .Use(() => { IsWeb ?new PerRequestUnitOfWorkScope<TrackingModelUnitOfWork>(context) : new PerThreadUnitOfWorkScope<TrackingModelUnitOfWork>(context) });
x.For<LightSpeedContext<TUnitOfWork>>().Singleton().Use(() => { var ctx = new LightSpeedContext<TrackingModelUnitOfWork>() { ConnectionString = "MyConnection", QuoteIdentifiers = true, DataProvider = DataProvider.SqlServer2012 }; return ctx; });
بوت استرپ 4 الفا 2 منتشر شد
Here’s a look at a handful of the changes since our last alpha:
- Overhauled spacing utilities to use a numerical tiering (to avoid confusion with grid tiers).
- Continued refactoring efforts to replace markup-specific selectors with classes across several components (including pagination, lists, and more). Still more to do here with additional components.
- Reverted media queries and grid containers from rems to pixels as viewports are not affected by font-size. See #17403 for details. We’ve got a ton of grid work left, too. Feel free to follow along with#18471.
- Reverted
.0625rem
width borders to1px
for more consistent component borders that avoid zoom and font-size bugs across browsers. - Renamed
.img-responsive
to.img-fluid
to avoid future confusion on the various responsive image solutions out there. - Replaced ZeroClipboard with clipboard.js for Flash-independent copy buttons.
- Inputs and buttons now share the same border variable to ensure components are always sized similarly.
- Updated all pseudo-element selectors to use the spec’s preferred double colon (e.g.,
::before
as opposed to:before
). - Cards now have outline variants and mixins to support extending base classes further.
- Utility classes for floats and text alignment now have responsive ranges. This means we’ve dropped the non-responsive classes to avoid duplication.
- Added support for jQuery 2.
- And hundreds more Sass improvements, bug fixes, documentation updates, and more.