پروژهای که بر اساس دستور dotnet new mvc و به کمک SDK جدید ایجاد میشود (این دستور وابستگی به IDE شما ندارد)، فقط به همراه فایلهای نهایی یکسری کتابخانهی جاوا اسکریپتی و CSS ای است و راهی را برای مدیریت آنها اضافه نکردهاند. بنابراین یا باید از روش مطلب جاری استفاده کنید (نمونهاش در اینجا) و یا از روش «LibMan».
سلام.
ممنون بابت معرفی این کتابخانهی مفید. جایی فرمودید که «در اینترفیس IJob در ASP.NET، به شی HttpContext دسترسی ندارید.» که درست نیست. شما در همه جای برنامه ASP.NET به کمک HttpContext.Current به HttpContext جاری دسترسی دارید. آیا استفاده از این روش مشکل خاصی داره؟
در ادامه مطالب قبل
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 1#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 2#
قبل از شروع توضیحات متدهای کلاس Shape در ادامه پستهای قبل در ^ و ^ ابتدا به تشریح یک تصویر میپردازیم.
متد های این کلاس:
حال به تشریح سازنده کلاس میپردازیم:
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 1#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 2#
قبل از شروع توضیحات متدهای کلاس Shape در ادامه پستهای قبل در ^ و ^ ابتدا به تشریح یک تصویر میپردازیم.
خوب همانگونه که در تصویر بالا مشاده مینمایید، برای رسم یک شی چهار حالت متفاوت ممکن است پیش بیاید. (دقت کنید که ربع اول محور مختصات روی بوم گرافیکی قرار گرفته است، در واقع گوشه بالا و سمت چپ بوم گرافیکی نقطه (0 و 0) محور مختصات است و عرض بوم گرافیکی محور Xها و ارتفاع بوم گرافیکی محور Yها را نشان میدهد)
- در این حالت StartPoint.X < EndPoint.X و StartPoint.Y < EndPoint.Y خواهد بود. (StartPoint نقطه ای است که ابتدا ماوس شروع به ترسیم میکند، و EndPoint زمانی است که ماوس رها شده و پایان ترسیم را مشخص میکند.)
- در این حالت StartPoint.X > EndPoint.X و StartPoint.Y > EndPoint.Y خواهد بود.
- در این حالت StartPoint.X > EndPoint.X و StartPoint.Y > EndPoint.Y خواهد بود.
- در این حالت StartPoint.X < EndPoint.X و StartPoint.Y > EndPoint.Y خواهد بود.
ابتدا یک کلاس کمکی به صورت استاتیک تعریف میکنیم که متدی جهت پیش نمایش رسم شی در حالت جابجایی ، رسم، و تغییر اندازه دارد.
using System; using System.Drawing; namespace PWS.ObjectOrientedPaint.Models { /// <summary> /// Helpers /// </summary> public static class Helpers { /// <summary> /// Draws the preview. /// </summary> /// <param name="g">The g.</param> /// <param name="startPoint">The start point.</param> /// <param name="endPoint">The end point.</param> /// <param name="foreColor">Color of the fore.</param> /// <param name="thickness">The thickness.</param> /// <param name="isFill">if set to <c>true</c> [is fill].</param> /// <param name="backgroundBrush">The background brush.</param> /// <param name="shapeType">Type of the shape.</param> public static void DrawPreview(Graphics g, PointF startPoint, PointF endPoint, Color foreColor, byte thickness, bool isFill, Brush backgroundBrush, ShapeType shapeType) { float x = 0, y = 0; float width = Math.Abs(endPoint.X - startPoint.X); float height = Math.Abs(endPoint.Y - startPoint.Y); if (startPoint.X <= endPoint.X && startPoint.Y <= endPoint.Y) { x = startPoint.X; y = startPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y >= endPoint.Y) { x = endPoint.X; y = endPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y <= endPoint.Y) { x = endPoint.X; y = startPoint.Y; } else if (startPoint.X <= endPoint.X && startPoint.Y >= endPoint.Y) { x = startPoint.X; y = endPoint.Y; } switch (shapeType) { case ShapeType.Ellipse: if (isFill) g.FillEllipse(backgroundBrush, x, y, width, height); //else g.DrawEllipse(new Pen(foreColor, thickness), x, y, width, height); break; case ShapeType.Rectangle: if (isFill) g.FillRectangle(backgroundBrush, x, y, width, height); //else g.DrawRectangle(new Pen(foreColor, thickness), x, y, width, height); break; case ShapeType.Circle: float raduis = Math.Max(width, height); if (isFill) g.FillEllipse(backgroundBrush, x, y, raduis, raduis); //else g.DrawEllipse(new Pen(foreColor, thickness), x, y, raduis, raduis); break; case ShapeType.Square: float side = Math.Max(width, height); if (isFill) g.FillRectangle(backgroundBrush, x, y, side, side); //else g.DrawRectangle(new Pen(foreColor, thickness), x, y, side, side); break; case ShapeType.Line: g.DrawLine(new Pen(foreColor, thickness), startPoint, endPoint); break; case ShapeType.Diamond: var points = new PointF[4]; points[0] = new PointF(x + width / 2, y); points[1] = new PointF(x + width, y + height / 2); points[2] = new PointF(x + width / 2, y + height); points[3] = new PointF(x, y + height / 2); if (isFill) g.FillPolygon(backgroundBrush, points); //else g.DrawPolygon(new Pen(foreColor, thickness), points); break; case ShapeType.Triangle: var tPoints = new PointF[3]; tPoints[0] = new PointF(x + width / 2, y); tPoints[1] = new PointF(x + width, y + height); tPoints[2] = new PointF(x, y + height); if (isFill) g.FillPolygon(backgroundBrush, tPoints); //else g.DrawPolygon(new Pen(foreColor, thickness), tPoints); break; } if (shapeType != ShapeType.Line) { g.DrawString(String.Format("({0},{1})", x, y), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(foreColor), x - 20, y - 25); g.DrawString(String.Format("({0},{1})", x + width, y + height), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(foreColor), x + width - 20, y + height + 5); } else { g.DrawString(String.Format("({0},{1})", startPoint.X, startPoint.Y), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(foreColor), startPoint.X - 20, startPoint.Y - 25); g.DrawString(String.Format("({0},{1})", endPoint.X, endPoint.Y), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(foreColor), endPoint.X - 20, endPoint.Y + 5); } } } }
- DrawPreview : این متد پیش نمایشی برای شی در زمان ترسیم، جابجایی و تغییر اندازه آماده میکند، پارامترهای آن
عبارتند از : بوم گرافیکی، نقطه شروع، نقطه پایان و رنگ قلم ترسیم پیش نمایش شی، ضخامت خط، آیا شی توپر باشد؟، الگوی پر کردن پس زمینه شی ، و نوع شی ترسیمی میباشد.
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Net; namespace PWS.ObjectOrientedPaint.Models { /// <summary> /// Shape (Base Class) /// </summary> public abstract partial class Shape { #region Constructors (2) /// <summary> /// Initializes a new instance of the <see cref="Shape" /> class. /// </summary> /// <param name="startPoint">The start point.</param> /// <param name="endPoint">The end point.</param> /// <param name="zIndex">Index of the z.</param> /// <param name="foreColor">Color of the fore.</param> /// <param name="thickness">The thickness.</param> /// <param name="isFill">if set to <c>true</c> [is fill].</param> /// <param name="backgroundColor">Color of the background.</param> protected Shape(PointF startPoint, PointF endPoint, int zIndex, Color foreColor, byte thickness, bool isFill, Color backgroundColor) { CalulateLocationAndSize(startPoint, endPoint); Zindex = zIndex; ForeColor = foreColor; Thickness = thickness; IsFill = isFill; BackgroundColor = backgroundColor; } /// <summary> /// Initializes a new instance of the <see cref="Shape" /> class. /// </summary> protected Shape() { } #endregion Constructors #region Methods (10) // Public Methods (9) /// <summary> /// Draws the specified g. /// </summary> /// <param name="g">The g.</param> public virtual void Draw(Graphics g) { if (!IsSelected) return; float diff = Thickness + 4; Color myColor = Color.DarkSeaGreen; g.DrawString(String.Format("({0},{1})", StartPoint.X, StartPoint.Y), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(myColor), StartPoint.X - 20, StartPoint.Y - 25); g.DrawString(String.Format("({0},{1})", EndPoint.X, EndPoint.Y), new Font(new FontFamily("Tahoma"), 10), new SolidBrush(myColor), EndPoint.X - 20, EndPoint.Y + 5); if (ShapeType != ShapeType.Line) { g.DrawRectangle(new Pen(myColor), X, Y, Width, Height); // 1 2 3 // 8 4 // 7 6 5 var point1 = new PointF(StartPoint.X - diff / 2, StartPoint.Y - diff / 2); var point2 = new PointF((StartPoint.X - diff / 2 + EndPoint.X) / 2, StartPoint.Y - diff / 2); var point3 = new PointF(EndPoint.X - diff / 2, StartPoint.Y - diff / 2); var point4 = new PointF(EndPoint.X - diff / 2, (EndPoint.Y + StartPoint.Y) / 2 - diff / 2); var point5 = new PointF(EndPoint.X - diff / 2, EndPoint.Y - diff / 2); var point6 = new PointF((StartPoint.X - diff / 2 + EndPoint.X) / 2, EndPoint.Y - diff / 2); var point7 = new PointF(StartPoint.X - diff / 2, EndPoint.Y - diff / 2); var point8 = new PointF(StartPoint.X - diff / 2, (EndPoint.Y + StartPoint.Y) / 2 - diff / 2); g.FillRectangle(new SolidBrush(myColor), point1.X, point1.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point2.X, point2.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point3.X, point3.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point4.X, point4.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point5.X, point5.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point6.X, point6.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point7.X, point7.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point8.X, point8.Y, diff, diff); } else { var point1 = new PointF(StartPoint.X - diff / 2, StartPoint.Y - diff / 2); var point2 = new PointF(EndPoint.X - diff / 2, EndPoint.Y - diff / 2); g.FillRectangle(new SolidBrush(myColor), point1.X, point1.Y, diff, diff); g.FillRectangle(new SolidBrush(myColor), point2.X, point2.Y, diff, diff); } } /// <summary> /// Points the in sahpe. /// </summary> /// <param name="point">The point.</param> /// <param name="tolerance">The tolerance.</param> /// <returns> /// <c>true</c> if [has point in sahpe] [the specified point]; otherwise, <c>false</c>. /// </returns> public virtual bool HasPointInSahpe(PointF point, byte tolerance = 5) { return point.X > (StartPoint.X - tolerance) && point.X < (EndPoint.X + tolerance) && point.Y > (StartPoint.Y - tolerance) && point.Y < (EndPoint.Y + tolerance); } /// <summary> /// Moves the specified location. /// </summary> /// <param name="location">The location.</param> /// <returns></returns> public virtual PointF Move(Point location) { StartPoint = new PointF(location.X, location.Y); EndPoint = new PointF(location.X + Width, location.Y + Height); return StartPoint; } /// <summary> /// Moves the specified dx. /// </summary> /// <param name="dx">The dx.</param> /// <param name="dy">The dy.</param> /// <returns></returns> public virtual PointF Move(int dx, int dy) { StartPoint = new PointF(StartPoint.X + dx, StartPoint.Y + dy); EndPoint = new PointF(EndPoint.X + dx, EndPoint.Y + dy); return StartPoint; } /// <summary> /// Resizes the specified dx. /// </summary> /// <param name="dx">The dx.</param> /// <param name="dy">The dy.</param> /// <returns></returns> public virtual SizeF Resize(int dx, int dy) { EndPoint = new PointF(EndPoint.X + dx, EndPoint.Y + dy); return new SizeF(Width, Height); } /// <summary> /// Resizes the specified start point. /// </summary> /// <param name="startPoint">The start point.</param> /// <param name="currentPoint">The current point.</param> public virtual void Resize(PointF startPoint, PointF currentPoint) { var dx = (int)(currentPoint.X - startPoint.X); var dy = (int)(currentPoint.Y - startPoint.Y); if (startPoint.X >= X - 5 && startPoint.X <= X + 5) { StartPoint = new PointF(currentPoint.X, StartPoint.Y); if (ShapeType == ShapeType.Circle || ShapeType == ShapeType.Square) { Height = Width; } } else if (startPoint.X >= EndPoint.X - 5 && startPoint.X <= EndPoint.X + 5) { Width += dx; if (ShapeType == ShapeType.Circle || ShapeType == ShapeType.Square) { Height = Width; } } else if (startPoint.Y >= Y - 5 && startPoint.Y <= Y + 5) { Y = currentPoint.Y; if (ShapeType == ShapeType.Circle || ShapeType == ShapeType.Square) { Width = Height; } } else if (startPoint.Y >= EndPoint.Y - 5 && startPoint.Y <= EndPoint.Y + 5) { Height += dy; if (ShapeType == ShapeType.Circle || ShapeType == ShapeType.Square) { Width = Height; } } } /// <summary> /// Sets the background brush as hatch. /// </summary> /// <param name="hatchStyle">The hatch style.</param> public virtual void SetBackgroundBrushAsHatch(HatchStyle hatchStyle) { var brush = new HatchBrush(hatchStyle, BackgroundColor); BackgroundBrush = brush; } /// <summary> /// Sets the background brush as linear gradient. /// </summary> public virtual void SetBackgroundBrushAsLinearGradient() { var brush = new LinearGradientBrush(StartPoint, EndPoint, ForeColor, BackgroundColor); BackgroundBrush = brush; } /// <summary> /// Sets the background brush as solid. /// </summary> public virtual void SetBackgroundBrushAsSolid() { var brush = new SolidBrush(BackgroundColor); BackgroundBrush = brush; } // Private Methods (1) /// <summary> /// Calulates the size of the location and. /// </summary> /// <param name="startPoint">The start point.</param> /// <param name="endPoint">The end point.</param> private void CalulateLocationAndSize(PointF startPoint, PointF endPoint) { float x = 0, y = 0; float width = Math.Abs(endPoint.X - startPoint.X); float height = Math.Abs(endPoint.Y - startPoint.Y); if (startPoint.X <= endPoint.X && startPoint.Y <= endPoint.Y) { x = startPoint.X; y = startPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y >= endPoint.Y) { x = endPoint.X; y = endPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y <= endPoint.Y) { x = endPoint.X; y = startPoint.Y; } else if (startPoint.X <= endPoint.X && startPoint.Y >= endPoint.Y) { x = startPoint.X; y = endPoint.Y; } StartPoint = new PointF(x, y); EndPoint = new PointF(X + width, Y + height); } #endregion Methods } }
حال به تشریح سازنده کلاس میپردازیم:
- Shape: پارامترهای این سازنده به ترتیب عبارتند از نقطه شروع، نقطه پایان، عمق شی، رنگ قلم، ضخامت خط، آیا شی توپر باشد؟، و رنگ پر کردن شی، در این سازنده ابتدا توسط متدی به نام CalulateLocationAndSize(startPoint, endPoint); b نقاط ابتدا و انتهای شی مورد نظر تنظیم میشود، در متد مذکور بررسی میشود در صورتی که نقاط شروع و پایان یکی از حالتهای 1 ، 2، 3، 4 از تصویر ابتدا پست باشد همگی تبدیل به حالت 1 خواهد شد.
سپس به تشریح متدهای کلاس Shape میپردازیم:
- Draw: این متد دارای یک پارامتر ورودی است که بوم گرافیکی مورد نظر میباشد، در واقع شی مورد نظر خود را بروی این بوم گرافیکی ترسیم میکند. در کلاس پایه کار این متد زیاد پیچیده نیست، در صورتی که شی در حالت انتخاب باشد (IsSelected = true) بروی شی مورد نظر 8 مربع کوچک ترسیم میشود و اگر شی مورد نظر خط باشد دو مربع کوچک در طرفین خط رسم میشود که نشان دهنده انتخاب شدن شی مورد نظر است. این متد به صورت virtual تعریف شده است یعنی کلاس هایی که از Shape ارث میبرند میتوانند این متد را برای خود از نو بازنویسی کرده (override کنند) و تغییر رفتار دهند.
- HasPointInSahpe : این متد نیز به صورت virtual تعریف شده است دارای خروجی بولین میباشد. پارامترهای این متد عبارتند از یک نقطه و یک عدد که نشان دهنده تلرانش نقطه بر حسب پیکسل میباشد. کار این متد این است که یک نقطه را گرفته و بررسی میکند که آیا نقطه مورد نظر با تلرانس وارد شده آیا در داخل شی واقع شده است یا خیر (مثلا وجود نقطه در مستطیل یا وجود نقطه در دایره فرمولهای متفاوتی دارند که در اینجا پیش فرض برای تمامی اشیا حالت مستطیل در نظر گرفته شده که میتوانید آنها را بازنویسی (override) کنید).
- Move: این متد به عنوان پارامتر یک نقطه را گرفته و شی مورد نظر را به آن نقطه منتقل میکند در واقع نقطه شروع و پایان ترسیم شی را تغییر میدهد.
- Move: این متد نیز برای جابجایی شی به کار میرود، این متد دارای پارامترهای جابجابی در راستای محور Xها , جابجایی در راستای محور Yها؛ و شی مورد نظر را به آن نقطه منتقل میکند در واقع نقطه شروع و پایان ترسیم شی را با توجه به پارامترهای ورودی تغییر میدهد.
- Resize: این متد نیز برای تغییر اندازه شی به کار میرود، این متد دارای پارامترهای تغییر اندازه در راستای محور Xها , تغییر اندازه در راستای محور Yها میباشد و نقطه پایان شی مورد نظر را تغییر میدهد اما نقطه شروع تغییری نمیکند.
- Resize: این متد نیز برای تغییر اندازه شی به کار میرود، در زمان تغییر اندازه شی با ماوس ابتدا یک نقطه شروع وجود دارد که ماوس در آن نقطه کلیک شده و شروع به درگ کردن شی جهت تغییر اندازه میکند (پارامتر اول این متد نقطه شروع درگ کردن جهت تغییر اندازه را مشخص میکند startPoint)، سپس در یک نقطه ای درگ کردن تمام میشود در این نقطه باید شی تغییر اندازه پیدا کرده و ترسیم شود ( پارامتر دوم این متد نقطه مذکور میباشد currentLocation). سپس با توجه با این دو نقطه بررسی میشود که تغییر اندازه در کدام جهت صورت گرفته است و اعداد جهت تغییرات نقاط شروع و پایان شی مورد نظر محاسبه میشوند. (مثلا تغییر اندازه در مستطیل از ضلع بالا به طرفین، یا از ضلع سمت راست به طرفین و ....). البته برای مربع و دایره باید کاری کنیم که طول و عرض تغییر اندازه یکسان باشد.
- CalulateLocationAndSize: این متد که در سازنده کلاس استفاده شده در واقع دو نقطه شروع و پایان را گرفته و با توجه به تصویر ابتدای پست حالتهای 1 و 2 و3 و 4 را به حالت 1 تبدیل کرده و StartPoint و EndPoint را اصلاح میکند.
- SetBackgroundBrushAsHatch: این متد یک الگوی Brush گرفته و با توجه به رنگ پس زمینه شی خصوصیت BackgroundBrush را مقداردهی میکند.
- SetBackgroundBrushAsLinearGradient: این متد با توجه به خصوصیت ForeColor و BackgroundColor یک Gradiant Brush ساخته و آن را به خصوصیت
BackgroundBrush نسبت میکند. - SetBackgroundBrushAsSolid: یک الگوی پر کردن توپر برای شی مورد نظر با توجه به خصوصیت BackgroundColor شی ایجاد کرده و آن را به خصوصیت BackgroundBrush شی نسبت میدهد.
تذکر: متدهای Move، Resize و HasPointInShape به صورت virtual تعریف شده تا کلاسهای مشتق شده در صورت نیاز خود کد رفتار مورد نظر خود را override کرده یا از همین رفتار استفاده نمایند.
خوشحال میشم در صورتی که در Refactoring کد نوشته شده با من همکاری کنید.
در پستهای آینده به بررسی و پیاده سازی دیگر کلاسها خواهیم پرداخت.
- «... محل قرار گیری صحیح ارجاعات ... »
هرجایی که کار کند «صحیح» است و مرورگر آنرا درک و پردازش کردهاست. اما «بهتر» است آنها را قبل از بسته شدن تگ body قرار داد تا بارگذاری آنها سبب ایجاد وقفه در نمایش صفحه و بروز یک فلش سفید نشود. البته برای رفع این مشکل هم async scripts ارائه شدهاست.
- «... زمانی که فایلهای ارجاع به js رو بعد از فوتر قرار میدهم این ویژگی غیر فعال است ...»
تقدم و تاخرها را رعایت نکردید. اسکریپتها را باید به ترتیب و با درنظر گرفتن پیشنیازها در قسمت خاصی از صفحه اضافه کرد (از دیدگاه اجرایی برای مرورگر مهم نیست که کجا باشند).
هرجایی که کار کند «صحیح» است و مرورگر آنرا درک و پردازش کردهاست. اما «بهتر» است آنها را قبل از بسته شدن تگ body قرار داد تا بارگذاری آنها سبب ایجاد وقفه در نمایش صفحه و بروز یک فلش سفید نشود. البته برای رفع این مشکل هم async scripts ارائه شدهاست.
- «... زمانی که فایلهای ارجاع به js رو بعد از فوتر قرار میدهم این ویژگی غیر فعال است ...»
تقدم و تاخرها را رعایت نکردید. اسکریپتها را باید به ترتیب و با درنظر گرفتن پیشنیازها در قسمت خاصی از صفحه اضافه کرد (از دیدگاه اجرایی برای مرورگر مهم نیست که کجا باشند).
در اکثر برنامههای سازمانی، مثل برنامههای مدیریت آرشیو اسناد، همواره این نیاز جزو خواستههای کاربران بوده که بتوانند به صورت مستقیم و از طریق تنها یک کلیک، تصویر مورد نظر را اسکن کرده و به صورت خودکار در برنامه وارد کنند؛ یعنی بدون اینکه نیاز باشد با استفاده از یک برنامه دیگر ابتدا تصویر را اسکن کرده و سپس در فرم وب، فایل اسکن شده را Browse کنند.
این نیاز اساسا به معنی دسترسی به سخت افزار کاربر از طریق مرورگر میباشد که به دلایل متعددی امکان پذیر نیست! مشکلات امنیتی ایجاد شده در این راه بسیار جدی است. اما خوشبختانه راههایی برای رسیدن به این هدف وجود دارند:
1- ActiveX : که به صورت native فقط در IE پشتیبانی میشود (در نسخههای جدیدتر IE نیاز به بروز رسانی پلاگین ActiveX controls میباشد) و برای استفادهی از آن در مرورگرهای Firefox و Chrome هم باید از پلاگینهای جانبی روی هر مرورگر استفاده کرد که مثلا برای اجرای بر روی Firefox، باید افزونهی IE Tab را نصب کرد که تنها کارش این است که سایت را از طریق موتور IE در پنجرهی فایرفاکس اجرا کند! که البته این مورد مثل این میماند که سایت در IE باز شده باشد که ممکن است زیاد خوشایند نباشد؛ در غیر اینصورت باید پروژه را از اول بر مبنای اجرای بر روی IE طراحی و پیاده سازی کرد. در ضمن از مشکلات اجرای پلاگین نوشته شده توسط برنامه نویس در IE و نسخههای مختلف آن هم چشم پوشی میکنیم. یکی از مزیتهای این روش نسبت به بقیه این هست که دانلود و نصب پلاگین مورد نیاز به صورت خودکار و توسط مرورگر انجام میشود.
2- استفاده از یک کامپوننت جانبی : مثل کامپوننتهای LEADTOOLS که ابزارهای متنوع و SDKهای بسیار قدرتمندی را برای اینکار و کارهای دیگر، مانند کار با اسکن تصاویر مغزی، پردازش تصویر، بارکد خوانها، مدیریت اسناد و ... فراهم کرده است. قیمت این ابزار بسیار زیاد است و در برخی موارد امکانات فراهم آورده بسیار بیشتر است از خواستهی ما. این ابزار، هم در HTML & Javascript و هم در DotNet قابل استفاده است و مستندات نسبتا خوبی هم در این زمینه ارائه کرده است. همچنین کامپوننت Dynamsoft که باز هم غیر رایگان هست و قیمت بالایی نیز دارد.
اگر روال کار کامپوننتهای بالا را مورد بررسی قرار دهید (از طریق اجرای Demo ها، اینجا و اینجا) متوجه خواهید شد که هر دو نیاز به نصب یک سرویس یا App سمت کلاینت جهت اجرای دستورات خود دارند. پس میشود اینطور نتیجه گرفت که انجام اینکار بدون اینکه چیزی سمت کاربر نصب شود، ممکن نیست و در هر دو، لینک نصب فایل exe سرویس برای دانلود قرار داده شده است. بر این اساس به راه حل سومی خواهیم رسید که خودمان این سرویس را جهت تعامل با اسکنر سمت کاربر طراحی و پیاده سازی نماییم.
اما چطور ممکن است که با اجرای یک فایل exe سمت کاربر (با این فرض که کاربر در یک دامین مطمئن قرار دارد و میشود درخواست نصب سرویس را نمود) این امکان را برای کاربر فراهم نمود که با یک کلیک در مرورگر، اسکنر به صورت خودکار اسکن را آغاز کرده و سپس تصویر حاصل را به یکی از کنترلرهای ما در سمت سرور ارسال نماید؟
برای اینکار ما با دو صورت مساله مواجه هستیم؛ اول اینکه چطور تصویر را سمت کاربر اسکن کنیم و دوم اینکه چطور این تصویر را به سرور ارسال نماییم!
برای مسالهی اول از کتابخانه Windows Image Acquisition (WIA) استفاده خواهیم نمود که این کتابخانه به ما این امکان را میدهد تا با سخت افزارهایی که از TWAIN پشتیبانی میکنند، بتوانیم ارتباط برقرار نماییم.
این نیاز اساسا به معنی دسترسی به سخت افزار کاربر از طریق مرورگر میباشد که به دلایل متعددی امکان پذیر نیست! مشکلات امنیتی ایجاد شده در این راه بسیار جدی است. اما خوشبختانه راههایی برای رسیدن به این هدف وجود دارند:
1- ActiveX : که به صورت native فقط در IE پشتیبانی میشود (در نسخههای جدیدتر IE نیاز به بروز رسانی پلاگین ActiveX controls میباشد) و برای استفادهی از آن در مرورگرهای Firefox و Chrome هم باید از پلاگینهای جانبی روی هر مرورگر استفاده کرد که مثلا برای اجرای بر روی Firefox، باید افزونهی IE Tab را نصب کرد که تنها کارش این است که سایت را از طریق موتور IE در پنجرهی فایرفاکس اجرا کند! که البته این مورد مثل این میماند که سایت در IE باز شده باشد که ممکن است زیاد خوشایند نباشد؛ در غیر اینصورت باید پروژه را از اول بر مبنای اجرای بر روی IE طراحی و پیاده سازی کرد. در ضمن از مشکلات اجرای پلاگین نوشته شده توسط برنامه نویس در IE و نسخههای مختلف آن هم چشم پوشی میکنیم. یکی از مزیتهای این روش نسبت به بقیه این هست که دانلود و نصب پلاگین مورد نیاز به صورت خودکار و توسط مرورگر انجام میشود.
2- استفاده از یک کامپوننت جانبی : مثل کامپوننتهای LEADTOOLS که ابزارهای متنوع و SDKهای بسیار قدرتمندی را برای اینکار و کارهای دیگر، مانند کار با اسکن تصاویر مغزی، پردازش تصویر، بارکد خوانها، مدیریت اسناد و ... فراهم کرده است. قیمت این ابزار بسیار زیاد است و در برخی موارد امکانات فراهم آورده بسیار بیشتر است از خواستهی ما. این ابزار، هم در HTML & Javascript و هم در DotNet قابل استفاده است و مستندات نسبتا خوبی هم در این زمینه ارائه کرده است. همچنین کامپوننت Dynamsoft که باز هم غیر رایگان هست و قیمت بالایی نیز دارد.
اگر روال کار کامپوننتهای بالا را مورد بررسی قرار دهید (از طریق اجرای Demo ها، اینجا و اینجا) متوجه خواهید شد که هر دو نیاز به نصب یک سرویس یا App سمت کلاینت جهت اجرای دستورات خود دارند. پس میشود اینطور نتیجه گرفت که انجام اینکار بدون اینکه چیزی سمت کاربر نصب شود، ممکن نیست و در هر دو، لینک نصب فایل exe سرویس برای دانلود قرار داده شده است. بر این اساس به راه حل سومی خواهیم رسید که خودمان این سرویس را جهت تعامل با اسکنر سمت کاربر طراحی و پیاده سازی نماییم.
اما چطور ممکن است که با اجرای یک فایل exe سمت کاربر (با این فرض که کاربر در یک دامین مطمئن قرار دارد و میشود درخواست نصب سرویس را نمود) این امکان را برای کاربر فراهم نمود که با یک کلیک در مرورگر، اسکنر به صورت خودکار اسکن را آغاز کرده و سپس تصویر حاصل را به یکی از کنترلرهای ما در سمت سرور ارسال نماید؟
برای اینکار ما با دو صورت مساله مواجه هستیم؛ اول اینکه چطور تصویر را سمت کاربر اسکن کنیم و دوم اینکه چطور این تصویر را به سرور ارسال نماییم!
برای مسالهی اول از کتابخانه Windows Image Acquisition (WIA) استفاده خواهیم نمود که این کتابخانه به ما این امکان را میدهد تا با سخت افزارهایی که از TWAIN پشتیبانی میکنند، بتوانیم ارتباط برقرار نماییم.
برای مسالهی دوم هم نیاز به پیاده سازی یک WCF Service و اجرای آن (هاست کردن) در سمت کلاینت داریم. در واقع با صدا زدن متدهای این سرویس، از کتابخانهی بالا استفاده کرده و اسکن را انجام میدهیم.
ادامه دارد...
مطالب
ASP.NET MVC #9
مروری بر HTML Helpers استاندارد مهیا در ASP.NET MVC
یکی از اهداف وجودی Server controls در ASP.NET Web forms، رندر خودکار HTML است. برای مثال Menu control، TreeView control، GridView و امثال آن کار تولید تگهای table، tr و بسیاری موارد دیگر را در پشت صحنه برای ما انجام میدهند. اما در ASP.NET MVC، هدف رسیدن به یک markup ساده و تمیز است که 100 درصد بر روی اجزای آن کنترل داشته باشیم و این مورد به صورت ضمنی به این معنا است که در اینجا تمام این HTMLها را باید خودمان تولید کنیم. البته در عمل خیر. یک نمونه از آنرا در قسمت قبل مشاهده کردیم که چطور میتوان منطق تولید تگهای HTML را کپسوله سازی کرد و بارها مورد استفاده قرار داد. به علاوه فریم ورک ASP.NET MVC نیز به همراه تعدادی HTML helper توکار ارائه شده است مانند CheckBox، ActionLink، RenderPartial و غیره که کار تولید تگهای HTML ضروری و پایه را برای ما ساده میکنند.
یک مثال:
@Html.ActionLink("About us", "Index", "About")
در اینجا از متدی به نام ActionLink استفاده شده است. شیء Html هم وهلهای از کلاس HtmlHelper است که در تمام Viewها قابل دسترسی میباشد.
در این متد، اولین پارامتر، متن نمایش داده شده به کاربر را مشخص میکند، پارامتر سوم، نام کنترلری است که مورد استفاده قرار میگیرد و پارامتر دوم، نام متد یا اکشنی در آن است که فراخوانی خواهد شد (البته هر کدام از این HtmlHelperها به همراه تعداد قابل توجهی overload هم هستند).
زمانیکه این صفحه را رندر کنیم، به خروجی زیر خواهیم رسید:
<a href="/About">About us</a>
در این لینک نهایی خبری از متد Index ایی که معرفی کردیم، نیست. چرا؟
متد ActionLink بر اساس تعاریف پیش فرض مسیریابی برنامه، سعی میکند بهترین خروجی را ارائه دهد. مطابق تعاریف پیش فرض برنامه، متد Index، اکشن پیش فرض کنترلرهای برنامه است. بنابراین ضرورتی به ذکر آن ندیده است.
مثالی دیگر:
همان کلاسهای Product و Products قسمت هفتم را در نظر بگیرید (قسمت بررسی «ساختار پروژه مثال جاری» در آن مثال). همچنین به اطلاعات «نوشتن HTML Helpers ویژه، به کمک امکانات Razor» قسمت هشتم هم نیاز داریم.
اینبار میخواهیم بجای نمایش لیست سادهای از محصولات، ابتدا نام آنها را به صورت لینکهایی در صفحه نمایش دهیم. در ادامه پس از کلیک کاربر روی یک نام، توضیحات بیشتری از محصول انتخابی را در صفحهای دیگر ارائه نمائیم. کدهای View ما اینبار به شکل زیر تغییر میکنند:
@using MvcApplication5.Models
@model MvcApplication5.Models.Products
@{
ViewBag.Title = "Index";
}
@helper GetProductsList(List<Product> products)
{
<ul>
@foreach (var item in products)
{
<li>@Html.ActionLink(item.Name, "Details", new { id = item.ProductNumber })</li>
}
</ul>
}
<h2>Index</h2>
@GetProductsList(@Model)
توضیحات:
ابتدا یک helper method را تعریف کردهایم و به کمک Html.ActionLink، از نام و شماره محصول، جهت تولید لینکهای نمایش جزئیات هر یک از محصولات کمک گرفتهایم. بنابراین در کنترلر خود نیاز به متد جدیدی به نام Details خواهیم داشت که پارامتری از نوع ProductNumber را دریافت میکند. سپس جزئیات این محصول را یافته و در View متناظر با خودش ارائه خواهد داد. پارامتر سومی که در متد ActionLink بکارگرفته شده در اینجا مشاهده میکنید، یک anonymously typed object است و توسط آن خواصی را تعریف خواهیم کرد که توسط تعاریف مسیریابی تعریف شده در فایل Global.asax.cs، قابل تفسیر و تبدیل به لینکهای مرتبط و صحیحی باشد.
اکنون اگر این مثال را اجرا کنیم، اولین لینک تولیدی آن به این شکل خواهد بود:
http://localhost/Home/Details/D123
در اینجا به یک نکته مهم هم باید دقت داشت؛ نام کنترلر به صورت خودکار به این لینک اضافه شده است. بنابراین بهتر است از ایجاد دستی این نوع لینکها خودداری کرده و کار را به متدهای استاندارد فریم ورک واگذار نمود تا بهترین خروجی را دریافت کنیم.
البته اگر الان بر روی این لینک کلیک نمائیم، با پیغام 404 مواجه خواهیم شد. برای تکمیل این مثال، متد Details را به کنترلر تعریف شده اضافه خواهیم کرد:
using System.Linq;
using System.Web.Mvc;
using MvcApplication5.Models;
namespace MvcApplication5.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var products = new Products();
return View(products);
}
public ActionResult Details(string id)
{
var product = new Products().FirstOrDefault(x => x.ProductNumber == id);
if (product == null)
return View("Error");
return View(product);
}
}
}
در متد Details، ابتدا ProductNumber دریافت شده و سپس شیء محصول متناظر با آن، به View این متد، بازگشت داده میشود. اگر بر اساس ورودی دریافتی، محصولی یافت نشد، کاربر را به View ایی به نام Error که در پوشه Views/Shared قرار گرفته است، هدایت میکنیم.
برای اضافه کردن این View هم بر روی متد کلیک راست کرده و گزینه Add view را انتخاب کنید. چون یک شیء strongly typed از نوع Product را قرار است به View ارسال کنیم (مانند مثال قسمت پنجم)، میتوان در صفحه باز شده تیک Create a strongly typed view را گذاشت و سپس Model class را از نوع Product انتخاب کرد و در قسمت Scaffold template هم Details را انتخاب نمود. به این ترتیب Code generator توکار VS.NET قسمتی از کار تولید View را برای ما انجام داده و بدیهی است اکنون سفارشی سازی این View تولیدی که قسمت عمدهای از آن تولید شده است، کار سادهای میباشد:
@model MvcApplication5.Models.Product
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Product</legend>
<div class="display-label">ProductNumber</div>
<div class="display-field">@Model.ProductNumber</div>
<div class="display-label">Name</div>
<div class="display-field">@Model.Name</div>
<div class="display-label">Price</div>
<div class="display-field">@String.Format("{0:F}", Model.Price)</div>
</fieldset>
<p>
@Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) |
@Html.ActionLink("Back to List", "Index")
</p>
در اینجا کدهای مرتبط با View نمایش جزئیات محصول را مشاهده میکنید که توسط VS.NET به صورت خودکار از روی مدل انتخابی تولید شده است.
اکنون یکبار دیگر برنامه را اجرا کرده و بر روی لینک نمایش جزئیات محصولات کلیک نمائید تا بتوان این اطلاعات را در صفحهی بعدی مشاهده نمود.
یک نکته:
اگر سعی کنیم متد @helper GetProductsList فوق را در پوشه App_Code، همانند قسمت قبل قرار دهیم، به متد Html.ActionLink دسترسی نخواهیم داشت. چرا؟
پیغام خطایی که ارائه میشود این است:
'System.Web.WebPages.Html.HtmlHelper' does not contain a definition for 'ActionLink'
به این معنا که در وهلهای از شیء System.Web.WebPages.Html.HtmlHelper، به دنبال متد ActionLink میگردد. در حالیکه ActionLink مورد نظر به کلاس System.Web.Mvc.HtmlHelper مرتبط میشود.
یک راه حل آن به صورت زیر است. به هر متد helper یک آرگومان WebViewPage page را اضافه میکنیم (به همراه دو فضای نامی که به ابتدای فایل اضافه میشوند)
@using System.Web.Mvc
@using System.Web.Mvc.Html
@using MvcApplication5.Models
@helper GetProductsList(WebViewPage page, List<Product> products)
{
<ul>
@foreach (var item in products)
{
<li> @page.Html.ActionLink(item.Name, "Details", new { id = item.ProductNumber })</li>
}
</ul>
}
@MyHelpers.GetProductsList(this, @Model)
متد ActionLink و عبارات فارسی
متد ActionLink آدرسهای وبی را که تولید میکند، URL encoded هستند. برای نمونه اگر رشتهای که قرار است به عنوان پارامتر به اکشن متد ما ارسال شود، مساوی Hello World است، آنرا به صورت Hello%20World در صفحه درج میکند. البته این مورد مشکلی را در سمت متدهای کنترلرها ایجاد نمیکند، چون کار URL decoding خودکار است. اما ... اگر مقداری که قرار است ارسال شود مثلا «مقدار یک» باشد، آدرس تولیدی این شکل را خواهد داشت:
http://localhost/Home/Details/%D9%85%D9%82%D8%AF%D8%A7%D8%B1%20%D9%8A%D9%83
و اگر این URL encoding انجام نشود، فقط اولین قسمت قبل از فاصله به متد ارسال میگردد.
مرورگرهایی مثل فایرفاکس و کروم، مشکلی با نمایش این لینک به شکل اصلی فارسی آن ندارند (حین نمایش، URL decoding را اعمال میکنند). اما اگر مرورگر مثلا IE8 باشد، کاربر دقیقا به همین شکل آدرسها را در نوار آدرس مرورگر خود مشاهده خواهد کرد که آنچنان زیبا نیستند.
حل این مشکل، یک نکته کوچک را به همراه دارد. اگر href تولیدی به شکل زیر باشد:
<li><a href="/Home/Details/مقدار یک">Super Fast Bike</a></li>
IE حین نمایش نهایی آن، آنرا فارسی نشان خواهد داد. حتی زمانیکه کاربر بر روی آن کلیک کند، به صورت خودکار کاراکترهایی را که لازم است encode نماید، به نحو صحیحی در URL نهایی قابل مشاهده در نوار آدرسها ظاهر خواهد کرد. برای مثال %20 را به صورت خودکار اضافه میکند و نگرانی از این لحاظ وجود نخواهد داشت که الان بین دو کلمه فاصلهای وجود دارد یا خیر (مرورگرهای دیگر هم دقیقا همین رفتار را در مورد لینکهای داخل صفحه دارند).
خلاصه این توضیحات متد کمکی زیر است:
@helper EmitCleanUnicodeUrl(MvcHtmlString data)
{
@Html.Raw(HttpUtility.UrlDecode(data.ToString()))
}
و برای نمونه نحوه استفاده از آن به شکل زیر خواهد بود:
@helper GetProductsList(List<Product> products)
{
<ul>
@foreach (var item in products)
{
<li>@EmitCleanUnicodeUrl(@Html.ActionLink(item.Name, "Details", new { id = item.ProductNumber }))</li>
}
</ul>
}
ضمن اینکه باید درنظر داشت کلا این نوع طراحی مشکل دارد! برای مثال فرض کنید که در این مثال، جزئیات، نمایش دهنده مطلب ارسالی در یک بلاگ است. یعنی یک سری عنوان و جزئیات متناظر با آنها در دیتابیس وجود دارند. اگر آدرس مطالب به این شکل باشد http://site/blog/details/text، به این معنا است که این text مساوی است با primary key جدول بانک اطلاعاتی. یعنی وبلاگ نویس سایت شما فقط یکبار در طول عمر این برنامه میتواند بگوید «سال نو مبارک!». دفعهی بعد به علت تکراری بودن، مجاز به ارسال پیام تبریک دیگری نخواهد بود! به همین جهت بهتر است طراحی را به این شکل تغییر دهید http://site/blog/details/id/text. در اینجا id همان primary key خواهد بود. Text هم عنوان مطلب. Id به جهت خوشایند بانک اطلاعاتی و Text هم برای خوشایند موتورهای جستجو در این URL قرار دارند. مطابق تعاریف مسیریابی برنامه، Text فقط حالت تزئینی داشته و پردازش نخواهد شد.
از این نوع ترفندها زیاد به کار برده میشوند. برای نمونه به URL مطالب انجمنهای معروف اینترنتی دقت کنید. عموما یک عدد را به همراه text مشاهده میکنید. عدد در برنامه پردازش میشود، متن هم برای موتورهای جستجو درنظر گرفته شده است.
شاید خیلی از برنامه نویسان با نظر من هم عقیده باشند، Visual Studio.NET بهترین ابزار و IDE جهت توسعه نرم افزار است. در این سری از مطالب قصد داریم نگاهی اجمالی به قابلیتهای جدید VS.NET 2012 بیاندازیم.
پیش نمایش تصاویر در Solution Explorer
پنجره New Project:
شاید بارزترین تغییراتی که در این پنجره به چشم میخورد اضافه شدن Templateهای جدید باشد. البته نباید از ظاهر گرافیکی به سبک Metro چشم پوشی کرد. یکی از این Templateها Portable Class Library است. توسط این گرینه میتوان dllهای قابل استفاده در Windows، Windows Phone،
Silverlight و البته XBox 360 ایجاد کرد.
Template جدید دیگری که در VS.NET 2012 اضافه شده است گروه مربوط به Visual Studio Light Switch است. توسط این گزینه امکان ایجاد برنامههای Light Switch، بدون نیاز به نصب Visual Studio Light Switch بصورت جداگانه، فراهم است و جهت توسعه این دسته از برنامهها میتوانید از VS.NET 2012 استفاده کنید.
یکی دیگر از قابلیتهای اضافه شده در این برنامه امکان پیش نمایش تصاویر در Solution Explorer است.
ابتدا نیاز است سورس فایل FileResult.cs را یکبار بررسی کنید. نکته جالبی که در آن وجود دارد نحوه ارسال نام فایل به مرورگر است که با پیاده سازی RFC 2183 و RFC 2231 انجام شده است. این خروجیهای مبتنی بر RFCهای یاد شده، با تمام مرورگرهای جدید مانند کروم و فایرفاکس بدون مشکل کار میکنند. بنابراین اگر استفاده کنندگان از برنامه ASP.NET MVC شما از مرورگری مانند IE8 استفاده نمیکنند، نیازی به مطالعه ادامه بحث نخواهید داشت!
اما ... IE8 یک چنین درک و قابلیت پردازشی را ندارد. به همین جهت زمانیکه از return File در ASP.NET MVC استفاده شود و مرورگر نیز IE 8 باشد، نام یونیکد خروجی نهایی دریافتی توسط کاربر، یک سری حروف hex بیمفهوم خواهد بود.
نحوه رفع مشکل با IE
مطابق کدهای ذیل نیاز است filename را توسط متد Server.UrlPathEncode بازگشت دهیم؛ تا IE 8 بتواند آنرا تفسیر کرده و درست نمایش دهد:
در اینجا عملا دو هدر Content-Disposition وجود خواهد داشت و IE8 اولین مورد را پردازش میکند. اما مرورگرهای جدید این مورد را به صورت یک حمله گزارش میدهند و پردازش نخواهند کرد! به همین جهت بررسی شده است که اگر مرورگر IE بود ... آنگاه این تغییر اعمال شود.
البته روش دیگر، بازنویسی FileResult و یا تهیه یک FileResult سفارشی است.
اما ... IE8 یک چنین درک و قابلیت پردازشی را ندارد. به همین جهت زمانیکه از return File در ASP.NET MVC استفاده شود و مرورگر نیز IE 8 باشد، نام یونیکد خروجی نهایی دریافتی توسط کاربر، یک سری حروف hex بیمفهوم خواهد بود.
نحوه رفع مشکل با IE
مطابق کدهای ذیل نیاز است filename را توسط متد Server.UrlPathEncode بازگشت دهیم؛ تا IE 8 بتواند آنرا تفسیر کرده و درست نمایش دهد:
var fileName = Path.GetFileName(filePath); if (Request.Browser.Browser == "IE") { string attachment = string.Format("attachment; filename=\"{0}\"", Server.UrlPathEncode(fileName)); Response.AddHeader("Content-Disposition", attachment); } return File(filePath, "application/octet-stream", fileName);
البته روش دیگر، بازنویسی FileResult و یا تهیه یک FileResult سفارشی است.
برای نصب Git ابتدا به msysgit رفته و مطابق شکل زیر بر روی گزینه دانلود کلیک کنید. سپس در صفحه باز شده آخرین نسخه Git را دانلود نموده و فایل مربوطه را اجرا کنید:
شروع نصب:
--------------
در این مرحله بخش Windows Explorer Integration اهمیت دارد. در صورت انتخاب این بخش، بعد از نصب، Git Bash و Git GUI به منوی راست کلیک شما اضافه میشود. به این ترتیب با سرعت بیشتری میتوانید به Git در یک پوشه خاص دسترسی داشته باشید.
--------------
در این مرحله از شما خواسته میشود تعیین کنید که آیا فقط میخواهید از طریق Git Bash با Git کار کنید یا با اضافه کردن فایل اجرایی Git به متغیرهای محلی ویندوز از طریق Command Prompt ویندوز نیز میخواهید به Git دسترسی داشته باشید. گزینه سوم هم Git و هم برخی از ابزارهای یونیکسی را به متغیرهای محلی اضافه میکند که سبب میشود شما یک خط فرمان قدرتمندتر در ویندوز داشته باشید. اما این کار ممکن است در برخی از برنامههای پیش فرض اختلال ایجاد کند بنابراین در انتخاب این گزینه احتیاط کنید.
--------------
در این مرحله کاراکتری را که نشان دهنده انتهای خط است تعیین میکنید. این کاراکتر در ویندوز و یونیکس متفاوت است. بنابراین Git از شما میخواهد که برای حفظ سازگاری در محیط هایی که چند سیستمی هستند، آنرا تعیین کنید.
گزینه اول به صورت فرمت یونیکس ذخیره و به شکل ویندوز بازیابی میشود (مناسب برای محیط ویندوز).
گزینه دوم ذخیره به فرمت یونیکسی است و مناسب محیطهای یونیکس است.
و آخرین گزینه فایل را بدون تغییر ذخیره و بازیابی میکند (از این گزینه نیز میتوان هم برای Unix و هم windows استفاده کرد).
بعد از این مرحله نصب آغاز میشود.
نکته: شما میتوانید جهت دسترسی به یک محیط گرافیکی قوی از gitextensions استفاده کنید. با دانلود این فایل، هم خود Git و هم GUI هایی برای کارهای مختلف، نظیر مشاهده تفاوتهای دو فایل یا نمایش گرافیکی شاخهها به سیستم شما اضافه میشود.
پیکربندی Git:
برای پیکربندی Git شما باید یک فایل config ایجاد کنید و با استفاده از دستوراتی که در ادامه میآید این تنظیمات را انجام دهید. البته پیکربندی Git از طریق ابزارهای گرافیکی که در محله قبل نصب کردید نیز امکانپذیر است.
Git دارای سه نوع دسترسی برای پیکرهبندی است:
سیستمی: این تنظیمات بر روی کل سیستمی که git برای روی آن نصب شده اعمال میشود. فایل gitconfig در مسیر program files/Git/etc/gitconfig قرار دارد و برای تغییر آن باید از سوئیچ system-- استفاده نمود.
در سطح کاربر: فایل config.در مسیر [users/[username/ برای این منظور است و تغییر این تنظیمات تنها بر روی همین کاربر اعمال میشود برای درسترسی به این فایل باید از سوئیچ global-- استفاده کرد.
در سطح Repository: برای هر پوشه repository این فایل موجود است و اگر از دستور config بدون هیچ سوئیچی استفاده کنیم تغییرات بر روی این فایل اعمال میشود.
نکته: معمولا فایل پیکربندی git در سطح سیستم را تغییر نمیدهند.
دستورات پیکربندی:
همانطور که گقته شد هر Commit حاوی اطلاعات فردی است که آنرا انجام داده است. این اطلاعات را میتوان به صورت زیر تنظیم کرد:
نام کاربر:
git config --global user.name "Hessam"
ایمیل کاربر:
git config --global user.email "hessam@localhost.com"
با استفاده از دستور زیر میتوان تنظیماتی را که تا کنون انجام شده ببینیم:
git config --global --list
همچنین میتوان ویرایشگر متن پیش فرضی برای git تعیین کرد. از این ویرایشگر میتوان به عنوان مثال بعد از فرخوانی دستور commit استفاده نمود تا دلیل commit مشخص شود. در صورت تعیین این ویرایشگر، git آنرا خودکار باز میکند:
git config --global core.editor notepad
من در اینجا notepad را انتخاب کردم توجه کنید که مسیر ویرایشگر باید در متغیرهای محلی ویندوز باشد.
و در نهایت جهت نمایش بهتر پیامهای git میتوانیم تنظیم کنیم که آنها را با رنگهای متفاوتی نمایش دهد:
git config --global color.ui auto
البته تنظیمات بیشتری را میتوان در اینجا انجام داد، مانند تعیین برنامه پیش فرض برای نمایش اختلاف فایلها و یا برنامه پیش فرض برای حل کردن مشکل conflict و غیره که این تنظیمات در همان بخشها گفته خواهد شد.
در قسمت بعد دستورات اولیه کار با git به صورت محلی گفته خواهد شد.