استفاده از Enum Descriptions برای رابط کاربری

اول باید اشاره کنم که علیرغم رجحان خودم، تصمیم گرفتم مطالب بلاگ را با ادبیات رسمی بنویسم تا مترجم ها قادر به ترجمه مقالات باشند.

مواقع بسیار زیادی پیش آمده که نیاز پیدا کردم مقدار Attribute یک شیء (مخصوصا enum) را دریافت کنم. تقریبا همیشه با هدف نمایش به کاربر.

مثلا پروژه ای بود که در آن باید به کاربر امکان فیلتر کردن اطلاعات را می دادیم. برای این کار، کاربر باید از بین عملگرها، یکی را انتخاب می کرد. برای این کار، enum زیر را تعریف کردیم: 

public enum Operands
{
    [Description("شامل ")]
    Like, 

    [Description("مساوی باشد با ")]
    Equal, 

    [Description("مخالف باشد با ")]
    Not, 

    [Description("بزرگتر باشد از ")]
    GreaterThan, 

    [Description("کوچکتر باشد از ")]
    LessThan
}

در مرحله بعد، باید این توضیحات را به کاربر نشان دهیم و هنگام انتخاب، از روی مورد انتخاب شده، متوجه می شویم که مورد انتخاب شده مربوط به کدام enum هست. در تصاویر زیر، هدف نهایی ما از این مقاله نشان داده شده. کنترل DropDownList، مقادیر Description مربوط به enum را نشان می دهد (تصویر 1) و بعد از انتخاب کاربر، مقدار Description و مقدار enum value مربوطه را مشاهده می کنیم. این پروژه را در انتهای این صفحه دانلود کنید.

12

برای استفاده از Description ها متد زیر را تعریف می کنیم: 

 

public static string GetDescription(this Enum value)
{
    var fi = value.GetType().GetField(value.ToString());
    var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
    return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}

متد GetDescription، یک enum value را دریافت کرده و با استفاده از Reflection، مقدار Description آن را بر می گرداند. این متد، یک Extension Method هست.

 

برای استفاده کافیست به شکل زیر عمل کنیم:

CustomEnum item = CustomEnum.CustomValue;
string itemDescription = item.GetDescription();

برای انجام معکوس این کار، ابتدا متد زیر را تعریف کردم: 

public static T GetEnumValueFromDescription<T>(string Description) where T : struct,IConvertible
{
    if (!typeof(T).IsEnum)
    {
        var errorMessage = string.Format("{0} must be an enumarted type", typeof(T).ToString());
        throw new ArgumentException(errorMessage, typeof(T).ToString());
    }

    foreach (var o in from Enum e in Enum.GetValues(typeof(T))
                       let sValue = e.GetDescription()
                       where sValue.ToLower() == Description.ToLower()
                       select e)
    {
        return (T)((object)o);
    }

    return default(T);
}

اما این متد چند مشکل دارد:

1- در حقیقت من یک Workaround برای تعریف T از نوع enum پیدا کرده ام که زیاد قشنگ نیست. استفاده از struct و IConvertible، تا حدی خطا در استفاده را محدود می کند اما در موراد نادری امکان دارد که به Runtime Exception (از نوع ArgumentException) برخورد کنیم.

