اشتراک‌ها
تغییر مجوز استفاده‌ی از Redis

Redis, the popular in-memory data store, is switching away from the open source three-clause BSD license. Instead, in a move that is clearly aimed to prevent the large cloud providers from offering free alternatives to Redis’ own hosted services, Redis will now be dual-licensed under the Redis Source Available License (RSALv2) and Server Side Public License (SSPLv1). Under this new license, cloud service providers hosting Redis will need to enter into a commercial agreement with Redis. The first company to do so is Microsoft.

تغییر مجوز استفاده‌ی از Redis
مطالب
حل مشکل ویژوال استودیو در سیستمهایی که از رزولوشن (DPI) بالا و مانیتور های 4K استفاده می کنند
مدتی بود بر روی یک پروژه‌ی اتوماسیون اداری در VB.NET کار می‌کردیم. پروژه‌ی ما بر روی سیستمی با رزولوشن بالا  2160 * 3840 و مانیتور 4K قرار داشت. بعد از اینکه لایه بندی و کد‌های نرم افزار نوشته شد، نوبت به طراحی اینترفیس پروژه رسید. با مشکلی عجیب روبرو شدیم، به این صورت که در قسمت طراحی ویژوال استودیو، منوها، دکمه‌ها و ... بیش از حد معمول کوچک و به هم ریخته بود. ولی زمانیکه پروژه اجرا می‌شد، نسبت به طراحی که در سمت وِیژوال استودیو انجام داده بودیم، دکمه‌ها  بزرگتر و منوها بزرگتر و شکسته شده بودند. در حقیقت، سمت طراحی و سمت اجرای پروژه، هم خوانی نداشتند. در حالیکه ما قبلا بر روی مانیتور‌های HD و رزولوشن‌ها HD، هیچ مشکلی در طراحی و اجرای پروژه نداشتیم و هم خوانی لازم را باهم داشتند. بعد از جستجوهای متعدد، به این مطلب پی بردیم که این مشکل بیشتر برنامه نویسانی هست که از سیستم‌هایی با رزولوشن بالا و مانیتور‌های 4K استفاده می‌کنند و پاسخ مناسبی به کاربران داده نشده است. با تست راه‌های متعدد و جستجوهای پی در پی، به پاسخ قطعی رسیدیم و خواستم این مطلب  را به صورت مقاله‌ای کوتاه، با شما به اشتراک بگذارم.
مشخصات سیستمی که مشکل زیر را دارد : ویندوز 10  نسخه  Enterprise  x64 و ویژوال استودیو 2015 نسخه  Enterprise 
نمونه اسکرین شات گرفته شده که مشکل یکی از کاربران ایرانی بود و برای حل مشکل خود، اسکرین شات صفحه نمایش خود را قرار داده بود و به پاسخ قطعی نرسیده بود:

  اگر ملاحظه کنید، تصویر سمت راست، از پروژه‌ای بر روی VirtualBox با رزولوشن و DPI پایین در دیزاین ویژوال استودیو و تصویر سمت چپ از همان پروژه در دیزاین ویژوال استودیو با رزولوشن بالا و مانیتور 4K تهیه شده‌است و ملاحظه می‌کنید که پروژه به هم ریخته است و این مشکل در خیلی از برنامه‌های دیگر نیز موجود می‌باشد؛ مانند SQL SERVER و ...
 نمونه اسکرین شات گرفته شده از بعضی پنجره‌های SQL server 

حال برای رفع این مشکل چه باید کرد؟ به صورت زیر عمل می‌کنیم  

  1. به مسیر زیر در رجیستری مراجعه می‌کنیم :    
HKEY_LOCAL_MACHINE > SOFTWARE > Microsoft > Windows > CurrentVersion > SideBySide
 و رایت کلیک کرده  NEW > DWORD (32 bit) Value  انتخاب کرده و نام را PreferExternalManifest وارد کرده و Value را بر روی 1 قرار می‌دهیم .

     

2. نرم افزار Resourcehacke را دانلود کرده و آن‌را اجرا کرده و از قسمت File، بر روی Open کلیک کرده و مسیر ویژوال استودیو را به نرم افزار داده و  Ok را انتخاب می‌کنیم . 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE


بعد از بارگذاری اطلاعات ویژوال استودیو در نرم افزار  Resourcehacke، از سمت چپ بر روی Manifest و 1:1033 کلیک کرده و dpiAware را بر روی False قرار می‌دهیم .
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <application xmlns="urn:schemas-microsoft-com:asm.v3"> 
    <windowsSettings> 
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">False</dpiAware> 
    </windowsSettings> 
  </application> 
</assembly>

ودر آخر سیستم را ریستارت کرده و با همان رزولوشن بالا و مانیتور 4K، ویژوال استودیو را اجرا می‌کنیم و ملاحظه میکنیم که مشکل خاصی وجود ندارد و سمت دیزاین با سمت اجرای پروژه همخوانی دارد و همسان می‌باشد.

مطالب
‫دریافت کل یک مخزن SVN به کمک برنامه نویسی

تمام قابلیت‌های موجود در SVN به کمک برنامه نویسی هم قابل دسترسی هستند. برای مثال تهیه خروجی از یک مخزن SVN به همراه تمامی فایل‌ها و ساختار آن. SVN به زبان C نوشته شده است و API آن نیز مبتنی بر همین زبان است اما یک سری محصور کننده برای استفاده از آن در سایر زبان‌های برنامه نویسی هم موجود است. برای مثال معروفترین آن‌ها جهت استفاده به کمک دات نت فریم ورک کتابخانه‌ی SharpSVN است. پس از دریافت و افزودن ارجاعی به اسمبلی آن، چند سطر ذیل کار دریافت یک مخزن SVN را به صورت تمام و کمال انجام خواهد داد:

using SharpSvn;
...
using (var sc = new SvnClient())
{
var target = SvnTarget.FromUri(new Uri("http://someproject.googlecode.com/svn/trunk/"));
var finalSaveToDir = "somepath ..."; //Note: this path should not exist
sc.Export(target, finalSaveToDir);
}

نمونه‌ای از کاربردها:
- راه اندازی یک سایت برای دریافت ساده‌تر مخازن کد برای مثال Google-code یا source forge و امثال آن.

مطالب
Garbage Collector در #C - قسمت دوم
در این مطلب قصد داریم به تفاوت‎های بین Stack و Heap در Memory و زبان #C بپردازیم.

به زبان ساده، وقتی شما متغیر جدیدی را ایجاد میکنید، با توجه به نوع (Type) آن متغیر، "مقدار" متغیر شما در Stack یا Heap قرار خواهد گرفت.

Stack

Stack نوعی ساختمان داده‌است که در آن، داده‌ها بصورت خطی قرار گرفته و اصطلاحا ساختار LIFO ( مخفف Last in, First Out ) دارند، بدین معنا که همیشه آخرین داده‌ای که داخل Stack قرار داده‌اید، اولین داده‌ای است که قادر به خواندن آن خواهید بود. وقتی در ساختار Stack داده‌ای را قرار میدهیم، اصطلاحا آن را Push کرده و وقتی میخواهیم آخرین داده را با توجه به ساختار خطی آن بخوانیم، داده را Pop میکنیم.


این ساختمان داده، داخل Memory پیاده سازی شده است و تعدادی از متغیرهایی را که ما داخل کد ایجاد میکنیم، در این نوع ساختمان داده از Memory نگهداری میشوند.

شرط قرار گرفتن مقدار یک متغیر داخل Stack این است که متغیر از نوع Value Type باشد. در زبان #C، بطور کلی Struct و Enum‌ها Value Type هستند و بصورت پیشفرض داخل Stack قرار میگیرند. تمامی ValueType‌ها در #C، بطور implicit از System.ValueType ارث بری میکنند.

Type‌های زیر، Value Type‌های پیشفرض تعریف شده‌ی در زبان #C هستند که به آن‌ها Simple Type نیز گفته میشوند:


