نظرات مطالب
خلاصه‌ای کوتاه در مورد WinRT
آیا واقعاً WinRT جایگزینی برای WPF نخواهد بود؟
بازخوردهای پروژه‌ها
عدم استفاده از HttpContext
سلام و خسته نباشید
با توجه به اینکه استفاده از HttpContext  در لایه بیزینس برنامه کار تست را کمی مشکل می‌کند، در صورت امکان به جای استفاده از آن در متدها، از یک دیکشنری برای  httpContext . Request   ها استفاده شود و از کاربر گرفته شود
با تشکر از پروژه خوبتان
مطالب
HTML5 Web Component - قسمت اول - معرفی و مفاهیم اولیه
Web Components مجموعه‌ای از تکنولوژی‌هایی می‌باشند که امکان ساخت المان‌های سفارشی با قابلیت استفاده‌ی مجدد و به همراه کپسوله‌سازی ساختار، استایل و عاملیت (Functionality) متناظر با المان ایجاد شده را در اختیار شما قرار می‌دهد. 

A Path to Standard Components

در این سری چند قسمتی، ابتدا روش ساخت Web Components را بدون استفاده از ابزار خاصی بررسی کرده و در ادامه با معرفی Stenciljs، چند کامپوننت سفارشی را طراحی خواهیم کرد.

سه تکنولوژی اصلی مورد استفاده برای ساخت Web Components عبارتند از:

  • Custom Elements
  • Shadow DOM
  • HTML Templates


Custom Elements

مجموعه‌ای از API‌های جاوااسکریپتی هستند که امکان تعریف یکسری المان معتبر HTML را با قالب‌، رفتار و نام سفارشی، فراهم می‌کنند. علاوه بر اینکه می‌توان یک المان سفارشی مستقل (Autonomous custom elements) را تعریف کرد، امکان سفارشی‌سازی و گسترش المان‌های موجود (Customized built-in elements) را نیز خواهیم داشت. 
المان‌های سفارشی تعریف شده را مانند کامپوننت‌های Angular و یا React تصور کنید؛ با این تفاوت که هیچ وابستگی به Angular و یا React ندارند. همچنین امکان استفاده از آنها در انوع و اقسام فریمورک‌های فرانت-اند وجود دارد.
شکل ساده‌ی یک Custom Element، متشکل است از کلاسی که کلاس HTMLElement را گسترش می‌دهد و یک نام یکتا که باید حتما دارای یک «-» باشد (kebab-case). برای مثال:
//app.component.js
class AppComponent extends HTMLElement {
  static is = 'app-root'
  connectedCallback(){
    this.innerHTML=`<h1>Hello World!</h1>`
  }
}

customElements.define(AppComponent.is, AppComponent)

//index.html
<app-root></app-root>



در تکه کد بالا، از متد connectedCallback به عنوان یکی از متدهای چرخه‌ی حیات یک المان سفارشی، برای تنظیم innerHTML آن استفاده شده است. سپس با استفاده متد define مربوط به CustomElementRegistry که از طریق window.customeElements قابل دسترسی می‌باشد و با مشخص کردن نام و کلاس مرتبط، المان مورد نظر را ثبت و معرفی کردیم.

برای سفارشی‌سازی یک المان موجود، کار با ارث‌بری از کلاس متناظر با آن المان شروع می‌شود. به عنوان مثال در اینجا قصد داریم با کلیک بر روی لینک‌های موجود در صفحه و قبل از هدایت به آدرس مقصد، یک تأییدیه را از کاربر بگیریم:
//confirm-link.component.js
class ConfirmLinkComponent extends HTMLAnchorElement {
  static is = "confim-link";
  connectedCallback() {
    this.addEventListener("click", e => {
      if (!confirm("Are you sure?")) {
        e.preventDefault();
      }
    });
  }
}
customElements.define(ConfirmLinkComponent.is, ConfirmLinkComponent, {
  extends: "a"
});
<a href="http://google.com" is='confirm-link'>
google.com
</a>
در اینجا در بدنه‌ی متد connectedCallback، مشترک رخداد کلیک المان جاری شده و براساس نتیجه‌ی تأییدیه، تصمیم به ادامه یا لغو رفتار پیش‌فرض آن گرفته‌ایم. برای معرفی این المان جدید، کافی است از طریق آرگومان سوم متد define، مشخص کنیم که قصد گسترش چه المانی را داریم. از این پس اگر لینک‌های موجود را از طریق ویژگی is با "confirm-link" تزئین کرده باشید، شاهد رفتار سفارشی جدید خواهیم بود.

Shadow DOM

جنبه‌ی بسیار مهم Web Components، کپسوله‌سازی می‌باشد که امکان مخفی کردن ساختار، استایل و رفتار متناظر با المان سفارشی را مهیا می‌کند تا با سایر قسمت‌های کد تداخلی نداشته باشد. در این میان Shadow DOM نقش اصلی را برای رسیدن به این سطح از کپسوله‌سازی ایفا خواهد کرد. Shadow DOM به عنوان یک نسخه‌ی کپسوله شده‌ی DOM می‌باشد که امکان کپسوله‌سازی Markup & Styling و همچنین کاهش پیچیدگی استایل‌دهی را مهیا خواهد کرد. می‌توان Shadow DOM را همانند iframe تصور کرد که امکان نشتی استایل‌ها و سلکتورها از Light DOM (همان DOM اصلی) به داخل و از داخل به Light DOM وجود ندارد؛ ولی برخلاف iframe همچنان Shadow Rootهای (المان ریشه Shadow DOM) متناظر، در همان کانتکست DOM اصلی قرار دارند و همچنین در اینجا برای دسترسی به مقادیر المان‌های موجود در Shadow DOM، می‌توان یک API مناسب را در سطح المان سفارشی در نظر گرفت، ولی در مقابل برای همین کار در یک iframe نیاز است تا DOM متناظر با آن را Traverse کنیم که البته به عمد به این صورت طراحی شده است؛ چرا که اهداف دیگری را نیز دنبال می‌کند.

به تصویر زیر توجه نمائید:

