Welcome to Kartones.Net Sign in

Sector 7G

Sector7G Live Search

About Me

About Me

Windows Live Messenger Presence Gadget

My Photo Albums

My Photo Web Albums

Logos

  • Sector7G
  • ilitia Technologies

February 2009 - Posts

Crear una Regla para el Analizador de Código de Visual Studio 2008

El analizador de código del Visual Studio 2008 es una herramienta que permite comprobar si el código cumple una serie de reglas. Funciona exactamente igual que FxCop, sólo que se encuentra integrado dentro del propio IDE.

El propio Visual Studio ya cuenta con un gran número de reglas predefinidas. Aunque este número puede ser aumentado por medio de la creación de nuevas reglas. Esto puede ser muy útil para equipos de trabajo que utilicen una guía de estilos específica. De esta forma, se puede comprobar que todos los miembros del equipo cumplen con los estándares fijados.

Creando una Regla

Cómo ejemplo vamos a definir una regla que permita comprobar los nombres de las pruebas unitarias. Según esta regla, el nombre de un test debe de tener este formato: NombreDelMetodoAProbar_DescripcionDeLaPruebas_ResultadoEsperado. Ej: GetCustomersById_NullParameter_ArgumentException.

Para ello habrá que crear un proyecto de tipo librería. Este proyecto debe de tener una referencia a estas tres dlls: FxCopSdk.dll, FxCopCommon.dll, Microsoft.Cci.dll. Estas dlls se pueden encontrar en la carpeta: %ProgramFiles%\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\.

Para que el analizador de código pueda determinar que clases son reglas. Las clases deben de heredar de la clase abstracta BaseIntrospectionRule. Esta clase se encuentra dentro del namespace Microsoft.FxCop.Sdk.

public abstract class BaseRule : BaseIntrospectionRule
{
    protected BaseRule(string name)
        : base(name, "Sector7G.VisualStudio.CodeAnalysisRules.Rules", 
        typeof(BaseRule).Assembly)
    {
    }

    public override TargetVisibilities TargetVisibility
    {
        get
        {
            return TargetVisibilities.All;
        }
    }
}

Nota: En este caso he creado una clase base de la cual heredarán todas mis reglas.

Al constructor de la clase BaseIntrospectionRule hay que pasarle como argumentos: El nombre de la regla, el namespace del archivo xml con la definición de la regla, y el ensamblado que tiene la regla.

Para crear la regla hay que sobre escribir el método Check de la clase BaseIntrospectionRule. Dentro de este método deberemos escribir el código de verifique si el nombre de un test es correcto.

public sealed class TestMethodsNamesMustHaveThreePartsRule : BaseRule
{
    public TestMethodsNamesMustHaveThreePartsRule()
        : base("TestMethodsNamesMustHaveThreePartsRule")
    {
    }

    public override ProblemCollection Check(Member member)
    {
        if (member == null || member.NodeType != NodeType.Method)
            return Problems;

        if (member.HasAttribute("TestMethodAttribute"))
        {
            if (!member.Name.Name.IsCorrectTestMethodName())
            {
                Problems.Add(new Problem(GetResolution(member.Name.Name)));
                return Problems;
            }
        }

        return Problems;
    }
}

En este código se puede ver como se comprueba que método este decorado con el atributo TestMethodAttribute. De esta forma podemos identificar si el método se trata de una prueba. Seguidamente se comprueba si el nombre cumple con la especificación establecida.

El código del método HasAttribute es:

public static class RuleHelper
{
    public static bool HasAttribute(this Member member, string type)
    {
        var info = member.Attributes.Where(a => a.Type.Name.Name == type);
        return info != null && info.Count() != 0;
    }
}

El códigio de método IsCorrectTestMethodName es:

public static class NamingHelper
{
    public static bool IsCorrectTestMethodName(this string name)
    {
        if (String.IsNullOrEmpty(name))
            return false;

        string[] parts = name.Split(new[] { '_'});
        return parts.Length == 3;
    }
}