Represents   Type
 Boolean value  bool
8-bit unsigned integer
 byte
 16-bit Unicode character  char
128-bit precise decimal values with 28-29 significant digits   decimal
 64-bit double-precision floating point type  double
 32-bit single-precision floating point type  float
 32-bit signed integer type  int
 64-bit signed integer type  long
 8-bit signed integer type  sbyte
 16-bit signed integer type  short
 32-bit unsigned integer type  uint
 64-bit unsigned integer type  ulong
16-bit unsigned integer type   ushort


اگر سورس هرکدام از این تایپ‌ها مانند  Int32 را در ریپازیتوری CoreFX مایکروسافت بررسی کنید، متوجه خواهید شد که تمامی این تایپ‌ها از نوع Struct تعریف شده‌اند و همانطور که گفتیم، بطور پیش‌فرض، Struct‌ها داخل Stack قرار خواهند گرفت.

طول عمر متغیرهایی که داخل Stack قرار گرفته‌اند، منحصر به پایان اجرای یک متد است. بدین معنا که بعد از به پایان رسیدن یک متد، تمامی متغیرهای مورد استفاده در آن متد، از حافظه Stack بطور خودکار حذف خواهند شد. متغیرهایی که داخل Stack قرار میگیرند، نوع و حجم مقادیرشان بر اساس Type ای دارند، در زمان Compile-Time مشخص است.

متغیرهای محلی (Local Variable ها)، پارامترهای ورودی متد و مقدار بازگشتی یک متد، جز مواردی هستند که مقادیرشان داخل Stack قرار میگیرد:
public static int Add(int number1, int number2)
{
    // number1 is on the stack (function parameter)
    // number2 is on the stack (function parameter)

    int sum = number1 + number2;
    // sum is on the stack (local variable)

    return sum;
}

در زبان #C و در مرحله Compile-Time، کدها به زبان IL (مخفف Intermediate Language) ترجمه میشوند که با نام‌های MSIL (مخفف Microsoft Intermediate Language ) و CIL (مخفف Common Intermediate Language ) نیز، این زبان شناخته میشود. ساختار این زبان Stack-based بوده و با شناخت آن، با مفهوم Stack نیز بهتر میتوانیم آشنا شویم.

IL زبانی است که CLR (مخفف Common Language Runtime) را که همان Runtime مایکروسافت است، شناخته و اجرا میکند. قابل ذکر است که Runtime مایکروسافت Open-Source بوده و سورس آن با نام CoreCLR در گذشته از این آدرس و در حال حاضر با نام Runtime از این آدرس قابل دسترسی است.

با استفاده از برنامه هایی مانند dotPeek یا dnSpy یا ILDASM یا ابزار آنلاینی مانند Sharplab  و ... میتوانید کدهای IL حاصل از dll‌های برنامه خود را ببینید. این ابزارها با یکدیگر تفاوت زیادی ندارند و تنها مزیت dnSpy به نسبت بقیه، قابلیت دیباگ کردن کدهای IL توسط آن میباشد و همچنین ILDASM با نصب Visual Studio، از این مسیر بدون نیاز به نصب برنامه اضافه ای قابل دسترسی است:
C:\Program Files (x86)\Microsoft SDKs\Windows\{version}\Bin\ildasm.exe

همانطور که پیش‌تر گفتیم، طول عمر Stack محدود به پایان یک متد است. به این نوع Stack که هنگام صدا زدن یک متد ایجاد میشود و شامل ورودی‌های متد، متغیرهای محلی و آدرس خروجی هستند، Stack Frame یا Activation Frame گفته میشود.

 

اگر متد Add بالا را با پارامترهای 2 و 5 صدا بزنیم، خروجی IL حاصل از آن، که این دو عدد را بعنوان ورودی گرفته و جمع آنها را بعنوان خروجی میدهد، به این صورت خواهد بود ( قسمت هایی از خروجی جهت سادگی، حذف شده است) :
.method private hidebysig static int32 Add(int32 number1, int32 number2) cil managed
{
  .locals init (int32 V_0, int32 V_1)
  
  IL_0001:  ldarg.0 // Stack is: [2]
  IL_0002:  ldarg.1 // Stack is: [2, 5]
  IL_0003:  add     // Stack is: [7]
  IL_0004:  stloc.0 // Stack is: [] and V_0's value is: 7
  
  IL_0005:  ldloc.0 // Stack is: [7]
  IL_0006:  stloc.1 // Stack is: [] and V_1's value is: 7

  IL_0009:  ldloc.1 // Stack is: [7]
  IL_000a:  ret     // Return [7]
}

میتوانید لیست دستورات مورد استفاده در CIL را از اینجا ببینید.

در ادامه، خط به خط، خروجی حاصل را بررسی میکنیم:

1- در زبان IL، میتوانید مقادیر حاصل از اعمال محاسباتی یا متدهای دیگر را داخل متغیرهای محلی ذخیره کنید، به شرط اینکه آنها را در ابتدا مشخص سازید.
    • با استفاده از locals. که به معنای local variables است، میتوانید متغیرهای مورد نیازتان را در طول عمر این متد، معرفی کنید. دادن نام برای این متغیرها اجباری نیست (V_0 و V_1) و صرفا جهت خوانایی استفاده میشوند.


2- از کلمه کلیدی ldarg (مخفف Load Argument) برای لود کردن آرگومان یا همان پارامتر ورودی متد، داخل Stack استفاده میشود.
    • ldarg.0 به معنای لود کردن پارامتر ورودی اول، داخل Stack است و با فراخوانی آن، Stack Frame دارای یک عضو که مقدار آن 2 است، میشود.
    • ldarg.1 به معنای لود کردن پارامتر ورودی دوم، داخل Stack است و با فراخوانی آن، Stack Frame دارای دو عضو که مقادیر آن 2 و 5 است، میشود.

3- با استفاده از کلمه کلیدی add، مقادیر موجود در Stack با یکدیگر جمع میشوند و Stack Frame دارای یک عضو که مقدار آن 7 است، میشود.

4- با استفاده از کلمه کلیدی stloc (مخفف Store Local)، آخرین عضو موجود در Stack، داخل متغیر محلی ذکر شده، قرار گرفته و ذخیره میشود.
    • stloc.0 به معنای ذخیره سازی آخرین مقدار موجود در Stack یعنی عدد 7، داخل متغیر 0 یعنی همان V_0 میباشد. 

5- با استفاده از کلمه کلیدی ldloc (مخفف Load Local)، میتوان متغیر محلی ذخیره شده را داخل Stack قرار داد.
    • ldloc.0 به معنای Load کردن مقدار ذخیره شده متغیر محلی 0 که همان V_0 است، داخل Stack میباشد.

6- در نهایت، مقدار 7، داخل متغیر 1 یا همان V_1 با دستور stloc.1 بار دیگر ذخیره، با ldloc.1 لود شده و با استفاده از دستور ret، برگشت داده میشود.

* نکته: اگر کدها را بطور دقیق بررسی کرده باشید، احتمالا فکر کرده اید که چه نیازی به ایجاد یک متغیر اضافی و ریختن نتیجه داخل آن و سپس برگشت دادن نتیجه، در مرحله 6 است؟!
در زبان #C، کدهای شما در زمان Release و همچنین JIT-Compilation، طی چندین مرحله Optimize میشوند و یکی از این مراحل، حذف این متغیرهای اضافی جهت Optimization و Performance است؛ پس از این بابت نگرانی وجود ندارد.

* نکته: احتمالا تا به اینجا دلیل بوجود آمدن StackOverflowException را متوجه شده باشید. فضای Stack محدود است. این فضا در سیستم‌های 32 بیت برابر با 1 مگابایت و در سیستم‌های 64 بیت برابر با 4 مگابایت است (Reference). اگر حجم متغیرهایی که روی استک Push میشوند، این محدودیت را رد کنند و یا اگر یک متد بطور دائم خودش را صدا بزند (Recursive) و هیچگاه از آن خارج نشود، با خطای StackOverflowException مواجه میشوید.