برای مشاهده‌ی Shadow DOM متناظر با یکسری المان توکار مانند input.range یا input.date در مرورگر کروم، لازم است به قسمت Developer tools آن مراجعه کرده و سپس با فشردن دکمه‌ی F1 به قسمت تنظیمات آن مراجعه کرده و در قسمت Preferences آن و بخش Elements گزینه "Show user agent shadow DOM" را انتخاب کنید. در اینجا input به عنوان المانی که Shadow DOM به آن متصل شده (attach) نقش Shadow Host را ایفا می‌کند.
برای اتصال یک Shadow DOM به یک المان، به شکل زیر می‌توان عمل کرد:
var div = document.createElement('div');
var shadowRoot = div.attachShadow({mode: 'open'}); //or mode: 'closed'
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>';

متد attachShadow یک Shadow DOM Tree جدید را به div متصل کرده و ارجاعی به shadowRoot آن را برمی‌گرداند. این shadowRoot از طریق خصوصیت host نیز ارجاعی را به میزبان خود دارد. از این پس نیز از طریق خصوصیت div.shadowRoot امکان دسترسی به Shadow DOM ایجاد شده خواهیم داشت. البته به دلیل اینکه در اینجا با حالت 'open' استفاده شده است، این دسترسی به shadowRoot از طریق المان وجود دارد، در غیر این صورت اگر با 'closed' مقداردهی شود، خصوصیت div.shadowRoot مقدار نال خواهد داشت و امکان دسترسی به المان‌های داخلی از طریق جاوااسکریپت ممکن نخواهد بود. 
نکته: 'user-agent' نیز حالتی است که برای المان‌های توکار مانند input.range و ... مورد استفاده قرار می‌گیرد و رفتاری شبیه به حالت 'closed' را دارد.
نکته: امکان اتصال Shadow DOM به المان‌های خاصی از جمله div و یا المان‌های سفارشی که کلاس HTMLElement را گسترش می‌دهند (این اتصال در زمان پیاده‌سازی آنها انجام خواهد شد)، وجود دارد.
در زمان استفاده از متد attachShadow، علاوه بر امکان تعیین حالت آن، امکان تنظیم delegatesFocus برای برطرف کردن موضوعات مرتبط با focus در زمان توسعه‌ی المان‌های سفارشی مورد استفاده قرار می‌گیرد. به این صورت که اگر در قسمتی از المان سفارشی که خاصیت و قابلیت focus را ندارد، کلیک شود، focus به اولین المان با قابلیت focus داخل المان سفارشی خواهد رسید و از این پس امکان استایل‌دهی با استفاده از سلکتور custom-element:focus را خواهیم داشت.

مفهوم دیگری وجود دارد تحت عنوان Shadow Boundary که تعیین کننده‌ی مرز بین Light DOM و Shadow DOM و همان لایه‌ی مهیا کننده‌ی کپسوله‌سازی Styling و Markup می‌باشد. در مطالب آتی خواهیم دید که به چه شکلی رخدادهای سفارشی ما قابلیت عبور از این لایه را خواهند داشت.


HTML Templates 

تا قبل از اینکه المان template معرفی شود، از یکی از روش‌های زیر برای استفاده‌ی مجدد از یک قالب HTML عمل می‌کردیم:
1- استفاده از یک المان خاص با ویژگی hidden یا استایل display:none 
  • استفاده از DOM و آگاه بودن مرورگر از وجود آن، عملیات clone را ساده خواهد کرد.
  • محتوای داخل آن رندر نخواهد شد.
  • اگرچه محتوای آن مخفی می‌باشد، با این حال درخواست‌های مرتبط با تصاویر یا اسکریپت‌ها انجام خواهد شد.
<div id="template" hidden>
  <img src="logo.png">
  <div class="comment"></div>
</div>

2- استفاده از تگ script با یک type سفارشی
<script id="template" type="text/x-template">
  <img src="logo.png">
  <div class="comment"></div>
</script>

<script id="template" type="text/x-kendo-template">
        <ul>
            # for (var i = 0; i < data.length; i++) { #
            <li>#= data[i] #</li>
            # } #
        </ul>
</script>
  • از آنجا که تگ script به صورت پیش‌فرض دارای استایل display:none می‌باشد، محتوای داخل آن رندر نخواهد شد.
  • به دلیل عدم مقداردهی ویژگی type آن با "text/javascript"، مرورگر محتوای آن را به عنوان کد جاوااسکریپت parse نخواهد کرد.
  • استفاده از خصوصیت innerHTML مشکل امنیتی XSS را بدنبال خواهد داشت .
HTML Template ویژگی‌های مثبت هر دو روش قبل را دارد. از طریق کد امکان clone و رندر کردن آن وجود دارد و همچنین کوئری المان‌های داخل آن نیز ممکن نیست:
<template id="template">
  <img src="logo.png">
  <div class="comment"></div>
</template>
و برای فعال‌سازی آن از طریق متد cloneNode متناظر با خصوصیت content به شکل زیر عمل خواهیم کرد:
  var template = document.querySelector("template");
  var clonedNode = template.content.cloneNode(true); //deep:true
  document.body.appendChild(clonedNode);
نکته: امکان تعریف قالب‌های تودرتو نیز وجود دارد که در این صورت به شکل جداگانه‌ای باید عملیات فعال‌سازی هر کدام از آنها انجام گیرد.
نظرات اشتراک‌ها
نگارش نهایی RTL Bootstrap 3 ویرایش Less منتشر شد!
ضمن تشکر از زحمتی که برای بروز نگاه داشتن این بسته متحمل می‌شید پیشنهاد می‌کنم جهت افزایش سازگاری با انواع مرورگرها در بومی سازی فریم ورک‌ها و مواردی این چنین استایل راست به چپ را مستقیماً به تگ body اعمال ننمایید. خاصیت direction را می‌توان به wrapper‌های دیگر و در صورت عدم وجود به همه المنت‌های لازم اعمال نمود. اعمال دایرکشن به تگ body معایبی در مرورگرهایی مانند IE و Opera و ... ایجاد می‌کند که بیش از مزایای آن است و با توجه به راه‌های جایگزین ضرورتی ندارد.
مطالب
نوشتن افزونه برای مرورگرها: قسمت اول : کروم
افزونه چیست؟
افزونه‌ها جزء مهمترین قسمت‌های یک مرورگر توسعه پذیر به شمار می‌آیند. افزونه‌ها سعی دارند تا قابلیت هایی را به مرورگر شما اضافه کنند. افزونه‌ها از آخرین فناوری‌های html,CSS و جاوااسکریپت تا به آنجایی که مرورگر آن‌ها را پشتیبانی کند، استفاده می‌کنند.
در این سری سعی خواهیم کرد برای هر مرورگر شناخته شده، یک افزونه ایجاد کنیم و ابتدا از آنجا که خودم از کروم استفاده می‌کنم، اولین افزونه را برای کروم خواهم نوشت.

