نظرات مطالب
سفارشی سازی Header و Footer در PdfReport
سلام
من نیاز دارم تا در Page Header و Group Header از قالب تهیه شده توسط Open Office استفاده کنم. قالب هایی که تهیه شدن، یه سری فیلد دارن که موقع ساخت گزارش باید پر بشن.
چطور باید فیلدهای موجود در قالب رو بعد از لود مقدار دهی کرد؟
مطالب
شروع کار با Apache Cordova در ویژوال استودیو #2
در قسمت اول، با Apache Cordova  آشنا شدیم. در این قست قصد دارم در مورد Phonegap, معایب و مزایای Cordova و روش نصب و راه اندازی آن را بر روی Visual Studio، خدمت شما ارائه دهم. 

توضیح مختصری در مورد  Adobe Phonegap
در حوالی سال 2009 ، phonegap به‌واسطه‌ی استارت آپی بنام Nitobi با هدف ایجاد یک راه حل سورس باز  برای  ساخت اپلیکیشن‌های بومی موبایل با تکنولوژی‌های تحت وب، تولید شد. شرکت Adobe در حوالی سال 2011 ، Notobi را به همرا حق مالکیت phonegap خریداری کرد و هسته‌ی سورس باز آن را با نام Cordova به شرکت Apache اهدا کرد. نسبت بین Cordova و phonegap مانند نسبت بین مرورگر Blink و کروم است. در واقع phonegap ترکیبی‌است از Cordova و یک سری امکانات اضافه‌ی شرکت Adobe. تفاوت اصلی بین Cordova و Phonegap مربوط است به ابزارهای Command-Line و سرویس Build فون گپ است که در مقالات بعدی به آنها خواهیم پرداخت.
بیشتر : اینجا و اینجا

 مزایای استفاده از Cordova:
  • محیط برنامه نویسی قدرتمند
  • هسته اصلی  کد‌های همه اپلیکیشن‌ها  تولید شده شبیه به هم است
  • نیازی به یادگیری زبان‌های مربوط به هر پلتفرم را ندارید
  • کم هزینه و زمان کمتر
  • طراحی رابط گرافیکی سریع و منعطف به کمک HTML5 , CSS3 
  • برنامه نویسی آسان و سریع با javascript , Typescript
  • قابلیت اجرا بر روی چندین پلتفرم مختلف(Android,iOS,Widnows Phone )
  • قابلیت استفاده از فریم‌ورکهای تحت وب مانند Bootstrap , Angular JS, ...
  • قابلیت طراحی پلاگین برای ارتباط با سیستم عامل
  • مناسب برای برای برنامه‌های چت و استفاد از وب سرویس‌ها
  • مناسب برای ساخت بازیهای آنلاین و آفلاین با تکنولوژی‌های تحت وب
  • راحتی کار با آن برای برنامه نویسان تحت وب 

معایب  استفاده از Cordova  :
  • نداشتن ابزار گزارش خطاهای مناسب؛ درنتیجه برطرف کردن خطاها خسته کننده خواهد بود .
  • UI, UX اپلیکیشن‌ها باید به نحوی باشد که کاربر حس کند با نرم‌افزارهای بومی گوشی کار می‌کند.
  • کاهش سرعت اجرایی جزئی نسبت به سایر برنامه‌ها (به دلیل استفاده از WebView)
  • عدم دسترسی مستقیم به سیستم عامل و امکانات آن 

نصب اتوماتیک وابستگی ها
ابزارهایی که ما نیاز داریم:

لازم است تا Visual Studio 2013، با حداقل آپدیت 2 بر روی سیستم شما نصب باشد.

بعد از اتمام دانلود فایل، اقدام به نصب آن نمایید. در این حین، یک سری وابستگی‌های مربوط به خود را دانلود و نصب خواهد کرد. لیست وابستگی ها:
  • Node.js
  • Git CLI
  • Google Chrome
  • Apache Ant
  • Oracle Java JDK 7 (حتما نسخه x86 نصب شود)
  • Android SDK 
  • SQLLite For Windows Runtime
  • Apple iTunes
فایل نصاب، همه‌ی این وابستگی‌ها را به‌غیر از Android SDK، نصب می‌کند. 
در آخر هم سیستم خود را راستارت کنید.

نصب دستی وابستگی‌ها:
اگر به هر دلیلی در نصب خودکار این وابستگی‌ها  توسط نصاب با مشکل بر خورد کردید، می‌توانید تک تک آنها را دانلود کرده و نصب کنید. لینک‌های مورد نظر را هم به همین دلیل قرار دادم. 
  1. node.js را از لینک مقابل دانلود کنید:  اینجا  (پیشنهاد می‌کنیم نسخه‌ی x86 آن را نصب کنید)
  2. Google Chrome را نصب کنید
  3. Git Command Line Tools را نصب کنید و توجه کنید که در هنگام نصب، گزینه مربوط به افزودن Git را به مسیر  Command Prompt  شما، انتخاب کرده باشید.
  4. Apchage Ant را  دانلود  و در مسیری از سیستم خودتان قرار دهید. 
  5. Java JDK 7 x86 را از لینک مشخص شده دانلود کنید و سپس عملیات نصب را انجام دهید.
  6. Android SDK را از آدرس مشحص شده دانلود کنید. پکیچ‌های مورد نیاز، به این SDK افزوده شده است. بعد از دانلود آن را در مسیری از سیستم خود قرار دهید.
  7. Apple iTunes و  SQLite را دانلود و نصب کنید.
  8. اگر از ویندوز 7 استفاده می‌کنید ، WebSocket4Net را از لینک مقابل دانلود کنید ( اینجا ) و سپس  فایل net45\Release\WebSocket4Net.dll  در مسیر زیر کپی کنید:
%ProgramFiles(x86)%\Microsoft Visual Studio 12.0\Common7\IDE\CommonExtensions\Microsoft\WebClient\Diagnostics\ToolWindows 


ویژوال استودیو  پیکربندی‌های مربوط به نرم افزار‌های  thrid-party (سوم شخص/ثالث: نرم افزارهایی که برای دستکاری بر روی سیستم عامل، توسط شرکت‌هایی غیر از شرکت‌های تولید کننده سیستم عامل تولید می‌شوند) را که شما نصب کرده‌اید، تشخیص می‌دهد و مسیر‌های نصب آنها را درون متغیر‌های محیطی (environment variables)  به شکل زیر نگه می‌دارد:
ADT_HOME :به مسیر نصب اندروید اشاره می‌کند
ANT_HOME: به فولدری که Apache Ant در آن قرار دارد اشاره می‌کند
JAVA_HOME: به مسیر نصب جاوا اشاره می‌کند
GIT_HOME: به مسیر نصب GIT اشاره می‌کند.
دقت کنید باید نام‌های متغیر‌ها، دقیقا به همین نام‌ها باشند.
برای تنظیم این متغیر‌ها، به مسیر Control Panel\System and Security\System وارد شده و گزینه‌ی Advanced System Setting را انتخاب کنید. سپس در پنجره‌ی باز شده گزینه‌ی Environment Variables را انتخاب کنید و در قست system variables، این 4 متغیری که ذکر شد را ایجاد کنید. سپس نیاز است این مسیر‌ها را به system path اضافه کنید. برای این کار از همان قسمت system variables متغییر path را انتخاب کرده و گزینه‌ی ویرایش را بزنید و ابتدا محتویات آن را در یک فایل notepad کپی کنید و مسیر‌های زیر را به اول آن اضافه کنید :
%GIT_HOME%\cmd;C:\Program Files (x86)\nodejs\;%JAVA_HOME%\bin;%ANT_HOME%\bin;
%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools; C:\ProgramData\Oracle\Java\javapath;
 دقت کنید که مسیر‌های ذکر شده فقط یکبار در کل فایل وجود داشته باشند و سپس کل محتوای فایل را کپی کرده و در قسمت مربوط به path پیست کنید و با کلیک بر روی دکمه‌های OK کار را به اتمام رسانید.












نکته تکمیلی
نیازمندی Apache Cordova CTP3.1   :
یکی از سیستم عامل‌های مقابل: Windows 7, Windows 8, Windows 8.1, or Windows Server 2012 R2. 
آپدیت 4 مربوط به ویژوال استدیو (دقت کنید قبل از نصب آپدیت 4 ویژوال استدیو باید نسخه قبلی  Cordova CTP  را حذف کنید(uninstall) )
امکان توسعه اپلیکیشن‌های windows phone , windows برای کاربران ویندوز 7 وجود ندارد .