Heap


.Heap: a group of things placed, thrown, or lying one on another


در مقابل ساختار ترتیبی و منظم Stack، ساختار Heap قرار دارد. Heap قسمتی از حافظه است که ساختار، ترتیب و Layout خاصی ندارد.
این نوع حافظه بر خلاف Stack، منحصر به یک متد نیست و اصطلاحا Global بوده و در هر قسمتی از برنامه قابل دسترسی است. تخصیص حافظه در این قسمت از حافظه اصطلاحا Dynamic بوده و هر نوع داده ای را در هر زمانی میتوان داخل آن ذخیره کرد.

 string‌ها نمونه‌ای از typeهایی هستند که داخل Heap نگه داری میشوند. دقت کنید وقتی میگوییم نگه داری میشود، منظور «مقدار» یک متغیر است.

وقتی یک متغیر از نوع string را ایجاد میکنیم، مقدار آن داخل Heap و Memory-Address آن متغیر روی Heap، در Stack نگه داری میشود:
public static void SayHi()
{
    string name = "Moien";
}

در این مثال، چون string یک class است، مقدار آن داخل heap ذخیره شده و آدرس آن قسمت (segment) از memory، روی Stack قرار میگیرد:
.method private hidebysig static void SayHi() cil managed
{
  .locals init (string V_0)

  IL_0001:  ldstr      "Moien" // Stack is: [memory-address of string in heap]
  IL_0006:  stloc.0
  
  IL_0007:  ret
}

به متغیرهایی که مقادیرشان داخل Heap ذخیره میشوند، Reference-Type گفته میشود.

* نکته: در این مثال متغیری به نام name ایجاد شده که از آن هیچ استفاده‌ای نشده است. در زمان JIT-Compilation، با توجه با Optimization‌های موجود در سطح CLR، این متد بطور کلی اضافه تشخیص داده شده و از آن صرفنظر خواهد شد.



Boxing and Unboxing


به فرایند تبدیل یک Value-Type مانند int که بصورت پیشفرض داخل Stack ذخیره میشود، به یک object که در داخل Heap ذخیره میشود، Boxing گفته میشود. انجام این عمل باعث allocation بر روی memory میشود که سربار زیادی دارد. 

با انجام عمل Boxing، قادر خواهیم بود تا بعنوان مثال یک عدد را بر خلاف روال عادی آن، روی Heap ذخیره کنیم:
public static void Boxing()
{
    const int number = 5;
    
    object boxedNumber = number;          // implicit boxing using implicit cast
    object boxedNumber = (object)number;  // explicit boxing using direct cast
}

در ابتدا عدد 5 روی Stack ذخیره شده بود، اما با Box کردن آن، یعنی قرار دادن مقدار آن داخل یک object، مقدار از Stack به Heap انتقال داده شده و allocation اتفاق خواهد افتاد:
.method public hidebysig static void Boxing() cil managed
{
  .locals init (object V_0)
  
  IL_0001:  ldc.i4.5                                // Stack is: [5]
  IL_0002:  box        [System.Runtime]System.Int32 // Stack is: [memory-address of 5 in heap]
  
  IL_0007:  stloc.0
  IL_0008:  ret
}

به عکس این عمل، یعنی تبدیل یک Reference-Type به یک Value-Type، اصطلاحا Unboxing گفته میشود:
public static void Unboxing()
{
    object boxedNumber = 5;
    
    int number = (int)boxedNumber;
}

که نتیجه آن، به این صورت خواهد بود:
.method public hidebysig static void Unboxing() cil managed
{
  .locals init (object V_0, int32 V_1)
  
  IL_0001:  ldc.i4.5                                  // Stack is: [5]
  IL_0002:  box        [System.Runtime]System.Int32   // Stack is: [memory-address of 5 in heap]
  IL_0007:  stloc.0                                   // Stack is: []
                                                      
  IL_0008:  ldloc.0                                   // Stack is: [memory-address of 5 in heap]
  IL_0009:  unbox.any  [System.Runtime]System.Int32   // Stack is: [5]
  IL_000e:  stloc.1                                   // Stack is: []
  
  IL_000f:  ret
}

تلاش تیم‌های مایکروسافت طی سال‌های اخیر، باعث افزایش Performance فوق العاده در NET Core. و ASP.NET Core شده است. یکی از دلایل این Performance، جلوگیری بسیار زیاد از allocation در کدهای خود NET. است، که این امر به واسطه اولویت قرار دادن استفاده از Structها میسر گردیده است.

برخلاف Stack که طول عمر متغیرهای موجود در آن، در انتهای یک متد پایان می‌یابند، متغیرهای allocate شده‌ی در Heap به این شکل نبوده و در صورت حذف نکردن آنها بصورت دستی، تا پایان طول عمر اجرای برنامه داخل memory باقی خواهند ماند. اینجا، جاییست که Garbage Collector در NET. وارد عمل میشود.
مطالب
ساخت یک گزارش ساز به کمک iTextSharp و Open Office

iTextSharp پایه کار با فایل‌های PDF را ارائه می‌دهد اما ابزاری را جهت ساده‌تر سازی تولید فایل‌های PDF به همراه ندارد؛ هر چند مثلا امکان تبدیل HTML به PDF را دارا است اما باید گفت: «تا حدودی البته». اگر نیاز باشد جدولی را ایجاد کنیم باید کد نویسی کرد، اگر نیاز باشد تصویری اضافه شود به همین ترتیب و الی آخر. البته این را هم باید در نظر داشت که کد نویسی انعطاف قابل توجهی را در اختیار برنامه نویس قرار می‌دهد؛ شاید به همین دلیل این روزها مباحث «Code first» بیشتر مورد توجه برنامه نویس‌ها است، تا مباحث «Wizard first» یک دهه قبل!
اما باز هم داشتن یک طراح بد نیست و می‌تواند در کاهش مدت زمان تولید نهایی یک فایل PDF مؤثر باشد. برای این منظور می‌توان از برنامه‌ی رایگان و معروف Open office استفاده کرد. توسط آن می‌توان یک فرم PDF را طراحی و سپس فیلدهای آن‌را (این قالب تهیه شده را) با iTextSharp پر کرد. این مورد می‌تواند برای تهیه گزارش‌هایی که تهیه آن‌ها با ابزارهای متداول گزارش سازی عموما میسر نیست،‌ بسیار مناسب باشد.


طراحی یک فرم PDF با استفاده از برنامه Open Office

آخرین نگارش برنامه Open office را از اینجا می‌توانید دریافت کنید و آنچنان حجمی هم ندارد؛ حدودا 154 مگابایت است.
پس از نصب و اجرای برنامه، حداقل به دو طریق می‌توان یک فرم جدید را شروع کرد:
الف) آغاز یک XML Form document جدید در Open office سبب خواهد شد که نوارهای ابزار طراحی فرم، مانند قرار دادن TextBox ، CheckBox و غیره به صورت خودکار ظاهر شوند.
ب) و یا آغاز یک سند معمولی و سپس مراجعه به منوی View->Toolbars->Form Controls هم همان حالت را به همراه خواهد داشت.


در اینجا برای طراحی یک گزارش یا فرم جدید تنها کافی است همانند روش‌های متداول تهیه یک سند معمولی رفتار کنیم و مواردی را که قرار است توسط iTextSharp مقدار دهی کنیم، با کنترل‌های نوار ابزار Form آن بر روی صفحه قرار دهیم که نمونه‌ی ساده آن‌را در شکل زیر ملاحظه می‌کنید:


برای گزارش‌های فارسی بهتر است Alignment یک کنترل به Right تنظیم شود و Border به حالت Without frame مقدار دهی گردد. نام این کنترل را هم بخاطر بسپارید و یا تغییر دهید. از این نام‌ها در iTextSharp استفاده خواهیم کرد. (صفحه خواص فوق با دوبار کلیک بر روی یک کنترل قرار گرفته بر روی فرم ظاهر می‌شود)