این افزونه قرار است چه کاری انجام دهد؟
کاری که برای این افزونه تدارک دیده‌ام این است: موقعی‌که سایت dotnettips.info به روز شد مرا آگاه کند. این آگاه سازی را از طریق یک نوتیفیکیشن به اطلاع کاربر میرسانیم. صفحه تنظیمات این افزونه شامل گزینه‌های "آخرین مطالب"،"نظرات آخرین مطالب"،"آخرین اشتراک ها"و"آخرین نظرات اشتراک ها" خواهد بود که به طور پیش فرض تنها گزینه اول فعال خواهد بود و همچنین یک گزینه نیز برای وارد کردن یک عدد صحیح جهت اینکه به افزونه بگوییم هر چند دقیقه یکبار سایت را چک کن. چک کردن سایت هم از طریق فید RSS صورت می‌گیرد.

فایل manifest.json
این فایل برای ذخیره سازی اطلاعاتی در مورد افزونه به کار می‌رود که شامل نام افزونه، توضیح کوتاه در مورد افزونه و ورژن و ... به کار می‌رود که همه این اطلاعات در قالب یا فرمت json نوشته می‌شوند و در بالاترین حد استفاده برای تعریف اهداف افزونه و اعطای مجوز به افزونه از آن استفاده می‌کنیم. این فایل بخش‌های زیر را در یک افزونه تعریف می‌کند که به مرور با آن آشنا می‌شویم.


کد زیر را در فایل manifest.json می‌نویسیم:
{
  "manifest_version": 2,

  "name": "Dotnettips Updater",
  "description": "This extension keeps you updated on current activities on dotnettips.info",
  "version": "1.0",
  "icons": { "16": "icon.png",
           "48": "icon.png",
          "128": "icon.png" },

  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "permissions": [
    "activeTab",
    "https://www.dntips.ir"
  ]
}
اطلاعات اولیه شامل نام و توضیح و ورژن افزونه است. ورژن برنامه برای به روزآوری افزونه بسیار مهم است. موقعی که ورژن جدیدی از افزونه ارائه شود، گوگل وب استور اعلان آپدیت جدیدی را برای افزونه میکند. آیکن قسمت‌های مختلف افزونه هم با icons مشخص می‌شود که در سه اندازه باید ارائه شوند و البته اگه اندازه آن نباشد scale می‌شود. قسمت بعدی تعریف UI برنامه هست که گوگل کروم، به آن Browser Action می‌گوید. در اینجا یک آیکن و همچنین یک صفحه اختصاصی برای تنظیمات افزونه معرفی می‌کنیم. این آیکن کنار نوار آدرس نمایش داده می‌شود و صفحه popup موقعی نشان داده می‌شود که کاربر روی آن کلیک می‌کند. آیکن‌ها برای browser action در دو اندازه 19 و 38 پیکسلی هستند و در صورتی که تنها یک آیکن تعریف شود، به صورت خودکار عمل scale و تغییر اندازه صورت می‌گیرد. برای تعیین عکس برای هر اندازه می‌توانید کد را به صورت زیر بنویسید:
"default_icon": {                    // optional
            "19": "images/icon19.png",           // optional
            "38": "images/icon38.png"            // optional
          }
قسمت popup برای نمایش تنظیمات به کار می‌رود و درست کردن این صفحه همانند صفحه همیشگی html هست و خروجی آن روی پنجره popup افزونه رندر خواهد شد.
گزینه default_title نیز یکی از دیگر خصیصه‌های مهم و پرکاربرد این قسمت هست که متن tooltip می‌باشد و موقعی که که کاربر، اشاره‌گر را روی آیکن ببرد نمایش داده می‌شود و در صورتی که نوشته نشود، کروم نام افزونه را نمایش می‌دهد؛ برای همین ما هم چیزی ننوشتیم.

صفحات پس‌زمینه
اگر بخواهید برای صفحه popup کد جاوااسکریت بنویسید یا از jquery استفاده کنید، مانند هر صفحه‌ی وبی که درست می‌کنید آن را کنار فایل popup قرار داده و در popup آنها را صدا کرده و از آن‌ها استفاده کنید. ولی برای پردازش هایی که نیاز به UI وجود ندارد، می‌توان از صفحات پس زمینه استفاده کرد. در این حالت ما دو نوع صفحه داریم:
  1. صفحات مصر یا Persistent Page
  2. صفحات رویدادگرا یا Events Pages
اولین نوع صفحه، همواره فعال و در حال اجراست و دومی موقعی فعال می‌شود که به استفاده از آن نیاز است. گوگل توصیه می‌کند که  تا جای ممکن از نوع دوم استفاده شود تا  مقدار حافظه مصرفی  حفظ شود  و کارآیی مروگر بهبود بخشیده شود. کد زیر یک صفحه پس زمینه را از نوع رویدادگرا می‌سازد. به وضوح روشن است در صورتی که خاصیت Persistent با true مقداردهی شود، این صفحه مصرانه در تمام وقت باز بودن مرورگر، فعال خواهد بود:
"background": {
    "scripts": ["background.js"],
    "persistent": false
}

Content Script یا اسکریپت محتوا
در صورتی که بخواهید با هر صفحه‌ای که باز یا رفرش می‌شود، به DOM آن دسترسی پیدا کنید، از این خصوصیت استفاده کنید. در کد زیر برای پردازش اطلاعات DOM از فایل جاوااسکریپت بهره برده و در قسمت matches می‌گویید که چه صفحاتی باید از این کد استفاده کنند که در اینجا از پروتکل‌های HTTP استفاده میشود و اگر مثلا نوع FTP یا file صدا زده شود کد مورد نظر اجرا نخواهد شد. در مورد اینکه matches چگونه کار می‌کند و چگونه می‌توان آن را نوشت، از این صفحه استفاده کنید.
"content_scripts": [
    {
        "matches": ["http://*/*", "https://*/*"],
        "js": ["content.js"]
    }
]

