۱۳۹۰/۰۷/۰۷

چند نكته در مورد WPF MediaElement و ويندوز XP


WPF MediaElement به صورت پيش فرض در ويندوز XP كار نمي‌كند؛ مگر اينكه حتما آخرين نگارش موجود Windows Media Player بر روي سيستم نصب شده باشد و حداقل نياز به نگارش 10 به بعد را دارد.
اگر اين نگارش نصب نباشد يا هر خطاي ديگري رخ دهد، آن‌را مي‌توان از طريق روال رويداد گردان MediaFailed بدست آورد.
اگر نگارش بتاي مدياپلير 11 بر روي سيستم نصب باشد، با پيغام نه چندان آشناي "insufficient memory" مواجه خواهيد شد و اهميتي هم ندارد كه سيستم در حال حاضر به چه ميزان حافظه‌ي مهيا دسترسي دارد.
و كلا هر آنچه را كه Windows Media Player بتواند پخش كند، WPF MediaElement نيز قادر به پخش آن‌ها خواهد بود. براي فرمت‌هاي ناشناخته و جديد بايد Codec مخصوص آن‌ها در سيستم نصب شده باشد.

راه حل بهتر، استفاده از پروژه‌ي ديگري است به نام WPF Media Kit. اين پروژه، هر آنچه را كه بتوان توسط برنامه GraphEdit پخش كرد، مي‌تواند نمايش دهد.

استفاده از WPF MediaElement به كمك الگوي MVVM درد بزرگي است؛ چون آنچنان از Binding و Commanding پشتيباني نمي‌كند.

خلاصه اشتراک‌های روز چهار شنبه 1390/07/06


۱۳۹۰/۰۷/۰۶

هيتلر و WinRT


يك سري ويديو در يوتيوب هست كه پايه اصلي آن‌ها قسمتي از بحث هيتلر با فرماندهان SS است. حالا اينجا افراد مختلف اومدن براي اين يك تك ويديو، زيرنويس‌هاي مختلفي تهيه و آپلود كرده‌اند. يكي از اين‌ها، به همين بحث داغ WinRT مرتبط است. اين زير نويس رو به فارسي ترجمه كردم كه به همراه اصل ويديو از آدرس‌هاي زير قابل دريافت هستند:



براي ديدن بدون دردسر زير نويس تهيه شده هم مي‌شود از برنامه‌ي عالي و رايگان KMPlayer استفاده كرد.


نمونه‌هاي مشابه ديگر در يوتيوب:

خلاصه اشتراک‌های روز سه شنبه 1390/07/05

۱۳۹۰/۰۷/۰۵

اضافه كردن كامنت جهت فضاهاي نام


در مورد «درست كردن فايل راهنماي CHM از توضيحات XML يك پروژه» پيشتر مطلبي در اين سايت منتشر شده است. تمام اين‌ها هم خوب! اما فايل راهنماي نهايي توليدي يك ايراد مهم دارد. Sandcastle Help File Builder نياز دارد كه به ازاي هر فضاي نامي كه در پروژه‌ي شما وجود دارد، يك summary و توضيح هم ارائه شود؛ در غير اينصورت خطاي قرمز رنگ زير را در ابتداي صفحه معرفي كلاس‌هاي يك فضاي نام، نمايش خواهند داد:


[Missing <summary> documentation for "N:Some.Test"]

از آنطرف كامپايلرهاي مورد استفاده چنين توضيحاتي را قبول ندارند و نمي‌توان براي فضاهاي نام، توضيحاتي را همانند كلاس‌ها يا متدها، ارائه داد. براي حل اين مشكل، يك راه حل ساده وجود دارد: به ازاي هر فضاي نامي كه در پروژه وجود دارد، يك كلاس خالي را به نام "NamespaceDoc" به پروژه اضافه كنيد. مثلا:

namespace Some.Test
{
    /// <summary>
    /// The <see cref="Some.Test"/> namespace contains classes for ....
    /// </summary>

    [System.Runtime.CompilerServices.CompilerGenerated]
    class NamespaceDoc
    {
    }
}

به اين ترتيب توضيحاتي كه ملاحظه مي‌كنيد توسط Sandcastle Help File Builder مورد استفاده قرار خواهد گرفت و آن اخطارهاي قرمز رنگ عدم وجود توضيحات مرتبط با فضاهاي نام، از فايل توليدي نهايي حذف خواهند شد.

