۱۳۹۰/۱۱/۰۶

زير نويس فارسي ويديوهاي ساخت برنامه‌هاي مترو توسط سي شارپ و XAML - قسمت چهارم


زيرنويس‌هاي فارسي قسمت چهارم «Building Windows 8 Metro Apps in C# and XAML» را از اينجا و يا اينجا مي‌تونيد دريافت كنيد.

ليست سرفصل‌هاي قسمت چهارم به شرح زير است:

List Controls  00:31:14 
This module shows the Metro controls available to XAML applications for working with collections of items.
This includes the new GridView and ListView controls, 
which are optimized for handling collections in a touch-based user interface. 

Introduction
Items Controls
Demo: ListBox vs ListView
Demo: GridView
Demo: FlipView 
Common ItemsControl
Semantic Zoom
Demo: JumpViewer
Summary

اين قسمت به بررسي يك سري كنترل ليستي جديد ويندوز 8 اختصاص دارد شامل ListView بازنويسي شده و همچنين GridView به همراه دو كنترل FlipView و JumpViewer كه تمام اين‌ها جهت كار با صفحات لمسي بهينه سازي شده‌اند.

۱۳۹۰/۱۰/۳۰

زير نويس فارسي ويديوهاي ساخت برنامه‌هاي مترو توسط سي شارپ و XAML - قسمت سوم


زيرنويس‌هاي فارسي قسمت سوم «Building Windows 8 Metro Apps in C# and XAML» را از اينجا و يا اينجا مي‌تونيد دريافت كنيد.

ليست سرفصل‌هاي قسمت سوم به شرح زير است:

Controls  00:48:08 
WinRT provides numerous XAML controls, which act as the building blocks of your applications.
Some of these will be familiar, while some are new to Metro. 
Introduction
Command Controls
Demo: Application Bar
Context Menus
Demo: Context Menus
Application Settings
CheckBox, RadioButton, and ToggleSwitch
Progress Controls
Demo: Progress Controls
Displaying Text
Demo: Displaying Text
Text Entry
Sliders
Styling
WebView
UserControl
Summary

مطالبي كه بيشتر در اين سري بر روي آن‌ها تمركز شده، مباحث صفحات لمسي و تغييرات كنترل‌هاي ويندوز 8 جهت سازگاري با اين مساله است. بنابراين در اين سري بيشتر از هرچيزي finger ، tap و touch را خواهيد شنيد. جايي كه چند جا را مي‌شود همزمان لمس كرد و با چندين انگشت همزمان كار كرد كه نكات قابل توجهي را نسبت به دنياي تك بعدي ماوس به همراه دارد.

۱۳۹۰/۱۰/۲۵

زير نويس فارسي ويديوهاي ساخت برنامه‌هاي مترو توسط سي شارپ و XAML - قسمت دوم


زيرنويس‌هاي فارسي قسمت دوم «Building Windows 8 Metro Apps in C# and XAML» را از اينجا و يا اينجا مي‌تونيد دريافت كنيد.

ليست سرفصل‌هاي قسمت دوم به شرح زير است:

Layout 00:46:16 
C# Metro applications have access to numerous XAML layout features. 
This module describes those services, and shows how to use them to support Windows 8 features such as display orientation, and snap. 

Introduction
Layout System
Size Properties
Alignment
Margin
Demo: Margin and Alignment 
Padding 
Panels 
Demo: Canvas 
Demo: Grid and Snap 
Data-Oriented Panels 
ScrollViewer 
Metro Layout Conventions 
Layout Change Events 
Summary

در كل اين قسمت هم آنچنان كاري به برنامه نويسي ندارد و به بررسي و معرفي امكانات طرحبندي XAML مي‌پردازد؛ به علاوه يك سري قراردادهاي خاص مترو و همچنين نحوه‌ي كنار آمدن با حالت snapping ويژه ويندوز 8.


قسمت سوم مروري دارد بر كنترل‌هاي XAML كه حدودا يك هفته ديگر زيرنويس‌هاي آن تمام خواهد شد.
لطفا اگر پس از مشاهده اين سري آماده شده، اصلاحي را انجام داديد، اون رو براي اعمال در اينجا ارسال كنيد. از اين برنامه هم جهت ويرايش فايل‌ها مي‌توان استفاده كرد.

۱۳۹۰/۱۰/۲۳

زير نويس فارسي ويديوهاي ساخت برنامه‌هاي مترو توسط سي شارپ و XAML - قسمت اول


سايت pluralsight ويديوهاي آموزشي بسيار با كيفيتي را در مورد مباحث مختلف دات نت تا بحال تهيه كرده و تقريبا هر موضوع جديدي هم كه اضافه مي‌شود، بلافاصله يك سري جديد را تهيه مي‌كنند. مدرسين انتخابي هم عموما افراد نامدار و باسوادي هستند.
پروژه‌اي رو در سايت كدپلكس شروع كردم جهت تهيه زيرنويس فارسي براي اين ويديوها:


اين كار نسبت به كار تهيه زيرنويس‌هاي فارسي موجود براي فيلم‌هاي انگليسي كار سخت‌تري است به چند دليل:
- اسكريپت آماده‌اي وجود ندارد. كار شنيداري است.
- زمانبندي آماده‌اي وجود ندارد.
- مباحث تخصصي است.
- مدرس از ثانيه اول ويديو تا ثانيه آخر آن حرف مي‌زند!