آغاز کدنویسی (رابط‌های کاربری)


اجازه دهید بقیه موارد را در حین کدنویسی تجربه کنیم و هر آنچه ماند را بعدا توضیح خواهیم داد. در اینجا من از یک صفحه با کد HTML زیر بهره برده ام که یک فرم دارد به همراه چهار چک باکس و در نهایت یک دکمه جهت ذخیره مقادیر. نام صفحه را popup.htm گذاشته ام و یک فایل popup.js هم دارم که در آن کد jquery نوشتم. قصد من این است که بتوان یک action browser به شکل زیر درست کنم:


کد html آن به شرح زیر است:
<html>
<head>
<meta charset="utf-8"/>

<script src="jquery.min.js"></script> <!-- Including jQuery -->
<script type="text/javascript" src="popup.js"></script>
</head>
<body style="direction:rtl;width:250px;">
<form >
<input type="checkbox" id="chkarticles" value="" checked="true">آخرین مطالب سایت</input><br/>
<input type="checkbox" id="chkarticlescomments" value="" >آخرین نظرات مطالب سایت</input><br/>
<input type="checkbox" id="chkshares" value="" >آخرین اشتراک‌های سایت</input><br/>
<input type="checkbox"  id="chksharescomments" value="" >آخرین نظرات اشتراک‌های سایت</input><br/>
<input id="btnsave" type="button" value="ذخیره تغییرات" />
    <div id="messageboard" style="color:green;"></div>
</form>

</body>
</html>
کد popup.js هم به شرح زیر است:
$(document).ready(function () {
    $("#btnsave").click(function() {
        var articles = $("#chkarticles").is(':checked');
        var articlesComments = $("#chkarticlescomments").is(':checked');
        var shares = $("#chkshares").is(':checked');
        var sharesComments = $("#chksharescomments").is(':checked');

        chrome.storage.local.set({ 'articles': articles, 'articlesComments': articlesComments, 'shares': shares, 'sharesComments': sharesComments }, function() {
            $("#messageboard").text( 'تنظیمات جدید اعمال شد');
        });
    });
});
در کد بالا موقعی که کاربر بر روی دکمه ذخیره، کلیک کند رویداد کلیک jquery فعال شده و مقادیر چک باکس‌ها را در متغیرهای مربوطه نگهداری می‌کند. نهایتا با استفاده از کلمه کلیدی کروم به ناحیه ذخیره سازی داده‌های کروم دست پیدا کرده و درخواست ذخیره مقادیر چک باکس را بر اساس ساختار نام و مقدار، ذخیره می‌کنیم و بعد از اعمال، توسط یک تابع callback به کاربر اعلام می‌کنیم که اطلاعات ذخیره شده است.
اولین مورد جدیدی که در بالا دیدیم، کلمه‌ی کلیدی chrome است. کروم برای توسعه دهندگانی که قصد نوشتن افزونه دارند api هایی را تدارک دیده است که میتوانید با استفاده از آنها به قسمت‌های مختلف مرورگر مثل بوک مارک یا تاریخچه فعالیت‌های مرورگر و ... دست پیدا کنید. البته برای اینکار باید در فایل manifest.json هم مجوز اینکار را درخواست نماییم. این ویژگی باید برای برنامه نویسان اندروید آشنا باشد. برای آشنایی هر چه بیشتر با مجوزها این صفحه را ببینید.
برای دریافت مجوز، کد زیر را به manifest اضافه می‌کنیم:
"permissions": [
    "storage"
  ]
مجوزی که در بالا درخواست کرده‌ایم مجوز دسترسی به ناحیه ذخیره سازی است. بعد از کلمه کلیدی chrome، کلمه‌ی local آمده است و می‌گوید که باید داده‌ها به صورت محلی و لوکال ذخیره شوند ولی اگر میخواهید داده‌ها در گوگل سینک شوند، باید به جای لوکال از کلمه کلیدی sync استفاده کنید یعنی:
chrome.storage.sync.set
فایل manifest نهایی:
{
  "manifest_version": 2,

  "name": "Dotnettips Updater",
  "description": "This extension keeps you updated on current activities on dotnettips.info",
  "version": "1.0",

  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "permissions": [
    "storage"
  ]
}
الان باید 4 فایل داشته باشید: فایل آیکن، popup.htm,popup.js و manifest.json. همه را داخل یک دایرکتوری قرار داده و در مروگر کروم به قسمت extensions بروید و گزینه Developer mode را فعال کنید تا یک تستی از کد نوشته شده بگیریم. گزینه Load Unpacked Extension را بزنید و آدرس دایرکتوری ایجاد شده را به آن بدهید.
chrome://extensions

الان باید مانند تصویر بالا یک آیکن کنار نوار آدرس یا به قول گوگل، Omni box ببینید. گزینه‌ها را تیک بزنید و روی دکمه ذخیره کلیک کنید. باید پیام مقادیر ذخیره شدند، نمایش پیدا کند. الان یک مشکل وجود دارد؛ داده‌ها ذخیره می‌شوند ولی موقعی که دوباره تنظیمات افزونه را باز کنید حالت اولیه نمایش داده میشود. پس باید تنظیمات ذخیره شده را خوانده و به آن‌ها اعمال کنیم. کد زیر را جهت دریافت مقادیر ذخیره شده می‌نویسیم. اینبار به جای استفاده از متد set از متد get استفاده می‌کنیم. به صورت آرایه، رشته نام مقادیر را درخواست می‌کنیم و در تابع callback، مقادیر به صورت آرایه برای ما برگشت داده می‌شوند.
    chrome.storage.local.get(['articles', 'articlesComments', 'shares', 'sharesComments'], function ( items) {
        console.log(items[0]);
        $("#chkarticles").attr("checked", items["articles"]);
        $("#chkarticlescomments").attr("checked", items["articlesComments"]);
        $("#chkshares").attr("checked", items["shares"]);
        $("#chksharescomments").attr("checked", items["sharesComments"]);

    });
حالا برای اینکه افزونه‌ی شما متوجه تغییرات شود، به تب extensions رفته و در لیست افزونه‌ها به دنبال افزونه خود بگردید و گزینه Reload را انتخاب نمایید تا افزونه تغییرات را متوجه شود و صفحه را تست کنید.