روش دوم:
در خود برنامه Sandcastle Help File Builder، در قسمت Project Properties > Comments > NameSpaceSummaries، امكان وارد كردن توضيحات فضاهاي نام نيز وجود دارد. (كه آنچنان جالب نيست؛ بهتر است همه چيز يكپارچه باشد.)

خلاصه اشتراک‌های روز دو شنبه 1390/07/04

۱۳۹۰/۰۷/۰۴

تبديل پروژه ويراستار به دات نت


پروژه ويراستار را از Ruby به سي شارپ تبديل كردم. سورس نهايي كامل، فايل‌هاي باينري، به همراه unit tests و راهنماي كتابخانه، از آدرس زير قابل دريافت هستند:



خلاصه كارهايي را كه انجام مي‌دهد:



خلاصه اشتراک‌های روز يك شنبه 1390/07/03


۱۳۹۰/۰۷/۰۳

يافتن آدرس نهايي يك Url پس از Redirect


براي مثال آدرس http://feedproxy.google.com/~r/nettuts/~3/tWCksueANyY/ را در نظر بگيريد. گوگل پس از خريد feedburner ، از feedproxy.google.com جهت رديابي آدرس‌هاي فيدها استفاده مي‌كند؛‌ مثلا فيد شما چند نفر خواننده دارد، كدام موارد بيشتر خوانده شده، با چه مرورگرهايي، از چه مكان‌هايي و مواردي از اين دست. البته مي‌توان در تنظيمات فيدبرنر اين نوع آدرس دهي را خاموش كرد ولي به صورت پيش فرض فعال است. مشكل feedproxy.google.com هم اين است كه در ايران فيلتر است. بنابراين يافتن آدرس اصلي اين نوع لينك‌ها پس از Redirect نهايي مي‌تواند جهت ارائه عمومي آن‌ها مفيد باشد. با استفاده از قطعه كد زير مي‌توان اين آدرس را يافت:

using System;
using System.Net;

namespace Linq2Rss
{
    public class RedirectFinder 
    {
        public CookieContainer Cookies { set; get; }
        public string Url { get; set; }

        public string GetRedirectUrl()
        {
            var hops = 1;
            const int MaxRedirects = 20;

            do
            {
                var request = (HttpWebRequest)WebRequest.Create(Url);
                request.UserAgent = "MyUA";
                request.KeepAlive = true;
                request.Referer = Url;
                if (Cookies == null) Cookies = new CookieContainer();
                request.CookieContainer = Cookies;
                request.AllowAutoRedirect = false;

                using (var webResp = request.GetResponse() as HttpWebResponse)
                {
                    if ((webResp.StatusCode == HttpStatusCode.Found) ||
                    (webResp.StatusCode == HttpStatusCode.Redirect) ||
                    (webResp.StatusCode == HttpStatusCode.Moved) ||
                    (webResp.StatusCode == HttpStatusCode.MovedPermanently))
                    {
                        var newLocation = webResp.Headers["Location"];
                        if (newLocation.StartsWith("/"))
                        {
                            var uri = new Uri(Url);
                            Url = string.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Host, uri.Port, newLocation);
                        }
                        else
                            Url = newLocation;
                    }
                    else
                    {
                        if (webResp.StatusCode == HttpStatusCode.OK)
                            return Url;
                    }
                }

                hops++;
            } while (hops <= MaxRedirects);
            return Url;
        }
    }
}


براي يافتن آدرس واقعي يك Url پس از Redirect راهي بجز درخواست آن از وب سرور اوليه وجود ندارد. سپس وضعيت پاسخ داده شده بررسي مي‌شود؛ اگر حاوي Found ، Moved يا Redirect بود، به اين معنا است كه بايد آدرس جديد را از هدر پاسخ دريافتي استخراج كنيم. اين آدرس، در كليد Location ذخيره مي‌شود. اكنون يكبار ديگر نياز است تا اين آدرس جديد بررسي شود، زيرا ممكن است اين مورد هم به آدرس ديگري اشاره كند. در كل، كد فوق 20 بار اين بررسي را انجام خواهد داد (هر چند عموما در دو يا سه سعي به جواب خواهيم رسيد).

خلاصه اشتراک‌های روز شنبه 1390/07/02


۱۳۹۰/۰۷/۰۲

خلاصه اشتراک‌های روز جمعه 1390/07/01