مرحله بعد، تبدیل این فرم به فایل PDF است. کلیک بر روی دکمه تهیه خروجی به صورت PDF در نوار ابزار اصلی آن برای اینکار کفایت می‌کند. این گزینه در منوی File نیز موجود است.


فرم‌های PDF تهیه شده در اینجا، فقط خواندنی هستند. مثلا یک کاربر می‌تواند آن‌ها را پر کرده و چاپ کند. اما ما از آن‌ها در ادامه به عنوان قالب گزارشات استفاده خواهیم کرد. بنابراین جهت ویرایش فرم‌های تهیه شده بهتر است فایل‌های اصلی Open Office مرتبط را نیز درجایی نگهداری کرد و هر بار پس از ویرایش، نیاز است تا خروجی جدید PDF آن‌ها تهیه شود.


استفاده از iTextSharp جهت مقدار دهی فیلدهای یک فرم PDF

در ادامه می‌خواهیم این قالب گزارشی را که تهیه کردیم با کمک iTextSharp پر کرده و یک فایل PDF جدید تهیه کنیم. سورس کامل اینکار را در ذیل مشاهده می‌کنید:


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

namespace PdfForm
{
class Program
{
//روش صحیح ثبت و معرفی فونت در این کتابخانه
public static iTextSharp.text.Font GetTahoma()
{
var fontName = "Tahoma";
if (!FontFactory.IsRegistered(fontName))
{
var fontPath = Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf";
FontFactory.Register(fontPath);
}
return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}

static void Main(string[] args)
{
string fileNameExisting = @"form.pdf";
string fileNameNew = @"newform.pdf";

using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
using (var stamper = new PdfStamper(pdfReader, newFileStream))
{
//نکته مهم جهت کار با اطلاعات فارسی
//در غیراینصورت شاهد ثبت اطلاعات نخواهید بود
stamper.AcroFields.AddSubstitutionFont(GetTahoma().BaseFont);

//form.Fields.Keys = تمام فیلدهای موجود در فرم
var form = stamper.AcroFields;

//مقدار دهی فیلدهای فرم
form.SetField("TextBox1", "مقدار1");
form.SetField("TextBox2", "مقدار2");

// "Yes" and "Off" are valid values here
form.SetField("Check Box 1", "Yes");

// "" and "Off" are valid values here
form.SetField("Option Button 1", "");

// نحوه مقدار دهی لیست
form.SetListOption("ListBox1", new[] { "1مقدار یک", "مقدار دو1" }, null);
form.SetField("ListBox1", null);

// به این ترتیب فرم دیگر توسط کاربر قابل ویرایش نخواهد بود
//stamper.PartialFormFlattening --> جهت غیرقابل ویرایش نمودن فیلدی مشخص
stamper.FormFlattening = true;

stamper.Close();
pdfReader.Close();
}
}

Process.Start("newform.pdf");
}
}
}

توضیحات:
چون در اینجا فایل PDF، از پیش تهیه شده است، پس باید از اشیاء PdfReader و PdfStamper جهت خواندن و نوشتن اطلاعات در آن‌ها استفاده کرد. سپس توسط شیء stamper.AcroFields می‌توان به این فیلدها یا همان کنترل‌هایی که در برنامه‌ی Open office بر روی فرم قرار دادیم، دسترسی پیدا کنیم.
در ابتدا نیاز است فونت این فیلدها توسط متد AddSubstitutionFont مقدار دهی شود. این مورد برای گزارش‌های فارسی الزامی است؛ در غیراینصورت متنی را در خروجی مشاهده نخواهید کرد.
ادامه کار هم مشخص است. توسط متد form.SetField مقداری را به کنترل‌های قرار گرفته بر روی فرم نسبت می‌دهیم. آرگومان اول آن نام کنترل است و آرگومان دوم، مقدار مورد نظر می‌باشد. اگر کنترل CheckBox را بر روی صفحه قرار دادید، تنها مقدارهای Yes و Off را می‌پذیرد (آن هم با توجه به اینکه به کوچکی و بزرگی حروف حساس است). اگر یک Radio button یا در اینجا Option button را بر روی فرم قرار دادید، تنها مقدارهای خالی و Off را قبول خواهد کرد. نحوه‌ی مقدار دهی یک لیست هم در اینجا ذکر شده است.
در پایان چون نمی‌خواهیم کاربر نهایی قادر به ویرایش اطلاعات باشد، FormFlattening را true خواهیم کرد و به این ترتیب، کنترل‌ها فقط خواندنی خواهند شد. البته اگر همانطور که ذکر شد، border کنترل‌ها را در حین طراحی حذف کنید، PDF نهایی تولیدی یکپارچه و یک دست به نظر می‌رسد و اصلا مشخص نخواهد بود که این فایل پیشتر یک فرم قابل پر کردن بوده است.

مطالب
توزیع کلاس های اندرویدی با استفاده از Gradle قسمت دوم
در مقاله قبل در مورد اینکه در پشت صحنه‌ی سیستم توزیع گریدل چه اتفاقاتی در حال رخ دادن است، توضیح دادیم. در این نوشتار سعی داریم به عنوان مثال کلاسی به اسم AndroidBreadCrumb را به این سرورها آپلود کنیم.

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

ابتدا نیاز است در سایت bintray ثبت نام کنید و با حساب جدید وارد شوید و گزینه‌ی maven را انتخاب کنید.

سپس روی گزینه‌ی Add New Package کلیک کنید تا یک پکیج جدید را ایجاد کنیم.

در صفحه‌ای که باز می‌شود، اطلاعات مربوط به این پکیج را وارد کنید که عموما شامل نام پکیج، مجوز آن، کلمات کلیدی، لینک گزارش باگ و .. می‌شود. در انتخاب نام پکیج، قانون اجباری یا خاصی وجود ندارد؛ ولی توصیه می‌شود که از حروف کوچک و - استفاده گردد. بعد از پرکردن فیلدهای الزامی، وارد صفحه‌ی جزئیات پکیج می‌شوید که در آن فیلدهای اضافه‌تری نیز وجود دارند که میتوانید در صورت تمایل آن‌ها را پر کنید. همچنین در بالای صفحه لینک به صفحه‌ی اختصاصی این پکیج نیز وجود دارد که در زیر عبارت Edit Package قرار گرفته است.

وارد صفحه‌ی اختصاصی پکیج شوید، می‌بینید که قسمت‌های مختلفی در اختیار شما قرار گرفته است و این مخزن الان آماده و پذیرای کتابخانه‌ی شماست. تا به اینجا، مرحله‌ی کار با jcenter تمام شد. حال به سایت SonaType می‌رویم و در آن هم ثبت نام می‌کنیم.


پی نوشت : اگر قصد آپلود کتابخانه‌ی خود را در این سایت ندارید، می‌توانید این سوال و مرحله‌ی امضای خودکار را از مراحل کاری خود حذف کنید.

سوال: چگونه این فایل را در SonaType آپلود کنیم؟ 
گام اول: ابتدا باید در سایت ثبت نام کنید. پس به این صفحه رفته و ثبت نام کنید. سپس در یک مرحله‌ی غیرمنطقی باید یک issue توسط سیستم JIRA ایجاد کنید. برای همین گزینه‌ی Creare را در بالای صفحه بزنید. اطلاعات زیر را به ترتیب پر کنید:
Project: Community Support - Open Source Project Repository Hosting

Issue Type: New Project

Summary: مثلا نام پروژه خودتان را بنویسید

یک نام پکیج که سعی کنید کتابخانه‌های هم خانواده این اشتراک را داشته باشند که در یک گروه قرار بگیرند
Group Id: AndroidBreadCrumb.Plus

آدرس جایی که پروژه قرار دارد
Project URL: https://github.com/yeganehaym/AndroidBreadCrumb