در مقاله‌ی بعدی یک پروژه جدید خواهیم ساخت .
منبع مفید برای نصب و راه اندازی :اینجا
ادامه دارد...
مطالب
بهینه‌سازی سایت برای شبکه‌های اجتماعی
چند سالی ست که شبکه‌های اجتماعی رشد چشمگیری در دنیای مجازی داشته‌اند و به حرف اول و پیشتاز آن بدل شده‌اند. این شبکه‌ها در همه‌ی زمینه‌ها از معرفی و فروش محصولات گرفته تا معرفی سایت و وبلاگ و ... بکار گرفته می‌شوند و لذا مقوله‌ی بهینه سازی یک وب سایت برای این شبکه‌های اجتماعی امری ناگزیر است. مباحث سئو که پیرامون بهینه سازی یک وب سایت برای موتورهای جستجو می‌باشند، امروزه با پدیده‌ی جدید دیگری آمیخته شده‌اند و به تعاریف قدیمی و معمول، گزینه‌هایی افزوده شده است. از این‌رو شناخت و بکارگیری روش‌های بهینه، برای این مباحث ضروری می‌باشند.
همانطور که می‌دانید هنگامیگه در ادیتور ارسال مطلب یکی از شبکه‌های اجتماعی (فیسبوک ، توییتر ، گوگل پلاس و ...) آدرس سایتی را وارد میکنید، بلافاصله لینک پردازش شده و پیش نمایشی از آن وبسایت در ادیتور نمایش داده می‌شود. برخی پیش نمایش‌ها حاوی عکس، عنوان لینک و خیلی منظم هستند و برخی دیگر فقط نام و عنوان سایت را در برمی‌گیرند.
برای نمونه به تصاویر زیر دقت کنید:

تصویر فوق مربوط به لینک یک سایت خبری در ادیتور فیسبوک می‌باشد. همانطور که می‌بینید عنوان خبر، عکس و توضیح مختصری در مورد خبر، با نظم و ترتیبی خاص نمایش داده شده است.

حال به تصویر زیر که مربوط به لینکی از همین سایت می‌باشد دقت کنید:


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

بخش اول: متاتگ‌های موردنیاز برای لینک‌های حاوی مقالات:

ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Article">
برای شناخت و اهمیت بکارگیری خصوصیت itemscope میتوانید اینجا را ببینید. این خصوصیت زوج‌های کلید/مقداری را تعریف می‌کند که جزئی از سند می‌باشند. در ادامه itemtype به تشریح یک زوج کلید/مقدار می‌پردازد که به مرورگر می‌گوید کدام آیتم‌ها برای یک مقاله بهینه شده‌اند.
<title>عنوان صفحه ، حداکثر 60 تا 70 کارکتر باشد</title>
<meta name="description" content="شرح صفحه ، حداکثر 150 کارکتر باشد" /> 

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="نام یا عنوان صفحه">
<meta itemprop="description" content="شرح صفحه">
<meta itemprop="image" content="نشانی اینترنتی عکسی که در پیشنمایش نشان داده میشود"> 

<!-- Twitter Card data -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="کپی رایت نام  سایت">
<meta name="twitter:title" content="نام یا عنوان صفحه">
<meta name="twitter:description" content="شرح صفحه">
<meta name="twitter:creator" content="نویسنده">
<!-- Picture size at least  280x150px -->عکس پیشنمایش با ابعاد حداقل   
<meta name="twitter:image:src" content="نشانی اینترنتی عکس مطلب"> 

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="نشانی عکس مطلب" />
<meta property="og:description" content="شرح مطلب" />
<meta property="og:site_name" content="نام سایت" />
<meta property="article:published_time" content="تاریخ انتشار" />
<meta property="article:modified_time" content="تاریخ بروزرسانی" />
<meta property="article:section" content="نام بخش محتوی متن مقاله" />
<meta property="article:tag" content="نام تگ محتوی متن مقاله" />
<meta property="fb:admins" content="شناسه عددی کاربری شما در فیسبوک" />

بخش دوم: متاتگ‌های موردنیاز برای لینک‌های حاوی محصولات:
ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Product">
و در نهایت متاتگ‌های زیر را به صفحه خود اضافه کنید:
<title>عنوان صفحه</title>
<meta name="description" content="شرح صفحه" />

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="عنوان صفحه">
<meta itemprop="description" content="Tشرح صفحه">
<meta itemprop="image" content="نشانی عکس محصول یا کالا">

<!-- Twitter Card data -->
<meta name="twitter:card" content="product">
<meta name="twitter:site" content="کپی رایت سایت">
<meta name="twitter:title" content="عنوان صفحه">
<meta name="twitter:description" content="شرح محصول یا کالا">
<meta name="twitter:creator" content="نویسنده">
<meta name="twitter:image" content="نشانی عکس محصول یا کالا">
<meta name="twitter:data1" content="قیمت محصول یا کالا">
<meta name="twitter:label1" content="Price">
<meta name="twitter:data2" content="رنگ کالا یا محصحول">
<meta name="twitter:label2" content="Color">

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="عکس محصول یا کالا" />
<meta property="og:description" content="شرح مصحول" />
<meta property="og:site_name" content="نام سایت" />
<meta property="og:price:amount" content="قیمت محصول یا کالا" />
<meta property="og:price:currency" content="واحد ارزی قیمت" />

در پایان
برای مشاهده لیست کاملی از اسکیما اینجا را ببینید.
og مخفف Open Graph Protocol می‌باشد که می‌توانید مطالب کاملی را در مورد آن اینجا بخوانید.
و برای آشنایی بیشتر با TwitterCard هم این لینک را مشاهده کنید.
مطالب
مروری بر کاربردهای Action و Func - قسمت دوم
در قسمت قبل از  Func و Actionها برای ساده سازی طراحی‌های مبتنی بر اینترفیس‌هایی با یک متد استفاده کردیم. این مورد خصوصا در حالت‌هایی که قصد داریم به کاربر اجازه‌ی فرمول نویسی بر روی اطلاعات موجود را بدهیم، بسیار مفید است.

مثال دوم) به استفاده کننده از API کتابخانه خود، اجازه فرمول نویسی بدهید

برای نمونه مثال ساده زیر را درنظر بگیرید که در آن قرار است یک سری عدد که از منبع داده‌ای دریافت شده‌اند، بر روی صفحه نمایش داده شوند:
public static void PrintNumbers()
{
    var numbers = new[] { 1,2,3,5,7,90 }; // from a data source
    foreach(var item in numbers)
    {
        Console.WriteLine(item);
    }    
}
قصد داریم به برنامه نویس استفاده کننده از کتابخانه گزارش‌سازی خود، این اجازه را بدهیم که پیش از نمایش نهایی اطلاعات، بتواند توسط فرمولی که مشخص می‌کند، فرمت اعداد نمایش داده شده را تعیین کند.
روال کار اکثر ابزارهای گزارش‌سازی موجود، ارائه یک زبان اسکریپتی جدید برای حل این نوع مسایل است. اما با استفاده از Func و ... روش‌های Code first (بجای روش‌های Wizard first)، خیلی از این رنج و دردها را می‌توان ساده‌تر و بدون نیاز به اختراع و یا آموزش زبان جدیدی حل کرد:
public static void PrintNumbers(Func<int,string> formula)
{
    var numbers = new[] { 1,2,3,5,7,90 };  // from a data source
    foreach(var item in numbers)
    {
        var data = formula(item);
        Console.WriteLine(data);
    }    
}
اینبار با استفاده از Func، امکان فرمول نویسی را به کاربر استفاده کننده از API ساده گزارش ساز فرضی خود داده‌ایم. Func تعریف شده در اینجا یک عدد int را در اختیار استفاده کننده قرار می‌دهد. در این بین، برنامه نویس می‌تواند هر نوع تغییر یا هر نوع فرمولی را که مایل است بر روی این عدد به کمک دستور زبان جاری مورد استفاده، اعمال کند و در آخر تنها باید نتیجه این عملیات را به صورت یک string بازگشت دهد. برای مثال:
 PrintNumbers(number => string.Format("{0:n0}",number));
البته سطر فوق ساده شده فراخوانی زیر است:
 PrintNumbers((number) =>{ return string.Format("{0:n0}",number); });
به این ترتیب اعداد نهایی با جدا کننده سه رقمی نمایش داده خواهند شد.
از این نوع طراحی، در ابزارها و کتابخانه‌های جدید گزارش سازی مخصوص ASP.NET MVC زیاد مشاهده می‌شوند.


مثال سوم) حذف کدهای تکراری برنامه

فرض کنید قصد دارید در برنامه وب خود مباحث caching را پیاده سازی کنید:
using System;
using System.Web;
using System.Web.Caching;
using System.Collections.Generic;

namespace WebToolkit
{
    public static class CacheManager
    {
        public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes)
        {
            if (data == null) return;
            httpContext.Cache.Add(
                key,
                data,
                null,
                DateTime.Now.AddMinutes(durationMinutes),
                TimeSpan.Zero,
                CacheItemPriority.AboveNormal,
                null);
        }
    }
}
در هر قسمتی از برنامه که قصد داشته باشیم اطلاعاتی را در کش ذخیره کنیم، الگوی تکراری زیر باید طی شود:
var item = httpContext.Cache[key];
if (item == null)
{
    item = ReadDataFromDataSource();
    if (item == null)
          return null;

    CacheInsert(httpContext, key, item, durationMinutes);
}
ابتدا باید وضعیت کش جاری بررسی شود؛ اگر اطلاعاتی در آن موجود نبود، ابتدا از منبع داده‌ای مورد نظر خوانده شده و سپس در کش درج شود.
می‌توان در این الگوی تکراری، خواندن اطلاعات را از منبع داده، به یک Func واگذار کرد و به این صورت کدهای ما به نحو زیر بازسازی خواهند شد:
using System;
using System.Web;
using System.Web.Caching;
using System.Collections.Generic;