براي مثال جهت تهيه زيرنويس‌هاي فارسي فيلم‌هاي انگليسي عموما به سايت‌هايي مانند subscene.com مراجعه مي‌شود. يك زيرنويس يا به قولي اسكريپت آماده يافت شده و شروع به ترجمه مي‌شود. متن آماده است. زمانبندي آماده است و فقط كار ترجمه باقي مي‌ماند.
اما در مورد ويديوهاي آموزشي انگليسي خير. به همين جهت در اين زمينه كار آنچناني تابحال صورت نگرفته.
كار زمانبري است. فعلا ركورد من براي هر سه دقيقه ويديوي آموزشي كه مدرس از ثانيه اول تا آخر آن حرف مي‌زند، 40 دقيقه كار تهيه زير نويس فارسي زمانبندي شده است.

سري جديدي رو كه شروع كردم تحت عنوان «Building Windows 8 Metro Apps in C# and XAML» در سايت pluralsight ارائه شده.
فعلا قسمت اول آن زيرنويس دار شده و از اينجا قابل دريافت است. براي مشاهده آن‌ها برنامه با كيفيت و رايگان KMPlayer توصيه مي‌شود.

ليست ويديوهاي قسمت اول آن به شرح زير است:

Building Windows 8 Metro Apps in C# and XAML 
Overview  00:50:41 
This modules provides an overview of how to develop Windows 8 Metro style applications in C# and XAML. 

Introduction
XAML and Codebehind
Asynchronous APIs
Demo: Asynchronous APIs
Files and Networking
Demo: Requesting Capabilities
Integrating with Windows 8
WinRT and .NET
Summary

كلمه سي شارپ در اين قسمت كمي غلط انداز است. بيشتر بحث و توضيح است تا كد نويسي. بنابراين براي عموم قابل استفاده است. خصوصا نگاهي دارد به تازه‌هاي ويندوز 8 از ديدگاه برنامه نويس‌ها مانند سطوح دسترسي برنامه‌هاي مترو، معرفي Charms ، نحوه به اشتراك گذاري اطلاعات در بين برنامه‌هاي مترو نصب شده، براي مثال جايي كه ديگر Clipboard سابق وجود ندارد و مواردي از اين دست.

5 قسمت ديگر اين مبحث باقيمانده كه هر زمان تكميل شد، خبرش رو خواهيد شنيد و تقريبا از اين پس اين سايت به همين ترتيب جلو خواهد رفت. فكر مي‌كنم اينطوري مفيدتر باشد. هر از چندگاهي يك مبحث جديد زيرنويس دار شده را مي‌توانيد مشاهده كنيد.

اگر علاقمند بوديد مي‌تونيد در اين پروژه شركت كنيد و يك موضوع جديد و مستقل را براي تهيه زيرنويس شروع كنيد. يا حداقل اگر پس از مشاهده اين سري آماده شده، اصلاحي را انجام داديد، مي‌تونيد اون رو براي اعمال در اينجا ارسال كنيد.

۱۳۹۰/۱۰/۲۱

مروري بر كدهاي كلاس SqlHelper


قسمتي از يك پروژه به همراه كلاس SqlHelper آن در كامنت‌هاي مطلب «اهميت Code review» توسط يكي از خوانندگان بلاگ جهت Code review مطرح شده كه بهتر است در يك مطلب جديد و مجزا به آن پرداخته شود. قسمت مهم آن كلاس SqlHelper است و مابقي در اينجا نديد گرفته مي‌شوند:

//It's only for code review purpose!  
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;


public sealed class SqlHelper
{
    private SqlHelper() { }

    
    //  Send Connection String
    //---------------------------------------------------------------------------------------
    public static string GetCntString()
    {
        return WebConfigurationManager.ConnectionStrings["db_ConnectionString"].ConnectionString;
    }

   
    //  Connect to Data Base SqlServer
    //---------------------------------------------------------------------------------------
    public static SqlConnection Connect2Db(ref SqlConnection sqlCnt, string cntString)
    {
        try
        {
            if (sqlCnt == null) sqlCnt = new SqlConnection();
            sqlCnt.ConnectionString = cntString;
            if (sqlCnt.State != ConnectionState.Open) sqlCnt.Open();
            return sqlCnt;
        }
        catch (SqlException)
        {
            return null;
        }
    }

  
    //  Run ExecuteScalar Command
    //---------------------------------------------------------------------------------------
    public static string RunExecuteScalarCmd(ref SqlConnection sqlCnt, string strCmd, bool blnClose)
    {
        Connect2Db(ref sqlCnt, GetCntString());
        using (sqlCnt)
        {
            using(SqlCommand sqlCmd = sqlCnt.CreateCommand())
            {
                sqlCmd.CommandText = strCmd;
                object objResult = sqlCmd.ExecuteScalar();
                if (blnClose) CloseCnt(ref sqlCnt, true);
                return (objResult == null) ? string.Empty : objResult.ToString();
            }
        }
    }

    //   Close SqlServer Connection
    //---------------------------------------------------------------------------------------
    public static bool CloseCnt(ref SqlConnection sqlCnt, bool nullSqlCnt)
    {
        try
        {
            if (sqlCnt == null) return true;
            if (sqlCnt.State == ConnectionState.Open)
            {
                sqlCnt.Close();
                sqlCnt.Dispose();
            }
            if (nullSqlCnt) sqlCnt = null;
            return true;
        }
        catch (SqlException)
        {
            return false;
        }
    }   
}