//آدرس سیستم کنترل نسخه
SCM url: https://github.com/yeganehaym/AndroidBreadCrumb
بقیه‌ی موارد الزامی نیست. کادر را تایید کنید. در این مرحله باید مدتی منتظر بمانید تا این مخزن را برای شما تایید کنند که طبق مستندات حدود یک هفته‌ای طول می‌کشد. بعد از اینکار باید نام کاربری اکانت خود را در پروفایل سایت Bintray  در برگه‌ی Accounts  قسمت sonatype oss account وارد کنید و پروفایل را آپدیت کنید.

فعال سازی امضای خودکار در Bintray

همانطور که در ابتدای مقاله گفتیم، می‌خواهیم کتابخانه‌ی خود را از طریق jcenter به maven ارسال کنیم. برای همین نیاز داریم که ابتدا کتابخانه‌ی خود را امضا کنیم. برای اینکار باید از طریق GPG یک کلید بسازیم. ساخت کلید به این شیوه، قبلا در مقاله‌ی «ساخت کلیدهای امنیتی با GnuPG» توضیح داده شد و از تکرار آن خودداری می‌کنیم. تنها به ذکر این نکته بسنده می‌کنیم که شما باید یک کلید ساخته و آن را به سرور کلیدها ارسال کنید و سپس کلید متنی عمومی و خصوصی آن را در پروفایل bintray برگه‌ی GPG Signing درج کنید.


به عنوان آخرین کار باید امضای خودکار را فعال کنید. بنابراین نیاز است که به صفحه‌ی اصلی رفته و بر روی maven کلیک کنید و گزینه‌ی Edit را انتخاب کنید. در صفحه‌ی باز شده، تیک GPG sign uploaded files automatically را بزنید.

این تنظیم از این پس بر روی تمامی کتابخانه‌ها اعمال می‌شود.


سوال : چگونه پروژه‌ی اندرویدی خودم را کامپایل کنم؟


فایل  build.gradle پروژه را باز کنید و پلاگین bintray را به آن معرفی کنید:
 dependencies {
        classpath 'com.android.tools.build:gradle:1.2.2'
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
        classpath 'com.github.dcendents:android-maven-plugin:1.2'
    }
بسیار مهم است که نسخه‌ی gradle build tools از نسخه‌ی 1.1.2 به بعد باشد. چون که در نسخه‌های پایین‌تر، یک باگ بسیار حیاتی یافت شده، که از این نسخه به بعد رفع گردیده است. حال فایل local.properties را باز کنید و اطلاعات زیر را وارد کنید:
bintray.user=YOUR_BINTRAY_USERNAME
bintray.apikey=YOUR_BINTRAY_API_KEY
bintray.gpg.password=YOUR_GPG_PASSWORD
نام کاربری که برای اکانت bintray انتخاب کردید را بنویسید. برای ApiKey در قسمت Edit Profile برگه‌ی Api Key در خط دوم می‌توانید آن را ببینید و کلمه‌ی عبور GPG هم همان عبارتی است که در انتهای ساخت کلید دوبار از شما پرسیده شده است.  (در صورتی که اقدامات لازم برای mavenCentral را انجام داده اید خط سوم را  وارد کنید)

در مرحله‌ی بعدی خطوط زیر را بعد از 'Apply Plugin 'com.android.library اضافه کنید و اطلاعاتی که در bintray وارد کرده‌اید را در اینجا وارد کنید:
apply plugin: 'com.android.library'

ext {
    bintrayRepo = 'maven'
    bintrayName = 'AndroidBreadCrumb'

    publishedGroupId = 'com.plus'
    libraryName = 'AndroidBreadCrumb'
    artifact = 'AndroidBreadCrumb'

    libraryDescription = 'create breadcrumb on android to show a path to user and let user to jump on them'

    siteUrl = 'https://github.com/yeganehaym/AndroidBreadCrumb'
    gitUrl = 'https://github.com/yeganehaym/AndroidBreadCrumb'

    libraryVersion = '1.0'

    developerId = 'yeganehaym'
    developerName = 'ali yeganeh.m'
    developerEmail = 'yeganehaym@gmail.com'

    licenseName = 'The Apache Software License, Version 2.0'
    licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
    allLicenses = ["Apache-2.0"]
}
بعد از آن هم در انتهای فایل گریدل ماژول، دو خط زیر را که شامل اسکریپت‌هایی برای راحتی کار است، معرفی کنید:
apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
با اطلاعاتی که بالا تعریف کردیم، کاربر باید شناسه‌ای به شکل زیر در گریدل به کار ببرد تا به کتابخانه‌ی ما دسترسی داشته باشد:
compile 'com.plus:AndroidBreadCrumb:1.0'

آپلود فایل‌ها به مخزن
برای آپلود فایل‌های ماژول به مخزن، ابتدا ترمینال اندروید استودیو را باز کنید و گام‌های زیر را به ترتیب انجام بدهید:
گام اول: با ارسال دستور زیر از صحت کدها و منابع مطمئن می‌شویم:
gradlew install
در صورتیکه خطایی نباشد، پیام موفقیت آمیزی را به شما نمایش می‌دهد:
BUILD SUCCESSFUL
حالا باید فایل‌ها را به سمت سرور ارسال کنیم:
gradlew bintrayUpload
در صورتی که عملیات موفقیت آمیز باشد پیام زیر نمایش می‌یابد:
SUCCESSFUL

حال صفحه‌ی اختصاصی پکیج‌تان را چک کنید. می‌بینید که قسمت‌هایی از آن تغییر کرده‌است و قسمت نسخه‌، به روز شده است:



و قسمت فایل‌ها هم دیگر خالی نیست:




با اینکه کتابخانه‌ی ما روی maven قرار گرفت، ولی هنوز نمی‌توان آن را توسط jcenter استفاده کرد و باید bintray maven را با jcenter هماهنگ نماییم. در حال حاضر استفاده از این کتابخانه بدون سینک به شکل زیر است:

گریدل پروژه
     maven{
            url 'https://dl.bintray.com/yeganehaym/maven'
        }

گریدل ماژول

dependencies {
    compile 'com.plus:AndroidbreadCrumb:1.0'
}
آدرس اولی را می‌توانید با نگاه در صفحه‌ی اختصاصی پکیج ببینید که لینک آن در جلوی نام پکیج وجود دارد و قابلیت کپی کردن لینک هم وجود دارد. اگر روی آن کلیک کنید، می‌توانید مسیر را در صفحه‌ی باز شده ببینید. البته راه مستقیم‌تر اینکه به جای yeganehaym در لینک بالا، نام کاربری خودتان را جایگزین کنید.

برای افزودن کتابخانه‌ی خود به سیستم jcenter با کلیک بر روی گزینه‌ی Add to jcenter می‌توانید به تیم jcenter درخواست دهید که آن را تایید کنند که بعد از درخواست حدود سه ساعت طول می‌کشد تا پاسخ شما را بدهند.




به این ترتیب دیگر نیازی به تعریف یک url به maven نخواهد بود.

برای دیدن این کتابخانه در صفحه jcenter به ترتیب شناسه‌های Group_ID.Artifact.version را دنبال کنید، یعنی برای ما می‌شود:

com/plus/androidbreadcrumb/1.0
نکته اول : این نکته را به یاد داشته باشید که عمل سینک تنها یکبار اتفاق می‌افتد و در زمان‌های بعد، هر تغییری مثل حذف، به روز آوری و ... مستقیما در jcenter بعد از چند دقیقه اعمال می‌شود.

نکته دوم: در صورتی که پکیج خودتان را حذف کنید، چیزی از روی jcenter حذف نمی‌شود. فقط به یاد داشته باشید که برای حذف آن باید ابتدا نسخه‌های مختلف آپلود شده را حذف کنید تا پکیج از جی سنتر هم حذف شود.