namespace WebToolkit
{
    public static class CacheManager
    {
        public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes)
        {
            if (data == null) return;
            httpContext.Cache.Add(
                key,
                data,
                null,
                DateTime.Now.AddMinutes(durationMinutes),
                TimeSpan.Zero,
                CacheItemPriority.AboveNormal,
                null);
        }

        public static T CacheRead<T>(this HttpContextBase httpContext, string key, int durationMinutes, Func<T> ifNullRetrievalMethod)
        {
            var item = httpContext.Cache[key];
            if (item == null)
            {
                item = ifNullRetrievalMethod();
                if (item == null)
                    return default(T);

                CacheInsert(httpContext, key, item, durationMinutes);
            }
            return (T)item;
        }
    }
}
و استفاده از آن نیز به نحو زیر خواهد بود:
var user = HttpContext.CacheRead(
                            "Key1",
                            15,
                            () => _usersService.FindUser(userId));
پارامتر سوم متد CacheRead به صورت خودکار تنها زمانیکه اطلاعات کش متناظری با کلید Key1 وجود نداشته باشند، اجرا شده و نتیجه در کش ثبت می‌گردد. در اینجا دیگر از if و else و کدهای تکراری بررسی وضعیت کش خبری نیست.
 
نظرات مطالب
ارسال عکس به stimulsoft و ایجاد گزارش
ممنون از پاسخ شما.
با استفاده از همین کد بالا عکس رو ارسال کردم، اما یه کار دیگه کردم توی خود گزارش که درست شد.
فقط با این تفاوت که این نیازی به این دو خط نیست:
StiImage stiImg = new StiImage();
                stiImg.Image = img;

من ستون Image رو در خود business Object ساخته بودم و همون رو در Data Band استفاده میکردم، اومدم یک باکس از نوع Image  اضافه کردم که تصویر اون رو Data Column  و همون ستون Image در business object انتخاب کردم و درست شد.
مطالب
آشنایی با Column Store Index در SQL Server 2012

 Column Store Index یکی از ویژگیهای جدید SQL Server 2012 می باشد، که کارایی Query های قایل اجرا روی دیتابیس‌های با حجم داده ای بسیار بالا را (که اصطلاحا به آنها Data Warehouse یا انبار داده گویند)، چندین برابر بهبود بخشیده است

 قبل از توضیح در مورد Column Store مختصری در مورد نحوه ذخیره سازی داده‌ها در SQL Server می پردازیم. می‌توان گفت در SQL Server دو روش ذخیره سازی وجود دارد،یکی بصورت ردیفی که اصطلاحا به آن  Row Storeیا  Row-Wise گویند، و دیگری بصورت ستونی که اصطلاحا به آن Column Store گویند

در روش ذخیره سازی Row Store، مقادیر ستونها در یک سطر بصورت متوالی ذخیره می‌شوند، در این روش ذخیره سازی از ساختار B-Tree یا Heap استفاده می‌شود.

یادآوری: در ساختار B-Tree، یک گره Root وجود دارد، و گره بعد از Root گره ای است که آدرس گره راست بعدی و آدرس گره چپ بعدی را در خود نگه می‌دارد.

شکل زیر نمای یک درخت B-Tree می‌باشد:

جهت کسب اطلاعات بیشتر درمورد ساختار B-Tree 

یادآوری: وقتی در یک جدول، ایندکسی از نوع Clustered ایجاد نماییم، SQL Server، در ابتدا یک کپی از جدول ایجاد و داده‌های جدول را از نو مرتب می‌نماید، و ساختار صفحه ریشه و دیگر صفحات را ایجاد می‌کند و سپس جدول اصلی را حذف می‌نماید. به جدولی که Clustered Index ندارد، اصطلاحا Heap گویند.

  برخلاف ذخیره سازی Row Store، در ذخیره سازی Column Store، داده‌ها بصورت ستونی ذخیره می‌شوند،در این روش داده ها، فشرده سازی می‌شوند و اینکار باعث می‌شود،در زمان درخواست یک Query، نیاز به Disk I/o به حداقل برسد، در نتیجه، زمان و سرعت پاسخگویی به پرس و جو‌ها بسیار افزایش می‌یابد.

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

  شکل بالا ذخیره سازی داده ها، در ساختار B-Tree یا Heap را نمایش می‌دهد، در شکل فوق یک جدول چهار ستونی با N سطر (Row) در نظر گرفته شده است.بطوریکه ستونهای هر Row بطور متوالی در یک صفحه (Page) یکسان ذخیره می‌شوند.

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

  مطابق شکل،ستونهای مربوط به هر Row،همگی در یک صفحه (Page) یکسان ذخیره شده اند. به عنوان مثال ستون C1 که مربوط به سطر اول (Row1) می‌باشد، با ستون C1 که مربوط به سطر دوم (Row2) می‌باشد، در یک ستون و در یک صفحه (Page1) ذخیره شده اند، و الی آخر ...

   سئوال: یکبار دیگر به هردو شکل با دقت نگاهی بیاندازید، عمده تفاوت آنها در چیست؟

   جواب: درست حدس زدید، تفاوت بارز بین دو روش Column Store و Row Store در نحوه ذخیره سازی داده‌ها می‌باشد. بطور مثال، فرض کنید،در روش ذخیره سازی Row Store، به دنبال مقادیری از ستون C2 می‌باشید، SQL Server می‌بایست کل رکورد‌های جدول (منظور همه Row‌ها در همه Page ها)را Scan نماید، تا مقادیر مربوط به ستون C2 را بدست آورد.درحالیکه در روش ذخیره سازی Column Store، جهت یافتن مقادیر ستون C2، نیازی به Scan نمودن کل جدول نیست،بلکه SQL Server فقط به Scan نمودن ستون دوم (C2) یا Page2 بسنده می‌نماید.همین امر باعث افزایش چندین برابری، زمان پاسخگویی به هر Query می‌شود.

  سئوال: در روش ذخیره سازی Column Store، چگونه مصرف حافظه بهینه می‌شود؟

  جواب: واضح است،  که در روش SQL Server، Row Store مجبور است، برای بدست آوردن داده‌های مورد نظرتان،کل اطلاعات جدول را وارد حافظه نماید(اطلاعات اضافه ای که به هیچ وجه بدرد، نتیجه پرس و جوی شما نمی‌خورد)، و شروع به Scan داده‌های مد نظر شما می‌نماید.بطوریکه در روش SQL Server، Column Store، فقط ستون داده‌های مورد پرس و جو را در حافظه قرار می‌دهد.(در واقع فقط داده هایی را در حافظه قرار می‌دهد، که شما به آن نیاز دارید)،بنابراین،طبیعی است که در روش Column Store مقدار حافظه کمتری نسبت به روش Row Store در هنگام اجرای Query استفاده می‌شود. به عبارت دیگر می‌توان گفت که در روش Column Store به دلیل، به حداقل رساندن استفاده از Disk I/o سرعت و زمان پاسخگویی به پرس و جو‌ها چندین برابر می‌شود.

  برای درک بیشتر Row Store و Column Store مثالی می‌زنیم:

   فرض کنید،قصد بدست آوردن ستونهای C1 و C2 از جدول A را داریم، بنابراین خواهیم داشت:

Select C1, C2 from A

روش Row Store:

    در این روش همه صفحات دیسک (مربوط به جدول A) درون حافظه قرار داده می‌شود، یعنی علاوه بر ستونهای C1 و C2، اطلاعات مربوط به ستونهای C3 و C4 نیز درون حافظه قرار می‌گیرد،بطوریکه مقادیر ستونهای C3 و C4 به هیچ وجه مورد قبول ما نیست، و در خروجی پرس و جوی ما تاثیری ندارد، و فقط بی جهت حافظه اشغال می‌نماید.