Ya sólo queda incluir un archivo xml con la información de la regla. Este archivo debe incluirse como recurso dentro de la dll, es decir, su propiedad Build Action debe de ser Embedded Resource. El nombre de este archivo debe de ser el mismo que declaro en el parámetro resourceName del constructor de la clase BaseIntrospectionRule. En el caso del ejemplo se llama Rules.xml y tiene el siguiente formato:

<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="Sector7G Code Analysis Rules">
  <Rule TypeName="TestMethodsNamesMustHaveThreePartsRule" 
        Category="Sector7G.Naming" CheckId="S7G0001">
    <Name>The name of a test method must have three parts</Name>
    <Description>A test method name must have three parts.</Description>
    <Resolution>
      The name "'{0}'" does not have three parts. 
      The correct format of a test method name is: 
      name of the method to test, a description and an expected result. Sample: 
      GetCustomerById_NullParameter_ArgumentException
    </Resolution>
    <MessageLevel Certainty="95">Error</MessageLevel>
    <FixCategories>NonBreaking</FixCategories>
    <Owner>Pedro Alvarez Fernandez</Owner>
    <Email></Email>
    <Url>http://www.sectorsieteg.net</Url>
  </Rule>
</Rules>

Una vez terminado el desarrollo, la regla debe de ser desplegada para poder ser utilizada desde el Visual Studio. Para ello, la dll debe de ser copiada a la carpeta: C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\Rules. En esta carpeta encontraras las dlls del resto de reglas que utiliza el Visual Studio.

Posted: Feb 08 2009, 05:22 PM by Pedroafa | with no comments
Ribbon Control y Prims

Prism o Composite for WPF se basa en la construcción de una aplicación modular. Esto quiere decir, que cada funcionalidad de la aplicación constará de su propio módulo. Así que por ejemplo, si queremos que nuestra aplicación tenga un lugar donde se puedan configurar diferentes valores de la misma, tendremos un módulo de “configuración de la aplicación”.

Lo que intenta este post es explicar cómo dar la posibilidad a cada módulo de la aplicación de cargar sus propias opciones en el menú. Cómo menú se utilizará un control Ribbon, así que cada módulo debe de ser capaz de añadir un RibbonTab cuando éste sea cargado. De esta manera, cada vez que se agregue más funcionalidad a la aplicación, más módulos, estos irán enriqueciendo las opciones del menú presentadas en el Ribbon.

La mejor aproximación para hacer esto, es utilizar un RegionAdapter. Los RegionAdapter permiten asociar una región con un control. De esta manera, cuando un RibbonTab sea añadido en una región específica, el RegionAdaptar añadirá este RibbonTab al Ribbon.

El primer paso es añadir el control Ribbon a la aplicación y asociarlo a una región. En este caso es asociado a la región “RibbonRegion”:

<r:Ribbon DockPanel.Dock="Top"  Title="Sector7G Sample App" 
          cal:RegionManager.RegionName="RibbonRegion" />

Nota: Aquí puedes obtener toda la información para obtener el control Ribbon.

El siguiente paso debe de ser crear el RegionAdapter. El RegionAdapter va a ser el que nos permita añadir el RibbonTab de un módulo, al Ribbon de la aplicación.

public class RibbonControlRegionAdapter : RegionAdapterBase<Ribbon>
{
    private Ribbon _regionTarget;

    protected override void Adapt(IRegion region, Ribbon regionTarget)
    {
        _regionTarget = regionTarget;
        region.ActiveViews.CollectionChanged += OnActiveViewsChanged;
    }

    private void OnActiveViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:

                foreach (RibbonTab tab in e.NewItems)
                    _regionTarget.Tabs.Add(tab);
                break;

            case NotifyCollectionChangedAction.Remove:

                foreach (RibbonTab v in e.NewItems)
                    _regionTarget.Tabs.Remove(v);
                break;
        }
    }

    protected override IRegion CreateRegion()
    {
        return new AllActiveRegion();
    }
}

Nota: Aquí puedes encontrar un How To que explica cómo crear un RegionAdapter.