ارسال کتابخانه به mavenCentra
 در این مرحله قصد داریم که این کتابخانه را بر روی mavenCentral هم داشته باشیم. اگر قصدش را ندارید از اینجا به بعد را نیازی نیست انجام بدهید و برای اینکار لازم است همه‌ی مراحل بالا انجام گرفته باشد.
قبل از اینکه این عمل ارسال انجام گیرد، باید دو عمل زیر از قبل صورت گرفته باشند:
  1. پکیج شما در jcenter تایید شده باشد.
  2. با مخزن شما در sonatype موافقت شده باشد.

در صورتیکه دو مرحله‌ی بالا صورت گرفته باشند، در صفحه‌ی پکیج اختصاصی، بر روی گزینه‌ی mavenCentral کلیک کنید:

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

در صورتیکه پیام موفقیت در سینک را بدهد، پکیج شما منتقل شده‌است. در غیر این صورت خطای آن را اعلام می‌کند و باید برای رفع آن تلاش کنید تا خطاها از بین بروند. برای اینکه بتوانید این پکیج را در لیست mavenCentral ببینید، مثل همان چیزی که در بالاتر گفته شد، شناسه‌ی گریدل را دنبال کنید.

اشتراک‌ها
تمرکز اصلی در امکانات پیش روی Visual Studio بر DotNET Core میباشد

.NET Core, the reinvention of the Microsoft .NET Framework as an open source, cross-platform development choice, is a key focus of the upcoming features planned for the Visual Studio IDE.

In conjunction with .NET Standard -- a spec detailing what .NET APIs should be available on all .NET implementations -- .NET Core retains compatibility with .NET Framework, Xamarin and Mono while letting developers use Windows, macOS or Linux machines to code for mobile, cloud and embedded/IoT projects.

While still primarily driven by Microsoft dev teams, .NET Core is hosted on a GitHub repository and is supported by the .NET Foundation, an independent organization created by Microsoft three years ago to improve its open source development and collaboration.

Peeking at the Visual Studio Roadmap (updated last week) reveals .NET Core figures prominently in upcoming functionality for the IDE, both in the near-term and long:  

تمرکز اصلی در امکانات پیش روی Visual Studio بر DotNET Core میباشد
مطالب
معرفی یک ابزار گزارشگیری رایگان مخصوص WPF

تا صحبت از گزارشگیری به میان بیاید احتمالا معرفی ابزارهای تجاری مانند Reporting services ، کریستال ریپورت، stimulsoft.com ، fast-report.com و امثال آن درصدر لیست توصیه کنندگان و مشاوران قرار خواهند داشت. اما خوب برای ایجاد یک گزارشگیری ساده حتما نیازی نیست تا به این نوع ابزارهای تجاری مراجعه کرد. ابزار رایگان و سورس باز جالبی هم در این باره جهت پروژه‌های WPF در دسترس است:



در ادامه در طی یک مثال قصد داریم از این کتابخانه استفاده کنیم:

1) تنظیم وابستگی‌ها
پس از دریافت کتابخانه فوق، ارجاعات زیر باید به پروژه شما اضافه شوند:
CodeReason.Reports.dll (از پروژه فوق) و ReachFramework.dll (جزو اسمبلی‌های استاندارد دات نت است)

2) تهیه منبع داده‌ گزارش
کتابخانه‌ی فوق به صورت پیش فرض با DataTable‌ کار می‌کند. بنابراین کوئری‌های شما یا باید خروجی DataTable داشته باشد یا باید از یک سری extension methods برای تبدیل IEnumerable به DataTable استفاده کرد (در پروژه پیوست شده در پایان مطلب، این موارد موجود است).
برای مثال فرض کنید می‌خواهیم رکوردهایی را از نوع کلاس Product زیر در گزارش نمایش دهیم:

namespace WpfRptTests.Model
{
public class Product
{
public string Name { set; get; }
public int Price { set; get; }
}
}
3) تعریف گزارش
الف) اضافه کردن فایل تشکیل دهنده ساختار و ظاهر گزارش
گزارش‌‌های این کتابخانه مبتنی است بر اشیاء FlowDocument استاندارد WPF . بنابراین از منوی پروژه گزینه‌ی Add new item در قسمت WPF آن یک FlowDocument جدید را به پروژه اضافه کنید ( باید دقت داشت که Build action این فایل باید به Content تنظیم گردد). ساختار ابتدایی این FlowDocument به صورت زیر خواهد بود که به آن FlowDirection و FontFamily مناسب جهت گزارشات فارسی اضافه شده است. همچنین فضای نام مربوط به کتابخانه‌ی گزارشگیری CodeReason.Reports نیز باید اضافه گردد.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="RightToLeft" FontFamily="Tahoma"
xmlns:xrd="clr-namespace:CodeReason.Reports.Document;assembly=CodeReason.Reports"
PageHeight="29.7cm" PageWidth="21cm" ColumnWidth="21cm">

</FlowDocument>

مواردی که در ادامه ذکر خواهند شد محتوای این گزارش را تشکیل می‌دهند:
ب) مشخص سازی خواص گزارش

<xrd:ReportProperties>
<xrd:ReportProperties.ReportName>SimpleReport</xrd:ReportProperties.ReportName>
<xrd:ReportProperties.ReportTitle>گزارش از محصولات</xrd:ReportProperties.ReportTitle>
</xrd:ReportProperties>
در اینجا ReportName و ReportTitle باید مقدار دهی شوند (دو dependency property که در کتابخانه‌ی CodeReason.Reports تعریف شده‌اند)

ج) مشخص سازی Page Header و Page Footer
اگر می‌خواهید عباراتی در بالا و پایین تمام صفحات گزارش تکرار شوند می‌توان از SectionReportHeader و SectionReportFooter این کتابخانه به صورت زیر استفاده کرد:
    <xrd:SectionReportHeader PageHeaderHeight="2" Padding="10,10,10,0" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportHeader>

<xrd:SectionReportFooter PageFooterHeight="2" Padding="10,0,10,10" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
نام کاربر:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
صفحه
<xrd:InlineContextValue PropertyName="PageNumber" FontWeight="Bold" /> از
<xrd:InlineContextValue PropertyName="PageCount" FontWeight="Bold" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportFooter>

دو نکته در اینجا حائز اهمیت هستند: xrd:InlineDocumentValue و xrd:InlineContextValue
InlineDocumentValue را می‌توان در کد‌های برنامه به صورت سفارشی اضافه کرد. بنابراین هر جایی که نیاز بود مقدار ثابتی از طریق کد نویسی به گزارش تزریق و اضافه شود می‌توان از InlineDocumentValue استفاده کرد. برای مثال در کدهای ViewModel برنامه که در ادامه ذکر خواهد شد دو مقدار PrintDate و RptBy به صورت زیر تعریف و مقدار دهی شده‌اند:
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");
برای مشاهده مقادیر مجاز مربوط به InlineContextValue به فایل ReportContextValueType.cs سورس کتابخانه مراجعه کنید که شامل PageNumber, PageCount, ReportName, ReportTitle است و توسط CodeReason.Reports به صورت پویا تنظیم خواهد شد.

د) مشخص سازی ساختار تولیدی گزارش

<Section Padding="80,10,40,10" FontSize="12">
<Paragraph FontSize="24" TextAlignment="Center" FontWeight="Bold">
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
<Paragraph TextAlignment="Center">
گزارش از لیست محصولات در تاریخ:
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
توسط:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
<xrd:SectionDataGroup DataGroupName="ItemList">
<Table CellSpacing="0" BorderBrush="Black" BorderThickness="0.02cm">
<Table.Resources>
<!-- Style for header/footer rows. -->
<Style x:Key="headerFooterRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Background" Value="LightGray"/>
</Style>

<!-- Style for data rows. -->
<Style x:Key="dataRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontSize" Value="12"/>
</Style>

<!-- Style for data cells. -->
<Style TargetType="{x:Type TableCell}">
<Setter Property="Padding" Value="0.1cm"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="0.01cm"/>
</Style>
</Table.Resources>