مثالي از نحوه استفاده ارائه شده:

protected void BtnTest_Click(object sender, EventArgs e)
        {
            SqlConnection sqlCnt = new SqlConnection();
            string strQuery = "SELECT COUNT(UnitPrice) AS PriceCount FROM [Order Details]";


            // در این مرحله پارامتر سوم یعنی کانکشن باز نگه داشته شود
            string strResult = SqlHelper.RunExecuteScalarCmd(ref sqlCnt, strQuery, false);



            strQuery = "SELECT LastName + N'-' + FirstName AS FullName FROM Employees WHERE (EmployeeID = 9)";
            // در این مرحله پارامتر سوم یعنی کانکشن بسته شود
            strResult = SqlHelper.RunExecuteScalarCmd(ref sqlCnt, strQuery, true);
        }


مروري بر اين كد:

1) نحوه كامنت نوشتن
بين سي شارپ و زبان سي++ تفاوت وجود دارد. اين نحوه كامنت نويسي بيشتر در سي++ متداول است. اگر از ويژوال استوديو استفاده مي‌كنيد، مكان نما را به سطر قبل از يك متد منتقل كرده و سه بار پشت سر هم forward slash را تايپ كنيد. به صورت خودكار ساختار خالي زير تشكيل خواهد شد:
/// <summary>
    /// 
    /// </summary>
    /// <param name="sqlCnt"></param>
    /// <param name="cntString"></param>
    /// <returns></returns>
    public static SqlConnection Connect2Db(ref SqlConnection sqlCnt, string cntString)

اين روش مرسوم كامنت نويسي كدهاي سي شارپ است. خصوصا اينكه ابزارهايي وجود دارند كه به صورت خودكار از اين نوع كامنت‌ها، فايل CHM‌ درست مي‌كنند.

2) وجود سازنده private
احتمالا هدف اين بوده كه نه شخصي و نه حتي كامپايلر، وهله‌اي از اين كلاس را ايجاد نكند. بنابراين بهتر است كلاسي را كه تمام متدهاي آن static است (كه به اين هم خواهيم رسيد!) ، راسا static معرفي كنيد. به اين ترتيب نيازي به سازنده private نخواهد بود.

3) وجود try/catch
يك اصل كلي وجود دارد: اگر در حال طراحي يك كتابخانه پايه‌اي هستيد، try/catch را در هيچ متدي از آن لحاظ نكنيد. بله؛ درست خونديد! لطفا try/catch ننويسيد! كرش كردن برنامه خوب است! لا‌يه‌هاي بالاتر برنامه كه در حال استفاده از كدهاي شما هستند متوجه خواهند شد كه مشكلي رخ داده و اين مشكل توسط كتابخانه مورد استفاده «خفه» نشده. براي مثال اگر هم اكنون SQL Server در دسترس نيست، لايه‌هاي بالاتر برنامه بايد اين مشكل را متوجه شوند. Exception اصلا چيز بدي نيست! كرش برنامه اصلا بد نيست!
فرض كنيد كه دچار بيماري شده‌ايد. اگر مثلا تبي رخ ندهد، از كجا بايد متوجه شد كه نياز به مراقبت پزشكي وجود دارد؟ اگر هيچ علامتي بروز داده نشود كه تا الان نسل بشر منقرض شده بود!

4) وجود ref و out
دوستان گرامي! اين ref و out فقط جهت سازگاري با زبان C در سي شارپ وجود دارد. لطفا تا حد ممكن از آن استفاده نكنيد! مثلا استفاده از توابع API‌ ويندوز كه با C نوشته شده‌اند.
يكي از مهم‌ترين كاربردهاي pointers در زبان سي، دريافت بيش از يك خروجي از يك تابع است. براي مثال يك متد API ويندوز را فراخواني مي‌كنيد؛ خروجي آن يك ساختار است كه به كمك pointers به عنوان يكي از پارامترهاي همان متد معرفي شده. اين روش به وفور در طراحي ويندوز بكار رفته. ولي خوب در سي شارپ كه از اين نوع مشكلات وجود ندارد. يك كلاس ساده را طراحي كنيد كه چندين خاصيت دارد. هر كدام از اين خاصيت‌ها مي‌توانند نمايانگر يك خروجي باشند. خروجي متد را از نوع اين كلاس تعريف كنيد. يا براي مثال در دات نت 4، امكان ديگري به نام Tuples معرفي شده براي كساني كه سريع مي‌خواهند چند خروجي از يك تابع دريافت كنند و نمي‌خواهند براي اينكار يك كلاس بنويسند.
ضمن اينكه براي مثال در متد Connect2Db، هم كانكشن يكبار به صورت ref معرفي شده و يكبار به صورت خروجي متد. اصلا نيازي به استفاده از ref در اينجا نبوده. حتي نيازي به خروجي كانكشن هم در اين متد وجود نداشته. كليه تغييرات شما در شيء كانكشني كه به عنوان پارامتر ارسال شده، در خارج از آن متد هم منعكس مي‌شود (شبيه به همان بحث pointers در زبان سي). بنابراين وجود ref غيرضروري است؛ وجود خروجي متد هم به همين صورت.