روش Column Store:

  در این روش فقط صفحات مروبط به ستون C1 و C2 در حافظه قرار می‌گیرد.(منظور Page1 و Page2 می‌باشد) بنابراین فقط اطلاعات مورد نیاز در خروجی، در حافظه قرار می‌گیرد.

  •  از دیگر مزایای استفاده از روش Column Store، فشرده سازی داده می‌باشد،برای درک بیشتر توضیح می‌دهم:
      همانطور که در اوایل مطلبم به عرض رساندم، در روش Row Store ، داده‌ها در یک سطر و در یک Page ذخیره می‌شوند، بنابراین امکان وجود داده‌های تکراری در یک سطر به حداقل می‌رسد، چرا که، اگر فرض کنیم چهار ستون  به نام‌های ID،FirstName،LastName و City، داشته باشیم،در آن صورت بطور حتم،در یک سطر، داده تکراری وجود نخواهد داشت، اما ممکن است در تعداد سطرهای زیاد داده‌های تکراری مانند Firstname یا City و غیرو بوجود بیاید، این موضوع را بیان کردم، چون می‌خواستم عنوان کنم،بسیاری از الگوریتم‌های فشرده سازی از الگوی تکراری بودن داده، جهت فشرده سازی داده‌ها استفاده می‌کنند، به همین جهت فشرده سازی در روش Row Store به حداقل می‌رسد و فضای اشغال شده در حافظه دراین روش بسیار زیاد خواهد بود. اما در روش Column Store ، امکان تکراری بودن مقادیر یک ستون بسیار زیاد  است، بطور مثال ممکن است تعداد افرادی را که نام شهر  آنها "تهران" باشد مثلا 20 بار تکرار شده باشد، و چون در روش Column Store، ستون‌ها در یک Page ذخیره می‌شوند، بنابراین امکان استفاده از الگوریتمهای فشرده سازی در این روش بسیار بالا می‌باشد، در نتیجه مقدار فضایی را که در حافظه یا دیسک سخت توسط این روش اشغال می‌شود، بسیار کمتر از روش Row Store است.

چه موقع می‌توانیم از Column Store استفاده نماییم:

   در تعریف Column Store گفته بودم، روش فوق، جهت بهبود بخشیدن به زمان و سرعت پاسخگویی به Query‌های اجرا شده روی دیتابیس‌های با حجم داده ای بسیار بالا(Data Warehouse ) می‌باشد، به بیان ساده‌تر Column Store را روی دیتابیس‌های offline یا دیتابیسهایی که صرفا جهت گزارش گیری مورد استفاده قرار می‌گیرند، تنظیم می‌نمایند.در واقع با تنظیم Column Store Index روی Database‌های بزرگ مانند Database‌های بانک‌ها که حجم داده ای میلیونی در جداول آنها وجود دارد، سرعت پاسخگویی Query ها، چندین برابر افزایش می‌یابد.

  •      در یک جدول می‌توانید، هم Column Store Index داشته باشید و هم یک Row Store Index (منظور یک  Clustered Index می باشد)
  • Syntax برای ایجاد  Column Store Index به شرح ذیل می‌باشد:
CREATE [ NONCLUSTERED ] COLUMNSTORE INDEX index_name 
    ON <object> ( column  [ ,...n ] )
    [ WITH ( <column_index_option> [ ,...n ] ) ]
    [ ON {
           { partition_scheme_name ( column_name ) } 
           | filegroup_name 
           | "default" 
         }
    ]
[ ; ]