Para finalizar con la parte del Shell, ya sólo queda registrar el nuevo RegionAdapter dentro del Bootstrapper. Para ello, hay que sobre escribir el método ConfigureRegionAdapterMappings. Ello permitirá que al cargar un módulo, el RegionAdapter se ejecute.

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(Ribbon), new RibbonControlRegionAdapter());
    return mappings;
}

Una vez llegado a este punto, es necesario crear un módulo. En este caso concreto el módulo de configuración. En este módulo se puede crear una clase que sirva de ayuda para crear el RibbonTab.

public class ToolBarPresenter : IToolBarPresenter
{
    #region IToolBar Members

    public RibbonTab CreateModuleToolBar()
    {
        var tab = new RibbonTab();
        var group = new RibbonGroup();
        var btn = new RibbonButton();

        tab.Label = "Configuration";
        
        var cmd = new RibbonCommand { LabelTitle = "Configuration" };
        cmd.Executed += new ExecutedRoutedEventHandler(cmd_Executed);
        cmd.CanExecute += new CanExecuteRoutedEventHandler(cmd_CanExecute);

        btn.Command = cmd;
        group.Controls.Add(btn);            
        tab.Groups.Add(group);
      
        return tab;
    }

    private void cmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void cmd_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        //Do something
    }

    #endregion
}

Una vez creado el RibbonTab simplemente hay que asociarlo a la región específica para el RegionAdapter actué al cargar el módulo en la aplicación.

IRegion ribbom = _regionManager.Regions[RegionNames.RibbonRegion];
var toolbar = _container.Resolve<IToolBarPresenter>();

RibbonTab tab = toolbar.CreateModuleToolBar();
ribbom.Add(tab);
ribbom.Activate(tab);
Posted: Feb 08 2009, 01:16 PM by Pedroafa | with no comments
Probando tipos internos

Internal es un operador utilizado para modificar la visibilidad de una tipo en .Net. El operador indica que tipo al que es aplicado sólo será visible dentro de ese ensamblado, es decir, dentro de ese proyecto.

El uso de este modificador puede suponer un problema a la hora de realizar un test. Ya que su utilización impide que desde el proyecto pueda ser visto el tipo.

Para permitir que el tipo pueda ser utilizado desde un test, y por lo tanto, poder probar toda la funcionalidad del tipo. El ensamblado del test debe de ser declarado como “amigo”. Para conseguir esto, se debe decorar la dll a probar con el atributo InternalsVisibleToAttribute. Este atributo especifica que los tipos solamente son visibles dentro del propio ensamblado y visibles desde el ensamblado marcado en el atributo.

Así que para probar los tipos Internals de mi ensamblado (Sector7G.Invertis.YahooService), deberé indicar que mi proyecto de test (Sector7G.Invertis.YahooService.Test) tiene acceso a estos tipos. Para ello, dentro del archivo AssemblyInfo.cs del ensamblado Sector7G.Invertis.YahooService añado la siguiente línea:

[assembly: InternalsVisibleTo("Sector7G.Invertis.YahooService.Test")]

Con esta decoración ya es posible probar, desde el proyecto de test, todos los tipos como si fueran públicos.

Posted: Feb 04 2009, 10:53 PM by Pedroafa | with no comments
[Code-Snipped] Leer Un Tipo Enumerado De Un Xml Con LinQ To Xml

El objetivo es cargar la entidad StockQuotes con los datos contenidos en el xml StockQuotes.xml. Para ello debemos de tener en cuenta que el tipo StockStates se trata de un enumerado.

<?xml version="1.0" encoding="utf-8"?>
<StockQuotes>
  <StockQuote>
    <Name>Santander</Name>
    <State>Green</State>
  </StockQuote>
  <StockQuote>
    <Name>Banesto</Name>
    <State>Red</State>
  </StockQuote>
</StockQuotes>
public class StockQuote
{
 public string Name {get; set;}
 public StockStates State {get; set;}
}

Leer el xml usando Linq To Xml resulta muy sencillo.