۱۳۹۰/۰۷/۰۱

گوگل ريدر و افزودن توضيحات


اگر به گوگل ريدر دقت كرده باشيد، دو گزينه‌ي به اشتراك گذاري دارد: share و share with note .


اگر گزينه‌ي share with note را انتخاب كرده و توضيحي را ارسال يا اضافه كنيم، اين توضيحات، به فيد از نوع Atom اشتراك‌ها هم اضافه مي‌شود. مثلا:

<?xml version="1.0"?>
<feed xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:gr="http://www.google.com/schemas/reader/atom/" 
   xmlns:idx="urn:atom-extension:indexing" 
   xmlns="http://www.w3.org/2005/Atom" 
   idx:index="no" 
   gr:dir="ltr">

 ...
   
  <entry gr:crawl-timestamp-msec="1316627782108">
    ...
    <gr:annotation>
      <content type="html">text-text-text</content>
      <author>
        <name>Vahid</name>
      </author>
    </gr:annotation>
 ...
  </entry>
  
    ...
  
</feed>



اين افزونه استاندارد نيست و همانطور كه در قسمت xmlns:gr اطلاعات فوق مشخص است، در فضاي نام http://www.google.com/schemas/reader/atom/ معنا پيدا مي‌كند. از دات نت سه و نيم به بعد هم كلاسي جهت خواندن فيدهاي استاندارد وجود دارد (تعريف شده در فضاي نام System.ServiceModel.Syndication). اما چگونه مي‌توان اين افزونه‌ي غير استاندارد را با كمك امكانات توكار دات نت خواند؟
روش كار با استفاده از ElementExtensions هر آيتم يك فيد است؛ به صورت زير :

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;

namespace Linq2Rss
{
    public class RssEntry
    {
        public string Title { set; get; }
        public string Description { set; get; }
        public string Link { set; get; }
        public DateTime PublicationDate { set; get; }
        public string Author { set; get; }
        public string BlogName { set; get; }
        public string BlogAddress { set; get; }
        public string Annotation { set; get; }
    }

    public static class AtomReader
    {
        private static string getAtomAnnotation(this SyndicationElementExtensionCollection items)
        {
            if (!items.Any()) return string.Empty;
            var item = items.Where(x => x.OuterName.ToLowerInvariant() == "annotation").FirstOrDefault();
            if (item == null) return string.Empty;

            var element = item.GetObject<XElement>();
            var content = element.Element("{http://www.w3.org/2005/Atom}content");
            return content == null ? string.Empty : content.Value;
        }

        public static IList<RssEntry> GetEntries(string feedUrl)
        {
            using (var reader = XmlReader.Create(feedUrl))
            {
                var feed = SyndicationFeed.Load(reader);
                if (feed == null) return null;

                return feed.Items.Select(x =>
                    new RssEntry
                    {
                        Title = x.Title.Text,
                        Author = x.Authors.Any() ? x.Authors.First().Name : string.Empty,
                        Description = x.Content == null ? string.Empty : ((TextSyndicationContent)x.Content).Text,
                        Link = x.Links.Any() ? x.Links.First().Uri.AbsoluteUri : string.Empty,
                        PublicationDate = x.PublishDate.UtcDateTime,
                        BlogName = x.SourceFeed.Title.Text,
                        BlogAddress = x.SourceFeed.Links.Any() ? x.SourceFeed.Links.First().Uri.AbsoluteUri : string.Empty,
                        Annotation = x.ElementExtensions.getAtomAnnotation()

                    }).ToList();
            }
        }
    }
}

در اين مثال به كمك متد الحاقي getAtomAnnotation، مجموعه‌ي SyndicationElementExtensionCollection هر آيتم يك فيد بررسي شده، در بين اين‌ها، موردي كه از نوع annotation باشد انتخاب و سپس content آن استخراج مي‌گردد.


نكته‌اي ديگر:
اكثر كلاس‌هاي موجود در فضاهاي نام مرتبط با XML در دات نت امكان خواندن اطلاعات را از يك Uri هم دارند؛ مانند مثال فوق و متد XmlReader.Create بكارگرفته شده در آن. اما اگر بخواهيم حين خواندن اطلاعات، يك پروكسي را نيز به پروسه جاري اضافه كنيم، به نظر خاصيت يا متدي جهت انجام اينكار وجود ندارد. براي رفع اين مشكل مي‌توان يك پروكسي سراسري را تعريف كرد. تنها كافي است خاصيت System.Net.WebRequest.DefaultWebProxy مقدار دهي شود. پس از آن به صورت خودكار بر روي كل برنامه تاثير خواهد گذاشت.