<object> ::=
{
    [database_name. [schema_name ] . | schema_name . ]
     table_name
{

<column_index_option> ::=
{
      DROP_EXISTING = { ON | OFF }
    | MAXDOP = max_degree_of_parallelism
 }
  • یک Column Store Index می‌بایست از نوع NONCLUSTERED باشد.
مثال از یک Column Store Index :
CREATE NONCLUSTERED COLUMNSTORE INDEX [IX_MyFirstName_ColumnStore]
ON [Test]
(Firstname)
در قطعه کد بالا، یک Column Store Index به نام Ix_MyFirstname_ColumnStore روی فیلد Firstname از جدول Test ایجاد شده است.
محدودیت‌های استفاده از Column Store Index به اختصار به شرح ذیل می‌باشد:
  • زمانی که در یک جدول، یک Column Store Index ایجاد نماییم، جدول ما  در حالت Read-only قرار می‌گیرد، بطوریکه از آن پس  اختیار Delete،Update و Insert روی جدول فوق را نخواهیم داشت. برای اینکه بتوانید عملیات Insert، Update یا Delete را انجام دهید، میبایست Column Store Index جدول مربوطه را Disable نمایید، و برای فعال نمودن Column Store Index، می‌بایست آن را Rebuild نمایید، با کلیک راست روی ایندکس ایجاد شده در SQL Server2012 موارد Disable و Rebuild قابل مشاهده می‌باشد.

یا بوسیله Script‌های زیر می‌توانید، عملیات Disable یا Rebuild را روی Column Store Index انجام دهید:
ALTER INDEX [IX_MyFirstName_ColumnStore] ON [Test] DISABLE

ALTER INDEX [IX_MyFirstName_ColumnStore] ON [Test] Rebuild
  • بیشتر از یک Column Store Index نمی‌توانید روی یک جدول ایجاد نمایید.
  • در صورتی که تمایل داشته باشید بوسیله Alter ، نوع فیلدی (Type)، را که Column Store Index روی آنها اعمال گردیده است، تغییر دهید، در ابتدا می‌بایست Column Store Index، خود را Drop یا حذف نمایید، سپس عملیات Alter را اعمال کنید، در غیر اینصورت با خطای SQL Server مواجه می‌شوید.
  • یک Column Store Index می‌تواند روی 1024 ستون در یک جدول اعمال گردد.
  • یک Column Store Index  نمی توانند، Unique باشد و نمی‌توان از آن به عنوان Primary Key یا Foreign Key استفاده نمود.
یاد آوری: با توجه به مزایای استفاده از Column Store Index، باید بگویم که در حجم‌های داده ای کم استفاده از Row Store Index بهتر می‌باشد. پیشنهاد مایکروسافت برای استفاده از Column Store Index برای دیتابیس‌های با حجم داده ای بسیار بالا می‌باشد.
موفق باشید
منابع:

مطالب
آشنایی با Gridify
Gridify چیست ؟

به طور خلاصه Gridify یک کتابخانه ساده و سریع است که عملیات‌های Filtering , Pagination و Sorting را با استفاده از شرط‌های متنی (string based) امکان پذیر میکند.
به طور مثال فرض کنید که یک API را برای دریافت لیست کاربران با نام UsersList نوشته‌اید. مثال:
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList()
 {
    var users =  await _dbContext.Users.AsNoTracking().ToListAsync();
    return Ok(users);
 }
طبیعتا بخش FrontEnd نرم افزار شما نیاز دارد این اطلاعات را به کاربر نمایش دهد. به همین جهت در بیشتر مواقع از یک گرید برای نمایش این اطلاعات استفاده میشود.
پس از دریافت اطلاعات از سرور با مشکلات زیر مواجه خواهیم شد.
  1. عدم پشتیبانی از Pagination: چون API، تمامی کاربران را به سمت کلاینت ارسال میکند؛ به همین جهت، هم با مشکل کارآیی (performance) در آینده مواجه میشویم و هم امکان گذاشتن صفحه بندی (Pagination) وجود نخواهد داشت. 
  2. عدم پشتیبانی از Sorting: اگر در گرید نمایش داده شده کاربر بخواهد اطلاعات را Sort کند، چون چنین امکانی هنوز برای API ما تعریف نشده، این عملیات سمت سرور امکان پذیر نیست.
  3. عدم پشتیبانی از Filtering: همیشه نمایش تمامی اطلاعات مفید نیست. در اکثر مواقع ما نیاز داریم تا قسمتی از اطلاعات را با شرطی خاص، برگردانیم. به طور مثال لیست کاربران فعال در سامانه یا لیست کاربران غیرفعال. 
این مشکلات بدون استفاده از هیچ کتابخانه‌ای قابل حل است ولی نه به سادگی؛ به طور مثال یا باید چندین API مختلف با امکانات مختلف بنویسیم، یا یک API را برای پشتیبانی از این موارد تغییر بسیار دهیم. من برای اینکه از بحث دور نشویم، به پیاده سازی نمونه دستی پشتیبانی از این موارد در اینجا نمی‌پردازم، چرا که اگر یکبار تلاشی را برای اینکار انجام داده باشیم، طبیعتا  مشکلات و کد کثیفی که در نهایت تولید شده است، یادآوری خواهد شد. 
برای رفع این مشکلات میتوان از کتابخانه Gridify استفاده کرد. مثال :
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList(GridifyQuery filter)
 {
    var users =  await _dbContext.Users.AsNoTracking().GridifyAsync(filter);
    return Ok(users);
 }
در مثال بالا با استفاده از کلاس GridifyQuery میتوانیم به کنترل هر سه مشکل Sorting - Pagination - Filtering سمت کلاینت بپردازیم. (در ادامه با این کلاس بیشتر آشنا خواهیم شد).


استفاده از Gridify به API‌ها محدود نمیشود. به طور کلی ما میتوانیم در هر نوع لیستی که امکان استفاده از IQueryable  را به ما میدهد از آن استفاده نماییم. 
فرض کنید در یک برنامه Console Application قصد داریم یک فیلتر را از کاربر دریافت کرده و آن را روی لیست خروجی خود اعمال کنیم. به دلیل اینکه امکان جستجوی متنی در دات نت وجود ندارد، برای انجام اینکار مجبور خواهیم شد که برای تک تک فیلدهایی که قرار است برای فیلترینگ پشتیبانی کنیم، یک query جداگانه بنویسیم؛ ولی این عملیات توسط کتابخانه Gridify امکان پذیر است. به طور مثال فرض کنید قصد داریم در لیست کاربران، کاربرانی را  با نام Ali، پیدا کنیم. 
var result = Users.AsQueryable().ApplyFiltering("name==Ali");
این کد دقیقا معادل کد زیر است.
var result = Users.AsQueryable().Where(q => q.Name == "Ali");
در اینجا با استفاده از کتابخانه Gridify ما توانستیم یک static Linq را به یک dynamic Linq که در runtime مقدار دهی خواهد شد، تغییر دهیم. به همین جهت استفاده از مورد اول در برنامه‌ی Console ما امکان پذیر است. تا اینجا ما با امکانات کلی این کتابخانه آشنا شدیم. در مقالات بعدی سعی میکنم به سایر امکانات این کتابخانه و بیشتر به جزئیات بپردازم. همینطور برای کسب اطلاعات بیشتر میتوانید به لینک زیر مراجعه نمایید.
مطالب
ساخت Nuget Manager شخصی
یکی از راحت‌ترین راه‌های افزودن پکیج‌های برنامه نویسی به پروژه‌های دات نت، از طریق Nuget میباشد. این ابزار به قدری راحت است که من تصمیم گرفتم پکیج‌های تیممان را از طریق این سیستم دریافت کنیم. مزیت آن هم این است که بچه‌های تیم همیشه به پکیج‌ها دسترسی راحت‌تری دارند و هم اینکه در آینده به روز رسانی ساده‌تری خواهند داشت. با توجه به اینکه سایت اصلی تنها پکیج‌های عمومی را پشتیبانی می‌کند و چیزی تحت عنوان پکیج‌های شخصی ندارد، پس باید خودمان این سرویس را راه اندازی کنیم. برای راه اندازی این سیستم می‌توان آن را بر روی سیستم شخصی قرار داد و یا اینکه به صورت اینترنتی بر روی یک سرور به آن دسترسی داشته باشیم. در این مقاله این دو روش را بررسی میکنم:

پی نوشت: برای داشتن نیوگت شخصی سایت‌های نظیر Nuget Server و Myget ( به همراه پشتیبانی از مخازن npm و Bower ) هم این سرویس را ارائه میکنند. ولی باید هزینه‌ی آن را پرداخت کنید. البته سایت GemFury مخازن رایگان مختلفی چون Nuget را نیز پشتیبانی می‌کند.


نصب بر روی یک سیستم شخصی یا لوکال


در اولین قدم، شما باید یک دایرکتوری را در سیستم خود درست کنید تا پکیج‌های خود را داخل آن قرار دهید. پنجره‌ی Package manager Settings را باز کنید و در آن گزینه‌ی Package Sources را انتخاب کنید. سپس در کادر باز شده،  بر روی دکمه‌ی افزودن، در بالا کلیک کنید تا در پایین کادر، از شما نام محل توزیع بسته و آدرس آن را بپرسد:


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


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

مرحله‌ی بعدی این است که از طریق ابزار خط فرمان نیوگت نسخه 3.3 پکیج‌هایتان را به دایرکتوری مربوطه انتقال دهید. نحوه‌ی صدا زدن این دستور به شکل زیر است:
nuget init e:\nuget\ e:\nuget\test
این دستور تمام پکیج‌هایی را که شما در مسیر اولی قرار داده‌اید، به داخل دایرکتوری test منتقل می‌کند. ولی اگر قصد دارید که فقط یکی از پکیج‌ها را به این دایرکتوری انتقال دهید از دستور زیر استفاده کنید:
nuget add GMap.Net.1.0.1.nupkg -source e:\nuget\test
اینبار با کلمه‌ی رزرو شده‌ی add و بعد از آن نام پکیچ، دایرکتوری منبع را به آن معرفی می‌کنیم.


ساخت منبع راه دور (اینترنت)


شما با استفاده از ویژوال استودیو و انجام چند عمل ساده می‌توانید پکیج‌های خودتان را مدیریت کنید. برای شروع، یک پروژه‌ی تحت وب خام (Empty) را ایجاد کنید و در کنسول Nuget دستور زیر را وارد کنید:
Install-Package NuGet.Server
شما با نصب این بسته و وابستگی‌هایش، به راحتی یک سیستم مدیریت بسته را دارید. ممکن است مدتی برای نصب طول بکشد و در نهایت از شما بخواهد که فایل web.config را رونویسی کند که شما اجازه‌ی آن را صادر خواهید کرد. بعد از اتمام نصب، فایل web.config را گشوده و در خط زیر، خصوصیت Value را به یک دایرکتوری که دلخواه که مدنظر شماست تغییر دهید. این آدرس دهی می‌تواند به صورت مطلق باشد، یا آدرس مجازی آن را وارد کنید؛ یا اگر هم خالی بگذارید به طور پیش فرض دایرکتوری Packages را در نظر میگیرد:
<appSettings>
    <!-- Set the value here to specify your custom packages folder. -->
    <add key="packagesPath" value="×\Packages" />
</appSettings>
حال فایل‌های دایرکتوری محلی خود را به دایرکتوری Packages انتقال دهید و سپس سایت را بر روی IIS، هاست نمایید. از این پس منبع شما به صورت آنلاین مانند آدرس زیر در دسترس خواهد بود.
(هر محلی که نصب کنید طبق الگوی مسیریابی، آدرس nuget را در انتها وارد کنید)
Dotnettips.info/nuget

در صورتی که قصد دارید مستقیما بسته‌ای را به سمت سرور push کنید، از یک رمز عبور قدرتمند که آن را می‌توانید در web.config، بخش apiKey وارد نمایید، استفاده کنید. اگر هم نمی‌خواهید، می‌توانید در تگ RequiredApiKey در خصوصیت Value، مقدار false را وارد نمایید.
برای اینکار می‌توانید از دستور زیر استفاده کنید:
nuget push GMap.Net.1.0.1.nupkg -source http://Dotenettips.info/nuget (ApiKey)
البته اگر قبل از دستور بالا، دستور زیر را وار کنید، دیگر نیازی نیست تا برای دستورات بعدی تا مدتی ApiKey را وارد نمایید.
nuget setapikey -source https://www.dntips.ir/Nuget (ApiKey)
مطالب
آموزش Prism #2
در پست قبلی توضیح کلی درباره فریم ورک Prism داده شد. در این بخش قصد داریم آموزش‌های داده شده در پست قبلی را با هم در یک مثال مشاهده کنیم. در پروژه‌های ماژولار طراحی و ایجاد زیر ساخت قوی برای مدیریت ماژول‌ها بسیار مهم است. Prism فریم ورکی است که فقط چارچوب و قواعد اصول طراحی این گونه پروژه‌ها را در اختیار ما قرار می‌دهد. در پروژه‌های ماژولار هر ماژول باید در یک اسمبلی جدا قرار داشته باشد که ساختار پیاده سازی آن می‌تواند کاملا متفاوت با پیاده سازی سایر ماژول‌ها باشد.
 برای شروع  باید فایل‌های اسمبلی Prism رو دانلود کنید(لینک دانلود).
تشریح پروژه:
می‌خواهیم برنامه ای بنویسیم که دارای سه ماژول زیر است.:
  1. ماژول Navigator : برای انتخاب و Switch کردن بین ماژول‌ها استفاده می‌شود؛
  2. ماژول طبقه بندی کتاب‌ها : لیست طبقه بندی کتاب‌ها را به ما نمایش می‌دهد؛
  3. ماژول لیست کتاب‌ها : عناوین کتاب‌ها به همراه نویسنده و کد کتاب را به ما نمایش می‌دهد.

*در این پروژه از UnityContainer برای مباحث Dependency Injection استفاده شده است.
ابتدا یک پروژه WPF در Vs.Net ایجاد کنید(در اینجا من نام آن را  FirstPrismSample گذاشتم). قصد داریم یک صفحه طراحی کنیم که دو ماژول مختلف در آن لود شود. ابتدا باید Shell پروژه رو طراحی کنیم. یک Window جدید به نام Shell بسازید و کد زیر را در آن کپی کنید.
<Window x:Class="FirstPrismSample.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:com="http://www.codeplex.com/CompositeWPF"
    Title="Prism Sample By Masoud Pakdel" Height="400" Width="600" WindowStartupLocation="CenterScreen">
    <DockPanel>
      <ContentControl com:RegionManager.RegionName="WorkspaceRegion" Width="400"/>
      <ContentControl com:RegionManager.RegionName="NavigatorRegion"  DockPanel.Dock="Left" Width="200" />     
    </DockPanel>
</Window>
در این صفحه دو ContentControl تعریف کردم یکی به نام Navigator و دیگری به نام Workspace. به وسیله RegionName که یک AttachedProperty است هر کدوم از این نواحی را برای Prism تعریف کردیم. حال باید یک ماژول برای Navigator و دو ماژول دیگر یکی برای طبقه بندی کتاب‌ها و دیگری برای لیست کتاب‌ها بسازیم.

#پروژه Common
قبل از هر چیز یک پروژه Common می‌سازیم و مشترکات بین ماژول‌ها رو در آن قرار می‌دهیم(این پروژه باید به تمام ماژول‌ها رفرنس داده شود).  این مشترکات شامل :
  • کلاس پایه ViewModel
  • کلاس ViewRequestEvent
  • کلاس ModuleService

کد کلاس ViewModelBase که فقط اینترفیس INotifyPropertyChanged رو پیاده سازی کرده است:

using System.ComponentModel;

namespace FirstPrismSample.Common
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChangedEvent( string propertyName )
        {
            if ( PropertyChanged != null )
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs( propertyName );
                PropertyChanged( this, e );
            }
        }
    }
}
کلاس ViewRequestEvent که به صورت زیر است:
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.Common.Events
{
    public class ViewRequestedEvent : CompositePresentationEvent<string>
    {
    }
}
توضیح درباره CompositePresentationEvent :
در طراحی و توسعه پروژه‌های ماژولار نکته ای که باید به آن دقت کنید این است که ماژول‌های پروژه نباید به هم وابستگی مستقیم داشته باشند در عین حال ماژول‌ها باید بتوانند با هم در ارتباط باشند. CPE یا CompositePresentationEventدقیقا برای این منظور به وجود آمده است. CPE که در این جا طراحی کردم فقط کلاسی است که از CompositePresentationEventارث برده است و دلیل آن که به صورت string generic استفاده شده است این است که می‌خواهیم در هر درخواست نام ماژول درخواستی را داشته باشیم و به همین دلیل نام آن را ViewRequestedEvent گذاشتم.