XDocument xml = XDocument.Load(@"C:\StockQuotes.xml");
var data = from s in xml.Descendants("StockQuote")
           select new StockQuote {
               Name = s.Element("Name").Value,
               State = s.Element("State").ParseToEnum<StockStates>()
           };

La gracia de este código reside en el método extendido ParseToEnum. Se trata de un método que permite convertir un elemento XElement en un enumerado.

public static class EnumExt
{
    public static T ParseToEnum<T>(this XElement node)
    {
        if (node == null)
            return default(T);

        try
        {
            return (T)Enum.Parse(typeof(T), node.Value);
        }
        catch(Exception)
        {
            return default(T);
        }
    }
}

Un ejemplo de la potencia del lenguaje combinando Genéricos con métodos extendidos.

Posted: Feb 03 2009, 10:49 PM by Pedroafa | with no comments
Filed under: , ,

El Nombre 'InitializeComponent' No Existe En El Contexto Actual

Estábamos copiando unos UserControls de un proyecto a otro para unificarlos en un solo proyecto y cuando quisimos compilar nos encontramos con este error.

The name 'InitializeComponent' does not exist in the current context

Este error puede producirse por tres causas diferentes:

Primera Causa

Que dentro del archivo xaml no esté correctamente referenciado el archivo de code-behind del UserControl. Ya que el namespace donde se encuentra la clase, debe de ser el mismo que el declarado en el atributo x:Class del archivo xaml.

<UserControl x:Class="Sector7G.Invertis.Wpf.Module.StatusBar.Views.StatusBarControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
namespace Sector7G.Invertis.Wpf.Module.StatusBar.Views
{
    public partial class StatusBarControl : UserControl, IStatusBarView
    {
        public IStatusBarPresenter Presenter { get; set; }

        public StatusBarControl()
        {
            InitializeComponent();
        }
    }
}

Segunda Causa

También puede deberse porque falten algunas declaraciones de las propiedades del proyecto. Suele ocurrir cuando se intenta añadir un UserControl de WPF a una Librería de clases en .NET 2.0. El error es debido a que el proyecto no sabe cómo debe compilar este tipo de controles. Ya que en .NET 2.0 ni existía el concepto WPF, así que hay que convertirlo en un proyecto de tipo WPF.

Para solucionarlo hay que editar el proyecto (el archivo .csproj). En el archivo encontraras una línea como la siguiente:

<Import Project=“$(MSBuildBinPath)\Microsoft.CSharp.targets” />

Debajo de esta línea copia esta otra. Esta segunda línea indica al proyecto que es de tipo WPF.

<Import Project=“$(MSBuildBinPath)\Microsoft.WinFX.targets” />

Tercera Causa

En nuestro caso habíamos copiado el UserControl de un proyecto a otro. Así que por alguna razón o fuerza entraña, el tipo de compilación (la propiedad Build Action) del archivo xaml se estableció a “CodeAnalysisDictionary” en vez de quedarse como “Page”. El build Action de un archivo xaml debe de ser Page.

Posted: Feb 03 2009, 10:43 PM by Pedroafa | with no comments
Filed under: ,

[Code-Snippet] Convertir una Colección en una Cadena Separada por Comas

Imaginemos que tenemos una entidad con la que se muestra a continuación y queremos concatenar el valor de su propiedad Ticker. Resumiendo el objetivo es crear una cadena de Tickers separada por comas.

public class StockQuote
{
    public string Name { get; set; }
    public string Ticker { get; set; }
}

Para conseguir esto, podemos utilizar la siguiente línea de código.

var quotes =  new Collection<StockQuote> { 
    new StockQuote{ Ticker = "^IBEX" },
    new StockQuote{ Ticker = "^SMSI" }
};

string list = String.Join(";", quotes.Select(s => s.Ticker).ToArray());

El resultado sería: "^IBEX;^SMSI".

Si simplemente se quiere concatenar la lista, se podría hacer:

string list = String.Join(String.Empty, quotes.Select(s => s.Ticker).ToArray());
Posted: Feb 01 2009, 12:32 PM by Pedroafa | with no comments
Filed under: , ,