بيشترين كاربرد دات نت فريم ورك تابحال در كجا بوده است؟


برخلاف تصور عموم، كاربرد اصلي دات نت فريم ورك در طي اين چندين و چند سالي كه از ارائه آن مي‌گذرد، در توسعه‌ي گسترده برنامه‌هاي دسكتاپ نبوده است. عمده كاربرد آن در تهيه برنامه‌هاي وب است. براي نمونه مي‌توان به آمارگيري زير سيستم‌هاي مورد استفاده دات نت در بين برنامه نويس‌ها در سال 2010 مراجعه كرد [^] و كاربردهاي وب آن را حداقل بايد در جمع استفاده از WebForms ، Ajax و MVC جستجو كرد (البته اگر WCF و ASMX را نديد بگيريم كه آن‌ها هم عمده كاربردشان در پروژه‌هاي وب است). اين اعداد و ارقام سال 2010 را اگر بخواهيم از بيشترين به كمترين ليست كنيم، حاصل آن به صورت زير درخواهد آمد:

01 - WebForms
02 - Ajax
03 - WCF
04 - Linq to SQL
05 - MVC
06 - WinForms
07 - ASMX
08 - Silverlight
09 - WPF
10 - ADO DataSets
11 - Entity-Framework (EF)
12 - Workflow
13 - ADO.NET Data Services
14 - DynamicData
15 - CardSpace

مورد ديگري كه شايد براي خيلي‌ها جالب توجه باشد، آمار تعداد سايت‌هايي است كه از ASP.NET استفاده مي‌كنند، در مقابل تعداد سايت‌هايي كه بر پايه PHP تهيه شده‌اند. مطابق آمار اين سايت [^] و [^] در حال حاضر در بين يك ميليون سايت برتر دنيا (سايت‌هايي كه بيشترين ترافيك وب را به خود اختصاص داده‌اند) حدود 216 هزار سايت از ASP.NET و 394 هزار سايت از PHP استفاده مي‌كنند. از مابقي وب سايت‌هاي موجود در وب، حدود 27 ميليون سايت از ASP.NET و 26 ميليون سايت از PHP استفاده مي‌كنند. اين اعداد و ارقام از اين جهت حائز اهميت هستند كه مدت زمان ارائه ASP.NET كمتر از PHP است و همچنين بيشترين كاربرد ASP.NET در سرورهاي ويندوزي است، برخلاف PHP كه علاوه بر ويندوز، در بين سرورهاي لينوكسي نيز گزينه‌ي بسيار محبوبي محسوب مي‌شود.

خلاصه اشتراک‌های روز پنج شنبه 1390/06/31


۱۳۹۰/۰۶/۳۱

خلاصه اشتراك‌هاي روز چهار شنبه 1390/06/30


۱۳۹۰/۰۶/۳۰

خلاصه اشتراك‌هاي روز سه شنبه 1390/06/29


۱۳۹۰/۰۶/۲۹

خلاصه‌اي كوتاه در مورد WinRT


WinRT چيست؟

مايكروسافت جهت سهولت توليد برنامه‌هاي جديد Metro-style ، لمسي (touch-centric) و tablets ويندوز 8 ، اقدام به بازنويسي مجدد Windows-API كرده و نام آن‌را WinRT گذاشته است. بنابراين همانند آنچه كه در مورد API‌ ويندوز از روز اول پيدايش آن مرسوم بوده، اين API جديد، از نوع native (نوشته شده با CPP) مي‌باشد و با كمك دات نت فريم ورك تهيه نشده است. اين API جديد مبتني بر فناوري قديمي COM است، بنابراين مطابق معمول توسط هر نوع برنامه‌ و سيستمي در ويندوز قابل دسترسي است. تفاوتي نمي‌كند كه CPP يا دلفي باشد يا دات نت. به صورت خلاصه ويندوز 8 دو طراحي جديد (WinRT) و قديم (Win32 API) را با هم پشتيباني مي‌كند. اگر آن‌را صحيح‌تر بخواهيم معرفي كنيم، WinRT درحقيقت محصور كننده‌ي (Wrapper) همان Win32 API سابق است (در پشت صحنه همان dll هاي سابق ويندوز را بارگذاري و استفاده مي‌كند) جهت تطابق با نيازهاي دهه اخير و سال‌هاي پيش رو.