توضیح درباره EventAggregator

EventAggregator یا به اختصار EA مکانیزمی است در پروژهای ماژولار برای اینکه در Composite UI‌ها بتوانیم بین کامپوننت‌ها ارتباط برقرار کنیم. استفاده از EA وابستگی بین ماژول‌ها را  از بین خواهد برد. برنامه نویسانی که با MVVM Light آشنایی دارند از قابلیت Messaging موجود در این فریم ورک برای ارتباط بین View و  ViewModel استفاده می‌کنند. در Prism این عملیات توسط EA انجام می‌شود. یعنی برای ارتباط با View‌ها باید از EA تعبیه شده در Prism استفاده کنیم. در ادامه مطلب، چگونگی استفاده از EA را خواهید آموخت.
اینترفیس IModuleService که فقط شامل یک متد است:
namespace FirstPrismSample .Common
{
    public interface IModuleServices
    {     
        void ActivateView(string viewName);
    }
}
کلاس ModuleService که اینترفیس بالا را پیاده سازی کرده است:
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

namespace FirstPrismSample.Common
{
    public class ModuleServices : IModuleServices
    {     
        private readonly IUnityContainer m_Container;  
     
        public ModuleServices(IUnityContainer container)
        {
            m_Container = container;
        }      
   
        public void ActivateView(string viewName)
        {        
            var regionManager = m_Container.Resolve<IRegionManager>();

            // غیر فعال کردن ویو
            IRegion workspaceRegion = regionManager.Regions["WorkspaceRegion"];
            var views = workspaceRegion.Views;
            foreach (var view in views)
            {
                workspaceRegion.Deactivate(view);
            }

            //فعال کردن ویو انتخاب شده 
            var viewToActivate = regionManager.Regions["WorkspaceRegion"].GetView(viewName);
            regionManager.Regions["WorkspaceRegion"].Activate(viewToActivate);
        }
    }
}
متد ActivateView نام view مورد نظر برای فعال سازی را دریافت می‌کند. برای فعال کردن View ابتدا باید سایر view‌های فعال در RegionManager را غیر فعال کنیم. سپس فقط view مورد نظر در RegionManager انتخاب و فعال می‌شود.

*نکته: در هر ماژول ارجاع به اسمبلی‌های Prism مورد نیاز است.

#ماژول طبقه بندی کتاب ها:
برای شروع یک Class Library جدید به نام ModuleCategory به پروژه اضافه کنید. یک UserControl به نام CategoryView بسازید و کد‌های زیر را در آن کپی کنید.
<UserControl x:Class="FirstPrismSample.ModuleCategory.CategoryView "
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             Background="LightGray" FlowDirection="RightToLeft" FontFamily="Tahoma">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text=" طبقه بندی ها"/>
        <ListView Grid.Row="1"  Margin="10" Name="lvCategory">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50" />
                    <GridViewColumn Header="عنوان" Width="200"  />                  
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>
یک کلاس به نام CategoryModule بسازید که اینترفیس IModule رو پیاده سازی کند.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.ModuleCategory
{
    [Module(ModuleName = "ModuleCategory")]
    public class CategoryModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleCategory";
            
        public CategoryModule(IUnityContainer container)
        {
            m_Container = container;
        }   
      
        ~CategoryModule()
        {
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();       
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            regionManager.Regions["WorkspaceRegion"].Add(new CategoryView(), moduleName);
         
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }
       
        public void ViewRequestedEventHandler(string moduleName)
        {
            if (this.moduleName != moduleName) return;
          
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(moduleName);
        }      
    }
}
چند نکته :
*ModuleAttribute استفاده شده در بالای کلاس برای تعیین نام ماژول استفاده می‌شود. این Attribute دارای دو خاصیت دیگر هم است :
  1. OnDemand : برای تعیین اینکه ماژول باید به صورت OnDemand (بنا به درخواست) لود شود.
  2. StartupLoaded : برای تعیین اینکه ماژول به عنوان ماژول اول پروزه لود شود.(البته این گزینه Obsolute شده است)

*برای تعریف ماژول کلاس مورد نظر حتما باید اینترفیس IModule را پیاده سازی کند. این اینترفیس فقط شامل یک متد است به نام Initialize.

*در این پروژه چون View‌های برنامه صرفا جهت نمایش هستند در نتیجه نیاز به ایجاد ViewModel برای آن‌ها نیست. در پروژه‌های اجرایی حتما برای هر View باید ViewModel متناظر با آن تهیه شود.

توضیح درباره متد Initialize

در این متد ابتدا با استفاده از Container موجود RegionManager را به دست می‌آوریم. با استفاده از RegionManager می‌تونیم یک CompositeUI طراحی کنیم. در فایل Shell مشاهده کردید که یک صفحه به دو ناحیه تقسیم شد و به هر ناحیه هم یک نام اختصاص دادیم. دستور زیر به یک ناحیه اشاره خواهد داشت:

regionManager.Regions["WorkspaceRegion"]
در خط بعد با استفاده از EA یا Event Aggregator توانستیم CPE را بدست بیاوریم. متد Subscribe در کلاس CPE  یک ارجاع قوی به delegate مورد نظر ایجاد می‌کند(پارامتر دوم این متد که از نوع boolean است) که به این معنی است که این delegate هیچ گاه توسط GC جمع آوری نخواهد شد. در نتیجه، قبل از اینکه ماژول بسته شود باید به صورت دستی این کار را انجام دهیم که مخرب را برای همین ایجاد کردیم. اگر به کد‌های مخرب دقت کنید می‌بینید که با استفاده از EA توانستیم ViewRequestEventHandler را Unsubscribe کنیم به دلیل اینکه از ارجاع قوی با strong Reference در متد Subscribe استفاده شده است.
دستور moduleService.ActiveateView ماژول مورد نظر را در region مورد نظر هاست خواهد کرد.

#ماژول لیست کتاب ها:
ابتدا یک Class Library به نام ModuleBook بسازید  و همانند ماژول قبلی نیاز به یک Window و یک کلاس داریم:
BookWindow که کاملا مشابه به CategoryView است.
<UserControl x:Class="FirstPrismSample.ModuleBook.BookView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Background="LightGray" FontFamily="Tahoma" FlowDirection="RightToLeft">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="لیست کتاب ها"/>
        <ListView Grid.Row="1" Margin="10" Name="lvBook">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50"  />
                    <GridViewColumn Header="عنوان" Width="200" />
                    <GridViewColumn Header="نویسنده" Width="150" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

کلاس BookModule که پیاده سازی  و توضیحات آن کاملا مشابه به CategoryModule می‌باشد.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;

namespace FirstPrismSample.ModuleBook
{
    [Module(ModuleName = "moduleBook")]
    public class BookModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleBook";     
    
        public BookModule(IUnityContainer container)
        {
            m_Container = container;          
        }     
       