<Table.Columns>
<TableColumn Width="0.8*" />
<TableColumn Width="0.2*" />
</Table.Columns>
<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>نام محصول</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>قیمت</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

<TableRowGroup Style="{StaticResource dataRowStyle}">
<xrd:TableRowForDataTable TableName="Product">
<TableCell>
<Paragraph>
<xrd:InlineTableCellValue PropertyName="Name" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<xrd:InlineTableCellValue PropertyName="Price" AggregateGroup="Group1" />
</Paragraph>
</TableCell>
</xrd:TableRowForDataTable>
</TableRowGroup>

<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Right">
<Bold>جمع کل</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" />
</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

</Table>

<Paragraph TextAlignment="Center" Margin="5">
در این گزارش
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Count"
EmptyValue="هیچ"
FontWeight="Bold" /> محصول با جمع کل قیمت
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" /> وجود دارند.
</Paragraph>
</xrd:SectionDataGroup>
</Section>
برای اینکه بتوان این قسمت‌ها را بهتر توضیح داد، نیاز است تا تصاویر مربوط به خروجی این گزارش نیز ارائه شوند:




در ابتدا توسط دو پاراگراف، عنوان گزارش و یک سطر زیر آن نمایش داده شده‌اند. بدیهی است هر نوع شیء و فرمت مجاز در FlowDocument را می‌توان در این قسمت نیز قرار داد.
سپس یک SectionDataGroup جهت نمایش لیست آیتم‌ها اضافه شده و داخل آن یک جدول که بیانگر ساختار جدول نمایش رکوردهای گزارش می‌باشد، ایجاد گردیده است.
سه TableRowGroup در این جدول تعریف شده‌اند.
TableRowGroup های اولی و آخری دو سطر اول و آخر جدول گزارش را مشخص می‌کنند (سطر عناوین ستون‌ها در ابتدا و سطر جمع کل در پایان گزارش)
از TableRowGroup میانی برای نمایش رکوردهای مرتبط با نام جدول مورد گزارشگیری استفاده شده است. توسط TableRowForDataTable آن نام این جدول باید مشخص شود که در اینجا همان نام کلاس مدل برنامه است. به کمک InlineTableCellValue، خاصیت‌هایی از این کلاس را که نیاز است در گزارش حضور داشته باشند، ذکر خواهیم کرد. نکته‌ی مهم آن AggregateGroup ذکر شده است. توسط آن می‌توان اعمال جمع، محاسبه تعداد، حداقل و حداکثر و امثال آن‌را که در فایل InlineAggregateValue.cs سورس کتابخانه ذکر شده‌اند، به فیلدهای مورد نظر اعمال کرد. برای مثال می‌خواهیم جمع کل قیمت را در پایان گزارش نمایش دهیم به همین جهت نیاز بود تا یک AggregateGroup را برای این منظور تعریف کنیم.
از این AggregateGroup در سومین TableRowGroup تعریف شده به کمک xrd:InlineAggregateValue جهت نمایش جمع نهایی استفاده شده است.
همچنین اگر نیاز بود در پایان گزارش اطلاعات بیشتری نیز نمایش داده شود به سادگی می‌توان با تعریف یک پاراگراف جدید، اطلاعات مورد نظر را نمایش داد.

4) نمایش گزارش تهیه شده
نمایش این گزارش بسیار ساده است. View برنامه به صورت زیر خواهد بود:
<Window x:Class="WpfRptTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CodeReason.Reports.Controls;assembly=CodeReason.Reports"
xmlns:vm="clr-namespace:WpfRptTests.ViewModel"
Title="MainWindow" WindowState="Maximized" Height="350" Width="525">
<Window.Resources>
<vm:ProductViewModel x:Key="vmProductViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vmProductViewModel}}">
<c:BusyDecorator IsBusyIndicatorHidden="{Binding RptGuiModel.IsBusyIndicatorHidden}">
<DocumentViewer Document="{Binding RptGuiModel.Document}" />
</c:BusyDecorator>
</Grid>
</Window>

تعریف ابتدایی RptGuiModel به صورت زیر است (جهت مشخص سازی مقادیر IsBusyIndicatorHidden و Document در حین بایندینگ اطلاعات):

using System.ComponentModel;
using System.Windows.Documents;

namespace WpfRptTests.Model
{
public class RptGuiModel
{
public IDocumentPaginatorSource Document { get; set; }
public bool IsBusyIndicatorHidden { get; set; }
}
}
و این View اطلاعات خود را از ViewModel زیر دریافت خواهد نمود:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using CodeReason.Reports;
using WpfRptTests.Helper;
using WpfRptTests.Model;

namespace WpfRptTests.ViewModel
{
public class ProductViewModel
{
#region Constructors (1)

public ProductViewModel()
{
RptGuiModel = new RptGuiModel();
if (Stat.IsInDesignMode) return;
//انجام عملیات نمایش گزارش در یک ترد دیگر جهت قفل نشدن ترد اصلی برنامه
showReportAsync();
}

#endregion Constructors

#region Properties (1)

public RptGuiModel RptGuiModel { set; get; }

#endregion Properties

#region Methods (3)

// Private Methods (3)

private static List<Product> getProducts()
{
var products = new List<Product>();
for (var i = 0; i < 100; i++)
products.Add(new Product { Name = string.Format("Product{0}", i), Price = i });

return products;
}

private void showReport()
{
try
{
//Show BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = false;

var reportDocument =
new ReportDocument
{
XamlData = File.ReadAllText(@"Report\SimpleReport.xaml"),
XamlImagePath = Path.Combine(Environment.CurrentDirectory, @"Report\")
};

var data = new ReportData();

// تعریف متغیرهای دلخواه و مقدار دهی آن‌ها
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");

// استفاده از یک سری اطلاعات آزمایشی به عنوان منبع داده
data.DataTables.Add(getProducts().ToDataTable());

var xps = reportDocument.CreateXpsDocument(data);
//انقیاد آن به صورت غیر همزمان در ترد اصلی برنامه
DispatcherHelper.DispatchAction(
() => RptGuiModel.Document = xps.GetFixedDocumentSequence()
);
}
catch (Exception ex)
{
//وجود این مورد ضروری است زیرا بروز استثناء در یک ترد به معنای خاتمه آنی برنامه است
//todo: log errors
}
finally
{
//Hide BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = true;
}
}

private void showReportAsync()
{
var thread = new Thread(showReport);
thread.SetApartmentState(ApartmentState.STA); //for DocumentViewer
thread.Start();
}

#endregion Methods
}
}

توضیحات:
برای اینکه حین نمایش گزارش، ترد اصلی برنامه قفل نشود، از ترد استفاده شد و استفاده ترد به همراه DocumentViewer کمی نکته دار است:
- ترد تعریف شده باید از نوع STA باشد که در متد showReportAsync مشخص شده است.
- حین بایندیگ Document تولید شده توسط کتابخانه‌ی گزارشگیری به خاصیت Document کنترل، حتما باید کل عملیات در ترد اصلی برنامه صورت گیرد که سورس کلاس DispatcherHelper را در فایل پیوست خواهید یافت.

کل عملیات این ViewModel در متد showReport رخ می‌دهد، ابتدا فایل گزارش بارگذاری می‌شود، سپس متغیرهای سفارشی مورد نظر تعریف و مقدار دهی خواهند شد. در ادامه یک سری داده آزمایشی تولید و به DataTables گزارش ساز اضافه می‌شوند. در پایان XPS Document متناظر آن تولید شده و به کنترل نمایشی برنامه بایند خواهد شد.

دریافت سورس این مثال

مطالب
آشنایی و استفاده از WCF Data Services در Visualstudio 2012
مقدمه:

WCF Data Services جزئی از NET Framework است که امکان ایجاد سرویس دهنده‌های با قرارداد OData را به روی وب یا Intranet با استفاده از REST مهیا می‌سازد. OData از داده هایی که با Url آدرس پذیر هستند استفاده می‌نماید. دسترسی و تغییر داده‌ها با استفاده از استاندارد HTTP و کلمات GET، PUT، POST و DELETE صورت می‌پذیرد. برای اینکه درک بهتری داشته باشید به یک مثال می‌پردازیم.

ایجاد یک برنامه سرویس دهنده WCF Data Service در 2012 VisualStudio
  1. یک ASP.NET Web Application با نام NorthwindService ایجاد نمایید و بر روی پروژه راست کلیک کنید و از منوی Add گزینه New Item را انتخاب نمایید  از پنجره باز شده از دسته Data گزینه ADO.NET Entity Data Model  را انتخاب و نام ان را Northwind بگذارید.
  2. از پنجره باز شده Generate from Databaseرا انتخاب و با انتخاب کانکشن از نوع Sql Server Compact 4  اتصال به فایل Northwind.sdf را انتخاب تا کلاس‌های لازم تولید شود.
  3. برای تولید data service بر روی پروژه راست کلیک کنید و از منوی Add گزینه New Item را انتخاب نمایید از پنجره باز شده گزینه  WCF Data Service را انتخاب و نام آن را Northwind.svc بگذارید. کد زیر خودکار تولید می‌شود
     public class Northwind : DataService< /* TODO: put your data source class name here */ >
        {
            // This method is called only once to initialize service-wide policies.
            public static void InitializeService(DataServiceConfiguration config)
            {
                // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
                // Examples:
                // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
                // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
            }
        }
  4. برای دسترسی به موجودیت‌های Northwind بجای عبارت put your data source نام مدل را تایپ کنید
    public class Northwind : DataService<NorthwindEntities>
  5. برای فعال کردن دسترسی به منابع data source متغیر config کلاس DataServiceConfiguration را بصورت زیر تنظیم نمایید. تابع SetEntitySetAccessRule با گرفتن نام موجودیت و نحوه دسترسی امکان استفاده از این موجودیت را با استفاده از WCF Data Service فزاهم می‌نمایید. مثلا در زیر امکان دسترسی به موجودیت Orders را با امکان خواندن همه، نوشتن ادقامی و جایگزین فراهم نموده است.
    config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead 
         | EntitySetRights.WriteMerge 
         | EntitySetRights.WriteReplace );
    config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
  6. اگر بخواهیم امکان خواندن همه موجودیت‌ها را فراهم کنیم از کد زیر می‌توانیم استفاده نمایید که * به معنای همه موجودیت‌های data model می‌باشد
    config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