سازگاري دات نت فريم ورك با WinRT چگونه است؟

اينبار WinRT برخلاف Win32 API (كه در زمان ارائه آن اصلا دات نتي در كار نبود)، جهت سازگاري با دات نت طراحي شده است. اين طراحي جديد ILDasm metadata را در اختيار برنامه نويس‌هاي دات نت قرار مي‌دهد و به اين ترتيب IntelliSense و قابليت‌هاي Debugging ويژوال استوديو همانند كدهاي مديريت شده‌ي دات نت جهت برنامه نويسي مبتني بر WinRT در اختيار برنامه نويس‌ها خواهد بود (فرمت ارائه شده، ECMA 335 metadata format مي‌باشد). همچنين اشياء COM متعلق به WinRT به خوبي توسط GC (آشغال جمع كن) دات نت جهت مديريت بهتر حافظه، تحت نظر مي‌باشند.
بنابراين از ديدگاه يك برنامه نويس دات نت، كل WinRT به صورت managed assemblies مشاهده مي‌شود، اينترفيس‌هاي آن همان اينترفيس‌هاي دات نتي خواهند بود و كلاس‌هاي آن نيز به همين ترتيب. اين مشكلي بود/هست كه با Win32 API در دات نت وجود دارد و دسترسي به آن به اين سادگي و يكپارچگي ميسر نيست (هر چند تا الان كل اينترفيس آن جهت استفاده در دات نت نيز ترجمه شده است). در اينجا شما ارجاعاتي را به فايل‌هايي با پسوند winmd يا windows metadata، به پروژه‌ي دات نتي خود اضافه مي‌كنيد و سپس CLR قادر خواهد بود تا كليه اطلاعات لازم جهت كار با WinRT را از آن‌ها استخراج كند (اين فايل‌ها در پوشه C:\Program Files (x86)\Windows Kits\8.0\Windows Metadata و C:\Windows\system32\winmetadat ويندوز 8 قابل مشاهده و دسترسي هستند).


تفاوت‌هاي مهم امكانات نمايشي WinRT با Win32 API كدامند؟

تفاوت مهم WinRT با Win32 API از ديدگاه برنامه نويس‌ها، امكان دسترسي بيشتر به آن از طريق زبان‌هاي مختلف مي‌باشد. WinRT همانند Win32 API توسط CPP ، دات نت و ساير روش‌هاي مرسوم ديگر قابل دسترسي و توسعه است. اما اينبار WinRT برخلاف Win32 API ، از طريق HTML و جاوا اسكريپت هم قابل توسعه است. در اين حالت كدهاي شما توسط Chakra JavaScript engine كه از اينترنت اكسپلورر 9 به بعد ارائه شده، اجرا خواهد شد. بنابراين «برفراز» WinRT دو لايه نمايشي (presentation layer) قابل طراحي و دسترسي است. XAML و زبان‌هاي متداول برنامه نويسي موجود مانند سي شارپ و وي بي دات نت و غيره. همچنين HTML/CSS هم مجال ابراز وجود يافته است. البته XAML تنها لايه نمايشي كليه زبان‌هاي قديمي موجود مانند سي شارپ، وي بي دات نت، CPP و غيره خواهد بود. به همين جهت Expression Blend جديد نيز از HTML 5 پشتيباني مي‌كند.
همچنين در WinRT ، قسمت‌هاي GDI و Message loop متداول Win32 API حذف شده است و از DirectX استفاده مي‌كند. براي نمونه كدهاي XAML شما توسط DirectX رندر مي‌شود. البته اين مطلب جديدي نيست و از زمان ارائه WPF شاهد اين مساله بوده‌ايم.


وضعيت توسعه پذيري WinRT  چگونه است؟

علاوه بر اين‌ها، برخلاف Win32 API ، اينبار WinRT قابل توسعه است و Extensions SDK براي آن ارائه شده است.


آيا WinRT شاهد تغييرات امنيتي خاصي هم بوده است؟