        ~BookModule()
        {           
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
          
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }     
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            var view = new BookView();
            regionManager.Regions["WorkspaceRegion"].Add(view, moduleName);
            regionManager.Regions["WorkspaceRegion"].Deactivate(view);
      
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }     
      
        public void ViewRequestedEventHandler(string moduleName)
        {           
            if (this.moduleName != moduleName) return;
         
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(m_WorkspaceBName);
        }
    }
}
#ماژول Navigator
برای این ماژول هم ابتدا View مورد نظر را ایجاد می‌کنیم:
<UserControl x:Class="FirstPrismSample.ModuleNavigator.NavigatorView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Grid>
        <StackPanel VerticalAlignment="Center">
            <TextBlock Text="انتخاب ماژول" Foreground="Green" HorizontalAlignment="Center"
                VerticalAlignment="Center" FontFamily="Tahoma" FontSize="24" FontWeight="Bold" />
        <Button Command="{Binding ShowModuleCategory}" Margin="5" Width="125">طبقه بندی کتاب ها</Button>
        <Button Command="{Binding ShowModuleBook}" Margin="5" Width="125">لیست کتاب ها</Button>
        </StackPanel>
        </Grid>
</UserControl>
حال قصد داریم برای این View یک ViewModel بسازیم. نام آن را INavigatorViewModel خواهیم گذاشت:
public interface INavigatorViewModel
    {    
        ICommand ShowModuleCategory { get; set; }       
        ICommand ShowModuleBook { get; set; }

        string ActiveWorkspace { get; set; }       

        IUnityContainer Container { get; set; }

        event PropertyChangedEventHandler PropertyChanged;
    }
 *در اینترفیس بالا دو Command داریم که هر کدام وظیفه لود یک ماژول را بر عهده دارند.
 *خاصیت ActiveWorkspace برای تعیین workspace فعال تعریف شده است.

حال به پیاده سازی مثال بالا می‌پردازیم:
public class NavigatorViewModel : ViewModelBase, INavigatorViewModel
    {        
        public NavigatorViewModel(IUnityContainer container)
        {
            this.Initialize(container);
        }   
       
        public ICommand ShowModuleCategory { get; set; }
      
        public ICommand ShowModuleBook { get; set; }      
              
        public string ActiveWorkspace { get; set; }       

        public IUnityContainer Container { get; set; }        
     
        private void Initialize(IUnityContainer container)
        {
            this.Container = container;
            this.ShowModuleCategory = new ShowModuleCategoryCommand(this);
            this.ShowModuleBook = new ShowModuleBookCommand(this);
            this.ActiveWorkspace = "ModuleCategory";
        }        
    }
تنها نکته مهم در کلاس بالا متد Initialize است که دو Command مورد نظر را پیاده سازی کرده است. ماژول پیش فرض هم ماژول طبقه بندی کتاب‌ها یا ModuleCategory در نظر گرفته شده است.  همان طور که می‌بینید پیاده سازی Command‌ها بالا توسط دو کلاس ShowModuleCategoryCommand و ShowModuleBookCommand انجام شده که در زیر کد‌های آن‌ها را می‌بینید.
#کد کلاس ShowModuleCategoryCommand  
public class ShowModuleCategoryCommand : ICommand
    {      
        private readonly NavigatorViewModel viewModel;
        private const string workspaceName = "ModuleCategory";         

        public ShowModuleCategoryCommand(NavigatorViewModel viewModel)
        {
            this.viewModel = viewModel;
        }          

        public bool CanExecute(object parameter)
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
     
        public void Execute(object parameter)
        {
            CommandServices.ShowWorkspace(workspaceName, viewModel);
        }      
    }
#کد کلاس ShowModuleBookCommand  
public class ShowModuleBookCommand : ICommand
    {
        private readonly NavigatorViewModel viewModel;
        private readonly string workspaceName = "ModuleBook";

        public ShowModuleBookCommand( NavigatorViewModel viewModel )
        {
            this.viewModel = viewModel;
        }

        public bool CanExecute( object parameter )
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute( object parameter )
        {
            CommandServices.ShowWorkspace( workspaceName , viewModel );
        }
    }
با توجه به این که فرض است با متد‌های Execute و CanExecute و CanExecuteChanged آشنایی دارید از توضیح این مطالب خودداری خواهم کرد. فقط کلاس CommandServices  در متد Execute دارای متدی به نام ShowWorkspace است که کد‌های زیر را شامل می‌شود:
public static void ShowWorkspace(string workspaceName, INavigatorViewModel viewModel)
  {           
            var eventAggregator = viewModel.Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Publish(workspaceName);
        
            viewModel.ActiveWorkspace = workspaceName;
 }
در این متد با استفاده از CPE که در پروژه Common ایجاد کردیم ماژول مورد نظر را لود خواهیم کرد. و بعد از آن مقدار ActiveWorkspace جاری در ViewModel به نام ماژول تغییر پیدا می‌کند. متد Publish در CPE این کار را انجام خواهد دارد.

عدم وابستگی ماژول ها
همان طور که می‌بینید ماژول‌های پروژه به هم Reference داده نشده اند حتی هیچ Reference هم به پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد، داده نشده است ولی در عین حال باید با هم در ارتباط باشند. برای حل این مسئله این ماژول‌ها باید در فولدر bin پروژه اصلی خود را کپی کنند. بهترین روش استفاده از Pre-Post Build Event خود VS.Net است. برای این کار از پنجره Project Properties وارد برگه Build Events شوید و از قسمت Post Build Event Command Line  استفاده کنید و کد زیر را در آن کپی نمایید:
xcopy "$(TargetDir)FirstPrismSample.ModuleBook.dll" "$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
قطعا باید به جای FirstPrismSample نام Solution خود و به جای ModuleBook نام ماژول را وارد نمایید.

مانند:


مراحل بالا برای هر ماژول باید تکرار شود(ModuleNavigation , ModuleBook , ModuleCategory). بعد از Rebuild  پروژه در فولدر bin پروژه اصلی یک فولدر به نام Module ایجاد می‌شود که اسمبلی هر ماژول در آن کپی خواهد شد.

ایجاد Bootstrapper
حال نوبت به Bootstrapper میرسد(در پست قبلی در باره مفهوم Bootstrapper شرح داده شد). در پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد کلاس زیر را ایجاد کنید.
    public class Bootstrapper : UnityBootstrapper
    {     
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();
            Container.RegisterType<IModuleServices, ModuleServices>();
        }
   
        protected override DependencyObject CreateShell()
        {
            var shell = new Shell();
            shell.Show();
            return shell;
        }
   
        protected override IModuleCatalog GetModuleCatalog()
        {           
            var catalog = new DirectoryModuleCatalog();
            catalog.ModulePath = @".\Modules";
            return catalog;
        }
    }
متد ConfigureContainer برای تزریق وابستگی به وسیله UnityContainer استفاده می‌شود. در این متد باید تمامی Registration‌های مورد نیاز برای DI را انجام دهید. نکته مهم این است که عملیات وهله سازی و Initialization برای  Container  در متد base کلاس UnityBootstrapper انجام خواهد شد پس همیشه باید متد base این کلاس در ابتدای این متد فراخوانی شود در غیر این صورت با خطا متوقف خواهید شد.
متد CreateShell برای ایجاد و وهله سازی از Shell پروژه استفاده می‌شود. در این جا یک وهله از Shell Window برگشت داده می‌شود.
متد GetModuleCatalog برای تعیین مسیر ماژول‌ها در پروژه کاربرد دارد. در این متد با استفاده از خاصیت ModulePath کلاس DirectoryModuleCatalog تعیین کرده ایم که ماژول‌های پروژه در فولدر Modules موجود در bin اصلی پروژه قرار دارد. اگر به دستورات کپی در Post Build Event قسمت قبل توجه کنید می‌بینید که دستور ساخت فولدر وجود دارد.
"$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
*نکته: اگر استفاده از این روش برای شناسایی ماژول‌ها توسط Bootstrapper را چندان جالب نمی‌دانید می‌تونید از MEF استفاده کنید که اسمبلی ماژول‌های پروژه را به راحتی شناسایی می‌کند و در اختیار Bootsrtapper قرار می‌دهد(از آن جا در مستندات مربوط به Prism، بیشتر به استفاده از MEF تاکید شده است من هم در پست‌های بعدی، مثال‌ها را با MEF پیاده سازی خواهم کرد)

در پایان باید فایل App.xaml را تغییر دهید به گونه ای که متد Run در کلاس Bootstapper ابتدا اجرا شود.
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();
        }
    }


اجرای پروژه:
بعد از اجرا، با انتخاب ماژول مورد نظر اطلاعات ماژول در Workspace Content Control لود خواهد شد.

ادامه دارد...


مطالب
آموزش فایرباگ - #7 - CSS Panel
پنل CSS مانند پنل جانبیِ Style که در مقاله‌ی قبل بررسی کردیم است با این تفاوت که امکانات بیشتری برای افرادی که قصد تصریف استایل دارند محیا کرده است.
در این پنل می‌توان به اضافه ، ویرایش و حذف استایل هایی که به صفحه‌ی جاری توسط فایل‌های مختلف اضافه شده اند و یا داخل خود صفحه تعریف شده اند پرداخت.



همچنین در این پنل امکان ویرایش یک فایل css بصورت کامل وجود دارد ، به این صورت که می‌توانید تمام محتویات فایل مورد نظر را در یک Text area ویرایش کنید.