5) استفاده از using در متد RunExecuteScalarCmd
استفاده از using خيلي خوب است؛ هميشه اينكار را انجام دهيد!
اما اگر اينكار را انجام داديد، بدانيد كه شيء sqlCnt در پايان بدنه using ، توسط GC نابوده شده است. بنابراين اينجا bool blnClose ديگر چه كاربردي دارد؟! تصميم شما ديگر اهميتي نخواهد داشت؛ چون كار تخريبي پيشتر انجام شده.

6) متد CloseCnt
اين متد زايد است؛ به دليلي كه در قسمت (5) عنوان شد. using هاي استفاده شده، كار را تمام كرده‌اند. بنابراين بستن اشياء dispose شده معنا نخواهد داشت.

7) در مورد نحوه استفاده
اگر SqlHelper را در اينجا مثلا يك DAL ساده فرض كنيم (data access layer)، جاي قسمت BLL (business logic layer) در اينجا خالي است. عموما هم چون توضيحات اين موارد را خيلي بد ارائه داده‌اند، افراد از شنيدن اسم آن‌ها هم وحشت مي‌كنند. BLL يعني كمي دست به Refactoring بزنيد و اين پياده سازي منطق تجاري ارائه شده در متد BtnTest_Click را به يك كلاس مجزا خارج از code behind پروژه منتقل كنيد. Code behind فقط محل استفاده نهايي از آن باشد. همين! فعلا با همين مختصر شروع كنيد.
مورد ديگري كه در اينجا باز هم مشهود است، عدم استفاده از پارامتر در كوئري‌ها است. چون از پارامتر استفاده نكرده‌ايد، SQL Server مجبور است براي حالت EmployeeID = 9 يكبار execution plan را محاسبه كند، براي كوئري بعدي مثلا EmployeeID = 19، اينكار را تكرار كند و الي آخر. اين يعني مصرف حافظه بالا و همچنين سرعت پايين انجام كوئري‌ها. بنابراين اينقدر در قيد و بند باز نگه داشتن يك كانكشن نباشيد؛ مشكل اصلي جاي ديگري است!

8) برنامه وب و اطلاعات استاتيك!
اين پروژه، يك پروژه ASP.NET است. ديدن تعاريف استاتيك در اين نوع پروژه‌ها يك علامت خطر است! در اين مورد قبلا مطلب نوشتم:
متغيرهاي استاتيك و برنامه‌هاي ASP.NET


يك درخواست عمومي!
لطف كنيد در پروژ‌هاي «جديد» خودتون اين نوع كلاس‌هاي SqlHelper رو «دور بريزيد». ياد گرفتن كار با يك ORM جديد اصلا سخت نيست. مثلا طراحي Entity framework مايكروسافت به حدي ساده است كه هر شخصي با داشتن بهره هوشي در حد يك عنكبوت آبي يا حتي جلبك دريايي هم مي‌تونه با اون كار كنه! فقط NHibernate هست كه كمي مرد افكن است و گرنه مابقي به عمد ساده طراحي شده‌اند.
مزاياي كار كردن با ORM ها اين است:
- كوئري‌هاي حاصل از آن‌ها «پارامتري» است؛ كه اين دو مزيت عمده را به همراه دارد:
امنيت: مقاومت در برابر SQL Injection
سرعت و همچنين مصرف حافظه كمتر: با كوئري‌هاي پارامتري در SQL Server همانند رويه‌هاي ذخيره شده رفتار مي‌شود.
- عدم نياز به نوشتن DAL شخصي پر از باگ. چون ORM يعني همان DAL كه توسط يك سري حرفه‌اي طراحي شده.
- يك دست شدن كدها در يك تيم. چون همه بر اساس يك اينترفيس مشخص كار خواهند كرد.
- امكان استفاده از امكانات جديد زبان‌هاي دات نتي مانند LINQ و نوشتن كوئري‌هاي strongly typed تحت كنترل كامپايلر.
- پايين آوردن هزينه‌هاي آموزشي افراد در يك تيم. مثلا EF را مي‌شود به عنوان يك پيشنياز در نظر گرفت؛ عمومي است و همه گير. كسي هم از شنيدن نام آن تعجب نخواهد كرد. كتاب(هاي) آموزشي هم در مورد آن زياد هست.
و ...


۱۳۹۰/۱۰/۲۰

قبل از رفع باگ، براي آن تست بنويسيد


از دقت كردن در نحوه اداره پروژه‌هاي خوب و بزرگ در سطح دنيا، مي‌توان به نكات آموزنده‌اي رسيد. براي مثال NHibernate را درنظر بگيريد. اين پروژه شايد روز اول كپي مطابق اصل نمونه جاواي آن بوده، اما الان از خيلي از جهات يك سر و گردن از آن بالاتر است. پشتيباني LINQ را اضافه كرده، خودش Syntax جديدي را به نام QueryOver ارائه داده و همچنين معادلي را جهت حذف فايل‌هاي XML به كمك امكانات جديد زبان‌هاي دات نتي مانند lambda expressions ارائه كرده. خلاصه اين تيم، فقط يك كپي كار نيست. پايه رو از يك جايي گرفته اما سبب تحول در آن شده. از اهداف پروژه‌هاي سورس باز هم همين است: براي هر كاري چرخ را از صفر ابداع نكنيد.