Page Action
روش دیگر برای ارائه یک رابط کاربری، page action هست. این روش دقیقا مانند روش قبلی است، ولی جای آیکن عوض می‌شود. قبلا بیرون از نوار آدرس بود، ولی الان داخل نوار آدرس قرار می‌گیرد. جالب‌ترین نکته در این مورد این است که این آیکن در ابتدا مخفی شده است و شما تصمیم می‌گیرید که این آیکن چه موقع نمایش داده شود. مثلا آیکن RSS تنها موقعی نمایش داده می‌شود که وب سایتی که باز شده است، دارای محتوای RSS باشد یا بوک مارک کردن یک آدرس برای همه‌ی سایت‌ها باز باشد و سایر موارد.
کد زیر نحوه‌ی تعریف یک page action را در manifest نشان می‌دهد. ما در این مثال یک page action را به طور موقت اضافه می‌کنیم و موقعی هم آن را نشان میدهیم که سایت dotnettips.info باز باشد. دلیل اینکه موقت اضافه می‌کنیم این است که باید یکی از دو گزینه رابط کاربری که تا به حال گفتیم، استفاده شود. در غیر این صورت کروم در هنگام خواندن فایل manifest در هنگام افزودن افزونه به مرورگر، پیام خطا خواهد داد و این مطلب را به شما گوشزد می‌کند. پس نمی‌توان دو گزینه را همزمان داشت و من میخواهم افزونه را در حالت browser action ارائه کنم. پس در پروژه نهایی، این مطلب page action نخواهد بود. برای داشتن یک page action کد زیر را در manifest بنویسید.
  "page_action": {
    "default_icon": {
        "19": "images/icon19.png",
        "38": "images/icon38.png"
    },
    "default_popup": "popup.html"
گزینه page action تعریف شد حالا باید کاری کنیم تا هر موقع صفحه‌ای باز می‌شود چک کند آیا سایت مورد نظر است یا خیر، اینکار را توسط صفحه‌ی پردازشی انجام می‌دهیم. پس تکه کد زیر را هم به manifest اضافه می‌کنیم:
"background": {
    "scripts": ["page_action_validator.js"]
}

تا اینجا فایل جاوااسکریپت معرفی شد که کد زیر را دارد و در پس زمینه شروع به اجرا می‌کند.
function UrlValidation(tabId, changeInfo, tab) {
if (tab.url.indexOf('dotnettips.info') >-1) {
chrome.pageAction.show(tabId);
}
};
chrome.tabs.onUpdated.addListener(UrlValidation);
چون از api در این کد بهره برده‌ایم و آن هم مدیریت بر روی تب هاست، پس باید مجوز آن هم گرفته شود. کلمه "tabs" را در قسمت permissions اضافه کنید.
یک listener برای tabها ایجاد کرده‌ایم که اگر تب جدید ایجاد شد، یا تب قبلی به آدرس جدیدی تغییر پیدا کرد تابع UrlValidation را اجرا کند و در این تابع چک می‌کنیم که اگر url این تب شامل نام وب سایت می‌شود، page action روی این تب ظاهر شود. پس از انجام تغییرات، مجددا افزونه را بارگذاری می‌کنیم و تغییرات اعمال شده را می‌بینیم. سایت dotnettips را باز کنید یا صفحه را مجددا رفرش کنید تا تغییر اعمال شده را ببینید.

تغییرات موقت را حذف و کدها را به حالت قبلی یعنی browser action بر میگردانم.

OmniBox
omnibox یک کلمه کلیدی است که در نوار آدرس مرورگر وارد می‌شود و در واقع می‌توانیم آن را نوع دیگری از رابط کاربری بنامیم. موقعی که شما کلمه کلیدی رزرو شده را وارد می‌کنید، در نوار آدرس کلماتی نشان داده میشود که کاربر میتواند یکی از آن‌ها را انتخاب کند تا عملی انجام شود. ما هم قرار است این کار را انجام دهیم. به این مثال دقت کنید:
میخواهیم موقعی که کاربرکلمه net. را تایپ می‌کند، 5 عبارت آخرین مطالب و آخرین اشتراک‌ها و آخرین نظرات مطالب و آخرین نظرات اشتراک‌ها و صفحه اصلی سایت نمایش داده شود و با انتخاب هر کدام، کاربر به سمت آن صفحه هدایت شود.
برای افزودن کلمه کلیدی در manifest خطوط زیر را اضافه کنید:
"omnibox": { "keyword" : ".net" }
با نوشتن خط بالا کلمه net. در مرورگر یک کلمه‌ی کلیدی به حساب خواهد آمد و موقعی که کاربر این کلمه را وارد کند، در سمت راست نوشته خواهد شد. در این حالت باید کلید تب را بزند تا به محیط دستوری آن برود.

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

 
 برای اینکار باید کدنویسی کنیم ، پس یک فایل پس زمینه را به manifest معرفی کنید:
"background": {
    "scripts": ["omnibox.js"]
در فایل ominbox.js دستوراتی که مرتبط با omnibox است را می‌نویسیم و کد زیر را به آن اضافه می‌کنیم:
chrome.omnibox.onInputChanged.addListener(function(text, suggest) {
    suggest([
  {content: ".net tips Home Page", description: "صفحه اصلی"},
      {content: ".net tips Posts", description: "آخرین مطالب"},
      {content: ".net tips News", description: "آخرین نظرات مطالب"},
      {content: ".net tips Post Comments", description: "آخرین اشتراک ها"},
      {content: ".net tips News Comments", description: "آخرین نظرات اشتراک ها"}
    ]);
});
chrome.omnibox شامل 4 رویداد می‌شود:
 onInputStarted   بعد از اینکه کاربر کلمه کلیدی را وارد کرد اجرا می‌شود
onInputChanged 
  بعد از وارد کردن کلمه کلیدی هربار که کاربر تغییری در ورودی نوارد آدرس می‌دهد اجرا می‌شود.
 onInputEntered   کاربر ورودی خود را تایید می‌کند. مثلا بعد از وارد کردن، کلید enter را می‌فشارد
 onInputCancelled  کاربر از وارد کردن ورودی منصرف شده است؛ مثلا کلید ESC را فشرده است. 
با نوشتن chrome.omnibox.onInputChanged.addListener ما یک listener ساخته‌ایم تا هر بار کاربر ورودی را تغییر داد، یک تابع callback که دو آرگومان را دارد، صدا بزند. این آرگومان‌ها یکی متن ورودی‌است و دیگری آرایه‌ی suggest که شما با تغییر آرایه می‌توانید عباراتی که همزمان با تایپ به کاربر پیشنهاد می‌شود را نشان دهید. البته می‌توانید با تغییر کد کاری کنید تا بر اساس حروفی که تا به حال تایپ کرده‌اید، دستورات را نشان دهد؛ ولی من به دلیل اینکه 5 دستور بیشتر نبود و کاربر راحت باشد، چنین کاری نکردم. همچنین وقتی شما برای هر یک description تعریف کنید، به جای نام پیشنهادی، توضیح آن را نمایش می‌دهد.
حالا وقت این است که کد زیر را جهت اینکه اگر کاربر یکی از کلمات پیشنهادی را انتخاب کرد، به صفحه‌ی مورد نظر هدایت شود، اضافه کنیم:
chrome.omnibox.onInputEntered.addListener(function (text) {

var location="";
    switch(text)
{
case ".net tips Posts":
location="https://www.dntips.ir/postsarchive";
break;
case ".net tips News":
location="https://www.dntips.ir/newsarchive";
break;
case ".net tips Post Comments":
location="https://www.dntips.ir/commentsarchive";
break;
case".net tips News Comments":
location="https://www.dntips.ir/newsarchive/comments";
break;
default:
location="https://www.dntips.ir/";
}

    chrome.tabs.getSelected(null, function (tab) {
        chrome.tabs.update(tab.id, { url: location });
    });
});
ابتدا یک listener برای روی رویداد onInputEntered قرار داده تا وقتی کاربر عبارت وارد شده را تایید کرد، اجرا شود. در مرحله بعد چک می‌کنیم که عبارت وارد شده چیست و به ازای هر عبارت مشخص شده، آدرس آن صفحه را در متغیر location قرار می‌دهیم. در نهایت با استفاده از عبارت chrome.tabs.getSelected تب انتخابی را به یک تابع callback بر میگردانیم. اولین آرگومان windowId است، برای زمانی که چند پنجره کروم باز است که می‌توانید وارد نکنید تا پنجره فعلی و تب فعلی محسوب شود. برای همین نال رد کردیم. در تابع برگشتی، شیء tab شامل اطلاعات کاملی از آن تب مانند url و id و title می‌باشد و در نهایت با استفاده از دستور chrome.tabs.update اطلاعات تب را به روز می‌کنیم. آرگومان اول id تب را میدهیم تا بداند کدام تب باید تغییر کند و آرگومان بعدی می‌توانید هر یک از ویژگی‌های تب از قبیل آدرس فعلی یا عنوان آن و ... را تغییر دهید که ما آدرس آن را تغییر داده ایم.

Context Menu
یکی دیگر از رابط‌های کاربری، منوی کانتکست هست که توسط chrome.contextmenus ارائه می‌شود و به مجوز "contextmenus" نیاز دارد. فعال سازی منوی کانتکست در قسمت‌های زیر ممکن است:
all, page, frame, selection, link, editable, image, video, audio 
من گزینه‌ی dotenettips.info را برای باز کردن سایت، به Contextmenus اضافه می‌کنم. کد را در فایلی به اسم contextmenus.js ایجاد می‌کنم و در قسمت background آن را معرفی می‌کنم. برای باز کردن یک تب جدید برای سایت، نیاز به chrome.tabs داریم که البته  نیاز به مجوز tabs هم داریم.
محتوای فایل contextmenus.js
var root = chrome.contextMenus.create({
    title: 'Open .net tips',
    contexts: ['page']
}, function () {
    var Home= chrome.contextMenus.create({
        title: 'Home',
        contexts: ['page'],
        parentId: root,
        onclick: function (evt) {
            chrome.tabs.create({ url: 'https://www.dntips.ir' })
        }
    });
var Posts = chrome.contextMenus.create({
        title: 'Posts',
        contexts: ['page'],
        parentId: root,
        onclick: function (evt) {
            chrome.tabs.create({ url: 'https://www.dntips.ir/postsarchive/' })
        }
    });
});
در کد بالا یک گزینه به context menu اضافه میشود و دو زیر منو هم دارد که یکی صفحه‌ی اصلی سایت را باز میکند و دیگری هم صفحه‌ی مطالب سایت را باز می‌کند.

تا به اینجا ما قسمت ظاهری کار را آماده کرده ایم و به دلیل اینکه مطلب طولانی نشود، این مطلب را در دو قسمت ارائه خواهیم کرد. در قسمت بعدی نحوه خواندن RSS و اطلاع رسانی و دیگر موارد را بررسی خواهیم کرد.
نظرات مطالب
ساخت DropDownList های مرتبط به کمک jQuery Ajax در MVC
1- من dropdownlist رو درست کردم ولی لازمه که توی کنترلرم مقدارش رو بدونم تا بتونم توی دیتابیسم ذخیره کنم ... چه جوری بدونم مقدارش چیه؟ 
2- با تگ input دوتا radio button درست کردم به مقدارهای این دو هم چه جوری از کنترلر دسترسی پیدا کنم (یعنی چطور متوجه شم که کاربر کودوم رو انتخاب کرده که براساس انتخابش باید توی جدولهای جداگونه ذخیره بشه )
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایل‌های استاتیک
یک موضوعی رو میخواستم مطرح کنم :
طبق یکی از مقالات سری ASP.net MVC سایت با استفاده از Controller فایل‌های آپلود شده رو با یک کلید ،خروجی میداد.

بنده همین موضوع رو در تکنولوژی جدید پیاده سازی کردم اما با مشکل عدم نمایش فایل یا تصویر در خروجی مواجه شدم


موجود بودن فیزیکی فایل هم در مسیر wwwroot/StaticImages/ و هم مسیر MyStaticImages/ :

و نحوه آدرس دهی :
<img src='@Url.Action("DownloadFile", "ImageHandler", new {Area = "", id = item.BaseFileGuids, imgSize = ImageHandlerController.ImgSize.M})' alt=""/>

مسیر به درستی نمایش داده شده و فایل هم پس از بررسی توسط : System.IO.File.Exists = true  می‌باشد.
اما در نمایش چه ادرس مستقیم و چه تگ <img>  خطای زیر نمایش داده میشود :


هر دو مسیر تست شده با قطعه کد زیر ، اما خطا مشابه می‌باشد
چه این گزینه hostingEnvironment.WebRootPath_
و چه این گزینه hostingEnvironment.ContentRootPath _ 
public IActionResult DownloadFile([FromRoute]string id, [FromQuery] ImgSize imgSize)
        {
            var result = _baseFileService.GetFileNameAndFileNameOnDsAndFileType(id);
            if (result == null) return View("Error");

            var fileName = result.Item1;
            string userAgent = Request.Headers["User-Agent"];
            if (IsInternetExplorer(userAgent))
            {
                var htencode = HtmlEncoder.Create();
                var attachment = string.Format("attachment; filename=\"{0}\"", htencode.Encode(fileName));
                _httpContext.HttpContext.Response.Headers.Add("Content-Disposition", attachment);
            }
            var rootPath = Path.Combine(_hostingEnvironment.WebRootPath, _settingsAppPathConfig.Value.ServerImagesRootPath);
            var filepath = Path.Combine(rootPath, imgSize.ToString().ToLower(), result.Item2);
            if (!System.IO.File.Exists(filepath))
            {
                const string notFoundImage = "notFound.jpg";
                var notFoundpath = Path.Combine(rootPath , notFoundImage);
                string contentType;
                new FileExtensionContentTypeProvider().TryGetContentType(notFoundImage, out contentType);
                return File(notFoundpath, contentType, notFoundImage);
            }
            string contentTypebase;
            new FileExtensionContentTypeProvider().TryGetContentType(result.Item3, out contentTypebase);
            return File(filepath, contentTypebase, fileName);
        }
مطالب
معرفی Selector های CSS - قسمت 5
41- :checked
برای تگ‌های radio و checkbox استفاده می‌شود و آنهایی را انتخاب می‌کند که گزینه‌ی آن‌ها انتخاب شده است یا شامل ویژگی checked می‌باشند.
<style>
    :checked {
        width: 50px;
        height: 50px;
    }
</style>
<input type="checkbox" checked="checked"/>
<input type="checkbox"/>
<input type="radio" name="test"/>
<input type="radio" name="test" checked="checked"/>
<input type="radio" name="test"/>
در مثال فوق تمامی checkbox هایی که گزینه‌ی آن‌ها انتخاب شده‌اند با ابعاد 50 در 50 نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :checked  3

42- :indeterminate
در HTML نمی‌توان وضعیت indeterminate را برای checkbox ایجاد نمود. اما با جاوا اسکریپت می‌توان این وضعیت را ایجاد کرد. هر checkbox می‌تواند دارای سه وضعیت انتخاب شده، انتخاب نشده و نامعلوم (indeterminate) باشد. این Selector تگهای checkbox ی را انتخاب می‌نماید که وضعیت نامعلوم دارند.
<style>
    :indeterminate {
        width: 50px;
        height: 50px;
    }
</style>
<input type="checkbox" id="chk1"/>
<input type="checkbox" id="chk2" checked="checked"/>
<input type="checkbox" id="chk3"/>
<script>
    document.getElementById("chk1").indeterminate=true;
</script>
در مثال فوق chk1 به صورت indeterminate انتخاب شده است که با ابعاد 50 در 50 نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :indeterminate  4

43- :default
برای تگ‌های radio و checkbox، آنهایی را انتخاب می‌کند که در زمان بارگزاری فرم و بصورت پیش فرض انتخاب شده‌اند. برای دکمه‌ی submit ایی نیز استفاده می‌شود که در حال حاضر در فرم فعال می‌باشد. پشتیبانی از این Selector در مرورگرها متفاوت می‌باشد؛ بنابراین در چند مرورگر کارآیی آن را بررسی کنید.
<style>
    :default {
        width: 50px;
        height: 50px;
        background: lime;
    }
</style>
<form>
    <input type="checkbox" checked="checked" />
    <input type="checkbox" />
    <input type="radio" name="radio1" />
    <input type="radio" name="radio1" checked="checked" />
    <input type="radio" name="radio1" />
    <input type="submit" value="Default" />
    <input type="submit" value="Submit 2" />
</form>
در مثال فوق checkbox و radio هایی که در لود اولیه فرم دارای ویژگی checked می‌باشند به همراه دکمه‌ی submit با عنوان Default با ابعاد 50 در 50 و رنگ پس زمینه‌ی لیمویی نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :default  4

44- :root
المنت ریشه‌ی یک صفحه را انتخاب می‌نماید. معمولا تگ html به عنوان المنت ریشه انتخاب می‌شود.
<style>
    :root {
        background: khaki;
    }
</style>
در مثال فوق رنگ پس زمینه به رنگ خاکی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :root  3

45- :after
جهت درج محتوا بعد از تگ استفاده می‌شود.
<style>
    form:after {
        content: "[ * : Required]";
        color: red;            
    }
        input+span:after {
            content: ' * ';
            color: red;
        }
</style>
<form>
    <div><input type="text" /><span></span></div>
    <div><input type="password" /><span></span></div>
    <div><input type="email" /><span></span></div>
</form>
در مثال فوق بعد از تمامی span‌های قرار گرفته در کنار تگ‌های input، یک کاراکتر * را به رنگ قرمز نمایش میدهد. بعد از فرم نیز رشته‌ی [ * : Required] را به رنگ قرمز نمایش می‌دهد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1 7.0  9.0 3.5  4.0 :after 2

46- :before
جهت درج محتوا قبل از تگ استفاده می‌شود.
<style>
    form:before {
        content: "[ * : Required]";
        color: red;
    }

    input + span:before {
        content: ' * ';
        color: red;
    }
</style>
<form>
    <div><input type="text" /><span></span></div>
    <div><input type="password" /><span></span></div>
    <div><input type="email" /><span></span></div>
</form>
در مثال فوق قبل از تمامی span‌های قرار گرفته در کنار تگ‌های input، یک کاراکتر * را به رنگ قرمز نمایش می‌دهد. قبل از فرم نیز رشته‌ی [ * : Required] را به رنگ قرمز نمایش می‌دهد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1 7.0  9.0 3.5  4.0 :before 2

47- ::selection
محتوایی را انتخاب می‌کند که در حال حاضر در صفحه انتخاب یا Select شده‌است.
<style>
    ::selection {
        background: navy;
        color: orange;
    }
</style>
<p>
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 
    Maecenas porttitor congue massa. Fusce posuere, 
    magna sed pulvinar ultricies, purus lectus malesuada libero, 
    sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.
</p>
در مثال فوق با انتخاب متن توسط ماوس، رنگ زمینه‌ی متن انتخاب شده به صورت سرمه ای و رنگ متن آن نارنجی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1  9.6  9.0 2.0
-moz-
 4.0 ::selection  در CSS 3 معرفی و سپس حذف شد ولی امکان بازگشت مجدد وجود دارد

48- :not(S1,S2)
تگ هایی را انتخاب می‌کند که شامل Selector‌های S1 یا S2 یا ... یا Sn نباشند.
<style>
    :not([readonly]) {
        background: yellow;
    }
</style>
<input type="text"/>
<input type="text" readonly="readonly"/>
<input type="text"/>
<input type="text" readonly="readonly" />
در مثال فوق پس زمینه‌ی تگهای input که ویژگی readonly ندارند به رنگ زرد نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :not(S1,S2)  3

49- :matches(S1,S2)
تگ هایی را انتخاب می‌کند که شامل Selector‌های S1 و/یا S2 و/یا ... و/یا Sn باشند. 
<style>
    :matches([readonly]) {
        background: yellow;
    }
</style>
<input type="text" />
<input type="text" readonly="readonly" />
<input type="text" />
<input type="text" readonly="readonly" />
در مثال فوق پس زمینه‌ی تگهای input که ویژگی readonly دارند به رنگ زرد نمایش می‌یابند. 
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :matches(S1,S2)  4

50- :has(S1,S2)
تگ هایی را انتخاب می‌کند که در ارتباط خاصی با Selector های S1 و/یا S2 و/یا ... و/یا Sn می‌باشند. 
<style>
    :has(>span) {
        color: red;
    }
    :has(+div) {
        color: blue;
    }
</style>
<div>
    <p>
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
        Maecenas <span>porttitor</span> congue massa. Fusce posuere,
        magna sed pulvinar ultricies, purus lectus malesuada libero,
        sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.
    </p>
    <h1>Header 1</h1>
    <div>
            
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
        Maecenas porttitor congue massa. Fusce posuere,
        magna sed pulvinar ultricies, purus lectus malesuada libero,
        sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.            
    </div>
در مثال فوق متن موجود در تگ p که دارای فرزند مستقیم span می‌باشد، با توجه به Selector ی که بصورت :has(>span) تعریف شده است، به رنگ قرمز نمایش می‌یابد. همچنین Header 1 که دارای تگ div هم تراز و دقیقا بعد از h1 است، با توجه به Selector ی که بصورت  :has(+div) تعریف شده است، به رنگ آبی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :has(S1,S2)  4 
پروژه‌ها
Mvc File Manager
به نظر من بهترین روش برای یادگیری برنامه نویسی انجام  یک پروژه واقعی و کاربردی است . (هرچند ساده)  به همین دلیل در حین یادگیری asp.net mvc  تصمیم گرفتم یک فایل منجیر درست کنم که با پیشرفت در یادگیری asp.net mvc اون رو تکمیل کنیم.

فاز اول : نسخه 0.1.1       نسخه 0.1.2
پیاده سازی کنترلر های Browse,Download,Upload, CreateFolder,Delete
هدف از این مرحله یادگیری کنترلر ، ویو ، ویو مدل ، مدل بایندینگ ، روتینگ ، اکشن ریزالت و....
فاز دوم : (انجام شد) نسخه 0.2.3
پیاده سازی کنترلر های Rename,Properties 
استفاده از  WebGrid  در مرورگر فایل
محاسبه حجم پوشه
نسخه اصلاحی آقای وکیلی:دریافت
تغییر در نوع چینش models
افزودن PlUploader برای آپلود فایل
تغییر در Partial به نام _breadCrumb
امکان چند انتخاب هم زمان - و همچنین حذف  چند مورد هم زمان
باز شدن پوشه‌ها و دانلود شدن فایل‌ها با دابل کلیک 
تغییرات مختصر در style
تغییر در ساختار لینک برگشت -> انتقال از model به view و پیاده سازی با jquery به دلیل سهولت بیشتر

فاز سوم:
پیاده سازی اعتبار سنجی کاربران و نقش‌های آنان 
تعریف نقش‌های زیر (ایده خام)
Admin (Full access)
FileManager_Read(readonly access)
FileManager_Write(Creat Folder & upload file)
FileManager_Change(Move & Rename)
FileManager_Delete(Delete file and Folder
فاز چهارم :
پیاده سازی مراحل قبل تحت Ajax
فاز پنجم:
بهینه سازی و تکمیل پروژه
ایده‌های پراکنده :
قابلیت کپی 
قابلیت انتخاب چندتایی برای کپی و حذف و ...
 قابلیت آپلود همزمان چند فایل
چند زبانه بودن 
توسعه اینترفیس (درختواره پوشه‌ها ، ویوهای مختلف جهت نمایش فایل (لیست، آیکون‌های کوچک ، آیکون‌های بزرگ))
نمایش و تغییر دسترسی‌های ویندوز
آپلود فایل فشرده و اکسترکت کردن آن 
جستجوی فایل
.
.
.
بازخوردهای پروژه‌ها
خطا در اجرای برنامه

با عرض سلام و خسته نباشید من با اجرای برنامه با خطای زیر مواجه می‌شم (ویندوز 7 و دات نت فریمورک 4 رو هم نصب کردم) :

و همینطور در زمانی که برنامه را در ویژوال استودیو اجرا می‌کنم خطای زیر را می‌دهد:

Unknown build error, 'Could not load file or assembly 'file:///C:\Users\VAHID\Downloads\DNTProfiler-1.8.1129.0\DNTProfiler-1.8.1129.0\DNTProfiler\obj\Release\DNTProfiler.exe' or one of its dependencies. The module was expected to contain an assembly manifest.'