مطابق با روالی که در قسمت قبل پیش گرفتیم عمل می‌کنیم و به ترتیب به تشریح ابزار پنل ، خود پنل ، منوی راست کلیک و پنل جانبی ِ Elements می‌پردازیم.


Options Menu
با راست کلیک کردن بروی تب CSS و یا کلیک کردن بروی فلش کوچک روی تب CSS قابل دسترس است و امکانات زیر را محیا می‌کند:

  • Expand Shorthand Properties : نمایش دستورات css بصورت کامل ، به این معنی که دستوراتی مانند margin که هم بصورت خلاصه و هم بصورت کامل تعریف می‌شوند را بصورت کامل نمایش می‌دهد. مثلا margin را چهار مقدار margin-top , margin-right , margin-bottom , margin-left نمایش می‌دهد.

  • Color As Hex , Color As RGB , Color As HSL : با انتخاب یکی از سه مقدار ذکر شده ، فرمت نمایش رنگ‌ها به حالت انتخاب شده تغییر می‌کند.
    مثلا دستور color : #000000 به این صورت نمایش داده می‌شود :‭ color : rgb(0, 0, 0) 

  • Refresh : این گزینه محتویات پنل را بروز می‌کند.

Panel Toolbar
ابزاری موجود در این پنل مشابه پنل‌های دیگر در بالای پنل و در زیر تب پنل‌ها قرار دارد و شامل ابزارهای زیر می‌شود:

  • Edit Button : این ابزار به دو صورت Live Edit و Source Edit در دسترس هست که با کلیک بروی فلش کوچکی که در سمت راست دکمه قرار دارد می‌توان نوع آن را تغییر داد.
    انتخاب حالت Source Edit ، باعث می‌شود سورس فایل به همان صورتی که به مرورگر ارسال شده نمایش داده شود. ( کامنت‌ها ، فرمت فایل و ... حفظ می‌شود. )
    حالت Live Edit هم باعث نمایش محتویات فایل بصورت مرتب شده می‌شود. ( کامنت‌ها و دستوراتی که توسط مرورگر تفسیر نشده اند نمایش داده نمی‌شوند. )



  • CSS Location Menu : نام فایل جاری که در پنل در حال نمایش است را نمایش می‌دهد و همچنین با کلیک بروی آن ، فایل‌های استایل دیگری که در صفحه بارگزاری شده اند را نمایش می‌دهد. با کلیک بروی هرکدام از فایل‌های نمایش داده شده ، همان فایل در پنل باز می‌شود.

    استایل هایی که در خود صفحه تعریف شده اند با نام خود صفحه در این قسمت نمایش داده می‌شوند و اگر استایل‌های در چندین تگ style تعریف شده باشند ، دومین تگ با #2 ، سومین با #3 و ... مشخص می‌شوند.

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



    2- با تایپ کردن عبارت دلخواه می‌توان نتایج را محدود کرد:



Panel
منظور از پنل ، قسمتی هست که استایل بصورت فرمت شده و نمایش داده شده است.


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

 Editing rules
برای ویرایش تعاریف CSS شامل Selector‌ها ، دستورات و مقادیر ، این پنل ابزارهای مفیدی ارائه می‌کند.
برای ویرایش یک selector ، دستور یا مقدار آن بروی آن عبارت کلیک کنید ، در همین حال یک text box ظاهر می‌شود و می‌توانید مقدار جدید را وارد کنید. پس از انجام ویرایش مورد نظر بروی قسمتی از صفحه کلیک کنید یا کلید Tab سپس Esc را بزنید. برای ویرایش دستورات بعدی ، کلید Tab را بزنید و برای لغو تغییر جاری ، کلید Esc را بزنید.

برای ایجاد یک rule جدید ، بروی قسمتی از پنل راست کلیک کرده و سپس گزینه‌ی "New Rule..." را برگزینید.
برای ایجاد یک property هم چند راه وجود دارد:

  • بروی قسمت از فضای خالی تعریف یک استایل دوبار کلیک کنید.
  • در قسمتی از تعریف یک استایل راست کلیک کرده و گزینه‌ی "New Property..." را انتخاب کنید.
  • بروی مقدار آخرین property کلیک کرده و کلید Tab را بزنید.


هنگام ویرایش یا ایجاد یک Rule , Propery یا مقدار بصورت inline ، حاشیه‌ی text box متناسب با مقدار وارد شده رنگی را که نمایانگر حالت ذخیره‌ی مقدار وارد شده است نمایش می‌دهد. برای مثال اگر مقداری که برای یک selector وارد شده است نامعتبر باشد ، رنگ حاشیه‌ی text box قرمز می‌شود.
جدول زیر حالت‌های مختلف را شرح می‌دهد:

رنگ حاشیه
Selectorها
نام Properyها
مقادیر Propertyها و Ruleها
 خاکستری تغییری ایجاد نشده استتغییری ایجاد نشده است
تغییری ایجاد نشده است
 قرمز selector نامعتبر است
نام نامعتبر است
مقدار نامعتبر است
 زرد selector صحیح است اما بروی Element فعلی تاثیر ندارد
نام صحیح است اما مقدار Property غیر مجاز است یا وارد نشده است
 
 سبز selector صحیح است نام و مقدار صحیح هستند
مقدار صحیح است


Auto-completion
زمانی که در حال ایجاد/ویراش کردن یک Rule, Propery یا مقدار آنها هستید ، میتوانید از این قابلیت استفاده کنید. مثلا با وارد کردن # تمام Selector هایی که می‌توانند با # شروع شوند در دسترس شما هستند و با دکمه‌های Up/Down می‌توانید مقادیر ممکن را مرور کنید.
اگر در حال ویرایش یک مقدار عددی هستید ، می‌توانید با دکمه‌های Up/Down مقادیر را یک واحد یک واحد افزایش دهید. با نگه داشتن کلید Shift و فشردن Up/Down می‌توانید مقادیر را 10تا 10تا تغییر دهید و با نگه داشتن Ctrl بجای Sihft ، یک دهم یک دهم.

Toggling styles
با این امکان می‌توانید یک Property را بطور موقت فعال/غیرفعال کنید. با حرکت موس از یک Property یک آیکون قرمز رنگ در کنار آن نمایش داده می‌شود که با کلیک بروی آن ، Property و مقدار آن کمرنگ شده و آیکون قرمز رنگ کنار آن ثابت می‌شود. با کلیک مجدد بروی آن ، Property فعال می‌شود.



Context Menu
این منو زمانی که در پنل راست کلیک کنید ظاهر می‌شود و نسبت به منطقه (Context)ای که در آن راست کلیک کرده اید ، گزینه‌های متفاوتی را مشاهده خواهید کرد. در جدول زیر ، گزینه‌ها ، Contextشان و توضیح هر گزینه آمده است.

 گزینهContext
توضیحات
Copy Location
CSS Location Menuآدرس فایل استایل را در حافظه کپی می‌کند.
Open in New TabCSS Location Menuفایل استایل را در یک تب جدید باز می‌کند.
Copy Image Location
image valuesآدرس تصویر را در حافظه کپی می‌کند.
Open Image in New Tabimage valuesتصویر را در یک تب جدید باز می‌کند.
Copy Colorcolor valuesمقدار رنگ را در حافظه کپی می‌کند.
Copy Rule DeclarationCSS selectorSelector و Propertyها را در حافظه کپی می‌کند.
Copy Style DeclarationCSS selectorفقط Propertyها را در حافظه کپی می‌کند.
New Rule...
همه جای پنل
یک Rule جدید بالای قسمتی که راست کلیک شده ایجاد می‌کند.
Delete "<selector>"
CSS selectorRule را حذف می‌کند.
New Property...
CSS rule
یک Property جدید در Ruleی که در آن راست کلیک شده ایجاد می‌کند.
Edit "<property name>"...
CSS property
Property فعلی به حالت ویرایش درمی آید. ( راه ساده‌تر ، کلیک بروی Property است. )
Delete "<property name>"...CSS propertyProperty فعلی را حذف می‌کند.
Disable "<property name>"...
CSS property
Property فعلی را غیرفعال می‌کند.
Refreshهمه جای پنل
محتویات پنل را بروز رسانی می‌کند.
Inspect in DOM panel
CSS Location Menu, CSS rule
فایل استایل یا Rule را در پنل DOM باز می‌کند.


Elements Side Panel
در این پنل که سمت راست پنل CSS قرار دارد ، با وارد کردن یک CSS Selector می‌توانید Elementهایی که در صفحه با آن مطابقت دارند را مشاهده کنید.
برای وارد کردن CSS Selector هم می‌توان مقدار مورد نظر را در قسمت Try a selector... وارد کرد هم می‌توان بروی یکی از Selectorهای پنل راست کلیک کرد و گزینه‌ی Get Matching Elements را برگزید.
Context Menu این قسمت هم مشابه Context Menu پنل HTML هست و فقط در ورژن 1.11 گزینه‌ی Paste HTML اضافه شده که در این کامنت از مقاله‌ی آموزش فایرباگ - #5 - HTML Panel توضیح داده شده است.