اگر به نحوه اداره كلي پروژه NHibernate‌ دقت كنيد يك مورد مشهود است:
  • تمام گزارش‌هاي باگ بدون Unit test نديد گرفته مي‌شوند.
  • از كليه بهبودهاي ارائه شده (وصله‌ها يا patch ها) بدون Unit test صرفنظر مي‌شود.
  • از كليه موارد جديد ارائه شده بدون Unit test هم صرفنظر خواهد شد.

بنابراين اگر در issue tracker اين تيم رفتيد و گفتيد: «سلام، اينجا اين مشكل هست»، خيالتان راحت باشد كه نديد گرفته خواهيد شد.

سؤال : چرا اين‌ها اينطور رفتار مي‌كنند؟!
- وجود Unit test دقيقا مشخص مي‌كند كه چه قسمت يا قسمت‌هايي به گزارش باگ شما مرتبط هستند. نيازي نيست حتما بتوانيد يك خطا را با جملات ساده شرح دهيد. اين مساله خصوصا در پروژه‌هاي بين المللي حائز اهميت است. ضعف زبان انگليسي همه جا هست. همينقدر كه توانسته‌ايد براي آن يك Unit test بنويسيد كه مثلا در اين عمليات با اين ورودي،‌ نتيجه قرار بوده بشود 10 و مثلا شده 5 يا حتي اين Exception صادر شده كه بايد كنترل شود، يعني مشكل را كاملا مشخص كرده‌ايد.
- وجود Unit tests ، انجام Code review و همچنين Refactoring را تسهيل مي‌بخشند. در هر دو حالت ياد شده، هدف تغيير كاركرد سيستم نيست؛ هدف بهبود كيفيت كدهاي موجود است. بنابراين دست به يك سري تغييرات زده خواهد شد. اما سؤال اينجا است كه از كجا بايد مطمئن شد كه اين تغييرات، سيستم را به هم نريخته‌اند. پروژه‌ي جاري چند سال است كه در حال توسعه است. قسمت‌هاي زيادي به آن اضافه شده. با نبود Unit tests ممكن است بعضي از قسمت‌ها زايد يا احمقانه به نظر برسند.
- بهترين مستندات كدهاي تهيه شده، Unit tests آن هستند. براي مثال علاقمند هستيد كه NHibernate را ياد بگيريد؟ هرچه مي‌گرديد مثال‌هاي كمي را در اينترنت در اين زمينه پيدا مي‌كنيد؟ وقت خودتان را تلف نكنيد! اين پروژه بالاي 2000 آزمون واحد دارد. هر كدام از اين آزمون‌ها نحوه‌ي بكارگيري قسمت‌هاي مختلف را به نحوي كاربردي بيان مي‌كنند.
- وجود Unit tests از پيدايش مجدد باگ‌ها جلوگيري مي‌كنند. اگر آزمون واحدي وجود نداشته باشد، امروز كدي اضافه مي‌شود. فردا همين كد توسط عده‌اي ديگر زايد تشخيص داده شده و حذف مي‌شود! بنابراين احتمال بروز مجدد اين خطا در آينده وجود خواهد داشت. با وجود Unit tests، فلسفه وجودي هر قسمتي از كدهاي موجود پروژه دقيقا مشخص مي‌شود و در صورت حذف آن‌، با اجراي آزمون‌هاي خودكار سريعا مي‌توان به كمبودهاي حاصل پي‌برد.

۱۳۹۰/۱۰/۱۸

وادار كردن خود به كامنت نوشتن


قابليت جالبي در ويژوال استوديو وجود دارد كه شايد كمتر در مورد آن مطلب نوشته شده است و آن هم تنظيم پروژه به نحوي است كه اگر براي كليه موارد public كامنتي نوشته نشود، برنامه كامپايل نخواهد شد. همچنين اگر نام پارامتري را تغيير داديد، اما كامنت مرتبط با آن را به روز نكرديد، باز هم خطاي كامپايل را دريافت خواهيد كرد كه از اين لحاظ هم بسيار عالي است و به نوعي «وادار كردن خود به كامنت نوشتن» است.

براي اين تنظيم، ابتدا به برگه خواص پروژه مراجعه كنيد. سپس در قسمت Build تنظيمات زير را اعمال نمائيد:
Treat warnings as errors را بر روي All قرار دهيد.
در ذيل آن، در قسمت Output‌، گزينه‌ي XML Documentation file را تيك بزنيد.

البته اين تغيير بهتر است در يك پروژه جديد مد نظر باشد، چون اگر الان اقدام به اين تنظيم كنيد، به طور قطع از خير آن خواهيد گذشت! كامنت نويسي به مرور و در حين توسعه يك برنامه يا كتابخانه قابل تحمل است وگرنه اگر براي روز آخر قرار داده شود، به احتمال زياد انجام نخواهد شد.

مطالب مرتبط:



۱۳۹۰/۱۰/۱۶

اهميت code review


تا جايي كه دقت كردم (در بلاگ‌هايي كه منتشر مي‌شوند) در آنسوي آب‌ها، «code review» يك شغل محسوب مي‌شود. سازمان‌ها، شركت‌ها و امثال آن از مشاورين يا برنامه نويس‌هايي با مطالعه بيشتر دعوت مي‌كنند تا از كدهاي آن‌ها اشكال‌گيري كنند و بابت اينكار هم هزينه مي‌كنند.
اگر علاقمند باشيد قسمتي از يك پروژه سورس باز دريافت شده از همين دور و اطراف را با هم مرور كنيم:

