sexta-feira, 2 de dezembro de 2016

Formatando um item ou membro de enum para texto amigável

Muitas vezes nós criamos Enums para expressar possíveis opções/valores de um campo de múltipla escolha onde os itens são fixos do sistema e não cadastrados em uma tabela.
Há casos em que os itens definem regras de negócio e rumos a tomar na aplicação. Nesses casos usar enum não é a melhor opção.
Em todos os outros casos usar enum pode ser legal, mas existe um problema: como popular um combo box / dropdown list com os itens do combo com nomes amigáveis? Sim, você não pode colocar espaços e caracteres especiais em um enum. Mas ao popular as opções, seja em uma aplicação windows forms ou web forms, você pode querer mostrar descrições longas e cheias de acentos e caracteres especiais para as opções no dropdown.
Você pode usar os atributos Description ou EnumMember para definir programaticamente essas descrições longas, mas obter esses valores de volta vai algumas linhas de código.
Nesse código que eu compartilho abaixo escrevi um extension method (não abuse deles) para o tipo enum chamado ToText() que tenta obter o Description, se não existir tenta obter o EnumMember, e se não existir eu criei um outro, chamado text, para exemplificar como podemos adicionar a qualquer membro do código propriedades e valores declarativamente através de atributos.
Você pode adaptar outros atributos, ou separar Description, EnumMember e Text em métodos separados se desejar.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace VtrFramework.Extensions
{
    /// 
    /// classe que permite usar um atributo texto em um item de enum, para ele ter uma descrição amigável
    /// 
    public class TextAttribute : Attribute
    {
        public string Text;
        public TextAttribute(string text)
        {
            Text = text;
        }
    }

    /// 
    /// Adiciona um método a mais em um  elemento do tipo enum para pegarmos seu texto amigável
    /// 
    public static class EnumExtensions
    {

        /// 
        /// Dado um enum, retorna seu correspondente string que pode ser: "" se ele for null, Seu decorator/atributo Text se houver, ToString caso contrário
        /// funciona também com o atributo Description e com o atributo EnumMember
        /// 
        /// O Enum a ser convertido
        /// string - O texto do enum
        public static string ToText(this Enum enumeration)
        {

            if (enumeration == null)
            {
                return "";
            }

            MemberInfo[] memberInfo = enumeration.GetType().GetMember(enumeration.ToString());

            if (memberInfo != null && memberInfo.Length > 0)
            {
                try
                {
                    object[] descrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                    if (descrs != null && descrs.Length > 0)
                    {
                        return ((DescriptionAttribute)descrs[0]).Description;
                    }


                    object[] enumbs = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false);

                    if (enumbs != null && enumbs.Length > 0)
                    {
                        return ((EnumMemberAttribute)enumbs[0]).Value;
                    }

                    object[] txts = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);

                    if (txts != null && txts.Length > 0)
                    {
                        return ((TextAttribute)txts[0]).Text;
                    }
                }
                catch
                {
                    //catch mudinho porque o fallback é direto para o ToString
                }

            }

            return enumeration.ToString();

        }



    }
}


Use-o assim:

   
 namespace VtrFramework.Test.Extensions
{
    [TestFixture, Description("Teste de métodos de extensão para enuns"), Category("ExtensionMethods")]
    public class EnumExtensionsTest
    {
        public enum TargetEnum
        {
            [System.ComponentModel.Description("Opção 1")]
            Opcao1,

            [EnumMember(Value ="Opção 2")]
            Opcao2,

            [Text("Opção 3")]
            Opcao3,

            Opcao4
        }

        [Test]
        [Description("Teste de método que traz descrição amigável do enum")]
        [Category("ExtensionMethods")]
        public void ToTextTest()
        {
            Assert.AreEqual("Opção 1", TargetEnum.Opcao1.ToText());
            Assert.AreEqual("Opção 2", TargetEnum.Opcao2.ToText());
            Assert.AreEqual("Opção 3", TargetEnum.Opcao3.ToText());
            Assert.AreEqual("Opcao4", TargetEnum.Opcao4.ToText());
        }
    }
}

Você pode popular listbox e dropdowns assim:
    
  /// <summary>
    /// Ferramentas para trabalhar com Enuns
    /// </summary>
    public class EnumTools
    {
        /// <summary>
        /// publica os itens de um enum em um ListItemCollection
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="lst">System.Web.UI.WebControls.ListItemCollection da ListBox ou DropDownList que vai ser preenchida</param>
        /// <param name="primeiroVazio">true se o primeiro item for o vazio/default, false caso contrário</param>
        /// <param name="textoPrimeiroVazio">texto do primeiro item, que não deve fazer parte do enum</param>
        /// <param name="valorPrimeiroVazio">valor do primeiro item, que pode ser String.Empty, 0 ou null dependendo da regra</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.ListItemCollection lst, bool primeiroVazio = true, string textoPrimeiroVazio = "Escolha uma opção", string valorPrimeiroVazio = "") where TEnum : struct, IConvertible
        {
            if (!typeof(TEnum).IsEnum)
            {
                throw new ArgumentException("TEnum precisa ser do tipo ENUM");
            }

            lst.Clear();
            if (primeiroVazio)
            {
                lst.Insert(0, new System.Web.UI.WebControls.ListItem(textoPrimeiroVazio, valorPrimeiroVazio));
            }

            var valores = Enum.GetValues(typeof(TEnum));

            foreach(var v in valores)
            {
                string text = ((Enum)v).ToText();
                string value = ((int)v).ToString();
                lst.Add(new System.Web.UI.WebControls.ListItem(text, value));
            }
        }

        /// <summary>
        /// publica os itens de um enum em um DropDownList
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="drop">o DropDown/ComboList a ser preenchido</param>
        /// <param name="primeiroVazio">true se o primeiro item for o vazio/default, false caso contrário</param>
        /// <param name="textoPrimeiroVazio">texto do primeiro item, que não deve fazer parte do enum</param>
        /// <param name="valorPrimeiroVazio">valor do primeiro item, que pode ser String.Empty, 0 ou null dependendo da regra</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.DropDownList drop, bool primeiroVazio = true, string textoPrimeiroVazio = "Escolha uma opção", string valorPrimeiroVazio = "") where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(drop.Items, primeiroVazio, textoPrimeiroVazio, valorPrimeiroVazio);
        }

        /// <summary>
        /// publica os itens de um enum em um RadioButtonList
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="rbl">o RadioButtonList a ser publicado</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.RadioButtonList rbl) where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(rbl.Items, false);
        }

        /// <summary>
        /// publica os itens de um enum em um ListBox
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="lbx">o ListBox a ser publicado</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.ListBox lbx) where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(lbx.Items, false);
        }

    }
Isso ajuda bastante na hora de criarmos listas com as UF's do Brasil, por exemplo. Veremos isso num próximo post.

Nenhum comentário:

Postar um comentário

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)