نكته‌ي مهمي كه در طراحي WinRT به آن توجه شده است، امنيت مي‌باشد. برنامه‌هاي WinRT شبيه به برنامه‌هاي سيلورلايت در يك Sandbox اجرا مي‌شوند. به اين معنا كه جهت ذخيره سازي اطلاعات خود از يك isolated storage استفاده مي‌كنند. براي كار با file system نياز به تائيد كاربر دارند و خلاصه ديگر به سادگي نمي‌توان از مرزهاي اين نوع برنامه‌ها رد شد و سيستم عاملي را root كرد. براي نمونه برنامه نويس‌هاي دات نت دسترسي به فضاي نام System.IO.FileStream را ديگر نخواهند داشت و تنها قسمتي از دات نت «برفراز» WinRT و مدل امنيتي جديد آن معنا پيدا مي‌كند. همچنين برفراز اين API جديد، توليد مثلا Device drivers هم ديگر معنا پيدا نمي‌كند. اين محدوديت‌هاي امنيتي براي برنامه‌ نويس‌هاي native هم وجود دارد و تفاوتي نمي‌كند. كلا برنامه‌هاي جديد Metro-style در يك قرنطينه‌ي كامل امنيتي اجرا مي‌شوند. براي مثال اگر برنامه‌اي نياز به دسترسي به يك WebCam را داشته باشد، همانند برنامه‌هاي سيلورلايت ابتدا بايد كاربر تائيد كرده و سپس برنامه مجوز امنيتي كار با مثلا يك WebCam را خواهد يافت. همچنين تمام برنامه‌هاي جديد Metro-style بايد جهت ارائه در فروشگاه جديد ويندوز 8، داراي امضاي ديجيتال معتبر نيز باشند.


آيا جهت توسعه‌ي برنامه‌هاي چندريسماني و غيرهمزمان تمهيدات خاصي در WinRT پيش‌بيني شده است؟

در طراحي جديد WinRT به اعمال asynchronous به شدت توجه شده است. هر عملي كه بيش از 50 ميلي ثانيه طول بكشد به صورت خودكار تبديل به يك عمل asynchronous خواهد شد تا برنامه‌ها مرتبا در حين اجراي اعمال زمانبر هنگ نكرده و ترد اصلي برنامه را بلاك نكنند. علاوه بر اين‌ها WinRT از طريق IAsyncOperation interface خود، امكان استفاده از واژه‌هاي جديد كليدي async/await سي شارپ 5 را نيز مهيا مي‌سازد.


آيا WinRT آمده است تا جايگزيني براي دات نت و سيلورلايت و امثال آن باشد؟

خير. WinRT نگارش دوم Win32 API است با هدف توسعه پذيري، استفاده از دايركت ايكس و فناوري‌هاي جديد كه عموما از شتاب دهنده‌هاي سخت افزاري هم بهره‌مند هستند بجاي GDI سابق، استفاده ساده‌تر در زبان‌هاي ديگر به كمك فايل‌هاي استاندارد Windows Meta data آن مي‌باشد. همچنين اين API جديد دسترسي به امكانات ويندوز را هم توسط HTML و جاوا اسكريپت، علاوه بر امكانات مهياي سابق ميسر ساخته است. هم اكنون نگارش 4 و نيم دات نت در ويندوز 8 ارائه شده است و توسط هر دو سيستم سابق و جديد قابل استفاده مي‌باشد. البته بايد در نظر داشت كه جهت استفاده از WinRT به دلايل محدوديت‌هاي امنيتي اعمال شده به آن و همچنين استفاده از XAML به تنها عنوان لايه نمايشي سيستم‌هاي متداول غير HTML ايي، دات نت فريم ورك به امكانات و كلاس‌هاي كمتري نسبت به حالت متداول كار با آن، دسترسي دارد (جهت درك بهتر اين محدوديت‌ها مي‌توان به طراحي سيلورلايت مراجعه كرد). اين را هم بايد اضافه كرد كه ويندوز 8 توانايي اجراي هر دو نوع برنامه‌هاي سبك جديد مترو و متداول دسكتاپ قديمي را دارا است.


جهت آشنايي بيشتر با WinRT مي‌توان به مجموعه‌اي از ويديوهاي مرتبط آن مراجعه كرد:
http://channel9.msdn.com/Events/BUILD/BUILD2011?t=windows%2Bruntime


خلاصه اشتراك‌هاي روز دو شنبه 1390/06/28


۱۳۹۰/۰۶/۲۸

تبديل عدد به حروف