//It's only for code review purpose!
protected void Button1_Click1(object sender, EventArgs e)
{        
        string  strcon;        
        string strUserURL;
        string strSQL;
        string strSQL1;
        strSQL = "SELECT UserLevel FROM listuser " + "WHERE Username='" + TextBox2.Text + "' " + "And Password='" + TextBox3.Text + "';";
        strSQL1 = "SELECT Pnumber FROM listuser " + "WHERE Username='" + TextBox2.Text + "' " + "And Password='" + TextBox3.Text + "';";
        strcon = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\bimaran.mdf;Integrated Security=True;User Instance=True";
        SqlConnection myConnection = new SqlConnection(strcon);

        SqlCommand myCommand = new SqlCommand(strSQL, myConnection);
        SqlCommand myCommand1 = new SqlCommand(strSQL1, myConnection);
        myConnection.Open();

        strUserURL = (string)myCommand.ExecuteScalar();
        send = (string)myCommand1.ExecuteScalar();
        myCommand.Dispose();
        myCommand1.Dispose();
        myConnection.Close();


        if (strUserURL != null)
        {                      
            Label1.Text = "";

            url = "?Pn=" + code(send);
            FormsAuthentication.SetAuthCookie(TextBox2.Text, true);
            Response.Redirect("Page/" + strUserURL + url);
       }
        else
            Label3.Text = "چنین کاربری با این مشخصات ثبت نشده است.";
}


مروري بر اين كد يا «مشكلات اين كد»:
- كانكشن استرينگ داخل كدها تعريف شده. يعني اگر نياز به تغييري در آن بود بايد كدهاي برنامه تغيير كنند. آن هم نه فقط در اين تابع بلكه در كل برنامه.
- از پارامتر استفاده نشده. كد 100 درصد به تزريق اس كيوال آسيب پذير است.
- نحوه‌ي dispose شيء كانكشن غلط است. هيچ ضمانتي وجود ندارد كه كدهاي فوق سطر به سطر اجرا شود و خيلي زيبا به سطر بستن كانكشن استرينگ برسد. فقط كافي است اين ميان يك استثنايي صادر شود و تمام. به عبارتي اين سايت فقط با كمتر از 30 كاربر همزمان از كار مي‌افته. بعد نيايد بگيد من يك سرور دارم با 16 گيگ رم ولي باز كم مياره! همش برنامه كند ميشه. همش سايت بالا نمياد!
- همين تعريف كردن متغيرها در ابتداي تابع يعني اين برنامه نويس هنوز حال و هواي ANSI C را دارد!
- مهم نيست لايه بندي كنيد. ولي يك لايه در اين نوع پروژه‌ها الزامي است و آن هم DAL نام دارد. DAL يعني كثافت كاري نكنيد. يعني داخل هر تابع كُپه كُپه بر نداريد open و close بذاريد. بريد يك تابع يك گوشه‌اي درست كنيد كه اين عمليات را محصور كند.
- همين وجود Button1 و Label1 يعني تو خود شرح مفصل بخوان از اين مجمل!

اندرباب اهميت به اشتراك گذاري اطلاعات

۱۳۹۰/۱۰/۱۴

تعيين تعداد رديف در صفحه جداول خودكار iTextSharp


پيشنياز : «تكرار خودكار سرستون‌هاي يك جدول در صفحات مختلف، توسط iTextSharp»
همانطور كه در مطلب پيشنياز عنوان شده ذكر گرديد، iTextSharp امكان درج خودكار header و footer به علاوه محاسبه خودكار تعداد رديف‌هاي يك جدول در يك صفحه را بر اساس طول و اندازه محتواي هر رديف، دارد. براي مثال يك صفحه ممكن است 2 رديف شود و يك صفحه 20 رديف. تمام اين‌ها را به صورت خودكار محاسبه مي‌كند و بسيار عالي است. (اين امكان مهمي است كه خيلي از ابزارهاي گزارشگيري موجود هنوز با آن مشكل دارند)
اما اگر فرض را بر اين بگذاريم كه اندازه سلول‌ها و در نتيجه طول هر رديف ثابت است و مثلا تمام صفحات نهايتا از يك تعداد رديف مشخص تشكيل خواهند شد، خاصيتي را به نام number of rows يا rows count و امثال آن‌را ندارد كه مثلا به آن گفت، من در هر صفحه فقط 5 رديف را مي‌خواهم نمايش دهم و نه 20 رديف را.
روش حل اين مساله را در ادامه ملاحظه خواهيد كرد و يك نكته‌ي خيلي ساده و مستند نشده دارد!