دسترسی به WCF Data Service بوسیله مرورگر وب
برای دسترسی به وب سرویس برنامه را اجرا نمایید تا آدرس http://localhost:8358/Northwind.svc مشخصات وب سرویس را نمایش دهد
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="http://localhost:8358/Northwind.svc/">
<workspace>
<atom:title>Default</atom:title>
<collection href="Categories">
<atom:title>Categories</atom:title>
</collection>
<collection href="Customers">
<atom:title>Customers</atom:title>
</collection>
<collection href="Employees">
<atom:title>Employees</atom:title>
</collection>
<collection href="Order_Details">
<atom:title>Order_Details</atom:title>
</collection>
<collection href="Orders">
<atom:title>Orders</atom:title>
</collection>
<collection href="Products">
<atom:title>Products</atom:title>
</collection>
<collection href="Shippers">
<atom:title>Shippers</atom:title>
</collection>
<collection href="Suppliers">
<atom:title>Suppliers</atom:title>
</collection>
</workspace>
</service>
 حال اگر آدرس را به http://localhost:8358/Northwind.svc/Products وارد نمایید لیست کالا‌ها بصورت Atom xml قابل دسترس می‌باشد.
ایجاد یک برنامه گیرنده WCF Data Service در Visual Studio 2012
  1. بر روی Solution پروژه جاری راست کلیک و از منوی Add گزینه New Project را انتخاب و یک پروژه از نوع WPF Application با نام NorthwindClient ایجاد نمایید.
  2. در پنجره MainWindow مانند کد زیر از یک Combobox و DataGrid برای نمایش اطلاعات استفاده نمایید
        <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Northwind Orders" Height="335" Width="425" 
            Name="OrdersWindow" Loaded="Window1_Loaded">
        <Grid Name="orderItemsGrid">
            <ComboBox DisplayMemberPath="Order_ID" ItemsSource="{Binding}"
                      IsSynchronizedWithCurrentItem="true" 
                      Height="23" Margin="92,12,198,0" Name="comboBoxOrder" VerticalAlignment="Top"/>
            <DataGrid ItemsSource="{Binding Path=Order_Details}"  
                      CanUserAddRows="False" CanUserDeleteRows="False"  
                      Name="orderItemsDataGrid" Margin="34,46,34,50"
                      AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTextColumn  Header="Product" Binding="{Binding Product_ID, Mode=OneWay}" />
                    <DataGridTextColumn  Header="Quantity" Binding="{Binding Quantity, Mode=TwoWay}" />
                    <DataGridTextColumn  Header="Price" Binding="{Binding UnitPrice, Mode=TwoWay}" />
                    <DataGridTextColumn  Header="Discount" Binding="{Binding Discount, Mode=TwoWay}" />                
                </DataGrid.Columns>     
            </DataGrid>
            <Label Height="28" Margin="34,12,0,0" Name="orderLabel" VerticalAlignment="Top" 
                   HorizontalAlignment="Left" Width="65">Order:</Label>
            <StackPanel Name="Buttons" Orientation="Horizontal" HorizontalAlignment="Right" 
                        Height="40" Margin="0,257,22,0">
                <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" 
                    Name="buttonSave" VerticalAlignment="Bottom" Width="75" 
                        Click="buttonSaveChanges_Click">Save Changes
                </Button>
                <Button Height="23" Margin="0,0,12,12" 
                    Name="buttonClose" VerticalAlignment="Bottom" Width="75" 
                        Click="buttonClose_Click">Close</Button>
            </StackPanel>
        </Grid>
    </Window>

  3. برای ارجاع به wcf data service بر روی پروژه راست کلیک و گزینه Add Service Reference را انتخاب نمایید در پنجره باز شده گزینه Discover را انتخاب تا سرویس را یافته و نام Namespase را Northwind بگذارید.
  4. حال مانند کد زیر یک شی از مدل NorthwindEntities با آدرس وب سرویس ایجاد نموده ایم و نتیحه کوئری با استفاده از کلاس DataServiceCollection به DataContext گرید انتصاب داده ایم که البته پیش فرض آن آشنایی با DataBinding در WPF است.
            private NorthwindEntities context;
            private string customerId = "ALFKI";
            private Uri svcUri = new Uri("http://localhost:8358/Northwind.svc");
    
            private void Window1_Loaded(object sender, RoutedEventArgs e)
            {
                try
                {
                    context = new NorthwindEntities(svcUri);
                    var ordersQuery = from o in context.Orders.Expand("Order_Details")
                                      where o.Customers.Customer_ID == customerId
                                      select o;
                    DataServiceCollection<Orders> customerOrders = new DataServiceCollection<Orders>(ordersQuery);
                    this.orderItemsGrid.DataContext = customerOrders;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
    
  5. با صدا زدن تابع SaveChanges مدل می‌توانید تغییرات را در پایگاه داده ذخیره نمایید.
    private void buttonSaveChanges_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            context.SaveChanges();
        }
        catch (DataServiceRequestException ex)
        {
            MessageBox.Show(ex.ToString());
    
        }
    }

  1. برنامه را اجرا نمایید تا خروجی کار را مشاهده نمایید. مقادیر Quantity را تغییر دهید و دکمه Save Changes را انتخاب تا تغییرات دخیره شود.
در اینجا در یک برنامه ویندوزی استفاده از WCF Data Service را تست نمودیم اما براحتی به همین شیوه در یک برنامه وب نیز قابل استفاده است.