به طور قطع توابع و كلاس‌هاي تبديل عدد به حروف، در جعبه ابزار توابع كمكي شما هم پيدا مي‌شوند. روز قبل سعي كردم جهت آزمايش، عدد 3000,000,000,000,000 ريال را با كلاسي كه دارم تست كنم و نتيجه overflow يا اصطلاحا تركيدن سيستم بود! البته اگر مطالب اين سايت را دنبال كرده باشيد پيشتر در همين راستا مطلبي در مورد نحوه‌ي صحيح بكارگيري توابع تجمعي SQL در اين سايت منتشر شده است و جزو الزامات هر سيستمي است (تفاوتي هم نمي‌كند كه به چه زباني تهيه شده باشد). اگر آ‌ن‌را رعايت نكرده‌ايد، سيستم شما «روزي» دچار overflow خواهد شد.

در كل اين كلاس تبديل عدد به حروف را به صورت ذيل اصلاح كردم و همچنين دو زبانه است؛ چيزي كه كمتر در پياده سازي‌هاي عمومي به آن توجه شده است:

using System.Collections.Generic;
using System.Linq;

namespace NumberToWordsLib
{
 /// <summary>
 /// Number to word languages
 /// </summary>
 public enum Language
 {
  /// <summary>
  /// English Language
  /// </summary>
  English,

  /// <summary>
  /// Persian Language
  /// </summary>
  Persian
 }

 /// <summary>
 /// Digit's groups
 /// </summary>
 public enum DigitGroup
 {
  /// <summary>
  /// Ones group
  /// </summary>
  Ones,

  /// <summary>
  /// Teens group
  /// </summary>
  Teens,

  /// <summary>
  /// Tens group
  /// </summary>
  Tens,

  /// <summary>
  /// Hundreds group
  /// </summary>
  Hundreds,

  /// <summary>
  /// Thousands group
  /// </summary>
  Thousands
 }

 /// <summary>
 /// Equivalent names of a group 
 /// </summary>
 public class NumberWord
 {
  /// <summary>
  /// Digit's group
  /// </summary>
  public DigitGroup Group { set; get; }

  /// <summary>
  /// Number to word language
  /// </summary>
  public Language Language { set; get; }

  /// <summary>
  /// Equivalent names
  /// </summary>
  public IList<string> Names { set; get; }
 }

 /// <summary>
 /// Convert a number into words
 /// </summary>
 public static class HumanReadableInteger
 {
  #region Fields (4)