using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace RowsCountSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();

                var table1 = new PdfPTable(3);
                table1.HeaderRows = 2;
                table1.FooterRows = 1;

                //header row  
                var headerCell = new PdfPCell(new Phrase("header"));
                headerCell.Colspan = 3;
                headerCell.HorizontalAlignment = Element.ALIGN_CENTER;
                table1.AddCell(headerCell);

                //footer row  
                var footerCell = new PdfPCell(new Phrase("footer"));
                footerCell.Colspan = 3;
                footerCell.HorizontalAlignment = Element.ALIGN_CENTER;
                table1.AddCell(footerCell);

                //adding some rows  
                for (int i = 0; i < 70; i++)
                {
                    //adds a new row
                    table1.AddCell(new Phrase("Cell[0], Row[" + i + "]"));
                    table1.AddCell(new Phrase("Cell[1], Row[" + i + "]"));
                    table1.AddCell(new Phrase("Cell[2], Row[" + i + "]"));

                    //sets the number of rows per page
                    if (i > 0 && table1.Rows.Count % 7 == 0)
                    {
                        pdfDoc.Add(table1);
                        table1.DeleteBodyRows();
                        pdfDoc.NewPage();
                    }
                }

                pdfDoc.Add(table1);
            }

            //open the final file with adobe reader for instance.  
            Process.Start("Test.pdf");
        }
    }
}

نكته جديد اين مثال، قسمت زير است:


if (i > 0 && table1.Rows.Count % 7 == 0)
{
      pdfDoc.Add(table1);
      table1.DeleteBodyRows();
      pdfDoc.NewPage();
}

هر زمان كه table1 به صفحه اضافه شود، header و footer هم اضافه خواهند شد، اما اگر BodyRows آن حذف نشود،‌ دفعه‌ي دومي كه اين table به صفحه اضافه مي‌شود، شامل رديف‌هاي مثلا يك تا 10 خواهد بود بجاي 6 تا 10 .

۱۳۹۰/۱۰/۱۳

نصب خودكار اطلاعات فايل‌هاي PFX در سيستم


در مورد نحوه رمزنگاري فايل‌هاي PDF به كمك روش Public-key encryption توسط iTextSharp مطلبي را پيشتر در اين سايت مطالعه كرده‌ايد.
اين روش يك مشكل مهم دارد: «ارائه فايل PFX و همچنين كلمه عبور آن به كاربر نهايي»
خوب، اين يعني اينكه شما به راحتي مي‌تونيد اطلاعات را رمزگشايي كنيد؛ چون همه چيز سخاوتمندانه در اختيارتان است. بنابراين ضرورت رمزنگاري آن در ابتداي امر زير سؤال مي‌رود.
اكنون اين سؤال مطرح مي‌شود كه آيا مي‌توان اين اطلاعات را تا حد قابل قبولي مخفي كرد؟ مثلا يك برنامه را در اختيار كاربر قرار داد كه اطلاعات فايل PFX را به همراه كلمه عبور آن در سيستم نصب كند.
پاسخ:
دات نت به صورت توكار از اين نوع فايل‌هاي مجوز پشتيباني مي‌كند:

using System.Security.Cryptography.X509Certificates;

namespace InstallPfx
{
    class Program
    {
        private static void InstallCertificate(string cerFileName, string password)
        {
            var certificate = new X509Certificate2(cerFileName, password, X509KeyStorageFlags.PersistKeySet);
            var store = new X509Store(StoreName.My);
            store.Open(OpenFlags.ReadWrite);
            store.Add(certificate);
            store.Close();
        }

        static void Main(string[] args)
        {
            InstallCertificate(@"D:\forTest\file.pfx", "123456");
        }
    }
}

پس از اجراي كد فوق، امكان مشاهده فايل‌هاي PDF رمزنگاري شده به كمك اطلاعات فايل file.pfx، ميسر مي‌شود.
براي مشاهده اين مجوز نصب شده هم مي‌توان در ديالوگ Run ويندوز نوشت : certmgr.msc تا كنسول مديريتي مجوز‌هاي ويندوز ظاهر شود. سپس به قسمت personal certificates بايد مراجعه كرد.

۱۳۹۰/۱۰/۱۲

MVVM و نمايش ديالوگ‌ها


بسياري از برنامه‌هاي دسكتاپ نياز به نمايش پنجره‌هاي ديالوگ استاندارد ويندوز مانند OpenFileDialog و SaveFileDialog را دارند و سؤال اينجا است كه چگونه اينگونه موارد را بايد از طريق پياده سازي صحيح الگوي MVVM مديريت كرد؛ از آنجائيكه خيلي راحت در فايل ViewModel مي‌توان نوشت new OpenFileDialog و الي آخر. اين مورد هم يكي از دلايل اصلي استفاده از الگوي MVVM را زير سؤال مي‌برد : اين ViewModel ديگر قابل تست نخواهد بود. هميشه شرايط آزمون‌هاي واحد را به اين صورت در نظر بگيريد:
سروري وجود دارد در جايي كه به آن دسترسي نداريم. روي اين سرور با اتوماسيوني كه راه انداخته‌ايم، آخر هر روز آزمون‌هاي واحد موجود به صورت خودكار انجام شده و يك گزارش تهيه مي‌شود (مثلا يك نوع continuous integration سرور). بنابراين كسي دسترسي به سرور نخواهد داشت تا اين OpenFileDialog ظاهر شده را مديريت كرده، فايلي را انتخاب و به برنامه آزمون واحد معرفي كند. به صورت خلاصه ظاهر شدن هر نوع ديالوگي حين انجام آزمون‌هاي واحد «مسخره» است!
يكي از روش‌هاي حل اين نوع مسايل، استفاده از dependency injection يا تزريق وابستگي‌ها است و در ادامه خواهيم ديد كه چگونه WPF‌ بدون نياز به هيچ نوع فريم ورك تزريق وابستگي خارجي، از اين مفهوم پشتيباني مي‌كند.