2- از آنجا که T از نوع class نیست، امکان برگرداندن مقدار null وجود ندارد. از طرفی اگر مقدار بازگشتی را از نوع nullable تعریف کنیم، نیاز به یک cast اضافه در سمت caller خواهیم داشت. بنابراین تنها راه، برگرداندن مقدار (default(T می باشد که در enum ها، تبدیل به مقدار 0 می شود. در نتیجه امکان بازگشتهای False Positive خواهد بود که کاملا غیر قابل قبول است (مثلا یک مقدار string که در enum وجود ندارد را پاس می کنیم اما به جای null، عنصر 0 enum به عنوان مقدار درست بازگشت داده می شود).

به دو دلیل بالا، مجبور شدم قید متدهای Generic را بزنم و گرچه صدا زدن تابع کمی طولانی تر میشد، اما دردسرش بسیار کمتر: 

public static Enum GetEnumValueFromDescription(Type MyType, string Description)
{
    return (from Enum e in Enum.GetValues(MyType)
            let sValue = e.GetDescription()
            where sValue.ToLower() == Description.ToLower()
            select e).FirstOrDefault();
}

متد GetEnumValueFromDescription، مقدار یک enum را از روی Description بر می گرداند. در صورتیکه مقداری پیدا نشود، مقدار null برگردانده می شود. نحوه استفاده:

var operand = EnumHelpers.GetEnumValueFromDescription(typeof(CustomEnum), customText);

حالا با استفاده از این متدها، می توانیم ابتدا Description ها را به کاربر نشان دهیم و از روی انتخاب کاربر، مقدار enum را تشخیص دهیم. در صورت تمایل، پروژه الحاق شده را برای دیدن مثال اول مقاله دانلود کنید.


نظرات (15) -

مسعود
مسعود
1390/03/21 06:56 ب.ظ #

ممنون مهندس.کارا و به دردبخور.
خسته نباشی...

کامیار
کامیار
1390/03/22 09:31 ق.ظ #

ممنون مسعود جان.

مهدی عادلی
مهدی عادلی
1390/03/22 12:13 ق.ظ #

با سلام
ممنون از وب سایت مفیدتون .
یه اشکالی ( شاید اشکال ) در سایت شما دیدم که لازم دونستم بگم که اینکه مترجم بالای سایت ظاهرا خوب کار نمی کنه و همه محتوای سایت رو ترجمه نمی کنه ، من سایت شما رو با http://translate.google.com امتحان کردم مشکلی نبود ، نمی دونم چرا این مشکل داره ( با این حالی که هر دو یکی هستند ) در ضمن من از Firefox 4.0 استفاده کردم و این مشکل  به وجود آمده .
به هر حال گفتم شاید براتون مهم باشه
موفق و پیروز باشید

کامیار
کامیار
1390/03/22 03:58 ب.ظ #

ممنون دوست عزیز،
حتما چک می کنم.

Rowerki dla dzieci
Rowerki dla dzieci
1390/03/23 11:31 ب.ظ #

Hi Smile I enjoy your blog

Rowerki dla dzieci
Rowerki dla dzieci
1390/03/23 11:31 ب.ظ #

so much Smile

کامیار
کامیار
1390/03/24 10:05 ق.ظ #

Thank you Rowerki. Glad you like my blog.

Jahan
Jahan
1390/03/31 07:48 ب.ظ #

salam
man rahe halle digeii dar pish gereftam ke fek konam kami behtar bashe

dar file code:
    public enum Gender : sbyte
    {
        None,
        Male,
        Female
    }

dar resourceha

  <data name="Gender_Male" xml:space="preserve">
    <value>مرد</value>
  </data>
  <data name="Gender_Female" xml:space="preserve">
    <value>زن</value>
  </data>
  <data name="Gender_None" xml:space="preserve">
    <value>...</value>
  </data>

dar code

public static MvcHtmlString DropDownListFor<TItem>(
            this HtmlHelper<TItem> htmlHelper, Expression<Func<TItem, Enum>> expression)
        {
            string propname = htmlHelper.ViewData.Model.Item(expression);
            Type enumType = typeof(TItem).GetProperty(propname).PropertyType;
            IEnumerable<SelectListItem> selectListItems = Enum.GetNames(enumType).Select(
                o =>
                    {
                        string resourceFor = HtmlHelperExtensionForResources.ResourceFor(typeof(EntityResource), enumType.Name + "_" + o);
                        return new SelectListItem { Selected = true, Text = resourceFor, Value = o };
                    });

            return htmlHelper.DropDownList(propname, selectListItems);
        }

کامیار
کامیار
1390/03/31 08:26 ب.ظ #

سلام.
روش جالبی هست و به نظرم مفید. تنها مساله ای هست، مساله ارتباط enum با resource هست( از طریق naming convention). اگر برنامه نویس بخواد در نام enum تغییری ایجاد کنه، باید تمامی تگهای xml رو بروز کنه. اما در قیاس با اینکه در اینجا از resource استفاده میشه، می تونه خیلی از جاها روش بهتری قلمداد بشه.
ممنون.

Social Bookmarking
Social Bookmarking
1390/04/08 03:25 ب.ظ #

Solid post, nice work. It couldn’t be written any improved. Thank you for the post. Hope to have more posts like this from your site
I got the best and the most appropriate information about social bookmarking on www.linkbullets.com.

SMAH1
SMAH1
1390/05/02 01:44 ب.ظ #

سلام
دست درد کنه ولی نمی دونم چرا این کد اینقدر آشناست! من سالها قبل شبیه این کار رو کردم و در یک سایت انگلیسی روش بالا رو دیدم.در هر حال از اینکه اطلاعاتت رو با دیگران اشتراک می گذازی ممونم.
موفق باشی

کامیار
کامیار
1390/05/02 01:52 ب.ظ #

سلام.
دوست عزیز، مسلما ایده استفاده از enum description، برای خیلی از برنامه نویسها ایده شناخته شده ای هست. مخصوصا در پروژه های تحت وب زیاد استفاده شده. سایت های زیادی هم نمونه کد های زیادی رو برای این روش ارائه دادند.
قصد من در این مقاله، ارائه یک روش جدید به همه نیست. اینجا هم من سعی کردم با استدلال خودم، از متد مناسبی استفاده کنم و دوستانی هم که با این روش آشنا نبودند رو باهاش آشنا کنم.
وگر نه استفاده از enum description ها چیزی هست که با یک جستجو در گوگل، مطالب خیلی زیادی در موردش پیدا خواهید کرد.

کامیار
کامیار
1390/05/02 02:00 ب.ظ #

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

حسین مرادی نیا
حسین مرادی نیا
1390/06/08 03:26 ق.ظ #

تشکر صد چندان از شما

سعید افشاری
سعید افشاری
1390/07/01 04:45 ب.ظ #

مرسی اطلاعاتت خیلی مفید بود.

امکان ارسال نظر برای این موضوع وجود ندارد

آرشیو ماهیانه

تقویم مقالات

<<  ۱۳۹۲ خرداد  >>
شیدسچپج
۲۸۲۹۳۰۳۱۱۲۳
۴۵۶۷۸۹۱۰
۱۱۱۲۱۳۱۴۱۵۱۶۱۷
۱۸۱۹۲۰۲۱۲۲۲۳۲۴
۲۵۲۶۲۷۲۸۲۹۳۰۳۱
۱۲۳۴۵۶۷

نمایش ارسالها در تقویم بزرگ