  private static readonly IDictionary<Language, string> And = new Dictionary<Language, string>
  {
   { Language.English, " " },
   { Language.Persian, " و " } 
  };
  private static readonly IList<NumberWord> NumberWords = new List<NumberWord>
  {
   new NumberWord { Group= DigitGroup.Ones, Language= Language.English, Names=
    new List<string> { string.Empty, "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" }},
   new NumberWord { Group= DigitGroup.Ones, Language= Language.Persian, Names=
    new List<string> { string.Empty, "يك", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", "نه" }},

   new NumberWord { Group= DigitGroup.Teens, Language= Language.English, Names=
    new List<string> { "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" }},
   new NumberWord { Group= DigitGroup.Teens, Language= Language.Persian, Names=
    new List<string> { "ده", "يازده", "دوازده", "سيزده", "چهارده", "پانزده", "شانزده", "هفده", "هجده", "نوزده" }},

   new NumberWord { Group= DigitGroup.Tens, Language= Language.English, Names=
    new List<string> { "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" }},
   new NumberWord { Group= DigitGroup.Tens, Language= Language.Persian, Names=
    new List<string> { "بيست", "سي", "چهل", "پنجاه", "شصت", "هفتاد", "هشتاد", "نود" }},

   new NumberWord { Group= DigitGroup.Hundreds, Language= Language.English, Names=
    new List<string> {string.Empty, "One Hundred", "Two Hundred", "Three Hundred", "Four Hundred", 
     "Five Hundred", "Six Hundred", "Seven Hundred", "Eight Hundred", "Nine Hundred" }},
   new NumberWord { Group= DigitGroup.Hundreds, Language= Language.Persian, Names=
    new List<string> {string.Empty, "يكصد", "دويست", "سيصد", "چهارصد", "پانصد", "ششصد", "هفتصد", "هشتصد" , "نهصد" }},

   new NumberWord { Group= DigitGroup.Thousands, Language= Language.English, Names=
     new List<string> { string.Empty, " Thousand", " Million", " Billion"," Trillion", " Quadrillion", " Quintillion", " Sextillian",
   " Septillion", " Octillion", " Nonillion", " Decillion", " Undecillion", " Duodecillion", " Tredecillion",
   " Quattuordecillion", " Quindecillion", " Sexdecillion", " Septendecillion", " Octodecillion", " Novemdecillion",
   " Vigintillion", " Unvigintillion", " Duovigintillion", " 10^72", " 10^75", " 10^78", " 10^81", " 10^84", " 10^87",
   " Vigintinonillion", " 10^93", " 10^96", " Duotrigintillion", " Trestrigintillion" }},
   new NumberWord { Group= DigitGroup.Thousands, Language= Language.Persian, Names=
     new List<string> { string.Empty, " هزار", " ميليون", " ميليارد"," تريليون", " Quadrillion", " Quintillion", " Sextillian",
   " Septillion", " Octillion", " Nonillion", " Decillion", " Undecillion", " Duodecillion", " Tredecillion",
   " Quattuordecillion", " Quindecillion", " Sexdecillion", " Septendecillion", " Octodecillion", " Novemdecillion",
   " Vigintillion", " Unvigintillion", " Duovigintillion", " 10^72", " 10^75", " 10^78", " 10^81", " 10^84", " 10^87",
   " Vigintinonillion", " 10^93", " 10^96", " Duotrigintillion", " Trestrigintillion" }},
  };
  private static readonly IDictionary<Language, string> Negative = new Dictionary<Language, string>
  {
   { Language.English, "Negative " },
   { Language.Persian, "منهاي " } 
  };
  private static readonly IDictionary<Language, string> Zero = new Dictionary<Language, string>
  {
   { Language.English, "Zero" },
   { Language.Persian, "صفر" } 
  };

  #endregion Fields

  #region Methods (7)

  // Public Methods (5) 

  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this int number, Language language)
  {
   return NumberToText((long)number, language);
  }


  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this uint number, Language language)
  {
   return NumberToText((long)number, language);
  }

  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this byte number, Language language)
  {
   return NumberToText((long)number, language);
  }

  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this decimal number, Language language)
  {
   return NumberToText((long)number, language);
  }

  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this double number, Language language)
  {
   return NumberToText((long)number, language);
  }

  /// <summary>
  /// display a numeric value using the equivalent text
  /// </summary>
  /// <param name="number">input number</param>
  /// <param name="language">local language</param>
  /// <returns>the equivalent text</returns>
  public static string NumberToText(this long number, Language language)
  {
   if (number == 0)
   {
    return Zero[language];
   }

   if (number < 0)
   {
    return Negative[language] + NumberToText(-number, language);
   }

   return wordify(number, language, string.Empty, 0);
  }
  // Private Methods (2) 

  private static string getName(int idx, Language language, DigitGroup group)
  {
   return NumberWords.Where(x => x.Group == group && x.Language == language).First().Names[idx];
  }

  private static string wordify(long number, Language language, string leftDigitsText, int thousands)
  {
   if (number == 0)
   {
    return leftDigitsText;
   }

   var wordValue = leftDigitsText;
   if (wordValue.Length > 0)
   {
    wordValue += And[language];
   }

   if (number < 10)
   {
    wordValue += getName((int)number, language, DigitGroup.Ones);
   }
   else if (number < 20)
   {
    wordValue += getName((int)(number - 10), language, DigitGroup.Teens);
   }
   else if (number < 100)
   {
    wordValue += wordify(number % 10, language, getName((int)(number / 10 - 2), language, DigitGroup.Tens), 0);
   }
   else if (number < 1000)
   {
    wordValue += wordify(number % 100, language, getName((int)(number / 100), language, DigitGroup.Hundreds), 0);
   }
   else
   {
    wordValue += wordify(number % 1000, language, wordify(number / 1000, language, string.Empty, thousands + 1), 0);
   }

   if (number % 1000 == 0) return wordValue;
   return wordValue + getName(thousands, language, DigitGroup.Thousands);
  }

  #endregion Methods
 }
}



دريافت پروژه كامل به همراه Unit tests مرتبط


خلاصه اشتراك‌هاي روز يك شنبه 1390/06/27


۱۳۹۰/۰۶/۲۷

خلاصه اشتراك‌هاي روز شنبه 1390/06/26

۱۳۹۰/۰۶/۲۶

خلاصه اشتراك‌هاي روز 1390/06/25