مروري مقدماتي بر تزريق وابستگي‌ها
امكان نوشتن آزمون واحد براي new OpenFileDialog وجود ندارد؟ اشكالي نداره، يك Interface بر اساس نياز نهايي برنامه درست كنيد (نياز نهايي برنامه از اين ماجرا فقط يك رشته LoadPath است و بس) سپس در ViewModel با اين اينترفيس كار كنيد؛ چون به اين ترتيب امكان «تقليد» آن فراهم مي‌شود.

يك مثال عملي:
ViewModel نياز دارد تا مسير فايلي را از كاربر بپرسد. اين مساله را با كمك dependency injection در ادامه حل خواهيم كرد.
ابتدا سورس كامل اين مثال:

ViewModel برنامه (تعريف شده در پوشه ViewModels برنامه):

namespace WpfFileDialogMvvm.ViewModels
{
    public interface IFilePathContract
    {
        string GetFilePath();       
    }

    public class MainWindowViewModel
    {
        IFilePathContract _filePathContract;
        public MainWindowViewModel(IFilePathContract filePathContract)
        {
            _filePathContract = filePathContract;
        }

        //...

        private void load()
        {
            string loadFilePath = _filePathContract.GetFilePath();
            if (!string.IsNullOrWhiteSpace(loadFilePath))
            {
                // Do something
            }
        }
    }
}

دو نمونه از پياده سازي اينترفيس IFilePathContract تعريف شده (در پوشه Dialogs برنامه):

using Microsoft.Win32;
using WpfFileDialogMvvm.ViewModels;

namespace WpfFileDialogMvvm.Dialogs
{
    public class OpenFileDialogProvider : IFilePathContract
    {
        public string GetFilePath()
        {
            var ofd = new OpenFileDialog
            {
                Filter = "XML files (*.xml)|*.xml"
            };
            string filePath = null;
            bool? dialogResult = ofd.ShowDialog();
            if (dialogResult.HasValue && dialogResult.Value)
            {
                filePath = ofd.FileName;
            }
            return filePath;
        }
    }

    public class FakeOpenFileDialogProvider : IFilePathContract
    {
        public string GetFilePath()
        {
            return @"c:\path\data.xml";
        }
    }
}

و View برنامه:

<Window x:Class="WpfFileDialogMvvm.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WpfFileDialogMvvm.ViewModels"
        xmlns:dialogs="clr-namespace:WpfFileDialogMvvm.Dialogs"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>        
        <ObjectDataProvider x:Key="mainWindowViewModel" 
                            ObjectType="{x:Type vm:MainWindowViewModel}">
            <ObjectDataProvider.ConstructorParameters>
                <dialogs:OpenFileDialogProvider/>
            </ObjectDataProvider.ConstructorParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource mainWindowViewModel}}">
        
    </Grid>
</Window>

توضيحات:
ما در ViewModel نياز داريم تا مسير نهايي فايل را دريافت كنيم و اين عمليات نياز به فراخواني متد ShowDialog ايي را دارد كه امكان نوشتن آزمون واحد خودكار را از ViewModel ما سلب خواهد كرد. بنابراين بر اساس نياز برنامه يك اينترفيس عمومي به نام IFilePathContract را طراحي مي‌كنيم. در حالت كلي كلاسي كه اين اينترفيس را پياده سازي مي‌كند، قرار است مسيري را برگرداند. اما به كمك استفاده از اينترفيس، به صورت ضمني اعلام مي‌كنيم كه «براي ما مهم نيست كه چگونه». مي‌خواهد OpenFileDialogProvider ذكر شده باشد، يا نمونه تقليدي مانند FakeOpenFileDialogProvider. از نمونه واقعي OpenFileDialogProvider در برنامه اصلي استفاده خواهيم كرد، از نمونه تقليدي FakeOpenFileDialogProvider در آزمون واحد و نكته مهم هم اينجا است كه ViewModel ما چون بر اساس اينترفيس IFilePathContract پياده سازي شده، با هر دو DialogProvider ياد شده مي‌تواند كار كند.
مرحله آخر نوبت به وهله سازي نمونه واقعي، در View برنامه است. يا مي‌توان در Code behind مرتبط با View نوشت:

namespace WpfFileDialogMvvm
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel(new OpenFileDialogProvider());
        }
    }
}

و يا از روش ObjectDataProvider توكار WPF هم مي‌شود استفاده كرد؛ كه مثال آن‌را در كدهاي XAML مرتبط با View ذكر شده مي‌توانيد مشاهده كنيد. ابتدا دو فضاي نام vm و dialog تعريف شده (با توجه به اينكه مثلا در اين مثال، دو پوشه ViewModels و Dialogs وجود دارند). سپس كار تزريق وابستگي‌ها به سازنده كلاس MainWindowViewModel،‌ از طريق ObjectDataProvider.ConstructorParameters انجام مي‌شود:

<ObjectDataProvider x:Key="mainWindowViewModel" 
                            ObjectType="{x:Type vm:MainWindowViewModel}">
            <ObjectDataProvider.ConstructorParameters>
                <dialogs:OpenFileDialogProvider/>
            </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>