<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7361892840793499128</id><updated>2012-01-30T21:45:11.273-08:00</updated><category term='C#'/><category term='Crystal Reports'/><category term='Visual Studio'/><category term='DataGridView'/><category term='jQuery'/><category term='MS Access'/><category term='WinForm'/><category term='Linq'/><category term='Office'/><category term='GridView'/><category term='NHibernate'/><category term='N-Tier'/><category term='VB.NET'/><category term='JavaScript'/><category term='ASP.NET'/><category term='ADO.NET'/><title type='text'>Leandro Tuttini Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default?start-index=101&amp;max-results=100'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>101</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-3292230199407718914</id><published>2011-11-27T22:35:00.001-08:00</published><updated>2011-11-27T22:35:18.954-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Linq'/><title type='text'>[Linq] Categoría Jerárquica múltiple niveles</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Introducción&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;El objetivo del artículo es demostrar como linq puede ser verdaderamente potente a la hora de convertir estructuras simple y planas como ser entidades (o tablas) en objetos jerárquicos que faciliten la asignación de datos a controles, en este caso un treeview.&lt;/p&gt;  &lt;p&gt;El resultado del artículo cargara un árbol como se muestra en la imagen, el cual podrá tener tantos niveles como uno necesite.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/linq/CategoriasJerarquicasVariosNiveles/imagen1.jpg" /&gt; &lt;/p&gt; &lt;strong&gt;Recuperar los registro de forma básica&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;El primer paso para armar la estructura será contar con información plana y sin procesar de la base de datos.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c9e05da0-9c79-43c1-ad54-8b5f15b0efbf" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;CategoriaEntity&amp;gt; ObtenerCategorias()
{
    string sql = @&amp;quot;SELECT C.IdCategoria,
                            C.IdCategoriaPadre,
                            C.Descripcion,
                            C.Posicion
                    FROM Categorias C
                    ORDER BY C.IdCategoria, C.Posicion&amp;quot;;

    List&amp;lt;CategoriaEntity&amp;gt; categorias = new List&amp;lt;CategoriaEntity&amp;gt;();

    using (SqlCeConnection conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        SqlCeCommand cmd = new SqlCeCommand(sql, conn);
        IDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            categorias.Add(ConvertirCategoria(reader));
        }
        
    }

    return categorias;
}

private static CategoriaEntity ConvertirCategoria(IDataReader reder)
{
    return new CategoriaEntity()
    {
        IdCategoria = Convert.ToInt32(reder[&amp;quot;IdCategoria&amp;quot;]),
        IdCategoriaPadre = reder[&amp;quot;IdCategoriaPadre&amp;quot;] == DBNull.Value ? null : (int?)Convert.ToInt32(reder[&amp;quot;IdCategoriaPadre&amp;quot;]),
        Descripcion = Convert.ToString(reder[&amp;quot;Descripcion&amp;quot;]),
        Posicion = Convert.ToInt16(reder[&amp;quot;Posicion&amp;quot;])
    };
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;strong&gt;Aplicar jerarquía a los datos&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;La información plana hay que darle jerarquía, determinando que ítem son hijos de que otros.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e386acaf-40e6-4fee-8514-e8bd632cd996" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;CategoriaJerarquica&amp;gt; ObtenerCategoriarJerarquia()
{
    List&amp;lt;CategoriaEntity&amp;gt; categoriasList = ObtenerCategorias();

    List&amp;lt;CategoriaJerarquica&amp;gt; query = (from item in categoriasList
                                        where item.IdCategoriaPadre == null
                                        select new CategoriaJerarquica
                                        {
                                            IdCategoria = item.IdCategoria,
                                            Descripcion = item.Descripcion,
                                            CategoriaHija = ObtenerHijos(item.IdCategoria, categoriasList)
                                        }).ToList();

    return query;
}

private static List&amp;lt;CategoriaJerarquica&amp;gt; ObtenerHijos(int idCategoria, List&amp;lt;CategoriaEntity&amp;gt; categoriasList)
{

    List&amp;lt;CategoriaJerarquica&amp;gt; query = (from item in categoriasList
                                       let tieneHijos = categoriasList.Where(o =&amp;gt; o.IdCategoriaPadre == item.IdCategoria).Any()
                                       where item.IdCategoriaPadre == idCategoria
                                       select new CategoriaJerarquica
                                        {
                                            IdCategoria = item.IdCategoria,
                                            Descripcion = item.Descripcion,
                                            CategoriaHija = tieneHijos ? ObtenerHijos(item.IdCategoria, categoriasList) : null
                                        }).ToList();

    return query;

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Con el primer linq al usar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;where item.IdCategoriaPadre == null&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;se obtienen los nodos padres, o sea aquellos que no dependen de ningún otro nodo&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Asignación de la jerarquía al control TreeView&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;Como ultimo paso y no menos importante será crear los TreeNode que requiere el control TreeView&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:5fa36c7f-6d18-48bd-9ba2-d9c234524f10" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        List&amp;lt;CategoriaJerarquica&amp;gt; categoriaList = CategoriasDAL.ObtenerCategoriarJerarquia();

        CrearNodoHijo(categoriaList, null);
                 
    }
}


private void CrearNodoHijo(List&amp;lt;CategoriaJerarquica&amp;gt; categorias, TreeNode parentNode)
{
    categorias.ForEach(x =&amp;gt;
    {
        TreeNode node = new TreeNode(x.Descripcion, Convert.ToString(x.IdCategoria));

        if (x.CategoriaHija != null)
        {
            CrearNodoHijo(x.CategoriaHija, node);
        }

        if (parentNode == null)
            TreeView1.Nodes.Add(node);
        else
            parentNode.ChildNodes.Add(node);
    });   

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Como se observa hay una invocación recursiva para armar la estructura del árbol.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Código de Ejemplo&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;El articulo fue creado con visual Studio 2008&lt;/p&gt;

&lt;p&gt;La db es Sql Compact Edition (.sdf), por lo tanto esta se encuentra en la carpeta App_Data&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/linq/CategoriasJerarquicasVariosNiveles/[csharp]%20CategoriasJerarquicas.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[C# SkyDrive] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/Blog/Linq/CategoriasJerarquicasVariosNiveles?cid=5c82aa0c9bbaf5b3&amp;amp;sc=documents" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-3292230199407718914?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/3292230199407718914/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=3292230199407718914' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3292230199407718914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3292230199407718914'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/11/linq-categoria-jerarquica-multiple.html' title='[Linq] Categoría Jerárquica múltiple niveles'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-4184559070904426349</id><published>2011-11-27T04:56:00.001-08:00</published><updated>2011-11-27T04:56:35.500-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jqGrid – Listar Orden Compra (Maestro-Detalle)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Introducción&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;La mayoría de las veces los controles que incluye una herramienta de desarrollo puedo no cumplir con las expectativas que uno busca si quiere alcanzar una interfaz rica que aproveche toda la potencia de desarrollo, es por eso que se debe recurrir a componentes externos. &lt;/p&gt;  &lt;p&gt;Esta situación suelo encontrarla al mostrar información en un grid, es por eso que jqGrid es un control ideal para potenciar el desarrollo de la interfaz del usuario en entorno web y además se trata de un componente de libre uso.&lt;/p&gt;  &lt;p&gt;Lo único aspecto a tener en cuenta se relaciona con la necesidad de conocer algo de javascript, concretamente jquery y de ser posible invocación a webmethods para recuperar la información del grid.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jquery/jqgrid/ListarOrdenCompra/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/jquery/jqgrid/ListarOrdenCompra/imagen1.jpg" width="678" height="515" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;strong&gt;Configuración&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Para poder hacer uso de jqGrid es necesario introducir en el proyecto algunas librerías de javascript.&lt;/p&gt;  &lt;p&gt;Las cuales pueden ser descargadas de la pagina &lt;a href="http://www.trirand.com/blog/" target="_blank"&gt;jqGrid&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;La referencia a estas librerías podrían hacerse de dos formas:&lt;/p&gt;  &lt;p&gt;- usando el tag &amp;lt;script&amp;gt; en cada una de las paginas donde se requiera el grid&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:a895c08e-6491-4180-9fa0-019cd4f8d298" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;script src=&amp;quot;Scripts/jquery-1.6.4.min.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;Scripts/jquery-ui-1.8.16.custom.min.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt; 

&amp;lt;script src=&amp;quot;Scripts/jqGrid/grid.locale-es.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;Scripts/jqGrid/jquery.jqGrid.min.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;link href=&amp;quot;Scripts/jqGrid/ui.jqgrid.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;- o usando ScriptManager.RegisterClientScriptInclude(), esto es útil cuando se quiere registrar librerías para todas las paginas de forma global, aplicándolo en el código de la Master Page&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:23ebac75-57b2-4421-99df-66642c90eba4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class SiteMaster : System.Web.UI.MasterPage
{
    protected override void OnInit(EventArgs e)
    {

        ScriptManager.RegisterClientScriptInclude(Page, typeof(Page), &amp;quot;jquery&amp;quot;, ResolveUrl(@&amp;quot;~/Scripts/jquery-1.6.4.min.js&amp;quot;));
        ScriptManager.RegisterClientScriptInclude(Page, typeof(Page), &amp;quot;jqueryui&amp;quot;, ResolveUrl(@&amp;quot;~/Scripts/jquery-ui-1.8.16.custom.min.js&amp;quot;));
        ScriptManager.RegisterClientScriptInclude(Page, typeof(Page), &amp;quot;json2&amp;quot;, ResolveUrl(@&amp;quot;~/Scripts/json2.js&amp;quot;));
        
        ScriptManager.RegisterClientScriptInclude(Page, typeof(Page), &amp;quot;gridlocale&amp;quot;, ResolveUrl(@&amp;quot;~/Scripts/jqGrid/grid.locale-es.js&amp;quot;));
        ScriptManager.RegisterClientScriptInclude(Page, typeof(Page), &amp;quot;jqgrid&amp;quot;, ResolveUrl(@&amp;quot;~/Scripts/jqGrid/jquery.jqGrid.min.js&amp;quot;));

        base.OnInit(e);
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Al usarse un Master Page este podría verse afectado por la rutas relativas de las paginas, lo que ocasionaría una incorrecta resolución de la url y el acceso a los archivos .js, el método ResolveUrl() nos ayuda a evitar este problema.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Definición del grid&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Definir el grid con las opciones básicas no es nada difícil, para separar el código de scripting del html de la pagina facilitando así el mantenimiento verán en el ejemplo que he definido 3 .js según la operación de cada uno&lt;/p&gt;

&lt;p&gt;En este caso en concreto, se usara el “Grid.js”, el cual define el grid maestro&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:cde64ca0-31ff-469b-8c55-b2fc60f9ffb9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;$(&amp;quot;#tbOrders&amp;quot;).jqGrid({
    datatype: 'json',
    colNames: ['Fecha Pedido', 'Fecha Solicitud', 'Direccion', 'Ciudad', 'Pais'],
    colModel: [
             { name: 'OrderDate', index: 'OrderDate', width: 100, sortable: false },
             { name: 'RequiredDate', index: 'RequiredDate', width: 100, sortable: false },
             { name: 'ShipAddress', index: 'ShipAddress', width: 250, sortable: false },
             { name: 'ShipCity', index: 'ShipCity', width: 110, sortable: false },
             { name: 'ShipCountry', index: 'ShipCountry', width: 110, sortable: false }
              ],
    height: &amp;quot;300px&amp;quot;,
    onSelectRow: function (id) {

        getDetailsOrderByOrder(id);

    }
});
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;y un grid detalle&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ed3061d1-a1ba-411b-a496-803f6bc4a785" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;$(&amp;quot;#tbDetailsOrder&amp;quot;).jqGrid({
    datatype: 'json',
    colNames: ['Producto', 'Cantidad', 'Precio'],
    colModel: [
             { name: 'ProductName', index: 'ProductName', width: 250, sortable: false },
             { name: 'Quantity', index: 'RequiredDate', width: 100, sortable: false },
             { name: 'UnitPrice', index: 'UnitPrice', width: 100, sortable: false }
              ],
    height: &amp;quot;200px&amp;quot;,
    width:&amp;quot;800px&amp;quot;
});
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La configuración es bastante estándar para un uso básico, a partir de aquí hay miles de opciones, pero básicamente se define las columnas (atributo “colNames”), así como también las propiedades de cada columnas como ser el ancho de las mismas.&lt;/p&gt;

&lt;p&gt;En el grid maestro además se define un evento, el cual enviara el id de la entidad seleccionada para cargar así el detalle, por supuesto el id que recibe como parámetro es el valor que mas adelante veremos en la estructura json devuelta por el webmethod&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Definición de los Page Métodos&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;El siguiente paso será definir la información en el servidor para poder recuperar los registros que cargaran el grid&lt;/p&gt;

&lt;p&gt;Para esto se define dos Page Methods en la propia pagina web que implementa los grid (podría usarse una pagina adicional para esta definición)&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:a389bee4-f9ff-4d02-af85-d3a2a39049a7" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static string GetOrdersByCustomer(string customer)
{

    var orders = NorthwindData.GetOrdersByCustomer(customer);

    var grid = new
    {
        page = 1,
        records = orders.Count(),
        total = orders.Count(),

        rows = from item in orders
               let orderdate = item.OrderDate.HasValue ? item.OrderDate.Value.ToShortDateString() : &amp;quot;&amp;quot;
               let requireddate = item.RequiredDate.HasValue ? item.RequiredDate.Value.ToShortDateString() : &amp;quot;&amp;quot;
              
               select new
               {
                   id = item.OrderID,
                   cell = new string[]{
                       orderdate,
                       requireddate,
                       item.ShipAddress,
                       item.ShipCity,
                       item.ShipCountry,
                       item.Customers.CompanyName

                   }
               }

    };

    return JsonConvert.SerializeObject(grid);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;la estructura que requiere jqGrid es un tanto especial, y gracias a los métodos anónimos es posible armarla, y como ultimo paso serializarla usando la librería &lt;a href="http://json.codeplex.com/" target="_blank"&gt;JSON.NET&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La estructura es bastante simple, se define la pagina, la cantidad de registros y el total, estos valores son útiles cuando el grid esta paginado, en este caso no implementamos la paginación.&lt;/p&gt;

&lt;p&gt;Luego se definen las filas, en donde se transforma la entidad obteniendo un identidicado en el “id”, mas una propiedad “cell” que es en definitiva un array de string con la información de cada columna requiere, es importante en este punto respetar las posiciones en que debe ir cada dato con respecto a las definición de las columnas en el paso anterior.&lt;/p&gt;

&lt;p&gt;El mismo proceso se aplica para recuperar los detalles&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:944af541-fea4-48f5-a8d6-244e4cf9b9b2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static string GetDetailsOrdersByOrder(int order)
{

    var orders = NorthwindData.GetDetailsOrdersByOrder(order);

    var grid = new
    {
        page = 1,
        records = orders.Count(),
        total = orders.Count(),

        rows = from item in orders
               select new
               {
                   id = item.OrderID,
                   cell = new string[]{
                       item.ProductsReference.Value.ProductName,
                       item.Quantity.ToString(),
                       item.UnitPrice.ToString(&amp;quot;N2&amp;quot;)
                   }
               }

    };

    return JsonConvert.SerializeObject(grid);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Invocar a los Page Methods&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Los grid definidos no están conectados de forma directa para que estos invoquen los servicios de datos, sino que se definieron por separado para que uno desde código controle la invocación de los servicios.&lt;/p&gt;

&lt;p&gt;En el archivos ServiceInvoke.js se encuentra la definición &lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:9c0c88a6-87bd-4a49-99b9-aeefed1375b4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function getOrdersByCustomer(customer) {

    var params = new Object();
    params.customer = customer;

    $.ajax({
        type: &amp;quot;POST&amp;quot;,
        contentType: &amp;quot;application/json; charset=utf-8&amp;quot;,
        url: &amp;quot;Default.aspx/GetOrdersByCustomer&amp;quot;,
        data: JSON.stringify(params),
        dataType: &amp;quot;json&amp;quot;,
        async: false,
        success: function (data, textStatus) {

            if (textStatus == &amp;quot;success&amp;quot;) {

                $(&amp;quot;#tbDetailsOrder&amp;quot;).clearGridData();

                var grid = $(&amp;quot;#tbOrders&amp;quot;)[0];
                grid.addJSONData(jQuery.parseJSON(data.d));

            }

        },
        error: function (request, status, error) {
            alert(jQuery.parseJSON(request.responseText).Message);
        }
    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Por medio de la línea&lt;/p&gt;

&lt;p&gt;$(&amp;quot;#tbDetailsOrder&amp;quot;).clearGridData();&lt;/p&gt;

&lt;p&gt;es que se limpian los registros del grid de detalle, ya que al recargarse el principal ya no hay un registro seleccionado.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:d07347d5-c954-43a1-8ee2-66531d890040" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function getDetailsOrderByOrder(order) {

    var params = new Object();
    params.order = order;

    $.ajax({
        type: &amp;quot;POST&amp;quot;,
        contentType: &amp;quot;application/json; charset=utf-8&amp;quot;,
        url: &amp;quot;Default.aspx/GetDetailsOrdersByOrder&amp;quot;,
        data: JSON.stringify(params),
        dataType: &amp;quot;json&amp;quot;,
        async: false,
        success: function (data, textStatus) {

            if (textStatus == &amp;quot;success&amp;quot;) {

                var grid = $(&amp;quot;#tbDetailsOrder&amp;quot;)[0];
                grid.addJSONData(jQuery.parseJSON(data.d));

            }

        },
        error: function (request, status, error) {
            alert(jQuery.parseJSON(request.responseText).Message);
        }
    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de Código&lt;/strong&gt; &lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;El ejemplo fue desarrollado con visual Studio 2008 y Sql Server 2008 R2 Express&lt;/p&gt;

&lt;p&gt;Dentro de la carpeta “DbScript” se encuentra un .sql con la estructura de la db en caso de no poder usar el .mdf adjunto en la solución&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jquery/jqgrid/ListarOrdenCompra/Northwind.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[C# SkyDrive] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/Blog/jQuery/jqGrid/ListarOrdenCompra?cid=5c82aa0c9bbaf5b3&amp;amp;sc=documents" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-4184559070904426349?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/4184559070904426349/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=4184559070904426349' title='9 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4184559070904426349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4184559070904426349'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/11/jqgrid-listar-orden-compra-maestro.html' title='jqGrid – Listar Orden Compra (Maestro-Detalle)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7753773973259456937</id><published>2011-10-10T15:41:00.000-07:00</published><updated>2012-01-15T17:09:33.590-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>[GridView] Eventos de controles contenidos en el GridView (2/2)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Introducción&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;Este artículo representa una continuación de:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/06/gridview-eventos-de-controles.html" target="_blank"&gt;[GridView] Eventos de controles contenidos en el GridView (1/2)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;en donde se planteara un ejemplo práctico del uso de eventos en controles contenidos en el gridview&lt;/p&gt;  &lt;p&gt;El ejemplo representa la edición de un empleado a quien se le asigna determinadas prendas de vestir por parte de la empresa. Lo importante es que debe registrarse la fecha en que fue asigna esa indumentaria.&lt;/p&gt;  &lt;p&gt;Un punto a que no debe dejarse pasar por alto es que la selección de la prenda asignada no debe actualizar la tabla en al db directamente, la información se encontrara registrada en memoria, cuando el usuario confirme los cambios mediante el botón de aceptar será el momento en que&amp;#160; toda la información, incluida las indumentarias, impactara en la base de datos.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://public.bay.livefilestore.com/y1pocw72waK1UrfiynbrbmL_4SReOYV_w52WU0eVTJ6G4Pky0Fm_dJiT52nneZY5iqMZnEiq8WNJ8eS4t8CbJ5XeA/imagen1.jpg" target="_blank"&gt;&lt;img src="https://public.bay.livefilestore.com/y1pocw72waK1UrfiynbrbmL_4SReOYV_w52WU0eVTJ6G4Pky0Fm_dJiT52nneZY5iqMZnEiq8WNJ8eS4t8CbJ5XeA/imagen1.jpg" width="385" height="495" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;strong&gt;Diseño de objetos&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;Las entidades estarán representadas mediante el siguiente diagrama&lt;/p&gt;  &lt;p&gt;&lt;a href="https://public.bay.livefilestore.com/y1pjEOlHG2A3GFAOeQaGnaGo6iwQYzkR6HHy7EL2q1xvgoFnPlbewsux208NhBMK9F4CBI3Y3pYClqR82V1CoRiOA/imagen2.jpg" target="_blank"&gt;&lt;img src="https://public.bay.livefilestore.com/y1pjEOlHG2A3GFAOeQaGnaGo6iwQYzkR6HHy7EL2q1xvgoFnPlbewsux208NhBMK9F4CBI3Y3pYClqR82V1CoRiOA/imagen2.jpg" width="441" height="379" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;La entidad Empleado se relaciona con las listas de estudios he indumentarias que tiene asociada. Es por esta razón que en la capa de Acceso a datos se recuperan estas relaciones cuando se obtiene una entidad.&lt;/p&gt;  &lt;p&gt;Esta información relacionada al empleado será utilizada en las acciones de actualización cuando se efectúen las acciones en la presentación, esto se llevara a cabo mediante el “EmpleadoManager”&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Actualizar Entidad&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;Una técnica bastante utilizada para encapsular la funcionalidad de entidades en memoria cuando se debe conservar la información dentro del objeto Session, consiste en crear una clase que aplique el patrón singleton.&lt;/p&gt;  &lt;p&gt;Este patrón usar el objeto Session para contener su información entre request.&lt;/p&gt;  &lt;p&gt;Se puede ver en este caso la implementación en la clase “EmpleadoManager”&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:02df9f1a-e856-46b9-80d0-8a4f0f482358" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;/// &amp;lt;summary&amp;gt;
/// El objetivo de esta clase es generar una abstraccion al uso de la Session para administrar la entidad del empleado
/// &amp;lt;/summary&amp;gt;
public class EmpleadoManager
{
    private EmpleadoEntity empleado;

    private EmpleadoManager()
    {
        empleado = new EmpleadoEntity();
    }

    public static EmpleadoManager Instance()
    {
        if(HttpContext.Current.Session[&amp;quot;empleado&amp;quot;] == null)
        {
            HttpContext.Current.Session[&amp;quot;empleado&amp;quot;] = new EmpleadoManager();
        }

        return (EmpleadoManager)HttpContext.Current.Session[&amp;quot;empleado&amp;quot;];
    }

    public static void NuevoEmpleado()
    {
        HttpContext.Current.Session.Remove(&amp;quot;empleado&amp;quot;);
    }

    public EmpleadoEntity Empleado
    {
        get { return empleado; }
        set { empleado = value; }
    }


    public void IndumentariaAsignar(int idIndumentaria)
    {
        if (HttpContext.Current.Session[&amp;quot;empleado&amp;quot;] == null)
            return;
        
        //Se recupera la entidad lista de la session
        EmpleadoManager empleadoManager =(EmpleadoManager)HttpContext.Current.Session[&amp;quot;empleado&amp;quot;];

        List&amp;lt;IndumentariaEntity&amp;gt; indumentaria = empleadoManager.Empleado.IndumentariaAsignada;

        //se recupera la entidad de la lista y se actualiza la fecha de asignacion
        if (indumentaria.Exists(x =&amp;gt; x.IdIndumentaria == idIndumentaria))
        {
            indumentaria.Where(x =&amp;gt; x.IdIndumentaria == idIndumentaria)
                                            .First()
                                            .FechaAsignacion = DateTime.Now;
        }
        else
        {
            indumentaria.Add(new IndumentariaEntity()
            {
                 IdIndumentaria = idIndumentaria,
                 FechaAsignacion = DateTime.Now
            });
        }

    }

    public void IndumentariaDesasignar(int idIndumentaria)
    {
        if (HttpContext.Current.Session[&amp;quot;empleado&amp;quot;] == null)
            return;

        //Se recupera la entidad lista de la session
        EmpleadoManager empleadoManager = (EmpleadoManager)HttpContext.Current.Session[&amp;quot;empleado&amp;quot;];

        List&amp;lt;IndumentariaEntity&amp;gt; indumentaria = empleadoManager.Empleado.IndumentariaAsignada;

        //se recupera la entidad de la lista y se actualiza la fecha de asignacion
        if (indumentaria.Exists(x =&amp;gt; x.IdIndumentaria == idIndumentaria))
        {
            indumentaria.RemoveAll(x =&amp;gt; x.IdIndumentaria == idIndumentaria);                           
        }

    }


}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La utilización desde la pagina es muy simple&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:72b68113-9b57-4b4d-a48c-2fbaf7bcac72" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void checkIndumentaria_CheckedChanged(object sender, EventArgs e)
{
    CheckBox checkBox = sender as CheckBox;

    //NamingContainer devuelve el objeto donde esta contenidos el checkbox
    //en este caso se trata de una row del gridview
    GridViewRow row = checkBox.NamingContainer as GridViewRow;
    
    //conociendo la row se puede obtener el id de la entidad que se esta seleccionando
    int idindumentaria = Convert.ToInt32(gvIndumentaria.DataKeys[row.RowIndex].Value);

    if (checkBox.Checked)
        EmpleadoManager.Instance().IndumentariaAsignar(idindumentaria);
    else
        EmpleadoManager.Instance().IndumentariaDesasignar(idindumentaria);


    AsignarIndumentaria(EmpleadoManager.Instance().Empleado);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es en este punto donde se aplica la técnica que permite recuperar la fila seleccionada al marcar el checkbox contendió en el gridview.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código&lt;/strong&gt; &lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;El ejemplo se desarrollo con VS 2008, y base de datos Sql Server Express 2008 R2&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/asp.net/GridViewEventosControles2/[csharp]ListaEmpleados.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[C# SkyDrive] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; padding-right: 0px; padding-top: 0px" title="Preview" height="120" marginheight="0" src="https://skydrive.live.com/embed?cid=5C82AA0C9BBAF5B3&amp;amp;resid=5C82AA0C9BBAF5B3%21189&amp;amp;authkey=AMPbO172BvP6Dcs" frameborder="0" width="98" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7753773973259456937?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7753773973259456937/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7753773973259456937' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7753773973259456937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7753773973259456937'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/10/gridview-eventos-de-controles.html' title='[GridView] Eventos de controles contenidos en el GridView (2/2)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-1852268148204723682</id><published>2011-09-17T20:10:00.000-07:00</published><updated>2011-10-10T20:28:43.543-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>Filtros Condicionales (2/2) – Implementar filtros múltiples</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Este artículo representa continuación del anterior&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/09/filtros-condicionales.html"&gt;Filtros Condicionales (1/2)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;en realidad se podría ver como una extensión, ya que aquí el objetivo consiste en poder aplicar múltiples filtro usando de cursos, y no solo uno como fue en el artículo previo&lt;/p&gt;  &lt;p&gt;La interacción en la pantalla, para esta funcionalidad en concreto, esta dada por los siguientes pasos:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/Parte2/imagen1.jpg" width="730" height="355" /&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;Algo que hay que aclarar antes de abordar las soluciones planteadas es que la instrucción IN de sql no soporta de forma directa el uso de parámetros, es por eso que existen actualmente varios caminos a tomar ante esta situación, aquí solo expondré dos de ellos, pero existen algunos otros.&lt;/p&gt;  &lt;p&gt;Entre los temas tratados se podrán encontrar&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Comunicación entre formularios&lt;/li&gt;    &lt;li&gt;Filtro IN, concatenado el string&lt;/li&gt;    &lt;li&gt;Filtro por medio de XML&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1- Comunicación entre formularios&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En el formulario de búsqueda (frmBusqueda) encontrar un código como el siguiente&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:eb63673e-393c-49ee-a49d-e5e21ddb8cac" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnBuscarCursos_Click(object sender, EventArgs e)
{
    List&amp;lt;CourseEntity&amp;gt; selectedCourses = txtCursos.Tag as List&amp;lt;CourseEntity&amp;gt;;

    using (frmSeleccionarCursos frmcursos = new frmSeleccionarCursos(selectedCourses))
    {
        if (frmcursos.ShowDialog(this) == DialogResult.OK)
        {
            txtCursos.Tag = frmcursos.CursosSeleccionados;
            txtCursos.Text = string.Join(&amp;quot;, &amp;quot;, frmcursos.CursosSeleccionados.Select(x =&amp;gt; x.Title).ToArray());
        }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Como frmSeleccionarCursos se abre de forma modal puede esperarse en el ShowDialog() hasta tanto el Form sea cerrado, cuando esta operación se lleve a cabo y se detecte la aceptación satisfactoria del Form se procede a tomar los cursos seleccionados y asignarlos al control que contendrá la información.&lt;/p&gt;

&lt;p&gt;En este caso se hace uso de la propiedad Tag del Textbox para mantener la información seleccionada&lt;/p&gt;

&lt;p&gt;En el formulario de selección dispone de una propiedad para que el formulario que lo invoco pueda acceder a la información sin necesidad de recurrir directo de los controles del propio Form. A su vez solo el evento del botón Aceptar es que el cierra el form con una resultado aceptado para procesar la selección.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2978aaa1-a965-4b0e-b1d4-3e1f451f419e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public List&amp;lt;CourseEntity&amp;gt; CursosSeleccionados
{
    get
    {
        return lstCursosSelected.Items.Cast&amp;lt;CourseEntity&amp;gt;().ToList();
    }
}

private void btnAceptar_Click(object sender, EventArgs e)
{
  this.DialogResult = DialogResult.OK;
}

private void btnCancelar_Click(object sender, EventArgs e)
{
  this.DialogResult = DialogResult.Cancel;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 -Filtro IN, concatenado el string&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta primera implementación se podría decir que es la mas estándar y directa, aunque hay que remarcar que no es la mas bonita.&lt;/p&gt;

&lt;p&gt;En la clase PersonDAL se cuenta el método Select()&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:53b96705-56f3-41b7-9371-ff8f6afc40d1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;PersonEntity&amp;gt; Select(PersonCriteria filter)
{
    string sql = @&amp;quot;SELECT   P.PersonID,
                            P.LastName,
                            P.FirstName,
                            P.HireDate,
                            P.EnrollmentDate
                   FROM Person P
                        LEFT JOIN CourseInstructor CI 
                        ON P.PersonID = CI.PersonID
                   WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName + '%'))
                    AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName + '%'))
                    AND ((@HireDateFrom IS NULL) OR (P.HireDate &amp;gt;= @HireDateFrom))
                    AND ((@HireDateTo IS NULL) OR (P.HireDate &amp;lt;= @HireDateTo))
                    AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate &amp;gt;= @EnrollmentDateFrom))
                    AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate &amp;lt;= @EnrollmentDateTo))
                    AND ((@Course IS NULL) OR (CI.CourseID IN ({0})))&amp;quot;;

    if (filter.Course != null)
    {
        string courseFilter = string.Join(&amp;quot;,&amp;quot;, filter.Course.ConvertAll(x =&amp;gt; x.CourseID.ToString()).ToArray());
        sql = sql.Replace(&amp;quot;{0}&amp;quot;, courseFilter);
    }

    List&amp;lt;PersonEntity&amp;gt; list = new List&amp;lt;PersonEntity&amp;gt;();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        SqlCommand cmd = new SqlCommand(sql, conn);

        if (string.IsNullOrEmpty(filter.FirstName))
            cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;, DBNull.Value);
        else
            cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;,  filter.FirstName);

        cmd.Parameters.AddWithValue(&amp;quot;@LastName&amp;quot;, string.IsNullOrEmpty(filter.FirstName) ? (object)DBNull.Value : filter.LastName);

        cmd.Parameters.AddWithValue(&amp;quot;@HireDateFrom&amp;quot;, filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date : (object)DBNull.Value);
        cmd.Parameters.AddWithValue(&amp;quot;@HireDateTo&amp;quot;, filter.HireDateTo.HasValue ? filter.HireDateTo.Value.Date : (object)DBNull.Value);

        cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateFrom&amp;quot;, filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date : (object)DBNull.Value);
        cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateTo&amp;quot;, filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date : (object)DBNull.Value);

        cmd.Parameters.AddWithValue(&amp;quot;@Course&amp;quot;, filter.Course == null ? (object)DBNull.Value : &amp;quot;&amp;quot;);

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            list.Add(LoadPerson(reader));
        }

        return list;
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El mismo sigue todas las reglas mencionadas en la parte 1 del articulo, solo que filtro de cursos tiene una particularidad.&lt;/p&gt;

&lt;p&gt;Es preciso notar las líneas 19-23, en estas es cuando se une al string de la consulta principal, la lista de cursos seleccionados.&lt;/p&gt;

&lt;p&gt;La línea 46, sigue representando la anulación o no del filtro del cursos, esto es necesario en caso de no enviarse ningún ítem en la selección.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 -Filtro por medio de XML&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si bien esta implementación no es estandar para todas las base de datos, ya que requiere de soporte para xml, si es la que mejor cierra en cuanto al uso de parámetros.&lt;/p&gt;

&lt;p&gt;En este caso la lista de cursos seleccionado es convertida a un xml, el cual se asigna al parámetro para luego unirlo al join de la consulta.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:1c0755c4-e192-4bbd-bfc8-f6d99b0c99ca" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;PersonEntity&amp;gt; SelectByXml(PersonCriteria filter)
        {
            string sql = @&amp;quot;
                           DECLARE @idoc  int
                           EXEC sp_xml_preparedocument @idoc OUTPUT, @Course

                           SELECT   P.PersonID,
                                    P.LastName,
                                    P.FirstName,
                                    P.HireDate,
                                    P.EnrollmentDate
                           FROM Person P
                                LEFT JOIN CourseInstructor CI 
                                ON P.PersonID = CI.PersonID
                                    LEFT JOIN OPENXML(@idoc, '/courses/course', 2)
                                    WITH (id  int 'text()') AS CL ON CI.CourseID = CL.id 
                           WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName + '%'))
                            AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName + '%'))
                            AND ((@HireDateFrom IS NULL) OR (P.HireDate &amp;gt;= @HireDateFrom))
                            AND ((@HireDateTo IS NULL) OR (P.HireDate &amp;lt;= @HireDateTo))
                            AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate &amp;gt;= @EnrollmentDateFrom))
                            AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate &amp;lt;= @EnrollmentDateTo))
                            AND ((@Course IS NULL) OR (CL.id IS NOT NULL))&amp;quot;;


            List&amp;lt;PersonEntity&amp;gt; list = new List&amp;lt;PersonEntity&amp;gt;();

            using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
            {
                conn.Open();

                SqlCommand cmd = new SqlCommand(sql, conn);

                if (string.IsNullOrEmpty(filter.FirstName))
                    cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;, DBNull.Value);
                else
                    cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;, filter.FirstName);

                cmd.Parameters.AddWithValue(&amp;quot;@LastName&amp;quot;, string.IsNullOrEmpty(filter.FirstName) ? (object)DBNull.Value : filter.LastName);

                cmd.Parameters.AddWithValue(&amp;quot;@HireDateFrom&amp;quot;, filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date : (object)DBNull.Value);
                cmd.Parameters.AddWithValue(&amp;quot;@HireDateTo&amp;quot;, filter.HireDateTo.HasValue ? filter.HireDateTo.Value.Date : (object)DBNull.Value);

                cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateFrom&amp;quot;, filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date : (object)DBNull.Value);
                cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateTo&amp;quot;, filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date : (object)DBNull.Value);

                if (filter.Course != null)
                {
                    XElement root = new XElement(&amp;quot;courses&amp;quot;);
                    List&amp;lt;XElement&amp;gt; couseList = filter.Course.ConvertAll(x =&amp;gt; new XElement(&amp;quot;course&amp;quot;, x.CourseID));
                    root.Add(couseList.ToArray());

                    cmd.Parameters.AddWithValue(&amp;quot;@Course&amp;quot;, root.ToString());
                }
                else
                {
                    cmd.Parameters.AddWithValue(&amp;quot;@Course&amp;quot;, DBNull.Value);
                }

                
                SqlDataReader reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    list.Add(LoadPerson(reader));
                }

                return list;
            }

        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Las líneas 47-58, implementan la conversión a xml de los cursos, esta toman la forma&lt;/p&gt;
&lt;em&gt;&amp;lt;courses&amp;gt; 
  &lt;br /&gt;&amp;#160; &amp;lt;course&amp;gt;1045&amp;lt;/course&amp;gt; 

  &lt;br /&gt;&amp;#160; &amp;lt;course&amp;gt;1061&amp;lt;/course&amp;gt; 

  &lt;br /&gt;&amp;lt;/courses&amp;gt;&lt;/em&gt;

&lt;p&gt;o sea cada curso seleccionado representa un tag en el xml&lt;/p&gt;

&lt;p&gt;La consulta tiene algunas particularidades como ser las dos primeras líneas&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DECLARE @idoc&amp;#160; int
    &lt;br /&gt;EXEC sp_xml_preparedocument @idoc OUTPUT, @Course&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;cuya finalidad es inicializar el xml que luego es usado en el join&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LEFT JOIN OPENXML(@idoc, '/courses/course', 2)
    &lt;br /&gt;WITH (id&amp;#160; int 'text()') AS CL ON CI.CourseID = CL.id&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Aquí el text() representa justamente el contenido del tag, y por medio del /courses/course (el cual es un selector de XPath), se toma cada tag de curso.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Para mas información sobre como trabajar el xml en T-SQL, un excelente recurso es el MSDN Library&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msdn.microsoft.com/es-es/library/ms186918.aspx" target="_blank"&gt;OPEN XML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msdn.microsoft.com/es-es/library/ms187897.aspx" target="_blank"&gt;Usar OPENXML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se puede además realizar pruebas puntuales del xml para entender su funcionamiento, por ejemplo&lt;/p&gt;

&lt;p&gt;DECLARE&amp;#160; @Course As VARCHAR(1000) 
  &lt;br /&gt;SET&amp;#160; @Course = N'&amp;lt;courses&amp;gt; 

  &lt;br /&gt;&amp;#160; &amp;lt;course&amp;gt;1045&amp;lt;/course&amp;gt; 

  &lt;br /&gt;&amp;#160; &amp;lt;course&amp;gt;1061&amp;lt;/course&amp;gt; 

  &lt;br /&gt;&amp;lt;/courses&amp;gt;' &lt;/p&gt;

&lt;p&gt;DECLARE @idoc&amp;#160; int 
  &lt;br /&gt;EXEC sp_xml_preparedocument @idoc OUTPUT, @Course &lt;/p&gt;

&lt;p&gt;SELECT * FROM OPENXML(@idoc, '/courses/course', 2) 
  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; WITH (id&amp;#160; int 'text()')&lt;/p&gt;

&lt;p&gt;Estas podrían ser ejecutadas en el Sql Server Management Studio &lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links Útiles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.sommarskog.se/arrays-in-sql-2000.html" target="_blank"&gt;Arrays and Lists in SQL Server 2000 and Earlier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de Código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La base de datos fue creada con Sql Server Express 2008 R2, en caso de tener problemas con al misma en el proyecto “DataAccess” esta la carpeta “script” con el .sql que puede usar para crear estructura y datos.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/Parte2/[csharp]FiltroCondicionales.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-1852268148204723682?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/1852268148204723682/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=1852268148204723682' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1852268148204723682'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1852268148204723682'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/09/filtros-condicionales-22-implementar.html' title='Filtros Condicionales (2/2) – Implementar filtros múltiples'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-1769289147562882100</id><published>2011-09-11T20:20:00.001-07:00</published><updated>2011-09-25T17:57:37.451-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>Filtros Condicionales (1/2)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Al desarrollar una aplicación un aspecto del cual seguramente habrá que pensar es como otorgar al usuario información que el sea útil y simple de analizar.&lt;/p&gt;  &lt;p&gt;En este punto juega un papel fundamental como diseñar los filtros aplicado a las entidades, para poder se lo mas preciso posible en la búsqueda.&lt;/p&gt;  &lt;p&gt;Es por eso que en este artículo se plantea la implementación de búsqueda por medio de clases que denominare Criteria, ya que esta serán las encargadas de proporcionar los datos seleccionados por el usuario en la pantalla. &lt;/p&gt;  &lt;p&gt;Como punto ventajoso de esta técnica se podría mencionar que la clase evita tener que redefinir los parámetros del método de búsqueda cada vez que se quiera extender la funcionalidad. Esto es muy importante si se tiene la idea de aplicar algún patrón como ser el Repository.&lt;/p&gt;  &lt;p&gt;Los requerimientos cambiantes en las aplicaciones hacen que en un primer momento un filtro simple de dos campos cubra la necesidad, pero el uso diario por parte del usuario ira cambiando esta visión, solicitando agregar nuevo filtros, el uso de una clase como témplate de filtro facilita esta tarea ya que el método no cambia en sus parámetros, solo la clase Criteria se vera afectada y por supuesto la query que aplique el filtro.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Estructura de la solución&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Analicemos un poco como esta estructurado el ejemplo.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/imagen2.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;Solo son dos capas, una de acceso a datos y otro de presentación, se realizo de esta forma para no complicar el desarrollo del ejemplo, porque aquí no se pretendía mostrar una arquitectura completa, sino solo apuntar a entender como aplicar filtros de forma correcta.&lt;/p&gt;  &lt;p&gt;Es muy importante remarcar la clase de nombre &lt;em&gt;PersonCriteria&lt;/em&gt;, la cual actuara solo cuando los filtros entran en accion, y la clase &lt;em&gt;PersonEntity, &lt;/em&gt;quien representa de la entidad de negocio.&lt;/p&gt;  &lt;p&gt;Analicemos la diferencia entre las dos entidades:&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4b247231-0e99-4dde-934a-99c29984cb0d" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class PersonCriteria
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public DateTime? EnrollmentDateFrom { get; set; }
    public DateTime? EnrollmentDateTo { get; set; }

    public DateTime? HireDateFrom { get; set; }
    public DateTime? HireDateTo { get; set; }

    public CourseEntity Curse { get; set; }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:54af2425-1966-4e77-941a-0c8c026a2010" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class PersonEntity
{
    public int PersonID {get; set;}
    public string LastName { get; set; } 
    public string FirstName { get; set; } 

    public DateTime? HireDate { get; set; }
    public DateTime? EnrollmentDate { get; set; } 
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Las propiedades de cada clase tienen unas cuantas diferencias porque sus responsabilidades y lo que representantas son diferentes.&lt;/p&gt;
&lt;strong&gt;&lt;/strong&gt;

&lt;p&gt;&lt;strong&gt;Análisis de la Presentación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La pantalla que se presentara al usuario incluirá varios filtros que actúan de forma combinada.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/imagen1.jpg" width="467" height="440" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El punto clave en al presentación será la lógica encargada de cargar el criterio de búsqueda:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:a91a1872-b7e1-430d-a4ed-c2f718bd670c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnFiltrar_Click(object sender, EventArgs e)
{
    PersonCriteria filter = new PersonCriteria()
    {
        FirstName = txtNombre.Text,
        LastName = txtApellido.Text,
        EnrollmentDateFrom = chkFechaInscripcionDesde.Checked ? (DateTime?)dtpFechaInscripcionDesde.Value : null,
        EnrollmentDateTo = chkFechaInscripcionHasta.Checked ? (DateTime?)dtpFechaInscripcionHasta.Value : null,
        HireDateFrom = chkFechaContratacionDesde.Checked ? (DateTime?)dtpFechaContratacionDesde.Value : null,
        HireDateTo = chkFechaContratacionHasta.Checked ? (DateTime?)dtpFechaContratacionHasta.Value : null,
        Curse = Convert.ToInt32(cmbCourse.SelectedValue)== -1 ? null : new CourseEntity() { CourseID = Convert.ToInt32(cmbCourse.SelectedValue) }
    };

    dgvPersonList.DataSource = PersonDAL.Select(filter);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La presentación conoce como asignar la propiedad con el dato correcto, es porque eso que allí se observan validaciones para determinar si asignar el dato o no.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acceso a Datos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta es la capa con mayor responsabilidad, lógicamente porque será la encargada de aplicar el filtro, pero todo se reduce a un simple truco en la query que habilita o no el filtro en el WHERE&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2ffc9e68-7e1b-4fa3-8d1a-bc525030ceb9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;PersonEntity&amp;gt; Select(PersonCriteria filter)
{
    string sql = @&amp;quot;SELECT   P.PersonID,
                            P.LastName,
                            P.FirstName,
                            P.HireDate,
                            P.EnrollmentDate
                   FROM Person P
                        LEFT JOIN CourseInstructor CI 
                        ON P.PersonID = CI.PersonID
                   WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName + '%'))
                    AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName + '%'))
                    AND ((@HireDateFrom IS NULL) OR (P.HireDate &amp;gt;= @HireDateFrom))
                    AND ((@HireDateTo IS NULL) OR (P.HireDate &amp;lt;= @HireDateTo))
                    AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate &amp;gt;= @EnrollmentDateFrom))
                    AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate &amp;lt;= @EnrollmentDateTo))
                    AND ((@Course IS NULL) OR (CI.CourseID = @Course))&amp;quot;;

    List&amp;lt;PersonEntity&amp;gt; list = new List&amp;lt;PersonEntity&amp;gt;();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        SqlCommand cmd = new SqlCommand(sql, conn);

        if (string.IsNullOrEmpty(filter.FirstName))
            cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;, DBNull.Value);
        else
            cmd.Parameters.AddWithValue(&amp;quot;@FirstName&amp;quot;,  filter.FirstName);

        cmd.Parameters.AddWithValue(&amp;quot;@LastName&amp;quot;, string.IsNullOrEmpty(filter.FirstName) ? (object)DBNull.Value : filter.LastName);

        cmd.Parameters.AddWithValue(&amp;quot;@HireDateFrom&amp;quot;, filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date : (object)DBNull.Value);
        cmd.Parameters.AddWithValue(&amp;quot;@HireDateTo&amp;quot;, filter.HireDateTo.HasValue ? filter.HireDateTo.Value.Date : (object)DBNull.Value);

        cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateFrom&amp;quot;, filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date : (object)DBNull.Value);
        cmd.Parameters.AddWithValue(&amp;quot;@EnrollmentDateTo&amp;quot;, filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date : (object)DBNull.Value);

        cmd.Parameters.AddWithValue(&amp;quot;@Course&amp;quot;, filter.Curse == null ? (object)DBNull.Value: filter.Curse.CourseID);


        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            list.Add(LoadPerson(reader));
        }

        return list;
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Analicemos una sección pequeña para entenderlo:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName + '%'))&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;en esta se tienen dos parte, la primera compara el parámetro con NULL, por lo tanto si desde el código .net enviamos un DbNull.Value estaríamos anulando este filtro, porque esta comparación aplicaría siempre para todos los registros.&lt;/p&gt;

&lt;p&gt;En la segunda mitad será donde el filtro se aplica, este al tener un valor no se vera afectado por la primer sección.&lt;/p&gt;

&lt;p&gt;Además quise mostrar que existen varias formas desde código de asignar el parámetro, es por eso que la línea 27 la asignación del parámetro se efectúa con un if completo, mientras que el resto lo hace en sola linea, esto fue solo para remarcar que no hay una única forma de asignar el parámetro, si la comparación en una línea queda compleja (o poco clara) se puede pasar a varias líneas.&lt;/p&gt;

&lt;p&gt;Este es todo el truco, no es nada difícil de implementar, y conocerlo puede ayudar brindar al usuario filtros mas potentes que agreguen valor a la aplicación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de Código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La base de datos fue creada con Sql Server Express 2008 R2, en caso de tener problemas con al misma en el proyecto “DataAccess” esta la carpeta “script” con el .sql que puede usar para crear estructura y datos.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/[csharp]FiltroCondicionales.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/FiltroCondicional/[vb.net]FiltroCondicionales.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-1769289147562882100?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/1769289147562882100/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=1769289147562882100' title='5 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1769289147562882100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1769289147562882100'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/09/filtros-condicionales.html' title='Filtros Condicionales (1/2)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-3918777356076396981</id><published>2011-08-22T19:00:00.001-07:00</published><updated>2011-08-22T19:00:47.707-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Archivos de Configuración - Crear secciones propias (3/3)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;En esta tercer parte se extenderá el ejemplo planteado en los artículos previos&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/07/archivos-de-configuracion-una.html"&gt;Archivos de Configuración - Una introducción (1/3)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/07/archivos-de-configuracion-creando.html" target="_blank"&gt;Archivos de Configuración – Creando secciones propias (2/3)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;A nivel de interfaz de usuario no ha sufrido cambios, pero si en funcionalidad, al ser esta mucho mas dinámica.&lt;/p&gt;  &lt;p&gt;Entere los puntos tratados aquí se incluirán:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;un modelo de configuración mas rico y extendido&lt;/li&gt;    &lt;li&gt;instancia dinámica de librerías, las cuales no serán referenciadas al utilizarse&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Analizando la estructura&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Empecemos analizando como se dividieron los proyecto y como se interrelacionan.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen1.jpg" width="274" height="530" /&gt; &lt;/p&gt;  &lt;p&gt;En esta oportunidad las clases que definen la configuración están separadas del proyecto de presentación, pero unidas por medio de una referencia.&lt;/p&gt;  &lt;p&gt;El proyecto de proveedores también esta en un proyecto separado, pero hay un detalle, el proyecto de UI no tiene referencia alguna a este. Esto es así porque se instanciara la librería se instanciara de forma dinámica.&lt;/p&gt;  &lt;p&gt;Para poder hacer uso de la misma se copia la dll resultante usando un “Post Build Event”, para esto se accede por medio de las propiedades del proyecto de providers.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen2.jpg" width="660" height="320" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;El post build copiara la dll del providers a la carpeta \bin\Debug del proyecto de UI, (donde estará el .exe), de esta forma al instanciarse dinámicamente la librería la encontrara sin problema, la carpeta local es uno de los sitios donde .net busca las instancias cuando necesita crearla.&lt;/p&gt;  &lt;p&gt;El ejemplo se va a dividir en dos implementaciones, una en donde los proveedores no definan un interfaz común para poder se utilizados, y otra en donde la interacción es normalizada por una interfaz.&lt;/p&gt;  &lt;p&gt;La definición de la configuración es común para ambas implementaciones, porque básicamente los datos son los mismos.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definicion de la configuración&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Los tag usados en esta oportunidad posee un mayor nivel de detalle comparado con los ejercicios de los artículos previos, en cuanto a funcionalidad he información que definen.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:cb44d857-cb49-4e55-be00-b0f852982984" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;configSections&amp;gt;
    &amp;lt;section name=&amp;quot;MediosPagoSection&amp;quot; type=&amp;quot;MediosPagos.UI.Configuration.MediosPagoConfigurationSection, MediosPagos.UI.Configuration&amp;quot; /&amp;gt;
  &amp;lt;/configSections&amp;gt;

  &amp;lt;MediosPagoSection&amp;gt;
    &amp;lt;assembly file=&amp;quot;MediosPagos.Providers.dll&amp;quot; /&amp;gt;
    &amp;lt;MediosPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;1&amp;quot; descripcion=&amp;quot;Efectivo&amp;quot;&amp;gt;
        &amp;lt;provider type=&amp;quot;MediosPagos.Providers.EfectivoProvider&amp;quot; method=&amp;quot;CalcularDescuento&amp;quot; /&amp;gt;
      &amp;lt;/MedioPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;2&amp;quot; descripcion=&amp;quot;Tarjeta Credito&amp;quot;&amp;gt;
        &amp;lt;provider type=&amp;quot;MediosPagos.Providers.TarjetaCreditoProvider&amp;quot; method=&amp;quot;CalcularRecargo&amp;quot;&amp;gt;
          &amp;lt;params&amp;gt;
            &amp;lt;param name=&amp;quot;recargo&amp;quot; value=&amp;quot;10&amp;quot; type=&amp;quot;System.Int32&amp;quot; /&amp;gt;
          &amp;lt;/params&amp;gt;
        &amp;lt;/provider&amp;gt;
      &amp;lt;/MedioPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;3&amp;quot; descripcion=&amp;quot;Tarjeta Debito&amp;quot;&amp;gt;
        &amp;lt;provider type=&amp;quot;MediosPagos.Providers.TarjetaDebitoProvider&amp;quot; method=&amp;quot;CalcularRecargo&amp;quot; /&amp;gt;
      &amp;lt;/MedioPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;4&amp;quot; descripcion=&amp;quot;Cheque&amp;quot;&amp;gt;
        &amp;lt;provider type=&amp;quot;MediosPagos.Providers.ChequeProvider&amp;quot; method=&amp;quot;Calcular&amp;quot;&amp;gt;
          &amp;lt;params&amp;gt;
            &amp;lt;param name=&amp;quot;recargo&amp;quot; value=&amp;quot;10&amp;quot; type=&amp;quot;System.Int32&amp;quot; /&amp;gt;
            &amp;lt;param name=&amp;quot;cargofijo&amp;quot; value=&amp;quot;5,4&amp;quot; type=&amp;quot;System.Decimal&amp;quot; /&amp;gt;
          &amp;lt;/params&amp;gt;
        &amp;lt;/provider&amp;gt;
      &amp;lt;/MedioPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;5&amp;quot; descripcion=&amp;quot;Transferencia Bancaria&amp;quot;&amp;gt;
        &amp;lt;provider type=&amp;quot;MediosPagos.Providers.TransferenciaBancariaProvider&amp;quot; method=&amp;quot;Calcular&amp;quot; /&amp;gt; 
      &amp;lt;/MedioPago&amp;gt;
    &amp;lt;/MediosPago&amp;gt;
  &amp;lt;/MediosPagoSection&amp;gt;
  
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Al tener el xml de configuración un tags anidados con una profundidad mayor, es lógico que la interacción entre las clases también refleje esta complejidad.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen3.jpg" width="686" height="481" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Hay algunos tag a remarcar en cuanto a utilidad:&lt;/p&gt;

&lt;p&gt;- el tag &amp;lt;assembly&amp;gt; definido dentro de la sección de medios de pago, es utilizado para conocer el nombre de la dll donde están los providers implementados, esta información será usada para crear la instancia de la librería&lt;/p&gt;

&lt;p&gt;- tag &amp;lt;provider&amp;gt;, ahora tiene atributos que definen el type, indicando la clase donde se implementa ese provider dentro de la librería, y el method, que se invocara. Para la versión con interfaz este atributo no será necesario, porque el contrato será único, los metodos llevaran el mismo nombre y cantidad de parametros.&lt;/p&gt;

&lt;p&gt;- tag &amp;lt;params&amp;gt;, proporciona información adicional y dinámica necesaria por la implementación de proveedor del calculo, esta es una colección por lo que podrá agregarse cualquier numero de tag ítems que sean necesarios&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementación sin una interfaz común&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Empecemos analizando la versión que no implementa una interfaz, en donde los nombres de los métodos puedes ser variados, al igual que el numero de parámetros.&lt;/p&gt;

&lt;p&gt;Con respecto a los parámetros, hay que mencionar una regla definida, el importe es fijo he ira siempre en primer lugar.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e0ced1d8-6369-41c1-aab2-34eb85057f24" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;string mediopago = Convert.ToString(cmbMediosPago.SelectedValue);
ProviderElement provider = Config.Instance().GetProviderById(mediopago);

Assembly _assembly = Assembly.LoadFrom(Config.Instance().MediosPago.assembly.file);
object instance = _assembly.CreateInstance(provider.type);

//
// defino los parametros
//
List&amp;lt;object&amp;gt; param = new List&amp;lt;object&amp;gt;();
param.Add(importe);

foreach (ParamElement paramItem in provider.Params)
{
    param.Add(Convert.ChangeType(paramItem.value, Type.GetType(paramItem.type)));
}

//
// invoco al metodo
//
object result = instance.GetType().InvokeMember(provider.method, BindingFlags.InvokeMethod, null, instance, param.ToArray());


txtTotal.Text = string.Format(&amp;quot;{0:N2}&amp;quot;, result);
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El código para realizar la invocación después de todo no parece ser tan complejo, no al menos como uno se lo imaginaba cuando observar el archivo de configuración.&lt;/p&gt;

&lt;p&gt;Primeramente, se crea la instancia, pero como primer paso se carga el Assembly, es por eso que se requiere el nombre de la dll.&lt;/p&gt;

&lt;p&gt;A continuación se defina la lista de parámetros en el mismo orden como se define en la implementación del método, colocando el importe en primer lugar, y a continuación los parámetros dinámicos.&lt;/p&gt;

&lt;p&gt;Por ultimo se invoca el método, para lo cual se ha usado el InvokeMember(), porque de este solo se tiene el nombre devuelto por la configuración, pudiendo ser distinto en cada implementación.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/[csharp]WinformsConfigSeccionesPropias.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementación con interfaz Común&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si bien esta debería ser una implementación correcta del ejemplo, la anterior tenia su objetivo practico, que apuntaba a demostrar como configurar he invocar cuando todo es dinámico, pero en un desarrollo real siempre hay que tratar de llevar a la estandarización, y las interfaces permiten que esto sea simple.&lt;/p&gt;

&lt;p&gt;Para que esto funcione fue necesario adaptar algo mas los proyecto, es por eso que ahora hay uno nuevo que actúa de intermediario entre la UI y la implementación de la proveedores, apunto al proyecto de interfaz, el cual es referenciado por los otros dos, como se observa en la imagen:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen4.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/imagen4.jpg" width="238" height="467" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;El proyecto de interfaz define el contrato entre las partes:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:697f4377-48dc-4edd-8535-843843c11037" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public interface ICalculo
{
    decimal Calcular(decimal importe);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Los proveedores los respetan y cumplen:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ee8345b8-a51a-4537-b1ac-627144c1f298" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class ChequeProvider : ICalculo
{

    /// &amp;lt;summary&amp;gt;
    /// El cheque recarga un porcentaje configurable
    /// Ademas se cobrara un cargo fijo de administracion
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
    public decimal Calcular(decimal importe)
    {
        //
        // se obtiene los parametros de la configuracion
        //
        ProviderElement provider = Config.Instance().GetProviderById(&amp;quot;4&amp;quot;); //Cheque
        int recargo = Convert.ToInt32(provider.Params[&amp;quot;recargo&amp;quot;].value);
        decimal cargofijo = Convert.ToDecimal(provider.Params[&amp;quot;cargofijo&amp;quot;].value);

        //
        // se realiza el calculo del importe final
        //
        return importe + (importe * ((decimal)recargo / 100)) + cargofijo;
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:69c9dcd3-794c-4ae0-b439-e0439b404244" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class TarjetaCreditoProvider : ICalculo 
{

    /// &amp;lt;summary&amp;gt;
    /// La tarjeta de Credito recarga configurable por sistema
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
    public decimal Calcular(decimal importe)
    {
        ProviderElement provider = Config.Instance().GetProviderById(&amp;quot;2&amp;quot;); //Tarjeta Credito
        int recargo = Convert.ToInt32(provider.Params[&amp;quot;recargo&amp;quot;].value);

        return importe + (importe * ((decimal)recargo / 100));
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Ahora los métodos solo reciben el importe por parámetro, si estos quieren un valor configurable ellos mismos acceden a los datos por el mismo lugar que lo hace la UI, y toman el valor directamente.&lt;/p&gt;

&lt;p&gt;La invocación al método ahora es bien simple, porque se tiene una interfaz:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:0063f76e-f940-4cc7-9ea2-1e05aa957558" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;string mediopago = Convert.ToString(cmbMediosPago.SelectedValue);
ProviderElement provider = Config.Instance().GetProviderById(mediopago);

Assembly _assembly = Assembly.LoadFrom(Config.Instance().MediosPago.assembly.file);
ICalculo instance = (ICalculo)_assembly.CreateInstance(provider.type);

//
// invoco al metodo
//
decimal result = instance.Calcular(importe);

txtTotal.Text = string.Format(&amp;quot;{0:N2}&amp;quot;, result);
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte3/[csharp]WinformsConfigSeccionesPropiasConInterfaz.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-3918777356076396981?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/3918777356076396981/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=3918777356076396981' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3918777356076396981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3918777356076396981'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/08/archivos-de-configuracion-crear.html' title='Archivos de Configuración - Crear secciones propias (3/3)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-698983479931100652</id><published>2011-07-31T19:04:00.001-07:00</published><updated>2011-07-31T19:04:52.959-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Archivos de Configuración – Creando secciones propias (2/3)</title><content type='html'>&lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Continuando con el planteo en la primer artículo&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/07/archivos-de-configuracion-una.html" target="_blank"&gt;Archivos de Configuración - Una introducción (1/3)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;avanzaremos en complejidad agregando funcionalidad que permita extender la configuración a nuestro capricho, modelando así los tags de la estructura xml que se considere adecuada para representar la información de configuración que requiere nuestra aplicación.&lt;/p&gt;  &lt;p&gt;Continuando con la idea del artículo anterior y los medios de pago, imaginemos que dado un importe hay que aplicarle un determinado recargo (o descuento) según el medio de pago seleccionado, pero esto debería poder configurarse, porque se prevé que puede aparecer otros medios de pago en el futuro.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/imagen1.jpg" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;La interfaz es muy simple, se selecciona un medio de pago y se ingresa un importe, el botón “calcular” invocara al proveedor definido para aplicar la operación devolviendo el resultado que se muestra.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definición de la configuración&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Definiremos la configuración con la cual nos basaremos en el ejemplo&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:8d1b58f8-da98-433a-a9e1-bd57d76a670c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;configSections&amp;gt;
    &amp;lt;section name=&amp;quot;MediosPagoSection&amp;quot; type=&amp;quot;WinformsConfigSeccionesPropias.Configuration.MediosPagoConfigurationSection, WinformsConfigSeccionesPropias&amp;quot; /&amp;gt;
  &amp;lt;/configSections&amp;gt;

  &amp;lt;MediosPagoSection&amp;gt;
    &amp;lt;MediosPago&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;1&amp;quot; descripcion=&amp;quot;Efectivo&amp;quot; provider=&amp;quot;WinformsConfigSeccionesPropias.EfectivoProvider&amp;quot; /&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;2&amp;quot; descripcion=&amp;quot;Tarjeta Credito&amp;quot; provider=&amp;quot;WinformsConfigSeccionesPropias.TarjetaCreditoProvider&amp;quot;/&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;3&amp;quot; descripcion=&amp;quot;Tarjeta Debito&amp;quot; provider=&amp;quot;WinformsConfigSeccionesPropias.TarjetaDebitoProvider&amp;quot;/&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;4&amp;quot; descripcion=&amp;quot;Cheque&amp;quot; provider=&amp;quot;WinformsConfigSeccionesPropias.ChequeProvider&amp;quot;/&amp;gt;
      &amp;lt;MedioPago id=&amp;quot;5&amp;quot; descripcion=&amp;quot;Transferencia Bancaria&amp;quot; provider=&amp;quot;WinformsConfigSeccionesPropias.TransferenciaBancariaProvider&amp;quot;/&amp;gt;
    &amp;lt;/MediosPago&amp;gt;
  &amp;lt;/MediosPagoSection&amp;gt;
  
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Un primer punto a remarcar es la definición del una sección de configuración de nombre “MediosPagoSection”, la cual se asocia a una clase diseñada para poder interpretar la región de configuración que necesitamos.&lt;/p&gt;

&lt;p&gt;La sección define una colección de medios de pago, en donde cada ítem cuenta con un “id”, “descripción” y el mas importante el “provider”, que no es nada mas que el nombre completo de la clase que implementa el calculo para ese medio de pago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definición de las clase de configuración&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En el siguiente diagrama&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/imagen2.jpg" width="508" height="360" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Se define la estructura de clases utilizadas para poder mapear los tag del .config con clases que permitan manipular esta información.&lt;/p&gt;

&lt;p&gt;La clase de nombre “Config” no seria en realidad parte de la implementación necesaria para interpretar los tag de configuración, sino mas bien es un adicional que aplica el patrón singleton para proporcionar un único acceso y directo a la información de configuración, pero su inclusión no es obligatoria.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:081785f7-bfe6-42fa-ab0c-b4295739a926" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class Config
{
    private static Config _config;

    private Config()
    {
        this.MediosPago = (MediosPagoConfigurationSection)ConfigurationManager.GetSection(&amp;quot;MediosPagoSection&amp;quot;);
    }

    public static Config Instance()
    {
        if (_config == null)
            _config = new Config();

        return _config;
    }

    public MediosPagoConfigurationSection MediosPago { get; private set; }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El resto de los archivos si son parte del mapeo y requieren unirse uno con otro para armar la estructura.&lt;/p&gt;

&lt;p&gt;La clase “MediosPagoConfigurationSection” que también se habrá observado en el tag del app.config, define el punto de entrada. Esta contiene una colección de ítems, es por eso que se asocia a “MediosPagoCollection”.&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:92bae456-83b3-4ed5-a720-b3d94c3d0c4b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class MediosPagoConfigurationSection : ConfigurationSection
{

    [ConfigurationProperty(&amp;quot;MediosPago&amp;quot;)]
    public MediosPagoCollection MedioPagoItems
    {
        get { return ((MediosPagoCollection)(base[&amp;quot;MediosPago&amp;quot;])); }
    }
    
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;La clase que representa la colección define como se trabaja con un ítem, en esta se puede definir la propiedad “this”, para que busque tanto por índice, así como por la key definida en el elemento.&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:3f9c1915-aba2-4694-9b69-96a5496f6641" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;[ConfigurationCollection(typeof(MedioPagoElement), AddItemName = &amp;quot;MedioPago&amp;quot;)]
public class MediosPagoCollection : ConfigurationElementCollection
{

    protected override ConfigurationElement CreateNewElement()
    {
        return new MedioPagoElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((MedioPagoElement)(element)).Id;
    }


    public MedioPagoElement this[int index]
    {
        get { return (MedioPagoElement)BaseGet(index); }
    }

    public MedioPagoElement this[string id]
    {
        get { return (MedioPagoElement)BaseGet(id); }
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;Cada elemento representado por la clase “MedioPagoElement” solo tiene el mapeo a las propiedades del tag.&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c8a8c6c4-a4d2-4882-af72-058eb4195244" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class MedioPagoElement : ConfigurationElement
{

    [ConfigurationProperty(&amp;quot;id&amp;quot;, DefaultValue = &amp;quot;&amp;quot;, IsKey = true, IsRequired = true)]
    public string Id
    {
        get { return ((string)(base[&amp;quot;id&amp;quot;])); }
        set { base[&amp;quot;id&amp;quot;] = value; }
    }

    [ConfigurationProperty(&amp;quot;descripcion&amp;quot;, DefaultValue = &amp;quot;&amp;quot;, IsKey = false, IsRequired = true)]
    public string descripcion
    {
        get { return ((string)(base[&amp;quot;descripcion&amp;quot;])); }
        set { base[&amp;quot;descripcion&amp;quot;] = value; }
    }

    [ConfigurationProperty(&amp;quot;provider&amp;quot;, DefaultValue = &amp;quot;&amp;quot;, IsKey = false, IsRequired = true)]
    public string provider
    {
        get { return ((string)(base[&amp;quot;provider&amp;quot;])); }
        set { base[&amp;quot;provider&amp;quot;] = value; }
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definición de las clase de calculo de pago&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En cada medio de pago se definición una clase especifica para realizar el calculo, para una mayor comodidad todas estas clases implementan una interfaz común, lo cual hace simple la instanciación.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:653a9829-92c4-4f8f-9ce3-b348850e454a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;interface ICalculoImpuesto
{
    decimal Calcular(decimal importe);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Cada clase concreta implementa la interfaz y aplica el calculo.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:dda9ef97-9053-40a8-9d57-dc71300da614" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class ChequeProvider : ICalculoImpuesto
   {

       /// &amp;lt;summary&amp;gt;
       /// El cheque recarga un 10%
       /// &amp;lt;/summary&amp;gt;
       /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
       /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
       public decimal Calcular(decimal importe)
       {
           return importe + (importe * (decimal)0.10);
       }

   }


public class EfectivoProvider : ICalculoImpuesto
   {

       /// &amp;lt;summary&amp;gt;
       /// En Efectivo se descuenta un 10%
       /// &amp;lt;/summary&amp;gt;
       /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
       /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
       public decimal Calcular(decimal importe)
       {
           return importe - (importe * (decimal)0.10);
       }

   }

public class TarjetaCreditoProvider : ICalculoImpuesto
   {

       /// &amp;lt;summary&amp;gt;
       /// La tarjeta de Credito recarga un 10%
       /// &amp;lt;/summary&amp;gt;
       /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
       /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
       public decimal Calcular(decimal importe)
       {
           return importe + (importe * (decimal)0.10);
       }

   }

public class TarjetaDebitoProvider : ICalculoImpuesto
   {

       /// &amp;lt;summary&amp;gt;
       /// La tarjeta de Debito recarga un 5%
       /// &amp;lt;/summary&amp;gt;
       /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
       /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
       public decimal Calcular(decimal importe)
       {
           return importe + (importe * (decimal)0.05);
       }

   }


public class TransferenciaBancariaProvider : ICalculoImpuesto
   {

       /// &amp;lt;summary&amp;gt;
       /// El Trsnaferencia Bancaria no afecta al importe
       /// &amp;lt;/summary&amp;gt;
       /// &amp;lt;param name=&amp;quot;importe&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
       /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
       public decimal Calcular(decimal importe)
       {
           return importe;
       }

   }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aplicación de todo lo definido&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bien, ahora llego el momento de poner manos a la obra y hacer uso de todo lo configurado en los pasos anteriores.&lt;/p&gt;

&lt;p&gt;Empezaremos por cargar el combo de medios de pago, tomando la información de esta nueva estructura.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:3808c25e-afd0-4310-bf40-c4768454e683" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void Form2_Load(object sender, EventArgs e)
{

    var result = (from config in Config.Instance().MediosPago.MedioPagoItems.Cast&amp;lt;MedioPagoElement&amp;gt;()
                  select new
                  {
                      key = config.Id,
                      value = config.descripcion
                  }).ToList();

    cmbMediosPago.DisplayMember = &amp;quot;value&amp;quot;;
    cmbMediosPago.ValueMember = &amp;quot;key&amp;quot;;
    cmbMediosPago.DataSource = result;

    cmbMediosPago.SelectedIndex = -1;

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Como se observa no ha cambiado mucho con respecto al artículo anterior, solo que esta vez se cuenta con la ayuda de&lt;/p&gt;

&lt;p&gt;Config.Instance().MediosPago.MedioPagoItems&lt;/p&gt;

&lt;p&gt;el cual nos abstrae de la operación de carga de config en las clases.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;El próximo punto involucra al calculo de impuesto.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e7cfc466-8211-4891-a66d-7b84d5826357" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnCalcular_Click(object sender, EventArgs e)
{
    errProvider.Clear();

    if (cmbMediosPago.SelectedIndex == -1)
    {
        errProvider.SetError(cmbMediosPago, &amp;quot;Debe seleccionar un medio de pago&amp;quot;);
        return;
    }

    decimal importe = 0;

    if (!decimal.TryParse(txtImporte.Text, out importe))
    {
        errProvider.SetError(txtImporte, &amp;quot;El importe ingresado es invalido&amp;quot;);
        return;
    }


    string mediopago = Convert.ToString(cmbMediosPago.SelectedValue);
    string provider = Config.Instance().MediosPago.MedioPagoItems[mediopago].provider;


    ICalculoImpuesto calculo = (ICalculoImpuesto)Activator.CreateInstance(Type.GetType(provider));

    txtTotal.Text = string.Format(&amp;quot;{0:N2}&amp;quot;, calculo.Calcular(importe));

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Como líneas a destacara se podría mencionar la 19, en donde se accede por medio de la key para recuperar el proveedor que se debe invocar, en este punto si es importante recuperar la información de la configuración ya que el control ComboBox no nos proporciona esta data, solo nos brinda la key.&lt;/p&gt;

&lt;p&gt;La línea 22, tiene de interesante el uso de la clase “Activator” para crear la instancia basada en el nombre completo de la clase (namespace + nombre clase).&lt;/p&gt;

&lt;p&gt;La línea 24, al contar con una interfaz común solo se invoca al método de la instancia creada y eso es todo lo que se necesita.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;Código de ejemplo&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/[csharp]WinformsConfigSeccionesPropias.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte2/[vb.net]WinformsConfigSeccionesPropias.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-698983479931100652?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/698983479931100652/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=698983479931100652' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/698983479931100652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/698983479931100652'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/07/archivos-de-configuracion-creando.html' title='Archivos de Configuración – Creando secciones propias (2/3)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-8260896622256389529</id><published>2011-07-31T14:43:00.001-07:00</published><updated>2011-07-31T19:13:43.096-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Archivos de Configuración - Una introducción (1/3)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Introducción&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Es muy común tener que definir parámetros en las aplicaciones que se desarrollan proporcionando cierta configuración que si bien se podría considerar casi estática (porque no cambia constantemente), si sea necesario prever la posibilidad de adaptación.&lt;/p&gt;  &lt;p&gt;Un ejemplo muy claro de estos es la cadena de conexión a la base de datos, por lo general una vez instalada la aplicación no cambia, pero cuando se esta en la etapa de implementación seguramente sea necesario su adaptación al entorno.&lt;/p&gt;  &lt;p&gt;Por lo general se busca que sea un lugar que impacte lo menos posible en el desarrollo, algunos tienden a crear una clase y colocar constantes o variables readonly dentro del propio código, pero el problema con esto es que requieren recompilar por completo el desarrollo, además de tener que actualizar cada cliente por un simple cambio de configuración.&lt;/p&gt;  &lt;p&gt;También se busca un lugar estándar y conocido, algunos usan la registry de windows para definir configuración, quizás era una opción se algo mas viable cuando se contaba con Win XP, pero con Sistemas Operativos como ser Vista o Win 7 esto cambio bastante, el modelo de seguridad que estos imponen aplica restricciones a estas acciones, no haciendo tan cómoda la escritura en este sitio.&lt;/p&gt;  &lt;p&gt;Entonces porque no usar la propuesta que hace .net al respecto, si se trata de los archivos de configuración, entre las ventajas que este presenta se pueden encontrar:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;una lectura simple, ya que se basa en xml &lt;/li&gt;    &lt;li&gt;fácil acceso y modificación (se puede editar con el notepad), por lo general este archivo se encuentra junto a la aplicación por lo que la seguridad debería permitir la escritura en esta carpeta &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Entre los temas que se trataran en este articulo&lt;/p&gt;  &lt;p&gt;1- Agregar un archivo de configuración a nuestro proyecto&lt;/p&gt;  &lt;p&gt;2- Definir una sección key-value&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;2a- usando la sección appSettings&lt;/p&gt;    &lt;p&gt;2b- definiendo una sección propia&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1- Agregar un archivo de configuración a nuestro proyecto&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Si bien es una acción sencilla, si recién se esta introduciendo en el desarrollo, puede que no resulte tan intuitivo encontrar los pasos para agregar un archivo de configuración.&lt;/p&gt;  &lt;p&gt;Estando sobre el proyecto en el “Solution Explorer”, se acciona el menú que aparece con el boton derecho del mouse, seleccionado el menu Add –&amp;gt; New Item…&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen1.jpg" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;en el recuadro se seleccionara la opción:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen2.jpg" width="499" height="305" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Esto agregar un archivo de nombre App.config, que en una primer instancia tendrá solo un tag.&lt;/p&gt;  &lt;p&gt;Empecemos por una tarea simple, la lectura de una cadena de conexión.&lt;/p&gt;  &lt;p&gt;Para lograr esta tarea se necesitara la ayuda de una clase en particular, me refiero al&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/es-es/library/system.configuration.configurationmanager(VS.90).aspx" target="_blank"&gt;ConfigurationManager&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;con esta clase tendremos acceso a los tag de información definidos, pero para hacer uso de la misma se requiere hacer referencia a la librería System.Configuration, los pasos para esta acción serian representados en la siguiente imagen:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen3.jpg" width="532" height="265" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Solo queda agregar el código que tomaría la información del archivo de configuración, usándolo luego con los objetos de ado.net para conectarse&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen4.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/imagen4.jpg" width="538" height="367" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;En el punto 1 se define el “using” a la librería referenciada en el paso anterior (definiendo así su namespace), mientras que en el 2 se hace uso de la clase ConfigurationManager para tomar la cadena de conexión.&lt;/p&gt;  &lt;p&gt;Por supuesto en el archivo de configuración se debió agregar la key que se define en el .config&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:41905aa6-5a2b-4654-a44c-32009a619979" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;connectionStrings&amp;gt;
    &amp;lt;add name=&amp;quot;default&amp;quot; connectionString=&amp;quot;Data Source=LecturaConnectionString\TestDb.sdf;Persist Security Info=False;&amp;quot;/&amp;gt;
  &amp;lt;/connectionStrings&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2- Definir una sección key-value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Definir información del tipo key-value en el archivo de configuración puede realizarse de varias formas&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;usando la sección appsetting &lt;/li&gt;

  &lt;li&gt;por medio de una seccion custom creada por uno &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2a- Usando AppSetting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta es la forma más simple y directa de definir valores simples en la configuración, solo basta con ingresar en la sección &amp;lt;appSettings&amp;gt; el tag “add” con cada item que se requiera.&lt;/p&gt;

&lt;p&gt;En el ejemplo se observará la sección definida como:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:fe54ccf4-ce51-4d0e-8d7d-bf1a6c8c1e22" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;appSettings&amp;gt;
  &amp;lt;add key=&amp;quot;1&amp;quot; value=&amp;quot;Efectivo&amp;quot; /&amp;gt;
  &amp;lt;add key=&amp;quot;2&amp;quot; value=&amp;quot;Tarjeta Credito&amp;quot; /&amp;gt;
  &amp;lt;add key=&amp;quot;3&amp;quot; value=&amp;quot;Tarjeta Debito&amp;quot; /&amp;gt;
  &amp;lt;add key=&amp;quot;4&amp;quot; value=&amp;quot;Cheque&amp;quot; /&amp;gt;
  &amp;lt;add key=&amp;quot;5&amp;quot; value=&amp;quot;Transferencia Bancaria&amp;quot; /&amp;gt;
&amp;lt;/appSettings&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;En el código, en el Form2, se trabaja con esta información con al ayuda de linq para cargar un combo&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:631f4292-6363-4578-bef4-076f4ef11bef" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void Form2_Load(object sender, EventArgs e)
{


    var result = (from configKey in ConfigurationManager.AppSettings.Keys.Cast&amp;lt;string&amp;gt;()
                  let configValue = ConfigurationManager.AppSettings[configKey]
                  select new
                  {
                      key = configKey,
                      value = configValue
                  }).ToList();

    
    cmbMediosPago.DisplayMember = &amp;quot;value&amp;quot;;
    cmbMediosPago.ValueMember = &amp;quot;key&amp;quot;;
    cmbMediosPago.DataSource = result;
    
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Acceder a un ítem en concreto es tan simple como usar&lt;/p&gt;

&lt;p&gt;ConfigurationManager.AppSettings[key]&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:cc4418d1-d7bf-419d-8c36-e9336a0c9655" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void cmbMediosPago_SelectionChangeCommitted(object sender, EventArgs e)
{
    string key = Convert.ToString(cmbMediosPago.SelectedValue);
    string value = ConfigurationManager.AppSettings[key];

    lblSeleccion.Text = string.Format(&amp;quot;Se ha seleccionado\n Key:{0} \n Value:{1}&amp;quot;, key, value);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2b- Definiendo una sección propia&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El uso de la sección &amp;lt;appSettings&amp;gt; en algunas circunstancia puede resultar demasiado genérico y no brindar un lugar que sea claramente identificable para el negocio que se esta programando, esta sección al ser tan común se puede llenar rápidamente de items key-value no relacionados, haciendo difícil el mantenimiento.&lt;/p&gt;

&lt;p&gt;Pero existe una alternativa a este problema y consiste en crear una sección propia para conserva estos pares key-value de forma personalizada, otorgando una visibilidad con sentido para la aplicación&lt;/p&gt;

&lt;p&gt;En el código del Form3 se implementa la solución usando una sección definida por uno mismo, la cual aplica el mismo concepto key-value.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:bc0bd519-a3db-445c-9956-1c5aa3ef0a45" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;configuration&amp;gt;
  &amp;lt;configSections&amp;gt;
    &amp;lt;section name=&amp;quot;MediosPago&amp;quot; type=&amp;quot;System.Configuration.DictionarySectionHandler&amp;quot; /&amp;gt;
  &amp;lt;/configSections&amp;gt;
  
  
  &amp;lt;MediosPago&amp;gt;
    &amp;lt;add key=&amp;quot;1&amp;quot; value=&amp;quot;Efectivo&amp;quot; /&amp;gt;
    &amp;lt;add key=&amp;quot;2&amp;quot; value=&amp;quot;Tarjeta Credito&amp;quot; /&amp;gt;
    &amp;lt;add key=&amp;quot;3&amp;quot; value=&amp;quot;Tarjeta Debito&amp;quot; /&amp;gt;
    &amp;lt;add key=&amp;quot;4&amp;quot; value=&amp;quot;Cheque&amp;quot; /&amp;gt;
    &amp;lt;add key=&amp;quot;5&amp;quot; value=&amp;quot;Transferencia Bancaria&amp;quot; /&amp;gt;
  &amp;lt;/MediosPago&amp;gt;
  
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Para recuperar la información y listarla&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2ebd3aee-592b-431e-9833-bbd22abe9f36" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void Form3_Load(object sender, EventArgs e)
{

    var result = (from config in ((Hashtable)ConfigurationManager.GetSection(&amp;quot;MediosPago&amp;quot;)).Cast&amp;lt;DictionaryEntry&amp;gt;()
                 select new 
                 {
                     key = config.Key,
                     value = config.Value
                 }).ToList();

    cmbMediosPago.DisplayMember = &amp;quot;value&amp;quot;;
    cmbMediosPago.ValueMember = &amp;quot;key&amp;quot;;
    cmbMediosPago.DataSource = result;

    cmbMediosPago.SelectedIndex = -1;

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Obtener un ítem basándonos en la key requiere recuperar la sección completa para luego si acceder al valor&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:bfb41e88-f37e-481f-8b21-22e108e59a5e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void cmbMediosPago_SelectionChangeCommitted(object sender, EventArgs e)
{
    string key = Convert.ToString(cmbMediosPago.SelectedValue);
    string value = ((Hashtable)ConfigurationManager.GetSection(&amp;quot;MediosPago&amp;quot;))[key].ToString();

    lblSeleccion.Text = string.Format(&amp;quot;Se ha seleccionado\n Key:{0} \n Value:{1}&amp;quot;, key, value);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Por supuesto tanto en estos ejemplo hacer uso de las clase de configuración para tomar el valor del ítem seleccionado, no tiene un sentido practico, ya que el propio combobox proporciona ambos datos key y value (usando el SelectedValue y SelectedText), solo se realiza con fines ilustrativos para poder aplicar los conceptos de programación con el archivo de configuración.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Código de ejemplo&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/[csharp]WinformsConfigSeccionesPropias.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ConfigSeccionesPropias/Parte1/[vb.net]WinformsConfigSeccionesPropias.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-8260896622256389529?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/8260896622256389529/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=8260896622256389529' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8260896622256389529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8260896622256389529'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/07/archivos-de-configuracion-una.html' title='Archivos de Configuración - Una introducción (1/3)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-5361951788893968014</id><published>2011-07-17T18:34:00.001-07:00</published><updated>2011-07-24T19:18:33.919-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[Winforms] Singleton - Pasar datos entre formularios</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Introducción&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En mas de una oportunidad surgió el hecho de comunicar formulario y pasar información entre los mismos, cuando estos se invocan uno a otra la solución es relativamente simple, pero que sucede cuando las distintas invocaciones deben capturar y conservar los datos en cada paso que realizan.&lt;/p&gt;  &lt;p&gt;Es aquí donde entra en juego un patrón bien simple de aplicar pero no por eso menos potente, hago referencia al singleton.&lt;/p&gt;  &lt;p&gt;Para el presente artículo platearemos un escenario nada complicado, simplemente algunos forms donde se capturan datos números, pero esta información deberá ser usada en la ultima pantalla, la cual realiza el calculo con los datos recolectados en los pasos previos.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/winforms/SingletonPasarInfoForm/imagen1.jpg" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/winforms/SingletonPasarInfoForm/imagen1.jpg" width="526" height="276" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1- Singleton,&amp;#160; Pasar datos entre formularios&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Implementar el patrón es muy simple, solo se deben aplicar ciertas reglas que abrigan a tomar la misma instancia.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:d0c9de82-be4b-4516-9ece-c5f692a070ae" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class RecolectarDatos
{
    private static RecolectarDatos datos;

    private RecolectarDatos()
    {
    }

    public static RecolectarDatos Instance()
    {
        if (datos == null)
        {
            datos = new RecolectarDatos();
        }

        return datos;
    }

    public int Dato1 { get; set; }
    public int Dato2 { get; set; }
    public int Dato3 { get; set; }

    public int Dato4 { get; set; }
    public int Dato5 { get; set; }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;El primer punto es definir una instancia static del propia clase singleton (línea 3)&lt;/p&gt;

&lt;p&gt;Segundo, definir el constructor de la clase como privado (línea 5), de esta forma no se podrán crear instancia de la clase, salvo desde dentro de la propia clase.&lt;/p&gt;

&lt;p&gt;Tercero, definir un método o propiedad static, la cual será el punto central por donde se podrá recuperar una instancia de la clase (línea 9), en este punto es donde se valida y crea la instancia de la variable static definida mas arriba, es por esto que siempre se retorna esta misma instancia.&lt;/p&gt;

&lt;p&gt;El resto del código pueden ser método, propiedades, otras instancias de clases, o sea se puede aplicar cualquier concepto de POO sin problemas, porque el patron singleton devuelve una única instancia, pero esta tiene comportamiento como cualquier otro objeto.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 – Eventos, Informar de cambios entre formulario&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Al llegar al ultimo formulario era necesario informar al primero que las operaciones se realizaron correctamente, pero entre estos formulario no hay visibilidad alguna, no se aplico ninguna pasamano de instancias entre los formularios, o algo que los comunique.&lt;/p&gt;

&lt;p&gt;Es por eso que la subscripción a un evento genérico resulta ideal para avisar de una determinada acción sin ser necesario pasar instancias.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c7a81403-ccf3-4adb-9b6a-98d1ecfad552" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class CompleteEvents
{
    public delegate void CompleteHandler(CompleteEventArgs args);
    public static event CompleteHandler Complete;

    public static void RaiseEvent(int calculo)
    {
        if(Complete != null)
            Complete(new CompleteEventArgs(calculo));
    }
}

public class CompleteEventArgs : EventArgs
{
    public CompleteEventArgs(int calculo)
    {
        this.Calculo = calculo;
    }

    public int Calculo { get; set; }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El formulario principal se adjunta al evento definido en la clase, mientras que en el ultimo formulario es invocado el método RaiseEvent() que lanzara la acción.&lt;/p&gt;

&lt;p&gt;Adicionalmente se define un argumento del evento para informar el resultado del calculo, igualmente en este caso por aplicar singleton, quizás no era necesario, ya que se podría haber tomado directamente los valores de cada dato recolectado y realizar nuevamente el calculo.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Código del ejemplo&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/winforms/SingletonPasarInfoForm/[csharp]WinFormsSingleton.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/winforms/SingletonPasarInfoForm/[vb.net]WinFormsSingleton.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-5361951788893968014?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/5361951788893968014/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=5361951788893968014' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5361951788893968014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5361951788893968014'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/07/winforms-singleton-pasar-datos-entre.html' title='[Winforms] Singleton - Pasar datos entre formularios'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-1774967321570763379</id><published>2011-07-02T23:20:00.001-07:00</published><updated>2011-07-02T23:20:46.789-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForm] Listar Archivos del Directorio seleccionado</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En el presente artículo se implementa un ejemplo sencillo de como listar directorios con su contenido, incluyendo además el icono asociado a la extensión del archivo.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/winforms/ListViewIconFile/imagen1.jpg" width="498" height="324" /&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Algunas pruebas&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Si bien durante las primeras pruebas se hizo uso de la funcionalidad &lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.drawing.icon.extractassociatedicon.aspx" target="_blank"&gt;Icon.ExtractAssociatedIcon&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;para obtener la imagen relacionada con la extensión del archivo, note que no siempre se recuperaba el icono correcto, es por eso que investigando algo mas en detalle encontré que por medio de las api se puede realizar esto mismo.&lt;/p&gt;  &lt;p&gt;La implementación de esta se puede hallar en el archivo ExtractIcon.cs&lt;/p&gt;  &lt;p&gt;Si bien en el código solo he dejado una de estas alternativas&lt;/p&gt;  &lt;p&gt;   &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:0e7d0b5a-5b3f-4ea4-add9-9bef9fece023" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void LoadFileList(string path)
        {
            string[] files = Directory.GetFiles(path);

            lvFiles.Items.Clear();

            foreach (var item in files)
            {
                string extension = Path.GetExtension(item);

                if (!imlSmall.Images.ContainsKey(extension))
                {
                    Icon iconSmall = FileExplorer.ExtractIconClass.GetIcon(item, true);
                    imlSmall.Images.Add(extension, iconSmall);
                    Icon iconLarge = FileExplorer.ExtractIconClass.GetIcon(item, false);
                    imlLarge.Images.Add(extension, iconLarge);
                }

                ListViewItem listviewItem = new ListViewItem(Path.GetFileName(item), extension);
                lvFiles.Items.Add(listviewItem);

            }
        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;podrían cambiarse las líneas 13 a 16 para hacer uso del &lt;/p&gt;

&lt;p&gt;Icon.ExtractAssociatedIcon()&lt;/p&gt;

&lt;p&gt;y comprobar por uno mismo la diferencia en el icono que se obtiene.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Debo remarcar que la implementación que se encuentre en la clase ExtractIcon.cs, no es de mi autoría, sino que use de guía ejemplo como ser&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.geekpedia.com/tutorial219_Extracting-Icons-from-Files.html" target="_blank"&gt;Extracting Icons from Files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.koders.com/csharp/fid78D7CA3D07195077018C04D8E6A1FF18BDCA2398.aspx" target="_blank"&gt;ExtractIconClass.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.brad-smith.info/blog/archives/164" target="_blank"&gt;Building a Better ExtractAssociatedIcon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ListViewIconFile/[csharp]%20ListViewIconFile.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ListViewIconFile/[vb.net]%20ListViewIconFile.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-1774967321570763379?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/1774967321570763379/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=1774967321570763379' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1774967321570763379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1774967321570763379'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/07/winform-listar-archivos-del-directorio.html' title='[WinForm] Listar Archivos del Directorio seleccionado'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-4811816007474927775</id><published>2011-06-23T19:56:00.001-07:00</published><updated>2011-12-11T16:29:21.093-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='GridView'/><title type='text'>[GridView] Eventos de controles contenidos en el GridView (1/2)</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Introducción&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;En ciertas ocasiones puede ser necesario trabajar directamente con los eventos de los controles contenidos en el propio GridView, pero el problema planteado es como trabajar con el control que ejecuta la acción y al mismo tiempo poder acceder a la información de la fila del gridview que contiene a dicho control.&lt;/p&gt;  &lt;p&gt;El articulo demuestra como conseguirlo de dos formas distintas:&lt;/p&gt;  &lt;p&gt;- una implicara extender el control que se quiera utilizar &lt;/p&gt;  &lt;p&gt;- la otra simplemente accederá al objeto que contiene al control que lanza el evento&lt;/p&gt;  &lt;p&gt;En los ejemplos se hará uso de un control radiobutton como parte de gridview y será este quien ejecute el evento con el cual se necesitara trabajar.&lt;/p&gt;  &lt;p&gt;&lt;img src="https://public.bay.livefilestore.com/y1p5aMFzNDjlUqYU3vSCny-Fz0h7eJcLKu2CWJQD8lsvc0vWrFUOv4qylRSaWmfWy_7_kt6d1XhuMX5NLstDAEDpA/imagen1.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;strong&gt;Extendiendo el control&lt;/strong&gt;   &lt;hr /&gt;  &lt;p&gt;Para poder implementar este camino se necesitara definir una propiedad que será útil cuando se trabaje con el evento del control.&lt;/p&gt;  &lt;p&gt;Esta propiedad no se dispone de forma estándar en el control, sino que deberá crearse un control que extienda del original. &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:afacb645-8171-48c4-8c35-d976af83e457" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class CustomRadioButtonList : RadioButtonList 
    {
        //
        // La creacion del custom control es justamente para agregar esta propiedad 
        // que por defecto no posee el RadioButtonList
        //
        [DefaultValue(&amp;quot;&amp;quot;)]
        public string CommandArgument
        {
            get
            {
                string s = ViewState[&amp;quot;CommandArgument&amp;quot;] as string;
                return s == null ? String.Empty : s;
            }
            set
            {
                ViewState[&amp;quot;CommandArgument&amp;quot;] = value;
            }
        }

    }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Es importante mencionar que el uso de este nuevo control requiere de su declaración en la pagina&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f40c51ce-da27-457a-8134-f436e0e50327" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;%@ Register Assembly=&amp;quot;GridViewEventosControles&amp;quot; Namespace=&amp;quot;GridViewEventosControles&amp;quot;
    TagPrefix=&amp;quot;cc1&amp;quot; %&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El siguiente paso sea definir del control que se ha creado dentro de la pagina.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:0f3711d2-cc3b-462f-9376-9a18fa9ca7bb" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Respuesta&amp;quot;&amp;gt;

    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;cc1:CustomRadioButtonList ID=&amp;quot;rblRespuesta&amp;quot; runat=&amp;quot;server&amp;quot; 
            CommandArgument='&amp;lt;%#Container.DataItemIndex %&amp;gt;' 
            onselectedindexchanged=&amp;quot;rblRespuesta_SelectedIndexChanged&amp;quot; AutoPostBack=&amp;quot;True&amp;quot;&amp;gt;
        &amp;lt;/cc1:CustomRadioButtonList&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;

&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;La línea donde se usa el Container.DataItemIndex permite asigna el índice de cada row a la propiedad, para que pueda ser tomada desde el código al ejecutarse el evento.&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:53b3e263-cbe8-45b0-b5ba-449f080741f7" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void rblRespuesta_SelectedIndexChanged(object sender, EventArgs e)
{
    CustomRadioButtonList radiolist = sender as CustomRadioButtonList;

    if (radiolist == null)
        return;

    //
    // Se toma el index de la row del gridview, el cual fue asociado en el CommandArgument
    //
    int rowindex = Convert.ToInt32(radiolist.CommandArgument);
    
    //
    // Sabiendo el index de la row con que se debe trabajar se pued recuperat el id
    //
    int idpregunta = Convert.ToInt32(GridView1.DataKeys[rowindex].Value);

    bool seleccion = Convert.ToBoolean(Convert.ToInt32(radiolist.SelectedValue));

    PreguntasManager.Update(idpregunta, seleccion);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEventosControles/[csharp]GridViewEventosControles.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[C# Skydrive] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; padding-right: 0px; padding-top: 0px" title="Preview" height="120" marginheight="0" src="https://skydrive.live.com/embed?cid=5C82AA0C9BBAF5B3&amp;amp;resid=5C82AA0C9BBAF5B3%21184&amp;amp;authkey=AA1Pp_oEhk4b-vk" frameborder="0" width="98" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
&lt;strong&gt;Accediendo al objeto contenedor&lt;/strong&gt; 

&lt;hr /&gt;

&lt;p&gt;Para esta implementación no se requiere el uso de ningún control adiciona, simplemente se hará uso de una propiedad que permite recuperar el contenedor del control.&lt;/p&gt;

&lt;p&gt;Específicamente se hace referencia a &lt;a href="http://msdn.microsoft.com/es-es/library/system.web.ui.control.namingcontainer(VS.80).aspx" target="_blank"&gt;NamingContainer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La definición del control en el grid será de forma normal&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:fde1d7da-d8ee-4165-933b-918cec2fa485" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Respuesta&amp;quot;&amp;gt;

    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;asp:RadioButtonList ID=&amp;quot;rblRespuesta&amp;quot; runat=&amp;quot;server&amp;quot; 
            onselectedindexchanged=&amp;quot;rblRespuesta_SelectedIndexChanged&amp;quot; AutoPostBack=&amp;quot;True&amp;quot;&amp;gt;
        &amp;lt;/asp:RadioButtonList&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;

&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Pero el acceso al row que contiene el control es donde esta el truco.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:843cc97b-b571-435b-910f-2251660aee2c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void rblRespuesta_SelectedIndexChanged(object sender, EventArgs e)
{
    RadioButtonList radiolist = sender as RadioButtonList;

    if (radiolist == null)
        return;

    //
    // Se recupera la row del GridView que contien el control
    //
    GridViewRow row = radiolist.NamingContainer as GridViewRow;
    
    //
    // Sabiendo el index de la row con que se debe trabajar se puede recuperar el id
    //
    int idpregunta = Convert.ToInt32(GridView1.DataKeys[row.RowIndex].Value);

    bool seleccion = Convert.ToBoolean(Convert.ToInt32(radiolist.SelectedValue));

    PreguntasManager.Update(idpregunta, seleccion);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es allí donde se usa la propiedad para obtener el row donde esta contenido el control que lanzo el evento, y el el index será utilizado para obtener el identificador de la entidad, en este caso de la pregunta a la cual se da la respuesta.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEventosControles/[csharp]GridViewEventosNameContainer.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[C# SkyDrive] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; padding-right: 0px; padding-top: 0px" title="Preview" height="120" marginheight="0" src="https://skydrive.live.com/embed?cid=5C82AA0C9BBAF5B3&amp;amp;resid=5C82AA0C9BBAF5B3%21185&amp;amp;authkey=AOFvZMa3HNVtTWY" frameborder="0" width="98" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-4811816007474927775?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/4811816007474927775/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=4811816007474927775' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4811816007474927775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4811816007474927775'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/06/gridview-eventos-de-controles.html' title='[GridView] Eventos de controles contenidos en el GridView (1/2)'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-1535127569788031613</id><published>2011-06-20T22:23:00.001-07:00</published><updated>2011-06-20T22:23:57.557-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='GridView'/><title type='text'>[GridView] Aplicar color en las filas</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Este articulo intentara explicar como aplicar color a las filas del gridview basándose en la aplicación de cierta regla definida&lt;/p&gt;  &lt;p&gt;en el ejemplo se tiene una lista de productos, y por error uno fue duplicado, la idea es poder detectar en que registros del grid se presenta este problema y remarcarlo con un color diferente&lt;/p&gt;  &lt;p&gt;Se hará uso de linq trabajando junto a un dataset para poder aplicar la verificación de cada ítem en memoria.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="200"&gt;[C#]         &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewAplicarColor//[csharp]AplicarColor.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;        &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-1535127569788031613?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/1535127569788031613/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=1535127569788031613' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1535127569788031613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1535127569788031613'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/06/gridview-aplicar-color-en-las-filas.html' title='[GridView] Aplicar color en las filas'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-1231704078671710946</id><published>2011-06-20T21:31:00.001-07:00</published><updated>2011-06-20T21:31:26.405-07:00</updated><title type='text'>[Winforms] Control global de Errores – Implementar Log</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#000080"&gt;Introducción&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;He notado en varias oportunidades que en ciertas circunstancias surgen errores no controlador que son difíciles de rastrear&lt;/p&gt;  &lt;p&gt;Estos es consecuencia de un descuido, al no agregar correctamente en todos los puntos importantes el try..catch, o simplemente por la lógica de la aplicación por se grande implicaría un gran esfuerzo poner en cada evento el control de errores.&lt;/p&gt;  &lt;p&gt;Es por eso que definir un control global a nivel de aplicación podría ayudar, atrapando el problema y registrando que sucedió y donde, esta información es muy preciada cuando se esta perdido y no se sabe que produce el problema, mas cuando se esta en el entorno de producción y no se cuenta con una herramienta de debug&lt;/p&gt;  &lt;p&gt;En este artículo se trataran los siguientes temas:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Control global de errores&lt;/li&gt;    &lt;li&gt;Log usando System.IO.Log&lt;/li&gt;    &lt;li&gt;Log usado Log4Net&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#000080"&gt;1- Control Global Errores&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El implementar la lógica para controlar globalmente los errores no es nada difícil, el truco esta en adjuntarse a un evento, concretamente:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/es-es/library/system.windows.forms.application.threadexception(VS.90).aspx" target="_blank"&gt;Application.ThreadException&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Un buen lugar para realizar esta asignación del evento es el archivos Program.cs, dentro del método Main()&lt;/p&gt;  &lt;p&gt;[C#]&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:468c82c7-fba3-4233-a898-73ced79ccc22" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

    Application.Run(new Form1());
}

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
    MessageBox.Show(e.Exception.Message);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Para lograr esto mismo en vb.net requiere de algunos paso adicionales, ya que vb.net no brinda un acceso transparente al método Main, ni permite que este sea asignado como inicio de la aplciacion, pero en este articulo&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2009/09/c-winforms-realizar-tareas-antes-de.html" target="_blank"&gt;Winforms, realizar tareas antes de inicializar aplicación&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;explico como destrabar esta situación para poder así implementarlo como lo harían en c#, definiendo el Main() como inicio de la aplicación.&lt;/p&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:147cea14-e5db-4d24-bcc6-99fae195e54c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;STAThread()&amp;gt; _
Friend Shared Sub Main()

    Application.EnableVisualStyles()
    Application.SetCompatibleTextRenderingDefault(False)

    AddHandler Application.ThreadException, AddressOf Application_ThreadException

    Application.Run(New Form1())

End Sub

Private Shared Sub Application_ThreadException(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)

    MessageBox.Show(e.Exception.Message)

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El código en ambos lenguajes preserva el mismo concepto, pero la forma en como se adjunta el evento difiera bastante.&lt;/p&gt;

&lt;p&gt;Entonces, ahora si sucediera algo como lo reflejado en la imagen&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ControlGlobalErrores/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/ControlGlobalErrores/imagen1.jpg" width="527" height="247" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;O sea, si se ingresara caracteres en el calculo, el no atrapar el error en un bloque try..catch, haría que la aplicación finalice de forma brusca cerrándose, pero al tener definido el evento ThreadException, entraría en acción tomando el error y mostrando el mensaje.&lt;/p&gt;

&lt;p&gt;Con estos simples paso ya tenemos el control global de errores implementado.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#000080"&gt;2- Log usando System.IO.Log&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si bien en una primer instancia hacer uso de un mensaje podría ayudar a detectar el problema en la aplicación, este podría evolucionar en un log a un archivo para dejar tracking de lo sucedido, en este caso no solo se pondría el mensaje del problema, sino que además se podría agregar el StackTrace para poder analizar que métodos se fueron ejecutando hasta causar el fallo.&lt;/p&gt;

&lt;p&gt;En este caso el log a un archivos será muy simple&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b2299a34-c17f-4cbc-8f2e-2fa68c2339de" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            using(FileRecordSequence record = new FileRecordSequence(&amp;quot;application.log&amp;quot;, FileAccess.Write))
            {

                string message = string.Format(&amp;quot;[{0}]Message::{1} StackTrace:: {2}&amp;quot;, DateTime.Now, 
                                                                                    e.Exception.Message, 
                                                                                    e.Exception.StackTrace);

                record.Append(CreateData(message), SequenceNumber.Invalid, 
                                                            SequenceNumber.Invalid, 
                                                            RecordAppendOptions.ForceFlush);
            }
        }


        private static IList&amp;lt;ArraySegment&amp;lt;byte&amp;gt;&amp;gt; CreateData(string str)
        {
            Encoding enc = Encoding.Unicode;

            byte[] array = enc.GetBytes(str);

            ArraySegment&amp;lt;byte&amp;gt;[] segments = new ArraySegment&amp;lt;byte&amp;gt;[1];
            segments[0] = new ArraySegment&amp;lt;byte&amp;gt;(array);

            return Array.AsReadOnly&amp;lt;ArraySegment&amp;lt;byte&amp;gt;&amp;gt;(segments);
        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:7aa715c4-7d79-481e-9fad-13960757c263" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Shared Sub Application_ThreadException(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)

    Using record As New FileRecordSequence(&amp;quot;application.log&amp;quot;, FileAccess.Write)

        Dim message As String = String.Format(&amp;quot;[{0}]Message::{1} StackTrace:: {2}&amp;quot;, DateTime.Now, e.Exception.Message, e.Exception.StackTrace)

        record.Append(CreateData(message), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush)
    End Using

End Sub


Private Shared Function CreateData(ByVal str As String) As IList(Of ArraySegment(Of Byte))

    Dim enc As Encoding = Encoding.Unicode

    Dim _array As Byte() = enc.GetBytes(str)

    Dim segments As ArraySegment(Of Byte)() = New ArraySegment(Of Byte)(0) {}
    segments(0) = New ArraySegment(Of Byte)(_array)

    Return Array.AsReadOnly(Of ArraySegment(Of Byte))(segments)

End Function
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Ahora el evento global de errores tiene bastante mas código, en donde se arma un mensaje bastante mas útil, el cual será enviado a la funcionalidad de log para registrar el suceso.&lt;/p&gt;

&lt;p&gt;Para recuperar el archivo, solo deben ir a la ubicación donde esta el .exe, en este caso debería esta en la carpeta \bin\Debug del proyecto.&lt;/p&gt;

&lt;p&gt;Lo mas probable es que el archivo no se legible a simple vista porque este sistema de log trabaja con el concepto de entradas de registros, es por eso que se confecciono un formulario muy simple para poder recuperar la información de forma visual.&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ControlGlobalErrores/[csharp]ControlGlobalErrores.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ControlGlobalErrores/[vb.net]ControlGlobalErrores.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#000080"&gt;3- Log usado Log4Net&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Como alternativa siempre es bueno conocer algún otro framework de log, y log4net es uno con muchas posibilidades y configuraciones.&lt;/p&gt;

&lt;p&gt;Lo bueno de este es que al ser configurable uno puede activarlo o cambiar el medio donde se quiere loguear sin tocar el código, hoy se loguea a un archivo, el día de mañana al visor de suceso de windows y si esto no converse, se podría enviar a una tabla en una db, y lo bueno de todo esto se puede lograr sin cambiar el código.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-1231704078671710946?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/1231704078671710946/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=1231704078671710946' title='9 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1231704078671710946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/1231704078671710946'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/06/winforms-control-global-de-errores.html' title='[Winforms] Control global de Errores – Implementar Log'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-8117744875691709980</id><published>2011-06-12T21:52:00.001-07:00</published><updated>2011-06-12T21:52:27.483-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='GridView'/><title type='text'>[ASP.NET][GridView] - Como seleccionar una fila</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Introducción&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;He visto en reiteradas oportunidad que una operación simple como es el caso de operar con una fila de un GridView se puede transformarse en algo complejo, mas que nada motivado por la distintas formas que hay para realizar esta acción.&lt;/p&gt;  &lt;p&gt;En este articulo veremos las algunas formas de lograrlo y como difieren las técnicas que se puede aplicarse.&lt;/p&gt;  &lt;p&gt;Temas que se tratar:&lt;/p&gt;  &lt;p&gt;1- Seleccionar una Row&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;a- Definiendo un CommandField&lt;/p&gt;    &lt;p&gt;b- Usando un ImageButton y CommandName&lt;/p&gt;    &lt;p&gt;c- Usando el evento RowCommand&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;2-Uso de DataKeyNames y DataKeys&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160; a- DataKeyNames con campos Múltiples&lt;/p&gt;  &lt;p&gt;Para todos los casos planteados partiremos del mismo gridview, el cual se ira modificando para agregarle opciones y ver los distintos temas.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;1- Seleccionar una Row&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Existen varias formas de realizar una misma tarea, pero veremos aquí las dos mas simples y directas que se suelen encontrar cuando se necesita seleccionar un registro en el control gridview.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1a- Definiendo un CommandField&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Iremos realizando los paso de forma visual así se comprende como proceder, remarcando luego como impacta esto en el html del grid&lt;/p&gt;  &lt;p&gt;El primer paso será editar las columnas del GridView hasta visualizar el cuadro con las opciones de CommandField disponibles.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen1.jpg" width="497" height="213" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen2.jpg" width="504" height="452" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Para este caso en particular se agregara solo la opción de selección. Mediante las flechas laterales se puede posicionar el comando. También se dispone de distintos tipos de representación visual, como ser un Link, Button o Image.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen3.jpg" width="500" height="497" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Para este caso usaremos un comando del tipo Image, por lo tanto se deberá definir la propiedad “SelectImageUrl”. Si se define del tipo Link y se quiere cambiar el texto, se usaría la propiedad “SelectText”.&lt;/p&gt;  &lt;p&gt;El próximo paso será el de definir el evento de selección, para esto solo marcamos el gridview, y yendo a sus propiedades se podrá activar el evento SelectedIndexChanged&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen4.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen4.jpg" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;El html resultante debería tener resaltadas las siguiente características&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen5.jpg" width="509" height="396" /&gt; &lt;/p&gt;  &lt;p&gt;Con estos pasos ya estamos listos para capturar la acción de selección del gridview.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1b - Usando un ImageButton y CommandName&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En esta alternativa se hará uso de un TemplateField, se prodece de la misma forma del paso 1a, pero se agrega un item diferente&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen6.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen6.jpg" width="474" height="496" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Una vez que esta el témplate, se adapta modificando directamente en el html, incluyendo de esta forma el control ImageButton.&lt;/p&gt;  &lt;p&gt;Es muy importante remarcar que el ImageButton deberá tener la propiedad CommandName=”Select” para que esta ejecute el evento SelectedIndexChanged&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen7.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen7.jpg" width="459" height="358" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;A nivel de código de la pagina se encontrara la definición del evento&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen8.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen8.jpg" width="463" height="289" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Hay que aclarar que en este caso se uso un ImageButton para corresponder con la acción del punto 1a, en donde se define una imagen, pero si se requiere de un link solo será cuestión de usar un LinkButton, definiendo en este el CommandName=”Select”, es justamente el CommandName quien define que evento será lanzado al presionarse.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1c- Usando el evento RowCommand&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Seguramente a estas alturas se preguntaran que cantidad de formas de hacer lo mismo, asi es, y para completarlo una opción extra.&lt;/p&gt;  &lt;p&gt;Resulta que al definir un ImageButton (o LinkButton) en un TemplateItem y usar el CommandName=”Select” se habilita un evento adicionar para poder capturar esta acción, si es que el SelectedIndexChanged no nos convence.&lt;/p&gt;  &lt;p&gt;Se trata del evento RowCommand.&lt;/p&gt;  &lt;p&gt;Hay un pequeño detalle con este evento y se trata de la definición del CommandArgument para determinar que fila lanza la acción.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen11.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen11.jpg" width="528" height="221" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;[C#]&lt;/p&gt;  &lt;p&gt;   &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:9f004906-4a27-4706-9318-4b5fa37f80e8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void gvPerson_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName == &amp;quot;Select&amp;quot;)
    {
        //
        // Se obtiene indice de la row seleccionada
        //
        int index = Convert.ToInt32(e.CommandArgument);
        
        //
        // Obtengo el id de la entidad que se esta editando
        // en este caso de la entidad Person
        //
        int id = Convert.ToInt32(gvPerson.DataKeys[index].Value); 

    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b421a92a-02a9-42d5-8a55-7eebe3e2559b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Protected Sub gvPerson_RowCommand(sender As Object, e As GridViewCommandEventArgs)
	
        If e.CommandName = &amp;quot;Select&amp;quot; Then
		'
		' Se obtiene indice de la row seleccionada
		'
		Dim index As Integer = Convert.ToInt32(e.CommandArgument)

		'
		' Obtengo el id de la entidad que se esta editando
		' en este caso de la entidad Person
		'

		Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(index).Value)
	End If

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;En el ejemplo de la pagina WebForm4.aspx, se podrá probar como ambos eventos, tanto el RowCommand y el SelectedIndexChanged, pueden definirse, aunque lo normal es utilizar solo uno de estos.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;2 - Uso de DataKeyNames y DataKeys&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Una de las mejores técnicas usada para detectar que entidad se esta editando o seleccionado es por medio de id o código que esta tenga asignada, pero como logar hacerlo sin mostrar el identificador al usuario en una columna ?, es justamente el trabajo de estas dos propiedades que se consigue resolver el problema.&lt;/p&gt;

&lt;p&gt;Si se presta atención al html este contaba con al definición de esta propiedad de nombre DataKeyNames&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen9.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen9.jpg" width="519" height="232" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Solo debe definirse que campo del origen de datos identifica a la entidad que se esta trabajando.&lt;/p&gt;

&lt;p&gt;Cuando se lance el evento solo será cuestión de tomar la row que ejecuta la acción, y de esta, por el índice recuperar el valor del id de la entidad, en este caso el PersonID.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:51ea140c-2d6c-43f8-a446-dbd8a8debdb9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void gvPerson_SelectedIndexChanged(object sender, EventArgs e)
{
    //
    // Se obtiene la fila seleccionada del gridview
    //
    GridViewRow row = gvPerson.SelectedRow;

    //
    // Obtengo el id de la entidad que se esta editando
    // en este caso de la entidad Person
    //
    int id = Convert.ToInt32(gvPerson.DataKeys[row.RowIndex].Value);


}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:6d065049-7670-437e-8a05-a15fd71c3c4c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Protected Sub gvPerson_SelectedIndexChanged(sender As Object, e As EventArgs)
	'
	' Se obtiene la fila seleccionada del gridview
	'
	Dim row As GridViewRow = gvPerson.SelectedRow

	'
	' Obtengo el id de la entidad que se esta editando
	' en este caso de la entidad Person
	'
	Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(row.RowIndex).Value)


End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2a- DataKeyNames con campos Múltiples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En este ejemplo solo se utilizo un valor simple para identificar a la entidad, pero el CommandName puede definir mas de un campo de información.&lt;/p&gt;

&lt;p&gt;Por ejemplo que sucede si se quiere enviar el PersonId y el Nombre, esto es tan solo un ejemplo para demostrar las funcionalidad, no tiene una aplicación práctica en este caso, ya que con solo el PersonID seria mas que suficiente.&lt;/p&gt;

&lt;p&gt;Para definir el DataKeyNames en el grid es tan simple como separar los campos por una coma.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen10.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/imagen10.jpg" width="511" height="197" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;y en el código solo se toma el valor de la propiedad Values&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f669f237-966b-43cb-b0c6-26d5969a2840" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void gvPerson_SelectedIndexChanged(object sender, EventArgs e)
{
    //
    // Se obtiene la fila seleccionada del gridview
    //
    GridViewRow row = gvPerson.SelectedRow;

    //
    // Obtengo el id y el nombre  de la entidad que se esta editando
    // en este caso de la entidad Person
    //
    int id = Convert.ToInt32(gvPerson.DataKeys[row.RowIndex].Values[&amp;quot;PersonID&amp;quot;]);

    string nombre = Convert.ToString(gvPerson.DataKeys[row.RowIndex].Values[&amp;quot;FirstName&amp;quot;]);


}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b3ef990d-0cf8-4563-9ccc-8e745017f31e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Protected Sub gvPerson_SelectedIndexChanged(sender As Object, e As EventArgs)
	'
	' Se obtiene la fila seleccionada del gridview
	'
	Dim row As GridViewRow = gvPerson.SelectedRow

	'
	' Obtengo el id y el nombre  de la entidad que se esta editando
	' en este caso de la entidad Person
	'
	Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(row.RowIndex).Values(&amp;quot;PersonID&amp;quot;))

	Dim nombre As String = Convert.ToString(gvPerson.DataKeys(row.RowIndex).Values(&amp;quot;FirstName&amp;quot;))


End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo Código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para el ejemplo se hizo uso de Visual Studio 2008 con SP1, el service pack es útil para poder hacer uso de Entity Framework y poder crear el ADO.NET Entity Data Model haciendo simple el acceso a la db.&lt;/p&gt;

&lt;p&gt;La base de datos es Sql Server 2008 Express R2, y se encuentra el mdf dentro de la carpeta App_Data, pero se podría adjuntar al servicio de Sql Server o hacer uso del script que se encuentra en el proyecto de DataAccess.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewSeleccionarFila/GridViewCommands.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-8117744875691709980?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/8117744875691709980/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=8117744875691709980' title='16 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8117744875691709980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8117744875691709980'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/06/aspnetgridview-como-seleccionar-una.html' title='[ASP.NET][GridView] - Como seleccionar una fila'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-5280826850284329407</id><published>2011-05-08T22:03:00.001-07:00</published><updated>2011-05-22T23:06:12.503-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>[ASP.NET] GridView – Edición Empleados</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En esta ocasión se implementara al versión web de un articulo previo:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/02/winforms-edicion-empleados-grabar.html" target="_blank"&gt;[WinForms] Edición Empleados – Grabar imagen en base de datos&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Los puntos que se trataran con detalle serán:&lt;/p&gt;  &lt;p&gt;- selección de un fila en el gridview&lt;/p&gt;  &lt;p&gt;- eliminar un registro del grid&lt;/p&gt;  &lt;p&gt;- visualizar una imagen que se encuentra dentro de la base de datos en un control de Image&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/pantalla.gif" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/pantalla.gif" width="532" height="325" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1 – Selección&amp;#160; de una fila en el GridView&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Existen varias formas de lograr este objetivo, en el ejemplo del este articulo aplique solo una de ellas haciendo uso del CommandField para definir las acciones sobre el grid, para ello hice uso de la opción visual:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/imagen1.jpg" width="398" height="225" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;esto desplegara el dialogo:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/imagen2.jpg" width="266" height="360" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;es aquí donde se definen las columnas entre ellas el CommandField, al seleccionarlo mostrara las propiedades de configuración, las propiedades de la sección “Behavior” es donde uno selecciona que botones quiere visualizar, es por eso que en este caso la propiedad “ShowSelecteButton” esta en True.&lt;/p&gt;  &lt;p&gt;Además es importante la sección “Appearance” en donde se define “ButtonType” del tipo imagen y el “SelectImageUrl”, con la url del botones que se ve en el grid.&lt;/p&gt;  &lt;p&gt;Concluido la definición de columnas y botones de acción, se debe especificar una propiedad muy importante en el grid, se trata del “DataKeyNames”, esta debería llevar el nombre la propiedad (o columna) del origen de datos que se usara como identificación de la entidad que se edita, en este caso como son empleado, será su id, definiéndose: DataKeyNames=&amp;quot;IdEmpleado&amp;quot;&lt;/p&gt;  &lt;p&gt;Este paso es importante ya que en los eventos se podrá recuperar sobre que entidad se debe aplicar la acción.&lt;/p&gt;  &lt;p&gt;El botón de selección lanzara el evento “SelectedIndexChanging”, es por eso que será necesario definirlo en el grid: onselectedindexchanging=&amp;quot;gvEmpleados_SelectedIndexChanging&amp;quot; &lt;/p&gt;  &lt;p&gt;mientras que en el código de la pagina:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:df0d6733-7b80-4471-b23c-a16aa59364d7" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void gvEmpleados_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    int idempleado = Convert.ToInt32(gvEmpleados.DataKeys[e.NewSelectedIndex].Value);

    Response.Redirect(string.Format(&amp;quot;EditarEmpleado.aspx?id={0}&amp;quot;, idempleado));

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Se hace uso del DataKeys para tomar el valor definido por el DataKeyNames, estas dos propiedades trabajan bien relacionadas entre ellas.&lt;/p&gt;

&lt;p&gt;Lo último paso que queda es redireccionar a la pagina de edición.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2- Eliminar un registro&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La operación de eliminar es muy parecida a la selección, solo cambia el comando usado para esto.&lt;/p&gt;

&lt;p&gt;Al igual que la selección hay que configurar el CommandField habilitando la propiedad “ShowDeleteButton” en true.&lt;/p&gt;

&lt;p&gt;Esta acción lanzara el evento “RowDeleting”, para lo cual especificamos su definición en el grid mediante: onrowdeleting=&amp;quot;gvEmpleados_RowDeleting&amp;quot;&lt;/p&gt;

&lt;p&gt;y luego en el código:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:8951bd3c-b034-461b-a068-a892f1ad228d" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void gvEmpleados_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    int idempleado = Convert.ToInt32(gvEmpleados.DataKeys[e.RowIndex].Value);

    EmpleadosDAL.Eliminar(idempleado);

    CargarGrid(); //luego de eliminar se recarga el grid
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Aquí también se hace uso del DataKeys para recuperar el id de la entidad que lanzo la acción.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3- Visualizar imagen&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Cuando la imagen se encuentra dentro de la base de datos es necesario usar un intermediario para poder asignarla al control Image, y se haga visible al usuario.&lt;/p&gt;

&lt;p&gt;Además todo esto sin generar archivos temporales que permitan el acceso a la imagen.&lt;/p&gt;

&lt;p&gt;En este caso es el handler quien nos brinda esta ayuda, si se observa el código del mismo se ve muy simple, se recupera la entidad del empleado, y a continuación si contiene una imagen asociada se poniéndola en el Response, sino hay imagen se envía una por defecto, la cual indicara que no esta disponible (líneas: 21-23).&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:8ff60217-24c6-4be5-a96f-6f7de4451851" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class HttpImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        //
        // Se recupera la entidad empleado
        //
        int id = Convert.ToInt32(context.Request.Params[&amp;quot;id&amp;quot;]);

        EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(id);

        //
        // Arma el contexto que enviara la imagen en el response
        // se usa el nombre del empleado para el nombre del archivo que se envia
        //
        context.Response.Clear();
        context.Response.AddHeader(&amp;quot;content-disposition&amp;quot;, string.Format(&amp;quot;attachment;filename={0}&amp;quot;, empleado.Nombre)); 
        context.Response.ContentType = &amp;quot;image/jpg&amp;quot;;

        byte[] imagenEmpleado = empleado.Imagen;
        if (empleado.Imagen == null)
            imagenEmpleado = File.ReadAllBytes(context.Server.MapPath(&amp;quot;Imagenes/NoDisponible.jpg&amp;quot;));

        //
        // Se escribe en el response la imagen asociada al empleado
        //
        context.Response.BinaryWrite(imagenEmpleado);
        context.Response.End();
    }

    public bool IsReusable
    {
        get { return false; }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Esto es tanto usado por el grid para recuperar las imágenes que lista, como así también por cualquier otro control individual que necesites mostrar la imagen del empleado.&lt;/p&gt;

&lt;p&gt;Es por eso que en el webform “EditarEmpleado.aspx”, cuando se carga el empleado todos sus datos se asignan directo a los controles, menos el control Image que recibe una url al handler para que tome de allí la imagen que debe mostrar (línea: 18)&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:5fd32d6c-57fc-4398-845a-872d80d265a0" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void CargarEmpleado(int id)
{
    EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(id);

    if (empleado == null)
    {
        imgEmpleado.ImageUrl = &amp;quot;Imagenes/NoDisponible.jpg&amp;quot;;
        return;
    }

    lblIdEmpleado.Text = Convert.ToString(empleado.IdEmpleado);
    txtNombre.Text = empleado.Nombre;
    txtApellido.Text = empleado.Apellido;
    txtFechaNacimiento.Text = empleado.FechaNacimiento.ToShortDateString();
    
    AsignarEstudios(empleado);

    imgEmpleado.ImageUrl = string.Format(&amp;quot;imagen.ashx?id={0}&amp;quot;, id);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código del artículo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El ejemplo fue desarrollado con Visual Studio 2008, y base de datos Sql Server 2008 R2 Express.&lt;/p&gt;

&lt;p&gt;Dentro de la carpeta “script” del proyecto “DataAccess” encontrara un archivo .sql que define la estructura de datos.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/[csharp]ListaEmpleados.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GridViewEdicionEmpleado/[vb.net]ListaEmpleados.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-5280826850284329407?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/5280826850284329407/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=5280826850284329407' title='11 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5280826850284329407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5280826850284329407'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/05/aspnet-gridview-edicion-empleados.html' title='[ASP.NET] GridView – Edición Empleados'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-563142794423451779</id><published>2011-05-08T16:58:00.001-07:00</published><updated>2011-05-08T16:58:32.433-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>[jQuery] RadioButton y CheckBox</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Después del artículo donde se analizo la forma de trabajar con controles de lista como ser los combos y listbox&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/04/jquery-trabajo-con-listbox-y-combos.html" target="_blank"&gt;[jQuery] Trabajo con ListBox y Combos&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;seguiremos con otro grupo de controles igual de importantes: Radio Buttons y Checkbox.&lt;/p&gt;  &lt;p&gt;Listado de temas&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;RadioButton&lt;/li&gt;    &lt;ol&gt;     &lt;li&gt;Recuperar ítem Seleccionado&lt;/li&gt;      &lt;li&gt;Asignar Selección&lt;/li&gt;      &lt;li&gt;RadioButtonList&lt;/li&gt;   &lt;/ol&gt;    &lt;li&gt;CheckBox&lt;/li&gt;    &lt;ol&gt;     &lt;li&gt;Recuperar ítem Seleccionado&lt;/li&gt;      &lt;li&gt;Asignar Selección&lt;/li&gt;      &lt;li&gt;CheckBoxList&lt;/li&gt;   &lt;/ol&gt; &lt;/ol&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1- RadioButton&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;1.1 – Recuperar ítem Seleccionado&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;En las siguientes líneas se observara algunas de las formas posibles en que se puede recuperar el ítem seleccionado en un radio button.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:31d25ef7-3772-458f-b618-eee801e6cb14" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj1SeleccionUsandoClass_OnClick() {

    var valor = $('.Ej1radio:checked').val()

    $('#Ej1Resultado').html(valor);
}

function btnEj1SeleccionUsandoName_OnClick() {

    var valor = $(&amp;quot;input[name='Ej1radio']:checked&amp;quot;).val()

    $('#Ej1Resultado').html(valor);
}

function btnEj1SeleccionUsandoTabla_OnClick() {

    var valor = $(&amp;quot;#Ej1Table :radio:checked&amp;quot;).val()

    $('#Ej1Resultado').html(valor);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Básicamente en todas se hace uso del selector :checked para tomar los radio marcados por el usuario, al saber que solo uno puede seleccionarse, se recupera el valor mediante el método val()&lt;/p&gt;

&lt;p&gt;1- se usa una clase asignada a los distintos radio button, por el uso del punto, este es el selector de clase de jquery&lt;/p&gt;

&lt;p&gt;2- se selecciona por medio de atributo name, todos los radio deben tener el mismo&lt;/p&gt;

&lt;p&gt;3- se selecciona la tabla que contiene los controles , luego los radio (mediante :radio), hay que mencionar que si en la misma tabla existe otro grupo de radio button este tipo de selección no podría ser aplicado, porque tomaría todos los radio sin discriminar cada agrupación&lt;/p&gt;

&lt;p&gt;&lt;u&gt;1.2 – Asignar selección&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;La asignación de un valor especifico a los radio se reduce a cambiar el atributo checked.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b45ec51a-a611-4d8b-8f26-970184541b76" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarConTabla_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(&amp;quot;#Ej2Table :radio[value='&amp;quot; + valor + &amp;quot;']&amp;quot;).attr('checked', true);

}


function btnEj2SeleccionarPorName_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;
        
    $(&amp;quot;:radio[name='Ej2radio'][value='&amp;quot; + valor + &amp;quot;']&amp;quot;).attr('checked', true);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;En ambos caso se asigna el atributo “checked” a true para que este se desmarque, como los radio trabajaban en grupo no hace falta desmarcar al resto, ya que esto se hace automáticamente.&lt;/p&gt;

&lt;p&gt;1- se selecciona la tabla que contiene el radio y se usa como filtro el valor ingresado en el textbox&lt;/p&gt;

&lt;p&gt;2- se usa el nombre que define al grupo de radio buttons, luego sobre estos filtrar por el valor&lt;/p&gt;

&lt;p&gt;También existen otras formas de seleccionar un ítem:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:dd9d74f3-5f70-47db-9f7a-26b51d0496a8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarAtributoJavascript_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    var option = $(&amp;quot;:radio[name='Ej2radio'][value='&amp;quot; + valor + &amp;quot;']&amp;quot;);

    if (option.length &amp;gt; 0)
        option[0].checked = true;

}

function btnEj2SeleccionarConForEach_OnClick() {
    
    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $.each($(&amp;quot;#Ej2Table :radio&amp;quot;), function() {

        if (this.value == valor) {
            this.checked = true;
        }

    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;1- se busca el option que coincide, pero la acción de checkear se logra por medio de la propiedad provista por javascript. El usar el ítem cero [0], no es porque la selección devuelve varios resultados en la búsqueda en un array, sino que se esta tomando el control javascript de la selección de jquery.&lt;/p&gt;

&lt;p&gt;2- aquí la búsqueda se realiza mediante un loop por todos los radio, comparando su valor con el buscado, el que coincida será marcado&lt;/p&gt;

&lt;p&gt;&lt;u&gt;1.3 – RadioButtonList&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;En esta sección se hará uso de control propuesto por asp.net para representar un conjunto de radios.&lt;/p&gt;

&lt;p&gt;Si se analiza con el “IE Developer Tools”, al cual se accede presionado F12, se podrá inspeccionar el html generado por el control de lista de radio buttons&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte3_RadioButtonsCheckBox/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte3_RadioButtonsCheckBox/imagen1.jpg" width="544" height="116" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Como se observa la opción esta compuesta por el input común de html, pero la descripción esta dentro de un label&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e7797a7e-c09c-4b59-a3e4-849c309dc298" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj3SeleccionUsandoName_OnClick() {

    var option = $(&amp;quot;:radio[name='rdlEj3']:checked&amp;quot;);
    var texto = $('label', option.parent());

    var msg = 'texto: {0}, valor: {1}'.format(texto.html(), option.val());

    $('#Ej3Resultado').html(msg);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es por eso que luego de tomar la option seleccionado se usa el parent() para subir un nivel, ahora estaremos posicionado en el tag &amp;lt;td&amp;gt;, y dentro de este se recupera el label, de esta forma se obtendrá la descripción del option marcado.&lt;/p&gt;

&lt;p&gt;Algo interesante aquí es que en la línea&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$('label', option.parent())&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;se esta usando un selector de un selector, o sea se buscara solo el label que este dentro de ese tag &amp;lt;td&amp;gt; y no en todo el sitio, es por eso que se separa por coma, a la izquierda iría la selección del ámbito donde aplica el selector de la derecha.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2- CheckBox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2.1 - Recuperar ítem Seleccionado&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;En la selección del checkbox se necesita algo mas de código, ya que pueden ser varios los ítems marcados.&lt;/p&gt;

&lt;p&gt;En todos los ejemplos se recorren los checks elegidos y se vuelca a una array para mostrar el resultado en pantalla, aquí se aplica la misma técnica que en el artículo previo, cuando se analizo el ListBox.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:220a8d3a-36bf-4a34-9539-cfe55a82e51a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj1SeleccionUsandoClass_OnClick() {

    var list = new Array();

    $.each($('.Ej1check:checked'), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
}

function btnEj1SeleccionUsandoName_OnClick() {
    
    var list = new Array();

    $.each($(&amp;quot;input[name='Ej1check']:checked&amp;quot;), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
    
}

function btnEj1SeleccionUsandoTabla_OnClick() {

    var list = new Array();

    $.each($(&amp;quot;#Ej1Table :checkbox:checked&amp;quot;), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
    
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;1- se hace uso de la clase asignada a los ckeck para determinar cuales fueron marcados, al igual que los radio se usa el punto de jquery como selector&lt;/p&gt;

&lt;p&gt;2- el mismo nombre es usado en cada check que forma parte del grupo, este es usando este como selector, el name es un atributo por eso es que se define entre []&lt;/p&gt;

&lt;p&gt;3- se selecciona la tabla que contiene los checks, recorriendo los seleccionados&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2.2 - Asignar Selección&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Aquí también hay varias formas de lograrlo al igual que los radio, pero a diferencia del control anterior si es necesario un paso previo que limpie la selección previa, es por eso que se hace uso del metodo attr() que asigna el atributo checked en false&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$(&amp;quot;#Ej2Table :checkbox&amp;quot;).attr('checked', false);&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;puede variar la forma en que se aplica la selección de jquery, pero la idea es obtener todos los checks y de un golpe desmarcarlos.&lt;/p&gt;

&lt;p&gt;En estos ejemplo se puede ingresar en el TextBox varios valores separados por coma, es por eso que se recorre cada numero ingresado para tomar el check que mapea con este valor y marcarlo. Se logra este objetivo por medio del $.each y el split() de la cadena de valores ingresados.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:38b4cf08-9eb8-4ae8-b846-51bafe717a90" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarConTabla_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(&amp;quot;#Ej2Table :checkbox&amp;quot;).attr('checked', false);
    
    $.each(valor.split(','), function() {
    
        $(&amp;quot;#Ej2Table :checkbox[value='&amp;quot; + this + &amp;quot;']&amp;quot;).attr('checked', true);

    });

}


function btnEj2SeleccionarPorName_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(&amp;quot;:checkbox[name='Ej2check']&amp;quot;).attr('checked', false);

    $.each(valor.split(','), function() {

        $(&amp;quot;:checkbox[name='Ej2check'][value='&amp;quot; + this + &amp;quot;']&amp;quot;).attr('checked', true);

    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;1- se hace uso de la tabla para tomar los check que están contenidos&lt;/p&gt;

&lt;p&gt;2- se recuperan los check por el atributo name asignado al grupo&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4afca5fc-092c-4a8f-a4dd-3fe8287ba0e3" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarAtributoJavascript_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(&amp;quot;:checkbox[name='Ej2check']&amp;quot;).attr('checked', false);

    $.each(valor.split(','), function() {

        var option = $(&amp;quot;:checkbox[name='Ej2check'][value='&amp;quot; + this + &amp;quot;']&amp;quot;);
        
        if (option.length &amp;gt; 0)
            option[0].checked = true;
    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;3- se hace uso de la propiedad checked de javascript para marcar el control, aquí la selección recupera el check concreto, pero luego con el uso de [0] se toma el control puro en javasscript que contiene esa propiedad checked&lt;/p&gt;

&lt;p&gt;Remarco que la propiedad checked no es parte de jquery, este usa el attr() para cambiar el valor, solo javascript posee esta propiedad, por eso el uso del [0] en el resultado del selector de jquery&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:3c299032-c83f-40cd-a6f7-70c9af5a6f32" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarConForEach_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;
    
    $(&amp;quot;:checkbox[name='Ej2check']&amp;quot;).attr('checked', false);
    
    
    $.each($(&amp;quot;#Ej2Table :checkbox&amp;quot;), function() {

        var check = this;
        
        $.each(valor.split(','), function() {

            if (check.value == this)
                check.checked = true;

        });

    });

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;4- se aplica la misma técnica del punto 3, pero se realiza un doblo loop, primero por cada check en la tabla y luego por los valores ingresados. No digo que esta sea la forma mas optima de implementarlo, pero me pareció interesante mostrarlo para conocer como unir dos ciclos.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2b5a6e7c-5400-411d-9d82-18a869d17ca9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj2SeleccionarUniendoValores_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(&amp;quot;:checkbox[name='Ej2check']&amp;quot;).attr('checked', false);

    var valuesList = Array();

    $.each(valor.split(','), function() {

        var value = &amp;quot;[value='{0}']&amp;quot;.format(this);
        valuesList.push(value);

    });

    $(&amp;quot;:checkbox[name='Ej2check']&amp;quot;).filter(valuesList.join(',')).attr('checked', true);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;5- esta ultima técnica de selección es bastante particular ya que arma un array de filtros que es ejecutado mediante el método &lt;a href="http://api.jquery.com/filter/" target="_blank"&gt;filter&lt;/a&gt;() de jquery sobre la selección de todos los checks. Es necesario marcar que cada ítem del filtro deberá ser separado por comas.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2.3- CheckBoxList&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Al igual que con el RadioButtonList, es una buena idea inspeccionar el html resultante del render&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte3_RadioButtonsCheckBox/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte3_RadioButtonsCheckBox/imagen2.jpg" width="542" height="125" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Algo interesante que se puede observar es que el control rendiza una tabla de nombre “rdlEj3” que podría ser usada para seleccionar los check contenidos en esta.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:89118729-fa55-462c-ad86-23ed300a82b9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj3SeleccionUsandoName_OnClick() {
   
    var options = $(&amp;quot;#rdlEj3 :checkbox:checked&amp;quot;);

    var list = new Array();

    $.each(options, function() {

        var option = $(this);
        var valor = $(this).parent().attr('hiddenValue');
        var texto = $('label', option.parent());

        var msg = 'texto: {0}, valor: {1}'.format(texto.html(), valor);
        list.push(msg);

    });

    $('#Ej3Resultado').html(list.join('
'));
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;En este caso se toma los check dentro de la tabla que asp.net crea para contener estos controles, tomando de esta solo los tildados.&lt;/p&gt;

&lt;p&gt;Algo que debe marcarse es que el CheckBoxList no ingresa el valor de cada ítem, es por eso que en el código .net en el evento Page_Load cuando se asignan los ítems, se pusieron unas líneas que agregan el valor del ítem&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4df32b98-c8a8-4ae5-b126-e99abf0358fe" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;foreach (ListItem item in rdlEj3.Items)
{
    item.Attributes[&amp;quot;hiddenValue&amp;quot;] = item.Value; 
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Esto se rendizan en el html dentro de un tag &amp;lt;span&amp;gt;, es por eso que en las líneas 9 al 11 se hace uso del parent() para subir un elemento al seleccionado y recuperar esta información.&lt;/p&gt;

&lt;p&gt;El control base que provee asp.net solo crea el atributo value=”on” como puede verse en la imagen, el atributo “hiddenValue” es creado al en el loop de cada ítem luego de cargar la lista.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de código&lt;/strong&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte3_RadioButtonsCheckBox/jQueryEjemplos.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-563142794423451779?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/563142794423451779/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=563142794423451779' title='8 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/563142794423451779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/563142794423451779'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/05/jquery-radiobutton-y-checkbox.html' title='[jQuery] RadioButton y CheckBox'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-8343887560566097023</id><published>2011-04-29T21:18:00.000-07:00</published><updated>2011-05-01T21:55:30.796-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>[jQuery] Trabajo con ListBox y Combos</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Este artículo se pretende continuar lo empezado en:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/04/jquery-por-donde-comenzar.html"&gt;[jQuery] Por donde comenzar&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;En esta ocasión trataremos un conjunto diferente de controles, estos muy usados en un desarrollo web.&lt;/p&gt;  &lt;p&gt;En este caso particular trataremos controles de lista: ListBox y Combos. Notando cuan similares son a la hora de trabajarlos.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Selección de Ítems&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En los ejemplos indicados como 1 y 2, se recuperan los ítems marcados por parte del usuario, pudiendo manipularlos y trabajar con la información.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ba47840d-53f6-4162-9343-d9e12a9a5ba4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;        function btnEj1Seleccion_OnClick() {

            var list = new Array();

            $.each($('#Ej1Select :selected'), function() {
               
                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej1Resultado').html(list.join(''));
            
        }

        function btnEj2Seleccion_OnClick() {
           
            var optionSelected = $('#Ej2Select :selected');

            var msg = 'texto: {0}, valor: {1}'.format(optionSelected.text(), optionSelected.val());

            $('#Ej2Resultado').html(msg);

        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Los selectores usados en ambos casos son idénticos, solo lo diferencia la selección múltiple que puede brindar un ListBox, es por eso que aplica un loop por cada ítem marcado por el usuario.&lt;/p&gt;

&lt;p&gt;En este código hay que marcar algunos puntos de interés:&lt;/p&gt;

&lt;p&gt;La función $.each() recorre la selección de una lista de ítems y aplica una función por cada uno de ellos. &lt;/p&gt;

&lt;p&gt;En este caso en concreto se recorre cada ítem seleccionado de la lista, o sea se obtiene cada &amp;lt;option&amp;gt; tomando de este el valor y el texto. Es importante notar que esta función tiene acceso a cada ítem por medio del this, pero se trata de un objeto javascript y no jquery, es por eso que se aplica nuevamente un selector $(this), para tener acceso a los métodos que permiten acceder recuperar la información.&lt;/p&gt;

&lt;p&gt;Con respecto al selector, este accede al control por medio del sus id, por eso el uso del #, pero de este se filtra además los seleccionados usando el :selected.&lt;/p&gt;

&lt;p&gt;Por fuera de la función se crea un array de javascript para ubicar cada dato marcado, se inserta el texto resultante de cada item por medio de push(), y al final se lo une por medio del join(), para lograr un solo string.&lt;/p&gt;

&lt;p&gt;Seguramente se habrá notado además el uso del una función format() para reemplazar en el texto ciertas posiciones con un dato en concreto, bien esta función en si no es estándar de javascript, sino que se encuentra definida en utils.js, y básicamente aplicaría como un método de extensión para los tipos de datos string.&lt;/p&gt;

&lt;p&gt;El trabajo con combos es idéntico a la lista, pero solo se toma un ítem accediendo de forma directa, no requiere ser recorrido sus ítems, por eso es algo más simple aplicar los selectores.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agregar / Remover Ítems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La sección marcada para el Ejemplo 3, representa la operación de agregar y remover elementos en una lista.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:34986fd9-9a0f-4e26-b7cb-9c4c6f6b1997" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;        function btnEj3Agregar_OnClick() {

            var ej3Texto = $('#txtEj3Texto');
            var ej3Valor = $('#txtEj3Valor');

            var newItem = $('&amp;lt;option/&amp;gt;').text(ej3Texto.val()).val(ej3Valor.val());
            $(&amp;quot;#Ej3Select&amp;quot;).append(newItem);

        }
        
        function btnEj3AgregarDespuesSeleccion_OnClick() {

            var itemsSelected = $('#Ej3Select :selected');

            if (itemsSelected.length == 0) {
                alert('Debe seleccionar un item');
                return;
            }
                
            var ej3Texto = $('#txtEj3Texto');
            var ej3Valor = $('#txtEj3Valor');

            var newItem = $('&amp;lt;option/&amp;gt;').text(ej3Texto.val()).val(ej3Valor.val());
            newItem.insertAfter(itemsSelected[0]);

        }
        

        function btnEj3Seleccion_OnClick() {
        
            var list = new Array();

            $.each($('#Ej3Select :selected'), function() {

                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej3Resultado').html(list.join(''));
            
        }

        function btnEj3RemoverSeleccion_OnClick() {

            $.each($('#Ej3Select :selected'), function() {

                $(this).remove();

            });
        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El agregar una nueva opción requiere que el html de &amp;lt;opción&amp;gt; sea creado, es por eso que se define este en el selector, para luego disponer de método de text() y val() para asignar la información proveniente del input del usuario.&lt;/p&gt;

&lt;p&gt;Una vez creado el nuevo ítem solo se hace un append() a la lista existente, también se podrían usar métodos como ser &lt;a href="http://api.jquery.com/insertBefore/" target="_blank"&gt;insertBefore&lt;/a&gt;() o &lt;a href="http://api.jquery.com/insertAfter/" target="_blank"&gt;insertAfter&lt;/a&gt;() para controlar la ubicación del nuevo elemento, pero esto requiere que se indique que elemento será tomado como objetivo. En el ejemplo al hacer uso de estos métodos se debe asegurar que al menos un ítem esta marcado para que funcione correctamente, si mas de uno se ha seleccionado solo se toma el primero.&lt;/p&gt;

&lt;p&gt;El remover un ítem es muy simple, solo se recorre la selección y se aplica el remove(), al igual que en la selección, el this representa cada ítem en el loop, aquí también es necesario aplicar nuevamente el selector para tener acceso a los método de jquery implementa.&lt;/p&gt;

&lt;p&gt;Trabajar con combos es idéntico a las listas, solo que se opera con un solo ítem a la vez, el ejemplo 4 implementa justamente estas acciones en un combo:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:0949a34e-ee15-4701-9b83-c9debf1a207a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj4Agregar_OnClick() {

    var ej4Texto = $('#txtEj4Texto');
    var ej4Valor = $('#txtEj4Valor');

    var newItem = $('&amp;lt;option/&amp;gt;').text(ej4Texto.val()).val(ej4Valor.val());
    $(&amp;quot;#Ej4Select&amp;quot;).append(newItem);

}

function btnEj4Seleccion_OnClick() {

    var optionSelected = $('#Ej4Select :selected');

    var msg = 'texto: {0}, valor: {1}'.format(optionSelected.text(), optionSelected.val());

    $('#Ej4Resultado').html(msg);

}

function btnEj4RemoverSeleccion_OnClick() {

    $('#Ej4Select :selected').remove()

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Algo que quizás no he marcado en los otros ejemplos es que al utilizar algo como esto&lt;/p&gt;

&lt;p&gt;&lt;em&gt;var optionSelected = $('#Ej4Select :selected');&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;se debe comprender que se esta asignado a la variable la selección del objeto completo, es por eso que luego se puede acceder a la funcionalidad provista por jquery.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asignar Selección&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En algún momento seguramente se requiera marcar un valor de la lista o combo sin que el usuario realice la acción, sino que será por medio de código, por suerte jquery hace esto muy simple, implementándose de igual forma no importa de que lista se trate.&lt;/p&gt;

&lt;p&gt;En los ejemplos 5 y 6, se puede apreciar la implementación:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:86cbe59d-4710-4528-b626-e09c9799135f" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function btnEj5SeleccionarPorValor_OnClick() {

    var valor = $('#txtEj5').val();
    
    $('#Ej5Select').val(valor);

}

function btnEj6SeleccionarPorValor_OnClick() {

    var valor = $('#txtEj6').val();

    $('#Ej6Select').val(valor);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Si se escribe un valor correcto en el TextBox, solo hará falta asignar el val() al selector del control y eso es todo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listas ASP.NET&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hasta ahora hemos estado trabajando en los ejemplos con controles html, pero se debió a que operar con estos o con el control de lista o combo del asp.net es idéntico.&lt;/p&gt;

&lt;p&gt;En el ejemplo 7 se podrá apreciar justamente este punto:&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:122f1e6c-c570-4bd4-a576-3ac18a8af112" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;        function btnEj7Agregar_OnClick() {

            var ej7Texto = $('#txtEj7Texto');
            var ej7Valor = $('#txtEj7Valor');

            var newItem = $('&amp;lt;option/&amp;gt;').text(ej7Texto.val()).val(ej7Valor.val());
            $('#&amp;lt;%=Ej7Select.ClientID%&amp;gt;').append(newItem);

        }

        function btnEj7Seleccion_OnClick() {

            var list = new Array();

            $.each($('#&amp;lt;%=Ej7Select.ClientID%&amp;gt; :selected'), function() {

                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej7Resultado').html(list.join(''));

        }
        
        function btnEj7RemoverSeleccion_OnClick() {

            $.each($('#&amp;lt;%=Ej7Select.ClientID%&amp;gt; :selected'), function() {

                $(this).remove();

            });
        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Aplicar selectores a un control de asp.net solo requiere el uso de la propiedad ClientID, si es que se usa el id del control para acceder al mismo, esto se aplica por si asp.net renombra esta propiedad al realizar el render del html que envía al cliente.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código de Ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte2_TrabajandoListasyCombos/jQueryEjemplos.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-8343887560566097023?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/8343887560566097023/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=8343887560566097023' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8343887560566097023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/8343887560566097023'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/04/jquery-trabajo-con-listbox-y-combos.html' title='[jQuery] Trabajo con ListBox y Combos'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-803154645698410352</id><published>2011-04-17T20:27:00.001-07:00</published><updated>2011-04-17T20:30:30.865-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>[jQuery] Por donde comenzar</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;jQuery es una de las librerías muy mencionada en los ultimo tiempo para el desarrollo de aplicaciones web.&lt;/p&gt;  &lt;p&gt;Usar javascript de forma estándar puede ser algo bastante tedioso ya que no es una sintaxis del todo amigable para el trabajo diario, o al menos para quien esta acostumbrado a programa en un lenguaje amenos como ser C# o VB.NET.&lt;/p&gt;  &lt;p&gt;Es por eso que surge jQuery, para solventar este problema, siendo el mas conocido entre los frameworks de javascript, (esta se gano su lugar en los témplate para la creación de proyectos como son los utilizados en aplicaciones con ASP.NET MVC), y el que dio impulso a la utilización de este tipo de frameworks,&amp;#160; vale aclarar además que también existen otro como ser: &lt;a href="http://www.prototypejs.org/" target="_blank"&gt;Prototype&lt;/a&gt;, &lt;a href="http://mootools.net/" target="_blank"&gt;MooTools&lt;/a&gt;, etc &lt;/p&gt;  &lt;p&gt;Este articulo en particular se sumara a los miles que existen publicados para aportar un punto de inicio para quienes aun no conocen la magia de esta librería.&lt;/p&gt;  &lt;p&gt;Aquí no se pretende explicar cada unos de los aspectos de jQuery, porque solo el tema se selectores es enorme y muy variado, sino que iremos a casos concretos, y analizaremos particularidades en el uso de la librería.&lt;/p&gt;  &lt;p&gt;jQuery es muy amplio, con mucha flexibilidad, cuando la usen verán que no hay una sola forma de hacer las cosas, a veces se pueden aplicar un selector con distintas combinaciones y todas estarán correctas.&lt;/p&gt;  &lt;p&gt;Es por eso que es necesario recurrir a la fuente y consultar la documentación provista por la propia librería en su sitio web: &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Para empezar veremos dos puntos básicos que afectan a la interacciones de asp.net y javascript, luego continuaremos con el análisis de dos controles simples para entrar en tema.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1 – Acceder a los controles de asp.net&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Algo que debe conocerse cuando se trabaja con controles desde javascript es como hacer referencia a estos.&lt;/p&gt;  &lt;p&gt;Por ejemplo, si en el html de una pagina se define:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;lt;input id=&amp;quot;Text1&amp;quot; type=&amp;quot;text&amp;quot; /&amp;gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;desde javascript podría seleccionar el control usando:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;var text1 = document.getElementById('Text1');&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;de esta forma se seleccionado el control cuando se trata de uno del tipo html, pero que sucede si ahora se tiene un control de asp.net, aquí ya no somos quienes controlamos el render del control, por lo tanto el ID asignado al mismo puede variar.&lt;/p&gt;  &lt;p&gt;Muy bien, ahora si realizamos unas pruebas y ponemos un asp:TextBox en una página&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;lt;asp:TextBox ID=&amp;quot;TextBox1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;&amp;lt;/asp:TextBox&amp;gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;al acceder desde el browser se vera como&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;lt;input name=&amp;quot;TextBox1&amp;quot; type=&amp;quot;text&amp;quot; id=&amp;quot;TextBox1&amp;quot; /&amp;gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;ups, pero que sucedió aquí el id no se modifico, seguro pensaran que estoy mintiendo, pues bien, lo que sucede es que en algunas situaciones asp.net no cambie el id del control, porque en este caso no lo requiere, el verdadero problema se presente cuando este control esta contenido dentro de ciertos contenedores, como ser: un User Control, la Master Page, o controles que repiten un témplate, como ser el ListView, GridView, Repeater, etc&lt;/p&gt;  &lt;p&gt;Entonces que sucede ahora si se crear un User Control (WebUserControl1.ascx) con el textbox dentro del mismo y se lo ubica en una pagina, al acceder al código del browser se obtendrá&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;lt;input name=&amp;quot;WebUserControl11$TextBox1&amp;quot; type=&amp;quot;text&amp;quot; id=&amp;quot;WebUserControl11_TextBox1&amp;quot; /&amp;gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Ahora si se comprueba como asp.net modifica el id asignado al control en tiempo de diseño, asp.net necesito ponerle un prefijo con el nombre del User Control para que este sea único, ya que en la pagina puede usarse mas de un mismo user control, sino redefine el id puede haber colisiones en los nombres.&lt;/p&gt;  &lt;p&gt;Como se haría para seleccionar este control sino se conoce previamente el id que será generado, pues bien, se dispone en asp.net de la propiedad ClientID, siendo usada de la siguiente forma:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;var userControlText1 = document.getElementById('&amp;lt;%=TextBox1.ClientID%&amp;gt;');&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;al inspeccionar el código resultante en el browser se obtendrá&lt;/p&gt;  &lt;p&gt;&lt;em&gt;var userControlText1 = document.getElementById('WebUserControl11_TextBox1');&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;el tag &lt;em&gt;&amp;lt;%=&amp;#160; %&amp;gt;&lt;/em&gt; permite ubicar allí el valor de la propiedad ClientID.&lt;/p&gt;  &lt;p&gt;Si bien esto que comento parece un dato menos, es bastante importante cuando se comienzan a dar los primeros paso en javascript mezclado con controles del asp.net&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2 – Acceder a controles asp.net desde un archivo .js&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Continuando con el tip del punto anterior, avanzaremos en un tema que por ahí pueda generar dificultades a la hora de ordenar el código.&lt;/p&gt;  &lt;p&gt;En algunas oportunidades uno requiera hacer uso de archivos .js para llevar allí el código javascript y trabajarlo de forma separada al html que esta en el aspx, pero es aquí donde empiezan los problemas.&lt;/p&gt;  &lt;p&gt;Resulta que si se hace uso del tag &amp;lt;% %&amp;gt;, este solo puede ser aplicado dentro de la página donde se hace el render por parte de asp.net, o sea solo funciona dentro del aspx&lt;/p&gt;  &lt;p&gt;El archivo .js es referenciado por el aspx, pero este no se incluye en el proceso de render, por lo tanto el ClientID nunca será reemplazado.&lt;/p&gt;  &lt;p&gt;Pero hay una solución a este punto, la cual consiste en dejar la definición del tag &amp;lt;% %&amp;gt; dentro del aspx, pero que la misma solo asigne a una variable javascript el nombre del control, luego sera usada en el .js para así poder tomar el control y trabajarlo.&lt;/p&gt;  &lt;p&gt;En la carpeta “Ejemplo2” esta la implementación del código de ejemplo, se observara la declaración de las líneas&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:71b691a3-d96b-46ea-a49a-aaf8307194a8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;

    var TextBox1 = '&amp;lt;%=TextBox1.ClientID%&amp;gt;';

&amp;lt;/script&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;en donde se asigna en la pagina aspx el nombre como string, para luego dentro del .js usar&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:8341b7e5-bfe8-4d65-b133-cb435779f994" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function ObtenerValor() {

    var text1 = document.getElementById(TextBox1);
    alert(text1.value);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es necesario apreciar como cambio la selección al usar el getElementById(), usando ahora el nombre de la variable.&lt;/p&gt;

&lt;p&gt;El único problema que se presenta es que por cada control que se quiera usar dentro de .js será necesario crear una variable en la pagina que incluya el ID que asp.net asigna.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 – Obtener y asignar el valor de un Texbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seguramente se preguntaran “cuando comenzamos”, es cierto hasta aquí se presentaron temas mas relacionados con javascript, que al propio jquery, pero eran necesario tenerlos presente ya que estos también afectan al desarrollo.&lt;/p&gt;

&lt;p&gt;Bien, comencemos con algo simple.&lt;/p&gt;

&lt;p&gt;Seleccionemos el valor de un TextBox, copiando el contenido a otro:&lt;/p&gt;

&lt;p&gt;[HTML]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:5fd8081c-9f5f-400b-8271-96983b7f3fc1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;form id=&amp;quot;form1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input id=&amp;quot;txtInfo&amp;quot; type=&amp;quot;text&amp;quot; /&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt; 
                &amp;lt;input id=&amp;quot;btnMostrar&amp;quot; type=&amp;quot;button&amp;quot; value=&amp;quot;button&amp;quot; onclick=&amp;quot;Mostrar_OnClick();&amp;quot;/&amp;gt;
            &amp;lt;/td&amp;gt;
            
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td colspan=&amp;quot;2&amp;quot;&amp;gt; 
                &amp;lt;input id=&amp;quot;txtInfoCopia&amp;quot; type=&amp;quot;text&amp;quot; /&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[JavaScript]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:84028f8d-7d78-431b-a25f-644a27803f66" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function Mostrar_OnClick() {

    var info = $('#txtInfo').val();

    $('#txtInfoCopia').val(info);
    
}
 
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;como se observa jquery lo hizo bien simple.&lt;/p&gt;

&lt;p&gt;Es preciso conocer que cuando uno selecciona un control y lo hace por el atributo “id” debe anteponer el “#”, este es uno de los tanto selectores tiene jquery que iremos viendo.&lt;/p&gt;

&lt;p&gt;Para que esto funcione es necesario contar con la librería, en este caso se encuentra en la línea&lt;/p&gt;

&lt;p&gt;&amp;lt;script src=&amp;quot;../script/jquery-1.5.2.min.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;por lo general se sitúa en la propia pagina, pero también es común verlo en la Master Page para que todas incluyan la librería, y no se tenga que declarar en cada una particularmente.&lt;/p&gt;

&lt;p&gt;Y que sucede si se tratan de controles de asp.net, bien es igual de simple, pero conociendo lo que vimos en el punto: “1 – Acceder a los controles de asp.net”, se obtendría la misma funcionalidad usando:&lt;/p&gt;

&lt;p&gt;[JavaScript]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:8e48d52e-2478-410e-af53-7c7c0a5e9f67" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function MostrarASPNET_OnClick() {

    var info = $('#&amp;lt;%=txtInfoASPNET.ClientID%&amp;gt;').val();

    $('#&amp;lt;%=txtInfoCopiaASPNET.ClientID%&amp;gt;').val(info);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Avancemos un poco inicializando los textbox, para hacerlo individualmente se podría aplicar algo como esto:&lt;/p&gt;

&lt;p&gt;[JavaScript]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4b649010-8e98-4e14-9869-5e950a8ed716" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function LimpiarASPNET_OnClick() {

    $('#&amp;lt;%=txtInfoASPNET.ClientID%&amp;gt;').val('');

    $('#&amp;lt;%=txtInfoCopiaASPNET.ClientID%&amp;gt;').val('');

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;es idéntico a lo que ya se viene viendo solo que no se le pasa valor&lt;/p&gt;

&lt;p&gt;Y si se quiere limpiar el contenido de todos los textbox, se deberá asignar uno a uno?, no es necesario, hay técnicas mucho mas simples:&lt;/p&gt;

&lt;p&gt;[JavaScript]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:68495854-12fe-43f9-9c32-ba420f079f75" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function LimpiarTodos_OnClick() {

    $(':text').val('');

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;aquí se puede ver otro selector distinto proporcionado por jQuery, en este caso se trata de un selector por tipo text el cual aplicara el valor a todos los controles que encuentre del tipo textbox.&lt;/p&gt;

&lt;p&gt;Ahora bien, lo lindo que tiene esta librería es que no hay una sola forma de hacer esto, si se revisa la documentación: &lt;a href="http://api.jquery.com/text-selector/" target="_blank"&gt;:text Selector&lt;/a&gt;, menciona que usar $('[type=text]') o $('*:text') y también $('input:text') son equivalentes, pero por simplicidad en la escritura se aplica :text&lt;/p&gt;

&lt;p&gt;Nota: los ejemplos de esta sección se encuentra en el “Ejemplo 3” del código publicado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 – Trabajar con controles Label&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Asignar un valor a un label difiere un poco de como se haría a un TextBox.&lt;/p&gt;

&lt;p&gt;En el ejemplo de la carpeta “Ejemplo 4” se encontrara una muestra de como trabajar con este otro control.&lt;/p&gt;

&lt;p&gt;en la pagina se agrego un label html&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&amp;lt;label id=&amp;quot;lblInfoCopia&amp;quot; /&amp;gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;y uno de asp.net&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&amp;lt;asp:Label ID=&amp;quot;lblInfoCopiaASPNET&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;&amp;lt;/asp:Label&amp;gt; &lt;/em&gt;&lt;/p&gt;

&lt;p&gt;accederlo de forma individual no representa mayor problema, salvo por el hecho de cambiar el val() por el html() para tomar o asignar un valor.&lt;/p&gt;

&lt;p&gt;Pero el seleccionar todos los labels de la pagina no es tan directo, ya que se renderiza diferentes en html estos controles, si se analiza el código generado en el browser se notara que el label de html sigue siendo un label, pero el de asp.net no lo es, ya que es transformado a un tag &amp;lt;span&amp;gt; cuando se convierte&lt;/p&gt;

&lt;p&gt;Es por esto que debe seleccionarse distinto cada control.&lt;/p&gt;

&lt;p&gt;[JavaScript]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b9e4e6af-d686-43c2-b13d-8056c5e4effa" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;function Mostrar_OnClick() {

    var info = $('#txtInfo').val();

    $('#lblInfoCopia').html(info);

}

function MostrarASPNET_OnClick() {

    var info = $('#&amp;lt;%=txtInfoASPNET.ClientID%&amp;gt;').val();

    $('#&amp;lt;%=lblInfoCopiaASPNET.ClientID%&amp;gt;').html(info);

}

function LimpiarASPNET_OnClick() {

    $('#&amp;lt;%=txtInfoASPNET.ClientID%&amp;gt;').val('');

    $('#&amp;lt;%=lblInfoCopiaASPNET.ClientID%&amp;gt;').html('');

}

function LimpiarTodos_OnClick() {

    $(':text').val('');
    $('label').html('');
    $('span').html('');
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hasta aquí hemos tenido una introducción general a las particularidades del uso de javascript cuando se tratan controles asp.net.&lt;/p&gt;

&lt;p&gt;También hemos visto como tratar dos controles muy usados en las paginas como son los textbox y labels.&lt;/p&gt;

&lt;p&gt;En próximos artículos veremos mas controles ya sean html o asp.net, como adjuntar evento y recorrer el resultado de una selección para trabajar con cada uno de sus ítems&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código Ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/jQuery/Parte1_pordondeemepzar/jQueryEjemplos.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; 

        &lt;br /&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-803154645698410352?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/803154645698410352/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=803154645698410352' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/803154645698410352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/803154645698410352'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/04/jquery-por-donde-comenzar.html' title='[jQuery] Por donde comenzar'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-3446860502647744964</id><published>2011-03-13T22:15:00.001-07:00</published><updated>2011-06-25T22:34:48.495-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>[ASP.NET] Grabar Archivo en base de datos</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1 - Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Este articulo podría considerarse la continuación de:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2010/03/aspnet-guardar-imagen-base-de-datos.html" target="_blank"&gt;[ASP.NET] - Guardar Imagen base de datos&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;básicamente se extenderá el articulo anterior aportando los temas:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;subir un archivo a una carpeta dentro del sitio      &lt;ul&gt;       &lt;li&gt;validación de las extensiones que puede subirse &lt;/li&gt;        &lt;li&gt;listar los archivos de la carpeta &lt;/li&gt;        &lt;li&gt;distintas formas de representar el link de descarga          &lt;ul&gt;           &lt;li&gt;link directo al archivo &lt;/li&gt;            &lt;li&gt;link a una pagina que administrara la descarga &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;borrado del archivo de la carpeta del sitio &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;subir el archivos a una base de datos      &lt;ul&gt;       &lt;li&gt;listar los registro de la tabla &lt;/li&gt;        &lt;li&gt;distintas formas de implementar la descarga          &lt;ul&gt;           &lt;li&gt;usando un handler &lt;/li&gt;            &lt;li&gt;usando una página que administre la descarga &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;eliminar registro de la base de datos, por ende el archivo &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;2 - Subir un archivo a una carpeta del sitio&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Para esta primera sección se trabajara con el contenido desarrollado en la carpeta “GuardarEnCarpeta” del proyecto que se puede descargar en el articulo, la carpeta contiene la página “ListarArchivos.aspx”, cuya funcionalidad será la de subir (FileUpload) y además listas los archivos (GridView), de una carpeta determinada dentro de la estructura del sitio web.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:5d335644-3c21-4b1c-a01f-bd7938529122" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;    &amp;lt;form id=&amp;quot;form1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;asp:FileUpload ID=&amp;quot;FileUpload1&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:Button ID=&amp;quot;btnSubirArchivo&amp;quot; runat=&amp;quot;server&amp;quot; Text=&amp;quot;Subir Archivo&amp;quot; 
            onclick=&amp;quot;btnSubirArchivo_Click&amp;quot;  /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:RegularExpressionValidator 
             id=&amp;quot;RegularExpressionValidator1&amp;quot; runat=&amp;quot;server&amp;quot; 
             ErrorMessage=&amp;quot;Solo pdf, doc o xls son permitidos.&amp;quot; 
             ValidationExpression=&amp;quot;^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$&amp;quot; 
             ControlToValidate=&amp;quot;FileUpload1&amp;quot;&amp;gt;
            &amp;lt;/asp:RegularExpressionValidator&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:GridView ID=&amp;quot;GridView1&amp;quot; runat=&amp;quot;server&amp;quot; AutoGenerateColumns=&amp;quot;False&amp;quot; CellPadding=&amp;quot;4&amp;quot;
            ForeColor=&amp;quot;#333333&amp;quot; GridLines=&amp;quot;None&amp;quot; onrowdeleting=&amp;quot;GridView1_RowDeleting&amp;quot; DataKeyNames=&amp;quot;Name&amp;quot;&amp;gt;
            &amp;lt;RowStyle BackColor=&amp;quot;#EFF3FB&amp;quot; /&amp;gt;
            &amp;lt;Columns&amp;gt;
               &amp;lt;asp:TemplateField HeaderText=&amp;quot;Eliminar&amp;quot; ItemStyle-HorizontalAlign=&amp;quot;Center&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:ImageButton ID=&amp;quot;imgEliminar&amp;quot; runat=&amp;quot;server&amp;quot; CommandName=&amp;quot;Delete&amp;quot;  ImageUrl=&amp;quot;~/imagenes/delete.png&amp;quot; Width=&amp;quot;24px&amp;quot; Height=&amp;quot;24px&amp;quot;/&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
                &amp;lt;asp:TemplateField HeaderText=&amp;quot;Nombre Archivo&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:HyperLink ID=&amp;quot;nombre&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;, &amp;quot;~/files/{0}&amp;quot;) %&amp;gt;'
                            Text='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;) %&amp;gt;'&amp;gt;
                        &amp;lt;/asp:HyperLink&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
                &amp;lt;asp:BoundField DataField=&amp;quot;Length&amp;quot; HeaderText=&amp;quot;Tama&amp;#241;o&amp;quot; /&amp;gt;
                &amp;lt;asp:TemplateField HeaderText=&amp;quot;Descargar&amp;quot; ItemStyle-HorizontalAlign=&amp;quot;Center&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:HyperLink ID=&amp;quot;descarga&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;, &amp;quot;~/GuardarEnCarpeta/Download.aspx?filename={0}&amp;quot;) %&amp;gt;'&amp;gt;
                               &amp;lt;img src=&amp;quot;../imagenes/download.gif&amp;quot; alt=&amp;quot;&amp;quot; width=&amp;quot;30px&amp;quot; height=&amp;quot;30px&amp;quot; style=&amp;quot;border-width:0px;&amp;quot; /&amp;gt;
                        &amp;lt;/asp:HyperLink&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
            &amp;lt;/Columns&amp;gt;
            &amp;lt;FooterStyle BackColor=&amp;quot;#507CD1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;White&amp;quot; /&amp;gt;
            &amp;lt;PagerStyle BackColor=&amp;quot;#2461BF&amp;quot; ForeColor=&amp;quot;White&amp;quot; HorizontalAlign=&amp;quot;Center&amp;quot; /&amp;gt;
            &amp;lt;SelectedRowStyle BackColor=&amp;quot;#D1DDF1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;#333333&amp;quot; /&amp;gt;
            &amp;lt;HeaderStyle BackColor=&amp;quot;#507CD1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;White&amp;quot; /&amp;gt;
            &amp;lt;EditRowStyle BackColor=&amp;quot;#2461BF&amp;quot; /&amp;gt;
            &amp;lt;AlternatingRowStyle BackColor=&amp;quot;White&amp;quot; /&amp;gt;
        &amp;lt;/asp:GridView&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Es importante resaltar algunos puntos de este código.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2.1 - Validación en las extensión que pueden ser subidas al sitio&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Se hace uso del control RegularExpressionValidator, para proporcionar una validación del lado del cliente, que impida subir archivos en una extensión invalida, o no deseada.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:56cf10ae-321a-4773-a548-03b234f6d7b2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:RegularExpressionValidator 
             id=&amp;quot;RegularExpressionValidator1&amp;quot; runat=&amp;quot;server&amp;quot; 
             ErrorMessage=&amp;quot;Solo pdf, doc o xls son permitidos.&amp;quot; 
             ValidationExpression=&amp;quot;^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$&amp;quot; 
             ControlToValidate=&amp;quot;FileUpload1&amp;quot;&amp;gt;
&amp;lt;/asp:RegularExpressionValidator&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;u&gt;2.2 Dos formas diferentes de exponer el link de descarga&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;E&lt;/u&gt;l gridview que lista los archivos de la carpeta expone dos link de descarga del archivo listado, uno de ellos al presionar el nombre:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:6f1d2e67-2c02-4bb4-81d2-0da9e41cdd1e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Nombre Archivo&amp;quot;&amp;gt;
    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;asp:HyperLink ID=&amp;quot;nombre&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;, &amp;quot;~/files/{0}&amp;quot;) %&amp;gt;'
            Text='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;) %&amp;gt;'&amp;gt;
        &amp;lt;/asp:HyperLink&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;
&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Prestar atención a la definición del NavidateUrl, en este la url especifica concretamente al archivo, por medio del path relativo usando el ~/, esto se puede lograr porque al archivo esta físicamente alojado en una carpeta en al estructura del sitio publicado.&lt;/p&gt;

&lt;p&gt;La otra alternativa se presenta al presionar sobre la imagen de descarga:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:763ab7ee-5b61-4f90-bd25-aaa90100d3a9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Descargar&amp;quot; ItemStyle-HorizontalAlign=&amp;quot;Center&amp;quot;&amp;gt;
    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;asp:HyperLink ID=&amp;quot;descarga&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Name&amp;quot;, &amp;quot;~/GuardarEnCarpeta/Download.aspx?filename={0}&amp;quot;) %&amp;gt;'&amp;gt;
               &amp;lt;img src=&amp;quot;../imagenes/download.gif&amp;quot; alt=&amp;quot;&amp;quot; width=&amp;quot;30px&amp;quot; height=&amp;quot;30px&amp;quot; style=&amp;quot;border-width:0px;&amp;quot; /&amp;gt;
        &amp;lt;/asp:HyperLink&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;
&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
Een este otro caso se define una pagina web “Download.aspx” con un valor que pasara por querystring, esta pagina será la responsable de tomar el archivo desde su lugar físico y lo enviara en el Response. 

&lt;p&gt;Es importante en este punto analizar que acciones toma esta pagina.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2f884eea-eab2-49ad-aa94-e1e753845a5f" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class Download : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string filename = Request.QueryString[&amp;quot;filename&amp;quot;].ToString(); 

        Response.Clear();

        Response.AddHeader(&amp;quot;content-disposition&amp;quot;, string.Format(&amp;quot;attachment;filename={0}&amp;quot;, filename));
        Response.ContentType = &amp;quot;application/octet-stream&amp;quot;;

        Response.WriteFile(Server.MapPath(Path.Combine(&amp;quot;~/files&amp;quot;, filename)));

        Response.End();

    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El código que implementa es muy simple, solo toma el archivo que recibe por la url, define información para la respuesta y escribe en el Response el archivo para ser enviado.&lt;/p&gt;

&lt;p&gt;Un punto no menor es la definición del ContentType del tipo “application/octet-stream”, este forzara la descarga mostrando el cuadro de download al usuario para que seleccione donde quiere descargarlo. Si en lugar de usar este se hubiera definido el tipo concreto para carda archivo, al realizarse la descarga se mostraría embebido el documento en el browser, por supuesto esto sucederá si el usuario posee un editor para este documento.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2.3 Eliminación del archivo subido el sitio&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;La eliminación del archivo esta implementada en los botones de gridview, pero para esto funcione se definen varios puntos, el primero tiene que ver con la columna en el grid&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:722df410-28fa-4858-abd9-36ee5b5a26dd" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Eliminar&amp;quot; ItemStyle-HorizontalAlign=&amp;quot;Center&amp;quot;&amp;gt;
     &amp;lt;ItemTemplate&amp;gt;
         &amp;lt;asp:ImageButton ID=&amp;quot;imgEliminar&amp;quot; runat=&amp;quot;server&amp;quot; CommandName=&amp;quot;Delete&amp;quot;  ImageUrl=&amp;quot;~/imagenes/delete.png&amp;quot; Width=&amp;quot;24px&amp;quot; Height=&amp;quot;24px&amp;quot;/&amp;gt;
     &amp;lt;/ItemTemplate&amp;gt;
 &amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;la cual es un ImagenButton con el CommandName = “Delete”, que lleve el nombre “Delete” no es arbitrario, sino que con este nombre lanzara el evento&amp;#160; RowDeleting del gridview, es por eso que se asocia el evento &lt;em&gt;onrowdeleting=&amp;quot;GridView1_RowDeleting&amp;quot; &lt;/em&gt;además de las propiedad &lt;em&gt;DataKeyNames=&amp;quot;Name&amp;quot;&lt;/em&gt;, para poder tomar el nombre del archivo que se quiere eliminar.&lt;/p&gt;

&lt;p&gt;Si ahora se analiza el evento:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:43a60c4b-d20e-46d7-872d-a6505e71ad70" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    //se obtiene el nombre de campo definido en el DataKeyNames del gridview
    string fileName = Convert.ToString(GridView1.DataKeys[e.RowIndex].Value);
    //se define el path fisico del archivo
    string fullPath = Path.Combine(Server.MapPath(&amp;quot;~/files&amp;quot;), fileName);

    File.Delete(fullPath);

    Response.Redirect(&amp;quot;ListarArchivos.aspx&amp;quot;);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;en este se hace uso del DataKeys, para obtener el nombre del archivo, esto se logra gracias al DataKeyNames definido con el campo del nombre del archivo, ahora solo queda obtener la ruta física y proceder a eliminar.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 – Subir un archivo a la base de datos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En esta otra sección cambiaremos de carpeta del trabajo dentro del sitio, ahora sera “GuardarEnDb”, aquí también se cuenta con un GridView para listar los archivos registrados en la db, y el FileUpload para subirlos al sitio.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4e675ce0-bb69-404f-a681-ec905866d6a6" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;    &amp;lt;form id=&amp;quot;form1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;asp:FileUpload ID=&amp;quot;FileUpload1&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:Button ID=&amp;quot;btnGuardar&amp;quot; runat=&amp;quot;server&amp;quot; Text=&amp;quot;Guardar Archivo&amp;quot; OnClick=&amp;quot;btnGuardar_Click&amp;quot; /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:RegularExpressionValidator 
             id=&amp;quot;RegularExpressionValidator1&amp;quot; runat=&amp;quot;server&amp;quot; 
             ErrorMessage=&amp;quot;Solo pdf, doc o xls son permitidos.&amp;quot; 
             ValidationExpression=&amp;quot;^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$&amp;quot; 
             ControlToValidate=&amp;quot;FileUpload1&amp;quot;&amp;gt;
        &amp;lt;/asp:RegularExpressionValidator&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;asp:GridView ID=&amp;quot;GridView1&amp;quot; runat=&amp;quot;server&amp;quot; AutoGenerateColumns=&amp;quot;False&amp;quot; CellPadding=&amp;quot;4&amp;quot;
            ForeColor=&amp;quot;#333333&amp;quot; GridLines=&amp;quot;None&amp;quot; DataKeyNames=&amp;quot;Id&amp;quot; 
            onrowdeleting=&amp;quot;GridView1_RowDeleting&amp;quot;&amp;gt;
            &amp;lt;RowStyle BackColor=&amp;quot;#EFF3FB&amp;quot; /&amp;gt;
            &amp;lt;Columns&amp;gt;
                &amp;lt;asp:TemplateField HeaderText=&amp;quot;Eliminar&amp;quot; ItemStyle-HorizontalAlign=&amp;quot;Center&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:ImageButton ID=&amp;quot;imgEliminar&amp;quot; runat=&amp;quot;server&amp;quot; CommandName=&amp;quot;Delete&amp;quot;  ImageUrl=&amp;quot;~/imagenes/delete.png&amp;quot; Width=&amp;quot;24px&amp;quot; Height=&amp;quot;24px&amp;quot;/&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
                &amp;lt;asp:TemplateField HeaderText=&amp;quot;Nombre Archivo&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:HyperLink ID=&amp;quot;nombre&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Id&amp;quot;, &amp;quot;~/DescargaArchivo.ashx?id={0}&amp;quot;) %&amp;gt;'
                            Text='&amp;lt;%# Eval(&amp;quot;Nombre&amp;quot;) %&amp;gt;'&amp;gt;
                        &amp;lt;/asp:HyperLink&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
                &amp;lt;asp:BoundField DataField=&amp;quot;Length&amp;quot; HeaderText=&amp;quot;Tama&amp;#195;&amp;#177;o&amp;quot; /&amp;gt;
                &amp;lt;asp:TemplateField HeaderText=&amp;quot;Descargar&amp;quot;&amp;gt;
                    &amp;lt;ItemTemplate&amp;gt;
                        &amp;lt;asp:HyperLink ID=&amp;quot;descarga&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Id&amp;quot;, &amp;quot;~/GuardarEnDb/Download.aspx?id={0}&amp;quot;) %&amp;gt;'&amp;gt;
                               &amp;lt;img src=&amp;quot;../imagenes/download.gif&amp;quot; alt=&amp;quot;&amp;quot; width=&amp;quot;30px&amp;quot; height=&amp;quot;30px&amp;quot; style=&amp;quot;border-width:0px;&amp;quot; /&amp;gt;
                        &amp;lt;/asp:HyperLink&amp;gt;
                    &amp;lt;/ItemTemplate&amp;gt;
                &amp;lt;/asp:TemplateField&amp;gt;
            &amp;lt;/Columns&amp;gt;
            &amp;lt;FooterStyle BackColor=&amp;quot;#507CD1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;White&amp;quot; /&amp;gt;
            &amp;lt;PagerStyle BackColor=&amp;quot;#2461BF&amp;quot; ForeColor=&amp;quot;White&amp;quot; HorizontalAlign=&amp;quot;Center&amp;quot; /&amp;gt;
            &amp;lt;SelectedRowStyle BackColor=&amp;quot;#D1DDF1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;#333333&amp;quot; /&amp;gt;
            &amp;lt;HeaderStyle BackColor=&amp;quot;#507CD1&amp;quot; Font-Bold=&amp;quot;True&amp;quot; ForeColor=&amp;quot;White&amp;quot; /&amp;gt;
            &amp;lt;EditRowStyle BackColor=&amp;quot;#2461BF&amp;quot; /&amp;gt;
            &amp;lt;AlternatingRowStyle BackColor=&amp;quot;White&amp;quot; /&amp;gt;
        &amp;lt;/asp:GridView&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;A simple vista pareciera idéntico al ejemplo tratado en el punto 2 del articulo, pero hay detalles que deben analizarse, porque hay unas cuantas diferencias.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;3.1 Dos formas diferentes de exponer el link de descarga&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Aquí también nos encontramos con distintas formas de descarga, pero en este caso al tratarse de una base de datos ya no se puede acceder directo al archivo por su url, será necesario contar con intermediarios que obtengan el archivo desde la db y lo envíen como respuesta.&lt;/p&gt;

&lt;p&gt;Una de esta formas se define por medio de un handler, en donde la columna en el grid poseerá una url al mismo.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:6e65ca63-8fd4-4bd7-9b61-a2f23c16865b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Nombre Archivo&amp;quot;&amp;gt;
    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;asp:HyperLink ID=&amp;quot;nombre&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Id&amp;quot;, &amp;quot;~/DescargaArchivo.ashx?id={0}&amp;quot;) %&amp;gt;'
            Text='&amp;lt;%# Eval(&amp;quot;Nombre&amp;quot;) %&amp;gt;'&amp;gt;
        &amp;lt;/asp:HyperLink&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;
&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Nótese que lo importante aquí no es el nombre que se use para referirse al handler, ya que este puede ser cualquiera, lo importante es la extensión usada (en este caso ashx), ya que la misma será definida en el web.config, en la línea:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ea1dc9aa-0daf-476a-b8e3-a24b582ae658" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;httpHandlers&amp;gt;
	.
	.
	&amp;lt;add verb=&amp;quot;*&amp;quot; path=&amp;quot;*.ashx&amp;quot; type=&amp;quot;GuardarArchivoBaseDatos.GuardarArchivo.HttpImageHandler&amp;quot;/&amp;gt;

&amp;lt;/httpHandlers&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La implementación del handler se encuentra en la clase HttpImageHandler.cs, dentro del código del mismo se simula el comportamiento que tendría la definición del link si se lo define directo al archivo, es por eso que se define un ContentType concreto para cada extensión especifica de los archivos soportados.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:49f9d21b-d0fc-4260-813a-95e26fe367ae" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class HttpImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        int id = Convert.ToInt32(context.Request.Params[&amp;quot;id&amp;quot;]);

        Archivo archivo = ArchivosDAL.GetById(id);

        context.Response.Clear();
        context.Response.AddHeader(&amp;quot;content-disposition&amp;quot;, string.Format(&amp;quot;attachment;filename={0}&amp;quot;, archivo.Nombre));

        switch (Path.GetExtension(archivo.Nombre).ToLower())
        {
            case &amp;quot;.pdf&amp;quot;:
                context.Response.ContentType = &amp;quot;application/pdf&amp;quot;;
                break;
            case &amp;quot;.doc&amp;quot;:
                context.Response.ContentType = &amp;quot;application/msword&amp;quot;;
                break;
            case &amp;quot;.xls&amp;quot;:
                context.Response.ContentType = &amp;quot;application/vnd.ms-excel&amp;quot;;
                break;
        }

        context.Response.BinaryWrite(archivo.ContenidoArchivo);
        context.Response.End();
    }

    public bool IsReusable
    {
        get { return false; }
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Nota: podrías haberse definido directamente el “application/octet-stream” en el hadler, pero se realizo de esta forma para mostrar distintas variantes que pueden aplicarse.&lt;/p&gt;

&lt;p&gt;Es en la otra alternativa se define la página “Download.aspx”, pero esta tiene algunas diferencias con respeto a su semejante cuando se trabaja con archivos.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:67920a0e-232e-4034-bb0e-00de419c3df1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;asp:TemplateField HeaderText=&amp;quot;Descargar&amp;quot;&amp;gt;
    &amp;lt;ItemTemplate&amp;gt;
        &amp;lt;asp:HyperLink ID=&amp;quot;descarga&amp;quot; runat=&amp;quot;server&amp;quot; NavigateUrl='&amp;lt;%# Eval(&amp;quot;Id&amp;quot;, &amp;quot;~/GuardarEnDb/Download.aspx?id={0}&amp;quot;) %&amp;gt;'&amp;gt;
               &amp;lt;img src=&amp;quot;../imagenes/download.gif&amp;quot; alt=&amp;quot;&amp;quot; width=&amp;quot;30px&amp;quot; height=&amp;quot;30px&amp;quot; style=&amp;quot;border-width:0px;&amp;quot; /&amp;gt;
        &amp;lt;/asp:HyperLink&amp;gt;
    &amp;lt;/ItemTemplate&amp;gt;
&amp;lt;/asp:TemplateField&amp;gt;
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Si bien pareciera engañar la igualdad con el ejemplo del punto 2.2, este tiene sus diferencias, una muy clara es el uso del campo “Id” como parámetro de la url del link, al usar una base de datos estamos trabajando con registro y los mismo se identifican por su clave.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e2697b05-76ee-4ae7-acb0-abfea8c8d4ab" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class Download : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        int id = Convert.ToInt32(Request.QueryString[&amp;quot;id&amp;quot;]);

        Archivo archivo = ArchivosDAL.GetById(id);

        Response.Clear();

        Response.AddHeader(&amp;quot;content-disposition&amp;quot;, string.Format(&amp;quot;attachment;filename={0}&amp;quot;, archivo.Nombre));
        Response.ContentType = &amp;quot;application/octet-stream&amp;quot;;

        Response.BinaryWrite(archivo.ContenidoArchivo);
        Response.End();

    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Se toma el id enviado en el querystring, se recupera la información del archivo por medio de una consulta a la DB y luego de definir el ContentType, se envía el array de byte por el Response, nótese que aquí no se escribe un archivo porque no se tiene uno, sino que se opera directamente con los byte.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;3.2 Eliminación del archivo subido el sitio&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Las diferencia en este punto con lo visto en el 2.3, es que aquí se debe hacer uso del id de la entidad que se quiere eliminar, y no el nombre del archivo, es por eso que se define el DataKeyNames, con el “Id”, y luego en el código:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:7490caea-f8f4-4ba4-a850-05f01d76f9c2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    //se obtiene el nombre de campo definido en el DataKeyNames del gridview
    int id = Convert.ToInt32(GridView1.DataKeys[e.RowIndex].Value);

    ArchivosDAL.DeleteById(id);

    CargarListadImagenes();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;se toma este para ejecuta la consulta de DELETE del registro en la db&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código de ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El desarrollo del ejemplo fue realizado con Visual Studio 2008, y la db empleada es Sql Server 2008 Express, el mdf de la misma se encuentra en la carpeta App_Data, y la cadena de conexión se define en el Web.config&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="439"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="145"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GuardarArchivosBaseDatos/[csharp]GuardarArchivoBaseDatos.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="155"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GuardarArchivosBaseDatos/[vb.net]GuardarArchivoBaseDatos.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="137"&gt;[Script DB]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/GuardarArchivosBaseDatos/script.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-3446860502647744964?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/3446860502647744964/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=3446860502647744964' title='26 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3446860502647744964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3446860502647744964'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/03/aspnet-grabar-archivo-en-base-de-datos.html' title='[ASP.NET] Grabar Archivo en base de datos'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-5312942225297046214</id><published>2011-03-08T16:37:00.001-08:00</published><updated>2011-05-01T07:41:39.605-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ADO.NET'/><title type='text'>[Análisis] CampusMVP - ADO.NET Entity Framework 4.0 - Aplicaciones y servicios centrados en datos</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Diariamente durante la participación en los foros noto que tema recurrente esta relacionado con el accesos a datos, existen muchas dudas de las técnicas que se deben aplicar para trabajar la información de un repositorio. &lt;/p&gt;  &lt;p&gt;En el blog trato de ejemplificar de forma practica las situaciones mas comunes que he visto planteadas en el foro, pero debo reconocer que son muchas y muy variadas como para tratarlas todas. &lt;/p&gt;  &lt;p&gt;Mas que nada estas se generan por un nivel inicial de quién consulta, sumado a la no dedicación del tiempo suficiente para madurar la tecnología, quizás realizando practicas que vayan subiendo en complejidad de de forma progresiva muchas de las dudas nunca se hubieran planteado. Por lo general si se busca como guía un libro sobre el tema, esta podría ser una alternativa, pero seguramente esté en ingles, y muchas veces el no tener un gran dominio del idioma puede terminar restando, haciendo muy lento el aprendizaje. &lt;/p&gt;  &lt;p&gt;Lo anteriormente dicho genera un problema : &amp;quot;falta de capacitación&amp;quot;. La profesión de sistemas requiere de constante capacitación que permita obtener nuevas habilidades sobre un tema; sino se madura la tecnología es más que seguro que se encontrarán miles de trabas.&lt;/p&gt;  &lt;p&gt;Es aquí donde &lt;a href="http://www.krasis.com/campusmvp/" target="_blank"&gt;CampusMVP&lt;/a&gt; puede aportar un gran beneficio, tuve la oportunidad gracias al contacto de Fred Lores de analizar unos de los cursos online que brindan     &lt;br /&gt;y la verdad me quedé maravillado, me refiero a este en particular: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://shop.campusmvp.com/Product-Preparaci%C3%B3n-de-la-certificaci%C3%B3n-70-516-TS-Accessing-Data-with-Microsoft%C2%AE-.NET-Framework-4_113.aspx" target="_blank"&gt;Preparación de la certificación 70-516 TS: Accessing Data with Microsoft® .NET Framework 4&lt;/a&gt;     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Como comenté más arriba el acceso a datos y manipulación de la información desde mi punto de vista es un aspecto importantísimo en el desarrollo de sistemas, ojo con esto no estoy minimizando que la presentación (WinForms, WFP, asp.net) y comunicación (WCF) sean aspectos menos importantes, pero sí noto que el trabajar con datos es uno de los aspecto que más dudas y consultas generan todos los días. &lt;/p&gt;  &lt;p&gt;Esta inquietud se la plateé a Fred y es por eso que de todos los cursos expuestos en CampusMVP el de acceso a datos fue el que destaco. &lt;/p&gt;  &lt;p&gt;Después de recorrerlo un buen rato destaco las características:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;u&gt;el curso está totalmente en español&lt;/u&gt;, lo cual no hay que menospreciar teniendo en cuenta que en sistemas no abunda documentación en nuestro idioma, el material comúnmente encontrado esta en inglés, lo cual implica ir algo más lento si es un idioma que no se domina del todo. &lt;/li&gt;    &lt;li&gt;&lt;u&gt;claramente explicado y con una lectura muy simple&lt;/u&gt;, algo que me llamó la atención es lo natural que se da la lectura de los temas, por supuesto esto también está relacionado a la experiencia previa que uno tenga. Si ya se han realizado algunas incursiones es lógico que resulte más simple la lectura, pero así y todo resultan muy claros los temas tratados.&amp;#160; &lt;/li&gt;    &lt;li&gt;&lt;u&gt;la complejidad de cada tema avanza de forma progresiva&lt;/u&gt;, a medida recorren los temas estos van aumentando de complejidad, el curso está estructurados de forma escalonada para que resulte práctico.       &lt;br /&gt;Este punto, unido a los ejemplo de código que se brindan durante la explicación, aporta una clara comprensión de cada tema tratado. &lt;/li&gt;    &lt;li&gt;&lt;u&gt;tiene la cantidad justa de imágenes que explican paso a paso como realizar cada tarea&lt;/u&gt;, pareciera un dato menor pero es importante que las explicaciones escritas se complementen con imágenes y que ésto se haga de forma adecuada no es simple (como se dice: una imagen vale más de mil palabras, pero tampoco es cuestión de desbordar todos los temas con miles de print screen de pantallas).       &lt;br /&gt;Además &lt;u&gt;se cuenta con videos&lt;/u&gt; que complementan aquellos temas en donde se requiere mostrar un proceso completo. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;En el link del curso se puede observar el temario tratado, pero este no solo incluye ADO.NET para el acceso a datos, sino que cubre en totalidad la certificación &lt;a href="http://www.microsoft.com/learning/en/us/exam.aspx?ID=70-516" target="_blank"&gt;70-516&lt;/a&gt; (para aquel que quiera rendirla, lo cual es recomendable si se realiza el curso)     &lt;br /&gt;Es por eso que se incluyen puntos relacionado con ORM, más que útil si se quiere aprender las últimas novedades que agilicen el desarrollo:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Entity Framework &lt;/li&gt;    &lt;li&gt;Linq to Sql &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Es más, este va mas allá incluyendo tecnologías actuales como ser: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Sync Framework &lt;/li&gt;    &lt;li&gt;WCF Data Service &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Al acceder al curso se dispone de autoevaluaciones para afianzar lo aprendido durante la unidad.&lt;/p&gt;  &lt;p&gt;Para coronar el postre con una frutilla, el acceso al curso permite la descarga de 3 libros en formato pdf, entre ellos el más destacado. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://shop.campusmvp.com/Product-ADO.NET-Entity-Framework-4.0---Aplicaciones-y-servicios-centrados-en-datos_110.aspx" target="_blank"&gt;ADO.NET Entity Framework 4.0 - Aplicaciones y servicios centrados en datos&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Conclusión, si se busca aprender o profundizar los conocimiento en el acceso a datos, en español, de simple lectura y comprensión, sumado a una buena estructura en el temario, este curso es el indicado.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-5312942225297046214?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/5312942225297046214/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=5312942225297046214' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5312942225297046214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5312942225297046214'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/03/analisis-campusmvp-adonet-entity.html' title='[Análisis] CampusMVP - ADO.NET Entity Framework 4.0 - Aplicaciones y servicios centrados en datos'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-4995866470577725521</id><published>2011-02-27T21:30:00.001-08:00</published><updated>2011-02-27T21:30:33.322-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Reports'/><title type='text'>Crystal Reports – Cargar imagen usando una capa de reportes</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En esta oportunidad se profundizara el trabajo de imágenes pero apuntando a Reportes, concretamente con el uso de Crystal Reports.&lt;/p&gt;  &lt;p&gt;Para este artículo se continua con el ejemplo de uno previo:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2011/02/winforms-edicion-empleados-grabar.html"&gt;[WinForms] Edición Empleados – Grabar imagen en base de datos&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;En el anterior se listaba y editaba los datos del empleado, incluida sus fotografías, en cambio en este artículo se vera como listar en un reporte esta misma información.&lt;/p&gt;  &lt;p&gt;El resultado del reporte final del reporte seria:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen1.jpg" width="530" height="349" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Se analizara además como incluir las imágenes provenientes de una base de datos, y también un logo tomado de un archivo de imagen.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Capa de Reportes&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Esto quizás aplique un poco mejor con una arquitectura en capas, pero en este caso aunque no las haya definido del todo, se puede separa en un proyecto concreto la responsabilidad de crear los reportes.&lt;/p&gt;  &lt;p&gt;Es por eso que se observara en la solución un proyecto de nombre ReportsLayer, este será el encargado de:&lt;/p&gt;  &lt;p&gt;- Encapsular la diseño del reporte&lt;/p&gt;  &lt;p&gt;- Definición y estructura de datos que requieres el reporte, en este caso implementada en dataset tipados&lt;/p&gt;  &lt;p&gt;- La carga de la información, conectándose para ello directamente a los datos, esta capa no hará uso del DataAccess, porque al usar dataset la carga de datos se torna particular, por lo tanto su funcionalidad requiere una conexión directa.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen2.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;La idea con esto es separar funcionalidad y además cubrir un defecto que tiene Crystal Reports, en donde el diseñador solo toma como entidades objetos que estén local al proyecto donde se encuentra el rpt. Muchas veces poner en la Presentación un reporte implicaría además poner allí mismo los dataset tipados, lo cual ensucia el modelo.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definición y carga de datos en el DataSet&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Para este reporte se definió un dataset tipado con dos datatable en su interior.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen3.jpg" width="495" height="263" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Para al carga de los empleados se hará uso de la funcionalidad de la clase EmpleadosDAL definida dentro del propio proyecto de Reportes.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e4940bd2-015b-4197-81fa-7ed70fd5f5b8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;internal static class EmpleadosDAL
    {

        internal static Empleados ObtenerTodos()
        {
            Empleados empleados = new Empleados();

            using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
            {
                conn.Open();

                string query = @&amp;quot;SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen
                                 FROM Empleados&amp;quot;;

                SqlCommand cmd = new SqlCommand(query, conn);

                SqlDataAdapter da = new SqlDataAdapter(cmd);
                //es necesario indicar la tabla del dataset que se quiere cargar
                da.Fill(empleados, &amp;quot;Empleados&amp;quot;); 

            }

            return empleados;
        }
    }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Allí se define la consulta en donde las columnas coinciden en nombre con los definidos en timepo de diseño en el dataset tipado, además vale aclarar que la clase ha sido declarada como internal de forma intencional, para que solo la capa de Reportes pueda usar esta funcionalidad, es mas la idea es que se limite al máximo el acceso a funcionalidad que solo esta clase debería utilizar, por eso el dataset tipado también tiene el modificador de acceso asignado a internal.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/CrystalReports/imagen4.jpg" /&gt; &lt;/p&gt;

&lt;p&gt;Un punto adicional es la carga de una imagen externa que representa el logo de la empresa, el mismo no se encuentra en la db sino que es un archivo, es por eso que luego de cargar el reporte se observan las líneas:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f3495cf1-646d-4f4f-aa67-9684c5c5b22a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
empleado.Empresa.Rows.Add(row);
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;encargadas justamente de crear una row en al datatable con la imagen del logo. En este caso se hace uso de la funcionalidad del Helper de Imágenes creado para tomar la imagen embebida como recurso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retorno del Reporte&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta cada de reportes solo debería ser accedida por medio de la clase Reports con su metodo ObtenerReporteEmpleados() este devolverá la instancia del reporte con la información asignada lista para ser mostrada en pantalla, o exportada si es necesario.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:be43ab5d-9e8e-4bac-a3c7-b44229b7e635" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class Reports
    {

        public static ReporteEmpleados ObtenerReporteEmpleados()
        {
            ReporteEmpleados report = new ReporteEmpleados();

            //
            // Se obtienen los datos de la lista de empleados
            //
            Empleados empleado = EmpleadosDAL.ObtenerTodos();

            //
            // Se agrega el logo de la empresa a la informacion del listado
            //
            Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
            row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
            empleado.Empresa.Rows.Add(row);

            //
            // Se asigna los datos a la instancia del reporte
            //
            report.SetDataSource(empleado);

            return report;
        }

    }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Lanzar el Reporte en al Presentación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El ultimo punto por tratar es como se usara lo anteriormente explicado desde la presentación.&lt;/p&gt;

&lt;p&gt;Por un lado contamos con un formulario especialmente creado para desplegar el reporte, el mismo solo cuanta con el CrystalReportViewer y recibe por parámetro la instancia del reporte que debe mostrar.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:320b35ab-0991-46ab-90c4-80e48ff3be1e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class Reporte : Form
{
    private ReportClass _report = null;

    public Reporte()
    {
        InitializeComponent();
    }

    public Reporte(ReportClass report)
        :this()
    {
        _report = report;
    }

    private void Reporte_Load(object sender, EventArgs e)
    {
        crystalReportViewer1.ReportSource = _report;
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Se define un nuevo constructor del formulario para pasar la instancia del reporte al formulario y es en el Load del mismo que se asigna al Viewer, para desplegar el reporte en pantalla.&lt;/p&gt;

&lt;p&gt;Por otro lado tenemos un botón en la pantalla de ListaEmpleados, el cual recupera el reporte, con los datos asignados, y se la pasa a la instancia del formulario para que la muestre en pantalla.&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:12b6de9b-8f52-4da0-9168-df55aa9dfb89" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnListar_Click(object sender, EventArgs e)
{

    ReporteEmpleados report = Reports.ObtenerReporteEmpleados();

    Reporte frmReporte = new Reporte(report);
    frmReporte.Show();

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código de ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La base de datos utilizada en el ejemplo es la Sql Server Express 2008 R2, como ver en la solución el mdf esta integrado al Visual Studio, por lo tanto con solo tener el sql server express instado esta debería funciona adjuntándose sola al servicio.&lt;/p&gt;

&lt;p&gt;En la carpeta “script” del proyecto “DataAccess”&amp;#160; se encuentra un archivo .sql con las instrucciones para crear la estructura de tablas y datos que se requieren para este articulo.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/CrystalReports/[csharp]ListaEmpleadosReporteCrystal.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/CrystalReports/[vb.net]ListaEmpleadosReporteCrystal.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-4995866470577725521?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/4995866470577725521/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=4995866470577725521' title='13 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4995866470577725521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/4995866470577725521'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/02/crystal-reports-cargar-imagen-usando.html' title='Crystal Reports – Cargar imagen usando una capa de reportes'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7360295296833352417</id><published>2011-02-22T04:24:00.001-08:00</published><updated>2011-02-22T04:24:00.262-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>[ASP.NET] Lockear Edición de Entidades</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;La finalidad del articulo será demostrar como implementar lockeos pesimistas usando las características que un entorno web puede brindar.&lt;/p&gt;  &lt;p&gt;Seguramente será bien conocido el lockeo por medio de la base de datos, definiendo un campo del tipo TimeSpan y ante una actualización validar si este ha cambiado, si lo hizo se informa al usuario y se evita la operación hasta resolver el conflicto. Este se lo denomina lockeo optimista ya que no evita que cada usuario edite el registro, pero lo valida al momento de realizar la transacción.&lt;/p&gt;  &lt;p&gt;Ahora bien como implementar aprovechando las características de un entorno web un lockeo de entidad distinto&lt;/p&gt;  &lt;p&gt;Primero mencionaremos porque un entorno web provee ciertas características que hacen posible este tipo de implementación&lt;/p&gt;  &lt;p&gt;- se dispone de un ambiente centralizado, todos lo usuario debes consultar el servidor para poder editar las entidades&lt;/p&gt;  &lt;p&gt;- distintas sesiones se pueden comunicar y mantener información global a nivel del sitio por medio de la declaración de variables estáticas, usar esto o el objeto Application de asp.net es semejando ya que se comportan de la misma forma&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Locker Helper&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El núcleo por el cual el lockeo es implementado se desarrolla en una clase muy simple&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:d7699301-1bd7-49d1-99eb-f4b5f55bbabe" class="wlWriterSmartContent"&gt;   &lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class LockHelper&amp;lt;TEntity&amp;gt; where TEntity : class
{
    private static Dictionary&amp;lt;string, List&amp;lt;LockInfo&amp;gt;&amp;gt; lockList = new Dictionary&amp;lt;string,List&amp;lt;LockInfo&amp;gt;&amp;gt;();

    public static LockResult Lock(int idEntity, string userName)
    {
        LockInfo newLock = null;

        if (!lockList.ContainsKey(typeof(TEntity).Name))
        {
            newLock = new LockInfo()
            {
                UserName = userName,
                IdEntity = idEntity
            };

            lock (lockList)
            {
                lockList.Add(typeof(TEntity).Name, new List&amp;lt;LockInfo&amp;gt;() { newLock });
            }
            
            
            return new LockResult()
                {
                    Result = LockResultEnum.Succesully,
                    LockInfo = newLock
                };
        }

        //
        // Se valida sino estaba previamente lockeado por alguien mas
        //
        LockInfo alreadyLockedResult = lockList[typeof(TEntity).Name]
                                            .FirstOrDefault(o =&amp;gt; o.IdEntity == idEntity
                                                                    &amp;amp;&amp;amp; o.UserName != userName);

        if (alreadyLockedResult != null)
            return new LockResult()
            {
                Result = LockResultEnum.AlreadyLocked,
                LockInfo = alreadyLockedResult
            };

        //
        // Si se intenta lockear algo que ya estaba previamente lockeado solo se devuelve
        // el LockInfo que se tenia
        //
        LockInfo lockResult = lockList[typeof(TEntity).Name]
                                    .FirstOrDefault(o =&amp;gt; o.IdEntity == idEntity 
                                                            &amp;amp;&amp;amp; o.UserName == userName);
        if (lockResult != null)
            return new LockResult()
                        {
                            Result = LockResultEnum.Succesully,
                            LockInfo = lockResult
                        }; 


        newLock = new LockInfo()
        {
            UserName = userName,
            IdEntity = idEntity
        };

        lock (lockList)
        {
            lockList[typeof(TEntity).Name].Add(newLock);
        }

        return new LockResult()
                {
                    Result = LockResultEnum.Succesully,
                    LockInfo = newLock
                };
    }

    public static void UnLock(int idEntity)
    {
        lock (lockList)
        {
            lockList[typeof(TEntity).Name].RemoveAll(o =&amp;gt; o.IdEntity == idEntity);
        }
    }


}


public class LockInfo
{
    public string UserName { get; set; }
    public int IdEntity { get; set; }
}

public class LockResult
{
    public LockResultEnum Result { get; set; }
    public LockInfo LockInfo { get; set; }
}

public enum LockResultEnum
{
    AlreadyLocked = 0,
    Succesully = 1
}&lt;/pre&gt;
&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Esta básicamente esta compuesta por método estáticos y particularmente es la que define el &lt;/p&gt;

&lt;p&gt;&lt;em&gt;private static Dictionary&amp;lt;string, List&amp;lt;LockInfo&amp;gt;&amp;gt; lockList&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;esta linea aunque parece insignificante es donde se contendrá la información de lockeos para todos los usuario que trabajen con la aplicación, el helper básicamente realizara consulta, agregara y quitara elementos en esta lista.&lt;/p&gt;

&lt;p&gt;Como comente anteriormente ls lista esta definida con static para que esta actué como si fuera el objeto Application y este disponible a nivel del sitio, o sea pueda ser accedida por todos las sesiones y request que se realicen, seria como un repositorio en memoria.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Como usarlo ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta es la gran pregunta, como hacer uso de la funcionalidad implementada.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:1671f4dc-22ae-498b-bb46-b6b46db8b52d" class="wlWriterSmartContent"&gt;
  &lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;        protected void gvCourse_RowEditing(object sender, GridViewEditEventArgs e)
        {
            lblMessage.Text = string.Empty;

            //
            //Obtengo el id de la entidad que se esta editando
            //
            int id = Convert.ToInt32(gvCourse.DataKeys[e.NewEditIndex].Value);

            LockResult resultLock = LockHelper&amp;lt;Course&amp;gt;.Lock(id, Thread.CurrentPrincipal.Identity.Name);

            if (resultLock.Result == LockResultEnum.Succesully)
            {
                gvCourse.EditIndex = e.NewEditIndex;

                DataBindGridView();
            }
            else
                lblMessage.Text = string.Format(&amp;quot;El Curso {0} esta siendo editado por: {1}&amp;quot;, id, resultLock.LockInfo.UserName);

        }

        protected void gvCourse_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
        {
            int id = Convert.ToInt32(gvCourse.DataKeys[e.RowIndex].Value);

            LockHelper&amp;lt;Course&amp;gt;.UnLock(id);

            gvCourse.EditIndex = -1;
            DataBindGridView();
        }&lt;/pre&gt;
&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Bien, como se observa es en la edición de registros de un GridView donde se realiza la operación de lockeo, al metodo Lock() se le proporciona el id de la entidad que se quiere bloquear y el usuario que solicita esta acción.&lt;/p&gt;

&lt;p&gt;Si todo va bien se edita el registro, pero puede ocurrir que este tomada la edición por otro usuario, en tal caso el método devolverá en su respuesta el estado que lo indica y es usado para notificar al usuario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codigo de Ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El código de ejemplo cuanta con una base de datos creada en Sql Server 2008, pero en caso de tener problemas en el uso podrían ejecutarse los script que acompañan al ejemplo.&lt;/p&gt;

&lt;p&gt;Para autenticarse al ejecutar el ejemplo solo debe ingresar correctamente el nombre del usuario, ya que aplica una validación simple sin verificar el password, en este puede ingresarse cualquier valor&lt;/p&gt;

&lt;p&gt;Como nombres de usuario podrías elegir: Alonso, Suarez, Lopez, cualquiera de estos serán validos, pero si se quiere se puede consultar la tabla Person allí esta el resto.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/ASP.NET/Lockear%20Edicion%20usuario/[csharp]LockEdition.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7360295296833352417?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7360295296833352417/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7360295296833352417' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7360295296833352417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7360295296833352417'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/02/aspnet-lockear-edicion-de-entidades.html' title='[ASP.NET] Lockear Edición de Entidades'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-703179656581672333</id><published>2011-02-13T20:03:00.001-08:00</published><updated>2011-02-13T20:03:43.964-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForms] Edición Empleados – Grabar imagen en base de datos</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El objetivo de este articulo será reflejar los siguiente puntos:&lt;/p&gt;  &lt;p&gt;- Grabar una imagen en un campo de una base de datos&lt;/p&gt;  &lt;p&gt;- Recuperar la imagen visualizándola en un celda del DataGridView y en un PictureBox&lt;/p&gt;  &lt;p&gt;- Comunicar un formulario pasándole información en el constructor para la edición de una entidad&lt;/p&gt;  &lt;p&gt;- Detectar el cierre del formulario para actualizar la información de los empleados&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Dominio de la aplicación&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Bien empezaremos definiendo al estructura de datos de la aplicación.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen1.jpg" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen1.jpg" width="464" height="284" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;El dominio consiste en Empleados, con información básica del mismo, mas un campo de imagen el cual es opcional por lo cual esta marcado para permitir nulos.&lt;/p&gt;  &lt;p&gt;El atributo Estado Civil tomara los valores de 1 a 4, los cuales fueron definido en un enumerado.&lt;/p&gt;  &lt;p&gt;Adicionalmente el empleado podrá tener Estudios realizados, los cuales se asignan marcando de una lista.&lt;/p&gt;  &lt;p&gt;El diseño de la interfaz es simple, un formulario principal que lista los usuarios.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen2.jpg" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen2.jpg" width="524" height="417" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Y otro que edita el usuario cuando se realiza un doble click en la fila de la lista.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen3.jpg" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen3.jpg" width="507" height="304" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;La arquitectura de la aplicación es en dos capas, la presentación se comunica con DataAccess pasándole la información que necesita persistir o recuperar, el medio de comunicación son entidades definidas por medio de clases que modelan el dominio de la aplicación.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/imagen4.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;Si bien el esquema de estas entidades son muy parecido al de la base de datos ,se debe apreciar que la entidad “Empleado” posee una relación con la de estudios, lo cual posibilita la navegación de la información asociada.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Cargar lista de empleados (con imágenes)&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En esta sección analizaremos como cargar las imágenes en el DataGridView, tomando esta información desde la base de datos.&lt;/p&gt;  &lt;p&gt;El primer paso será recuperar los registros de la tabla, incluyendo la imagen.&lt;/p&gt;  &lt;p&gt;[C#]&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:dad5161a-d313-441c-91fd-836b6b83d8b9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;EmpleadoEntity&amp;gt; ObtenerTodos()
{
    List&amp;lt;EmpleadoEntity&amp;gt; lista = new List&amp;lt;EmpleadoEntity&amp;gt;();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string query = @&amp;quot;SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen
                         FROM Empleados&amp;quot;;

        SqlCommand cmd = new SqlCommand(query, conn);

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            lista.Add(ConvertirEmpleado(reader, false));
        }

    }

    return lista;
}

private static EmpleadoEntity ConvertirEmpleado(IDataReader reader, bool cargarRelaciones)
{
    EmpleadoEntity empleado = new EmpleadoEntity();

    empleado.IdEmpleado = Convert.ToInt32(reader[&amp;quot;IdEmpleado&amp;quot;]);
    empleado.Nombre = Convert.ToString(reader[&amp;quot;Nombre&amp;quot;]);
    empleado.Apellido = Convert.ToString(reader[&amp;quot;Apellido&amp;quot;]);
    empleado.FechaNacimiento = Convert.ToDateTime(reader[&amp;quot;FechaNacimiento&amp;quot;]);
    empleado.EstadoCivil = Convert.ToInt16(reader[&amp;quot;EstadoCivil&amp;quot;]);

    if (reader[&amp;quot;Imagen&amp;quot;] != DBNull.Value)
        empleado.Imagen = (byte[])reader[&amp;quot;Imagen&amp;quot;];

    if (cargarRelaciones)
    {
        empleado.Estudios = EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado);
    }

    return empleado;
}		
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:0f5cb424-9972-4e03-804f-43e16d6c2acc" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Public Shared Function ObtenerTodos() As List(Of EmpleadoEntity)

    Dim lista As New List(Of EmpleadoEntity)()

    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings(&amp;quot;default&amp;quot;).ToString())
        conn.Open()

        Dim query As String = &amp;quot;SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen&amp;quot; &amp;amp; _
                              &amp;quot;FROM Empleados&amp;quot;

        Dim cmd As New SqlCommand(query, conn)

        Dim reader As SqlDataReader = cmd.ExecuteReader()

        While reader.Read()
            lista.Add(ConvertirEmpleado(reader, False))
        End While

    End Using

    Return lista

End Function


Private Shared Function ConvertirEmpleado(ByVal reader As IDataReader, ByVal cargarRelaciones As Boolean) As EmpleadoEntity

    Dim empleado As New EmpleadoEntity()

    empleado.IdEmpleado = Convert.ToInt32(reader(&amp;quot;IdEmpleado&amp;quot;))
    empleado.Nombre = Convert.ToString(reader(&amp;quot;Nombre&amp;quot;))
    empleado.Apellido = Convert.ToString(reader(&amp;quot;Apellido&amp;quot;))
    empleado.FechaNacimiento = Convert.ToDateTime(reader(&amp;quot;FechaNacimiento&amp;quot;))
    empleado.EstadoCivil = Convert.ToInt16(reader(&amp;quot;EstadoCivil&amp;quot;))

    If reader(&amp;quot;Imagen&amp;quot;) IsNot DBNull.Value Then
        empleado.Imagen = DirectCast(reader(&amp;quot;Imagen&amp;quot;), Byte())
    End If

    If cargarRelaciones Then
        empleado.Estudios = EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado)
    End If

    Return empleado

End Function	
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es preciso notar como se asigna la imagen a la entidad&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:971b3901-30a7-4659-822a-f383093521e9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;if (reader[&amp;quot;Imagen&amp;quot;] != DBNull.Value)
    empleado.Imagen = (byte[])reader[&amp;quot;Imagen&amp;quot;];
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:7a89e422-2aac-47f5-9b4a-f9830424f3ec" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;If reader(&amp;quot;Imagen&amp;quot;) IsNot DBNull.Value Then
    empleado.Imagen = DirectCast(reader(&amp;quot;Imagen&amp;quot;), Byte())
End If
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;lo cual resulta muy simple.&lt;/p&gt;

&lt;p&gt;En el formulario “ListaEmpleados” se usa esta información para cargar el DataGridView&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e36d7f6a-b986-4407-93cd-9aafd2e96347" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void CargarListaEmpleados()
{
    dgvEmpleados.AutoGenerateColumns = false;
    dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos();

    foreach (DataGridViewRow row in dgvEmpleados.Rows)
    {
        //se asigna el alto de la fila para poder ver la imagen correctamente
        row.Height = 120; 

        //se recupera la entidad que es asignada como dato
        EmpleadoEntity empleado = row.DataBoundItem as EmpleadoEntity;

        if (empleado.Imagen == null)
            row.Cells[&amp;quot;Imagen&amp;quot;].Value = ImageHelper.ObtenerImagenNoDisponible();
        else
            row.Cells[&amp;quot;Imagen&amp;quot;].Value = ImageHelper.ByteArrayToImage(empleado.Imagen); 
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:3e417a8a-0502-4450-8539-34861ae4d887" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Sub CargarListaEmpleados()
	dgvEmpleados.AutoGenerateColumns = False
	dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos()

	For Each row As DataGridViewRow In dgvEmpleados.Rows
		'se asigna el alto de la fila para poder ver la imagen correctamente
		row.Height = 120

		'se recupera la entidad que es asignada como dato
		Dim empleado As EmpleadoEntity = TryCast(row.DataBoundItem, EmpleadoEntity)

		If empleado.Imagen Is Nothing Then
			row.Cells(&amp;quot;Imagen&amp;quot;).Value = ImageHelper.ObtenerImagenNoDisponible()
		Else
			row.Cells(&amp;quot;Imagen&amp;quot;).Value = ImageHelper.ByteArrayToImage(empleado.Imagen)
		End If
	Next

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;seguramente una pregunta que se hagan es porque en este método se esta recorriendo las filas del DataGridView después de asignar los datos al mismo ?&lt;/p&gt;

&lt;p&gt;bien esto tiene una explicación, resulta que la celda de imagen del DataGridView requiere un tipo Image para poder trabajar, pero la entidad que proporcionamos devuelve un array de byte, lo cual no es compatible, entonces es que se aprovecha la capacidad del DataGridView para retornar la entidad que bindea a cada fila para obtener la entidad “Empleado” y convertir el array de byte al tipo Image.&lt;/p&gt;

&lt;p&gt;También debo aclara que esto se intento hacer en un evento, como ser el CellFormatting, pero resulto un desastre porque este evento repintaba constantemente la información generando un parpadeo molesto en el control, se podría haber realizado esto mismo en el evento DataBindingComplete, pero para este caso no valía la pena separa la funcionalidad, por eso se dejo allí mismo.&lt;/p&gt;

&lt;p&gt;En ese método se hace uso del la funcionalidad de “helper” que no es mas que un grupo de funciones que facilitan ciertas tareas, en este caso convierten el array de byte en Image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comunicar Formulario y detectar cierre del form hijo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Al realizar un doble click en la celda del DataGridView este entra en modo edición, esto implica simplemente toma la información y pacérsela al formulario&lt;/p&gt;

&lt;p&gt;Desde “ListaEmpleados” se trabaja con el evento tomando solo el id del empleado, y en la instancia del formulario se lo pasa en el constructor. También en la misma operación se adjunta el evento FormClosing para poder detectar el cierre el form y proceder a la actualización del la lista de empleados.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:239df362-c125-419f-9406-0c67410bcfac" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void dgvEmpleados_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
    int IdEmpleado = Convert.ToInt32(dgvEmpleados.Rows[e.RowIndex].Cells[&amp;quot;IdEmpleado&amp;quot;].Value);

    //
    // al pasarle un id de empleado este lo cargara para su edicion
    //
    EditarEmpleado frmEditar = new EditarEmpleado(IdEmpleado);
    frmEditar.FormClosing += new FormClosingEventHandler(frmEditar_FormClosing);

    frmEditar.Show();
}

void frmEditar_FormClosing(object sender, FormClosingEventArgs e)
{
	//
	// al cerrarse el form de edicion se ingresa a este evento 
	// para actualizar la informacion del listado
	//

	EditarEmpleado frmEdit = sender as EditarEmpleado;

	if(frmEdit.DialogResult == DialogResult.OK)
		CargarListaEmpleados();

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c7e40986-8116-4e91-8e21-ec73845f7a11" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Sub dgvEmpleados_CellContentDoubleClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)

    Dim IdEmpleado As Integer = Convert.ToInt32(dgvEmpleados.Rows(e.RowIndex).Cells(&amp;quot;IdEmpleado&amp;quot;).Value)

    '
    ' al pasarle un id de empleado este lo cargara para su edicion
    '
    Dim frmEditar As New EditarEmpleado(IdEmpleado)
    AddHandler frmEditar.FormClosing, New FormClosingEventHandler(AddressOf frmEditar_FormClosing)

    frmEditar.Show()

End Sub

Private Sub frmEditar_FormClosing(sender As Object, e As FormClosingEventArgs)
	'
	' al cerrarse el form de edicion se ingresa a este evento 
	' para actualizar la informacion del listado
	'

	Dim frmEdit As EditarEmpleado = TryCast(sender, EditarEmpleado)

	If frmEdit.DialogResult = DialogResult.OK Then
		CargarListaEmpleados()
	End If

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;En el formulario que recibe esta información “EditarEmpleado” solo utiliza el id del empleado, porque con este ira a la db y recuperara la entidad para cargar el formulario, no hace falta enviar la entidad completa, solo la mínima información como para que este form pueda recuperar el resto.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f3f65637-5c0d-428e-a192-1f9308a3acd4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;  public partial class EditarEmpleado : Form
   {
       private int? _idEmpleado = null;

       public EditarEmpleado()
       {
           InitializeComponent();
       }

       public EditarEmpleado(int idEmpleado)
           : this()
       {
           _idEmpleado = idEmpleado;
       }
	
	.
	.
	.
}	
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ef37558e-b44d-4e13-929a-e173a354c762" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Public Partial Class EditarEmpleado
    Inherits Form

    Private _idEmpleado As Nullable(Of Integer) = Nothing

	Public Sub New()
		InitializeComponent()
	End Sub

	Public Sub New(idEmpleado As Integer)
		Me.New()
		_idEmpleado = idEmpleado
	End Sub
	
	.
	.
	.
End Class
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;Cuando se termina la edición se retorna como un resultado del formulario que indicara si se grabo o se cancelo el formulario, esto se logra por medio del DialogResult.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:33fae4d2-1aea-46e3-a733-24eeaf198d32" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;		
private void btnGuardar_Click(object sender, EventArgs e)
{
	.
	.
	.

	//
	// se graba 
	//
	EmpleadosDAL.Save(empleado);

	this.DialogResult = DialogResult.OK;
	this.Close();
}

private void btnCancelar_Click(object sender, EventArgs e)
{
	this.DialogResult = DialogResult.Cancel;
	this.Close();
}		
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:15684d64-90da-471b-abee-d423de8bb8cb" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Sub btnGuardar_Click(sender As Object, e As EventArgs)

	.
	.
	.

	'
	' se graba 
	'
	EmpleadosDAL.Save(empleado)

	Me.DialogResult = DialogResult.OK
       Me.Close()

End Sub

   Private Sub btnCancelar_Click(ByVal sender As Object, ByVal e As EventArgs)

       Me.DialogResult = DialogResult.Cancel
       Me.Close()

   End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Editar Empleado (mostrando la imagen en un Picturebox)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta tarea es bastante simple, solo implica recuperar una entidad en concreto haciendo uso de la cada de datos y cargar la entidad en los controles para presentar esta al usuario.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:cefa1b46-8f12-4529-90e2-02fa1d9bb981" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static EmpleadoEntity ObtenerById(int idEmpleado)
{
    EmpleadoEntity empleado = null;

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string query = @&amp;quot;SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen
                         FROM Empleados WHERE IdEmpleado = @idEmpleado&amp;quot;;

        SqlCommand cmd = new SqlCommand(query, conn);
        cmd.Parameters.AddWithValue(&amp;quot;@idEmpleado&amp;quot;, idEmpleado);

        SqlDataReader reader = cmd.ExecuteReader();

        if (reader.Read())
        {
            empleado = ConvertirEmpleado(reader, true);
        }

    }

    return empleado;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:674181f3-8df6-4577-805c-e6c7ad8f6d78" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Public Shared Function ObtenerById(ByVal idEmpleado As Integer) As EmpleadoEntity

    Dim empleado As EmpleadoEntity = Nothing

    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings(&amp;quot;default&amp;quot;).ToString())
        conn.Open()

        Dim query As String = &amp;quot;SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen &amp;quot; &amp;amp; _
                              &amp;quot;FROM Empleados WHERE IdEmpleado = @idEmpleado&amp;quot;

        Dim cmd As New SqlCommand(query, conn)
        cmd.Parameters.AddWithValue(&amp;quot;@idEmpleado&amp;quot;, idEmpleado)

        Dim reader As SqlDataReader = cmd.ExecuteReader()

        If reader.Read() Then
            empleado = ConvertirEmpleado(reader, True)
        End If

    End Using

    Return empleado

End Function
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Una vez que se tiene la entidad solo se carga esta en los contoles.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ebdd325d-a0a1-401b-a4b1-1b83ab2b1d31" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void EditarEmpleado_Load(object sender, EventArgs e)
{
    CargarEstadoCivil();
    CargarEstudio();

    //por defecto se carga una imagen de no disponible
    picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();

    //
    // se carga la informacion del empleado que se quiere editar
    //
    if (_idEmpleado.HasValue)
    {
        EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(_idEmpleado.Value);

        _idEmpleado = empleado.IdEmpleado;
        txtNombre.Text = empleado.Nombre;
        txtApellido.Text = empleado.Apellido;
        dtpFechaNacimiento.Value = empleado.FechaNacimiento;

        cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil);

        if (empleado.Imagen == null)
            picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();
        else
            picImagenEmpleado.Image = ImageHelper.ByteArrayToImage(empleado.Imagen);

        //
        // Se obtienen los estudios del empleado
        //
        AsignarEstudios(empleado);

    }

}

private void CargarEstadoCivil()
{
    cbEstadoCivil.DisplayMember = &amp;quot;Descripcion&amp;quot;;
    cbEstadoCivil.ValueMember = &amp;quot;IdEstadoCivil&amp;quot;;
    cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos();
}

private void CargarEstudio()
{
    dgvEstudios.AutoGenerateColumns = false;
    dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos();
}

private void AsignarEstudios(EmpleadoEntity empleado)
{
    
    List&amp;lt;DataGridViewRow&amp;gt; rows = (from row in dgvEstudios.Rows.Cast&amp;lt;DataGridViewRow&amp;gt;()
                                    let idEstudio = Convert.ToInt32(row.Cells[&amp;quot;IdEstudio&amp;quot;].Value)
                                  join dd in empleado.Estudios on idEstudio equals dd.IdEstudio
                                  select row).ToList();


    rows.ForEach(o =&amp;gt; o.Cells[&amp;quot;Seleccion&amp;quot;].Value = true);
    
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f256f255-5f78-4b69-baec-5798ca12185c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;   Private Sub EditarEmpleado_Load(ByVal sender As Object, ByVal e As EventArgs)

       CargarEstadoCivil()
       CargarEstudio()

       'por defecto se carga una imagen de no disponible
       picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible()

       '
       ' se carga la informacion del empleado que se quiere editar
       '
       If _idEmpleado.HasValue Then
           Dim empleado As EmpleadoEntity = EmpleadosDAL.ObtenerById(_idEmpleado.Value)

           _idEmpleado = empleado.IdEmpleado
           txtNombre.Text = empleado.Nombre
           txtApellido.Text = empleado.Apellido
           dtpFechaNacimiento.Value = empleado.FechaNacimiento

           cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil)

           If empleado.Imagen Is Nothing Then
               picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible()
           Else
               picImagenEmpleado.Image = ImageHelper.ByteArrayToImage(empleado.Imagen)
           End If

           '
           ' Se obtienen los estudios del empleado
           '

           AsignarEstudios(empleado)
       End If


   End Sub

   Private Sub CargarEstadoCivil()

       cbEstadoCivil.DisplayMember = &amp;quot;Descripcion&amp;quot;
       cbEstadoCivil.ValueMember = &amp;quot;IdEstadoCivil&amp;quot;
       cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos()

   End Sub

Private Sub CargarEstudio()
	dgvEstudios.AutoGenerateColumns = False
	dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos()
End Sub

Private Sub AsignarEstudios(empleado As EmpleadoEntity)

       Dim rows As List(Of DataGridViewRow) = (From row In dgvEstudios.Rows.Cast(Of DataGridViewRow)() _
                                                Let idEstudio = Convert.ToInt32(row.Cells(&amp;quot;IdEstudio&amp;quot;).Value) _
                                                Join dd In empleado.Estudios On idEstudio Equals dd.IdEstudio _
                                                Select row).ToList()


       'rows.ForEach(Function(o) o.Cells(&amp;quot;Seleccion&amp;quot;).Value = True)
       rows.ForEach(Function(o) InlineAssignHelper(o.Cells(&amp;quot;Seleccion&amp;quot;).Value, True))

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La asignación de la imagen es muy simple y similar a como se trabajo en el DataGridview, solo se convierte el array de byte recuperado de la tabla en la base de datos y se carga directo en el PictureBox.&lt;/p&gt;

&lt;p&gt;Algo interesante a remarcar es la forma en como se carga la lista de Estudios, en esta se usan dos consultas separadas, por un lado la primera que carga todos los estudios existentes, si se ingresara en modo de alta de empleado solo esta lista seria cargada, pero si se esta editando se recupera la lista de estudios asignados a ese empleado en particular y por medio de una consulta linq (método AsignarEstudios) se realiza la unión entres ambas listas aquellas filas del DataGridView que coincidan serán marcadas porque son las que el empleado tiene registradas en la tabla.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Búsqueda de una imagen (cargar archivo a un PictureBox)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un punto que debería ser menor pero que he visto con mucha dificultad en preguntas en los foros, es como cargar seleccionar un archivo y cargarlo en un PictureBox?&lt;/p&gt;

&lt;p&gt;El código es muy simple pero suele generar problemas.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f3914187-2475-43ca-94a5-8db524d449c5" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnBuscarImagen_Click(object sender, EventArgs e)
{
    OpenFileDialog fileDialog = new OpenFileDialog();
    fileDialog.Filter = &amp;quot;Archivo JPG|*.jpg&amp;quot;;

    if (fileDialog.ShowDialog() == DialogResult.OK)
    {
        picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName);
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:80c14dea-829c-47cf-ac2e-1a8bdcc9307c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Sub btnBuscarImagen_Click(ByVal sender As Object, ByVal e As EventArgs)

    Dim fileDialog As New OpenFileDialog()
    fileDialog.Filter = &amp;quot;Archivo JPG|*.jpg&amp;quot;

    If fileDialog.ShowDialog() = DialogResult.OK Then
        picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName)
    End If

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Persistir la edición&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;La operación de persistir los datos o guardarlos requiere dos etapas, una implica tomar la información de los controles y generar los objetos de dominio que la capa de datos requiere para llevar a cabo la actualización en la base de datos, la segunda será simplemente procesar la entidad generando los INSERT y UPDATE necesario.&lt;/p&gt;

&lt;p&gt;[C#]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:96079598-3ac7-48ec-b926-4f5c2e2fed8f" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnGuardar_Click(object sender, EventArgs e)
{
    //
    // Se crea la entidad
    //
    EmpleadoEntity empleado = new EmpleadoEntity() 
    { 
        IdEmpleado = _idEmpleado.GetValueOrDefault(),
        Nombre = txtNombre.Text,
        Apellido= txtApellido.Text,
        FechaNacimiento = dtpFechaNacimiento.Value,
        EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue),
        Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image)
    };

    //
    // Se asignan los estudios seleccionados, se unicializa la lista 
    // para cargar la selecciond el usuario
    //
    empleado.Estudios = new List&amp;lt;EstudioEntity&amp;gt;();

    IEnumerable&amp;lt;DataGridViewRow&amp;gt; rowsSelected = dgvEstudios.Rows.Cast&amp;lt;DataGridViewRow&amp;gt;()
                                                                .Where(o =&amp;gt; Convert.ToBoolean(o.Cells[&amp;quot;Seleccion&amp;quot;].Value));

    foreach (DataGridViewRow row in rowsSelected)
    {
        EstudioEntity estudio = new EstudioEntity()
        {
            IdEstudio = Convert.ToInt32(row.Cells[&amp;quot;IdEstudio&amp;quot;].Value)
        };
        
        empleado.Estudios.Add(estudio);
    }

    //
    // se graba 
    //
    EmpleadosDAL.Save(empleado);

    this.DialogResult = DialogResult.OK;
    this.Close();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[VB.NET]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:30e7bda0-9877-4d36-a274-015fdef91ee9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: vb; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
	'
	' Se crea la entidad
	'
       Dim empleado As New EmpleadoEntity() With { _
                                                  .IdEmpleado = _idEmpleado.GetValueOrDefault(), _
                                                  .Nombre = txtNombre.Text, _
                                                  .Apellido = txtApellido.Text, _
                                                  .FechaNacimiento = dtpFechaNacimiento.Value, _
                                                  .EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue), _
                                                  .Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image) _
                                                 }

	'
	' Se asignan los estudios seleccionados, se unicializa la lista 
	' para cargar la selecciond el usuario
	'
	empleado.Estudios = New List(Of EstudioEntity)()

       Dim rowsSelected As IEnumerable(Of DataGridViewRow) = dgvEstudios.Rows.Cast(Of DataGridViewRow)().Where(Function(o) Convert.ToBoolean(o.Cells(&amp;quot;Seleccion&amp;quot;).Value))

       For Each row As DataGridViewRow In rowsSelected

           Dim estudio As New EstudioEntity() With { _
                                                    .IdEstudio = Convert.ToInt32(row.Cells(&amp;quot;IdEstudio&amp;quot;).Value) _
                                                   }

           empleado.Estudios.Add(estudio)

       Next

	'
	' se graba 
	'
	EmpleadosDAL.Save(empleado)

	Me.DialogResult = DialogResult.OK
       Me.Close()

End Sub
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es preciso remarcar como en la entidad “Empleado” es cargada la lista de Estudios que este posee y solo una entidad es enviada desde la Presentación a la capa de datos. Durante esta operación también se toma la imagen del PictureBox y se convierte en un array de byte.&lt;/p&gt;

&lt;p&gt;La actualización realiza varias operaciones las cuales son muy extensas en código, por lo tanto no será puesta directa en el articulo, pero si podrán verse en el código de ejemplo que puede descargarse.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código de Ejemplo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La base de datos utilizada en el ejemplo es la Sql Server Express 2008 R2, como ver en la solución el mdf esta integrado al Visual Studio, por lo tanto con solo tener el sql server express instado esta debería funciona adjuntándose sola al servicio.&lt;/p&gt;

&lt;p&gt;En la carpeta “script” del proyecto “DataAccess”&amp;#160; se encuentra un archivo .sql con las instrucciones para crear la estructura de tablas y datos que se requieren para este articulo.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/[csharp]ListaEmpleados.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/EdicionEmpleado/[vb.net]ListaEmpleados.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-703179656581672333?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/703179656581672333/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=703179656581672333' title='46 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/703179656581672333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/703179656581672333'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2011/02/winforms-edicion-empleados-grabar.html' title='[WinForms] Edición Empleados – Grabar imagen en base de datos'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>46</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-2549759340072114062</id><published>2010-11-18T08:51:00.001-08:00</published><updated>2010-11-18T08:51:32.345-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForms] Realizar Acciones en formularios hijo</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El objetivo del articulo es mostrar como distintas acciones pueden ser ejecutadas en formularios que implementen la interfaz correspondiente.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="200"&gt;[C#]          &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/RealizarAccionesFormularioHijo/[csharp]ComunicacionToolBarForms.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;        &lt;td valign="top" width="200"&gt;[VB.NET]          &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/RealizarAccionesFormularioHijo/[vb.net]ComunicacionToolBarForms.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-2549759340072114062?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/2549759340072114062/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=2549759340072114062' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2549759340072114062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2549759340072114062'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/11/winforms-realizar-acciones-en.html' title='[WinForms] Realizar Acciones en formularios hijo'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-2985380683237757454</id><published>2010-11-15T08:59:00.001-08:00</published><updated>2010-11-15T08:59:21.381-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForms] Sumar TextBox asociados a CheckBox</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El ejemplo demuestra como sumar los controles TextBox que están asociados al un checkbox, sumando solo aquellos marcados.&lt;/p&gt;  &lt;p&gt;Es importante notar como la propiedad Tag del control checkbox posee el nombre del TextBox relacionado, es por medio de esta propiedad que se puede buscar el control por su nombre para sumarlo&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="200"&gt;[C#]          &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/RelacionarControles/[csharp]%20RelacionarControles.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;        &lt;td valign="top" width="200"&gt;[VB.NET]          &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/RelacionarControles/[vb.net]%20RelacionarControles.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-2985380683237757454?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/2985380683237757454/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=2985380683237757454' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2985380683237757454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2985380683237757454'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/11/winforms-sumar-textbox-asociados.html' title='[WinForms] Sumar TextBox asociados a CheckBox'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7252818193458708415</id><published>2010-11-14T19:54:00.001-08:00</published><updated>2010-11-14T19:54:03.416-08:00</updated><title type='text'>[Reporting Service] Campo Imagen desde un DataSet Tipado</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El siguiente ejemplo explica como vincular un campo imagen que se obtiene desde una base de datos.&lt;/p&gt;  &lt;p&gt;En este caso se utiliza un reporte de Reporting Service en modo local, por eso se cuanta integrado al VS un archivo de extensión .rdlc&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definición de los datos&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Para asignar los datos se ha creado un DataSet Tipado, con dos DataTable, uno que contendrá un campo que representa el logo de la compañía, el segundo DataTable tendrá los datos del listado.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen4.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definición del campo de imagen&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Dentro del reporte se ubico un campo de imagen, pero para que este tome la información provista por el datatable debe definirse algunas propiedades&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen1.jpg" width="507" height="228" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Las 3 dentro de la sección “Data” son las que deben tener nuestra atención.&lt;/p&gt;  &lt;p&gt;La propiedad Source debe asignarse como “Database” para que tome la info del datatable, y es recomendable además asignar el MiMeType para que sepa con que formato vendrá la imagen.&lt;/p&gt;  &lt;p&gt;Pero la propiedad algo mas compleja de definir es “Value”, ya que esta requiere de una expression&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen2.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;Se puede hacer uso de la ventana que ayude con esta tarea:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/imagen3.jpg" width="478" height="451" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;En este caso se seleccionando del primer Data Source de Empresa el campo de Logo, pero se hará uso de método First() para tomar solo un valor del mismo, ya que la imagen es solo una.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="200"&gt;[C#]         &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/[csharp]%20ReportingImage.zip" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;        &lt;td valign="top" width="200"&gt;[VB.NET]         &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ReportingService/ImageLogo/[vb.net]%20ReportingImage.zip" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7252818193458708415?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7252818193458708415/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7252818193458708415' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7252818193458708415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7252818193458708415'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/11/reporting-service-campo-imagen-desde-un.html' title='[Reporting Service] Campo Imagen desde un DataSet Tipado'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-2610770375996769685</id><published>2010-11-11T19:15:00.001-08:00</published><updated>2010-11-11T19:15:02.786-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Contar Palabras</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El objetivo del articulo es demostrar como Linq puede ayudar en una tarea tan simple como el trabajo con cadenas de texto.&lt;/p&gt;  &lt;p&gt;En el ejemplo se contaran las palabras de un Textbox que sea coincidentes, al encontrar una repetida se mantendrá la cuenta y se mostrara al final en una lista&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:e97f27ce-4a38-4ee0-90ba-e96567eda159" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void contar_Click(object sender, EventArgs e)
{
    //separo cada palabra
    string[] palabras = txtTexto.Text.Split(' ');

    //agrupo las palabras que se repiten 
    //y filtro solo las que tengan mas de una coincidencia
    var query = from item in palabras
                group item by item into g
                where g.Count() &amp;gt; 1
                select new
                {
                    Palabra = g.Key,
                    Cantidad = g.Count()
                };

    //limpio los items previos de la lista
    lvListado.Items.Clear();

    //agrego el resultado al listado
    foreach (var item in query)
    {
        ListViewItem row = new ListViewItem(item.Palabra);
        row.SubItems.Add(item.Cantidad.ToString());

        lvListado.Items.Add(row);
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/ContarPalabras/ContarPalabras.zip" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-2610770375996769685?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/2610770375996769685/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=2610770375996769685' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2610770375996769685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2610770375996769685'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/11/contar-palabras.html' title='Contar Palabras'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-2608055799022043769</id><published>2010-11-03T20:32:00.001-07:00</published><updated>2011-06-26T07:03:22.865-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>[ASP.NET][jQuery] Validación sin Postback</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Uno de los principales problemas en un desarrollo web es implementar una confirmación del usuario, especialmente cuando estas necesitas ser realizadas en el servidor.&lt;/p&gt;  &lt;p&gt;Acciones implementadas en botones que requieren de una conformación del usuario puede ser un problema, ya que estas necesitan ir al servidor, aplicar la validación, retornar o registrar el script que mostraría al usuario el mensaje javascript de confirmación y ante la aceptación, realizar nuevamente el postback al servidor&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Solución&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El uso de jquery puede aportarnos un alivio a esta problemática, concretamente con el uso de la funcionalidad de $.ajax, el cual no permitirá invocar funcionalidad del servidor para realizar validaciones sin postback de la pagina&lt;/p&gt;  &lt;p&gt;El resultado de la validación podrá ser validado y allí mismo mostrar el mensaje de confirmación, si este es aceptado se continua con la operación normalmente.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Ejemplo de código&lt;/strong&gt;&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="200"&gt;[C#]          &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ASP.NET/ValidacionesSinPostBack/[csharp]ConfirmarValidacion.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;        &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-2608055799022043769?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/2608055799022043769/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=2608055799022043769' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2608055799022043769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2608055799022043769'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/11/aspnetjquery-validacion-sin-postback.html' title='[ASP.NET][jQuery] Validación sin Postback'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-3808771786778064239</id><published>2010-10-02T17:15:00.000-07:00</published><updated>2010-10-16T17:17:47.564-07:00</updated><title type='text'>Microsoft MVP C# 2010</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Aun no puedo creerlo, cuando me llego el mail informándome del nombramiento, por poco se me sale el corazón de la emisión. &lt;/p&gt;  &lt;p&gt;No puedo dejar de dar agradecer: &lt;/p&gt;  &lt;p&gt;- &lt;a href="http://juank.black-byte.com/" target="_blank"&gt;Juan Carlos Ruiz&lt;/a&gt;, sin el apoyo que me brindo desde un primer momento no lo habría conseguido, fue quien me permitió ser parte del grupo de moderadores de los foros de MSDN, además de presentar el reporte de actividades en cada nominación a MVP. &lt;/p&gt;  &lt;p&gt;- &lt;a href="http://ingphillip.wordpress.com/" target="_blank"&gt;Felipe Sotelo&lt;/a&gt;, con un gesto simple como ser un post en el foro de forma espontanea, logro tanto. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/9142c889-e943-4cca-a3d6-6f7b01d7ebde"&gt;http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/9142c889-e943-4cca-a3d6-6f7b01d7ebde&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;A todos los que en este post volcaron sus comentarios, un agradecimiento enorme, la verdad me dejaron sin palabras, la fuerza allí expresadas en cada respuesta es invaluable. &lt;/p&gt;  &lt;p&gt;- &lt;a href="http://blogs.msdn.com/b/mvplead/" target="_blank"&gt;Fernando García Loera&lt;/a&gt; y al comité evaluador, quienes han depositado su confianza al otorgarme este nombramiento.&lt;/p&gt;  &lt;p&gt;Espero estar a la altura de lo que un MVP representa, todavía me falta un camino muy largo por recorrer y mucha tecnología por descubrir, pero creo que voy por la senda correcta. &lt;/p&gt;  &lt;p&gt;Por si le interesa les dejo el link a mi perfil: &lt;/p&gt;  &lt;p&gt;&lt;a title="https://mvp.support.microsoft.com/profile/Leandro.Tuttini" href="https://mvp.support.microsoft.com/profile/Leandro.Tuttini"&gt;https://mvp.support.microsoft.com/profile/Leandro.Tuttini&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;También podrán ver este mismo agradecimiento publicado en el foro:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/d52d3fce-52c3-416a-a979-37934a8db491" href="http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/d52d3fce-52c3-416a-a979-37934a8db491"&gt;http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/d52d3fce-52c3-416a-a979-37934a8db491&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-3808771786778064239?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/3808771786778064239/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=3808771786778064239' title='13 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3808771786778064239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/3808771786778064239'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/10/microsoft-mvp-c-2010.html' title='Microsoft MVP C# 2010'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-2328663046541017302</id><published>2010-09-19T19:47:00.001-07:00</published><updated>2010-12-26T18:46:34.562-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ADO.NET'/><title type='text'>[ADO.NET] Filtrar rango de fechas</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Uno de los principales problemas que se pueden encontrar cuando se confecciona una consulta es el trabajo con campos del tipo fecha.&lt;/p&gt;  &lt;p&gt;Por lo general estos campos no solo persisten la fecha, sino que también registran la hora.&lt;/p&gt;  &lt;p&gt;Es por eso que ciertas consultas pueden no retornar los valores deseados, aunque se este definiendo correctamente los parámetros de la consulta.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Planteo del problema&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Para demostrar el problema se plantea el log de actividades del usuario, en donde se registras las acciones que este tiene en la aplicación. Pero el problema se presenta cuando se necesita consultar estos registros para analizar las acciones realizadas en un determinado rango de días, por supuesto se quiere ver los días completo, y es aquí donde nos encontramos con el inconveniente.&lt;/p&gt;  &lt;p&gt;Si se analiza los registros de la tabla se ve que el registro de la fecha no solo usa el día, sino también la hora.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen1.jpg" width="496" height="278" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;La aplicación de ejemplo cuenta con dos opciones de búsqueda, pero en este primer análisis nos centraremos en el primer botón de nombre “Buscar (sin usar CONVERT)”, al utilizarlo veremos el resultado de la imagen:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen2.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen2.jpg" width="408" height="316" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Pero con solo comparar los registros obtenido con los que se encuentran en la tabla, nos damos cuenta que hay un problema, esta retornando menos filas de las esperadas, la pregunta seria, porque sucedió esto ?&lt;/p&gt;  &lt;p&gt;El código utilizado para obtener los registro es el siguiente :&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:aac3bab7-1442-4f0a-8d64-fb5eb30c7a61" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static LogActividades.RegistroActividadesDataTable GetFilterByDateRange(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string sql = @&amp;quot;SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE fecharegistro &amp;gt;= @desde AND fecharegistro &amp;lt;= @hasta&amp;quot;;

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue(&amp;quot;@desde&amp;quot;, desde);
        cmd.Parameters.AddWithValue(&amp;quot;@hasta&amp;quot;, hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);

    }

    return dt;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El parámetro de la fechas toma el valor directo de los controles que estan en la pantalla, pero esta fecha aunque no se conozca también lleva consigo un componente de hora, que puede ser apreciado si se detiene el código y se analiza el valor:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen3.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen3.jpg" width="468" height="119" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;El tipo de dato DataTime, lleva una hora aunque esta no se especifique concretamente.&lt;/p&gt;

&lt;p&gt;Esto aclara bastante el porque la consulta arroja menos ítems de los esperados, resulta que esta quitando aquellos registros en donde la hora sea menos a las 12 AM&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución del problema&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La solución al problema se obtiene usando una funciona en la query, que aplicada sobre los campos de fecha en el filtro quiten la componente de la hora. En realidad no remueve la hora, sino que la normaliza para que esta también tenga las 12 AM, por lo tanto el filtro no descartara ningún registro.&lt;/p&gt;

&lt;p&gt;Para esto se hará uso de la función CONVERT, la cual permite convertir entre tipos de datos.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msdn.microsoft.com/es-es/library/ms187928(SQL.90).aspx" target="_blank"&gt;CAST y CONVERT (Transact-SQL)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Si se ejecuta una consulta usando esta función se podría apreciar lo comentado mas arriba:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen4.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen4.jpg" width="379" height="350" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Las fechas de los registros dejan de tener la hora original, ahora todos presentan las 12 AM, esto es justamente lo que ayudara en el filtro.&lt;/p&gt;

&lt;p&gt;Entonces si ahora se aplica lo comentado al código se obtiene el resultado esperado (usar el botón “Buscar (usando CONVERT)”):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen5.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/imagen5.jpg" width="433" height="338" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Para que esto resulte se utilizo el siguiente código:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:00589fba-a97f-465d-828a-04e22a48295c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static LogActividades.RegistroActividadesDataTable GetFilterByDateRangeRemoveHour(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string sql = @&amp;quot;SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE CONVERT(smalldatetime, CONVERT(char(10), fecharegistro, 103), 103)  &amp;gt;= @desde 
                        AND CONVERT(smalldatetime, CONVERT(char(10), fecharegistro, 103), 103) &amp;lt;= @hasta&amp;quot;;

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue(&amp;quot;@desde&amp;quot;, desde);
        cmd.Parameters.AddWithValue(&amp;quot;@hasta&amp;quot;, hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);
    }

    return dt;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;El uso del CONVERT aplicado en los campo de “fecharegistro”, es quien quita la componente de la hora y permite aplicar el filtro correctamente.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternativa usando Between&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Como alternativa al método anterior se podría lograr usando el Between en la query para filtrar por el rango de fechas.&lt;/p&gt;

&lt;p&gt;En el Form2 del ejemplo se podrá encontrar la implementación de este caso.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:409d1796-37d4-4fc9-a300-4ebe71f8588a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static LogActividades.RegistroActividadesDataTable GetFilterByDateRangeWithBetween(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {

        string sql = @&amp;quot;SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE CAST(CONVERT(CHAR(8), fecharegistro, 112) AS INT) BETWEEN CAST(CONVERT(CHAR(8), @desde, 112) AS INT) 
                                    AND CAST(CONVERT(CHAR(8), @hasta, 112) AS INT)&amp;quot;;

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue(&amp;quot;@desde&amp;quot;, desde);
        cmd.Parameters.AddWithValue(&amp;quot;@hasta&amp;quot;, hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);
    }

    return dt;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La lógica principal se encontrara en el método creado para realizar la consulta, allí se ha utilizado el BETWEEN para definir el filtro entre un rango, pero este tienen un problema requiere de un formato especial que justamente el CONVERT (con el código 112) nos proporciona.&lt;/p&gt;

&lt;p&gt;Esta conversión lleva la fecha al formato yyyyMMdd, o sea si tenemos la fecha 10/06/2010, será formateada a 20100610, claramente un numero, pero primero deberá pasar por un CHAR para adecuar la conversión del formato de la fecha y luego si será convertido a un valor numérico.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de Código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El código publicado fue desarrollado con visual Studio 2008.&lt;/p&gt;

&lt;p&gt;La base de datos utilizada es el Sql Server Express 2008.&lt;/p&gt;

&lt;p&gt;En el proyecto encontraran una carpeta de nombre “Script” que contiene el archivo .sql que podrán usar en el “Sql Server Management Studio” para crear la estructura de la base de datos en caso de tener problemas con el archivo .mdf&lt;/p&gt;

&lt;p&gt;En caso de usar la base de datos integrada al servicio de Sql Server, se deberá cambiar la cadena de conexión definida en el App.config&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/[csharp]FiltrarRangoFechas.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ADO.NET/FiltrarRangoFechas/[vb.net]FiltrarRangoFechas.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-2328663046541017302?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/2328663046541017302/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=2328663046541017302' title='24 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2328663046541017302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/2328663046541017302'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/09/adonet-filtrar-rango-de-fechas.html' title='[ADO.NET] Filtrar rango de fechas'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7335086878908806624</id><published>2010-09-18T10:16:00.001-07:00</published><updated>2010-10-03T19:09:16.874-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForms] - Mover UserControl</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El objetivo de este articulo será el revisar algunos aspectos en la creación de User Controls, como ser propiedades, eventos, definición de argumentos en los eventos, propiedades con enumeración.&lt;/p&gt;  &lt;p&gt;Consistirá en un control en donde se pueda definir una imagen y a esta asociarle un texto descriptivo, la idea es que el usuario pueda mover el control por la pantalla para posicionarlo de forma correcta.&lt;/p&gt;  &lt;p&gt;Contara con al opción para cambiar el texto del label en runtime, además de permitir asignar un estado que bloquear la movilidad. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Definición de las propiedades&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El control contara con propiedades que definen estados y aspectos visuales, también habrá otras que solo podrán ser usadas solo desde código, como ser el “HasImage”, la cual indica si hay una imagen asociada al control, esta ha sido marcada con el atributo “Browsable” en falso para evitar que sea visualizada en la cuadro de propiedades.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:73de08ea-febd-46b0-9b24-5055dac89563" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;[Browsable(false)]
public bool HasImage
{
    get { return _image != null; }
}


private Image _image;

[Browsable(true)]
[Category(&amp;quot;Custom&amp;quot;)]
public Image Image 
{
    get { return _image; }
    set { _image = value; }
}

private string _texto;

[Browsable(true)]
[Category(&amp;quot;Custom&amp;quot;)]
public string Texto
{
    get { return _texto; }
    set 
    { 
        _texto = value;
        lblMensaje.Text = _texto;
    }
}


private EnumEstado _estado;

[Browsable(true)]
[Category(&amp;quot;Custom&amp;quot;)]
public EnumEstado Estado
{
    get { return _estado; }
    set
    {
        _estado = value;
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;También se definen el atributo “Category”, para que sean agrupadas todas juntas cuando se visualicen en el cuadro de propiedades, lo cual facilita la búsqueda.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evento MouseMove&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El permitir que un control tenga la capacidad de movimiento, no es tan directo como parece, con solo asignar el evento MouseMove en el formulario no alcanza para que esto funcione. &lt;/p&gt;

&lt;p&gt;O sea hacer esto, no es suficiente:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:114cd456-c3fc-4b11-825b-7985da0d9261" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public void testUserControl1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        TestUserControl us = sender as TestUserControl;
        if (us != null &amp;amp;&amp;amp; us.HasImage)
        {
            us.Left += e.X;
            us.Top += e.Y;
        }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Si bien al arrastrar el mouse en las zonas libres del User Control se lanzara el evento, si por casualidad el puntero del mouse se sitúa por sobre un control definido en el User Control, el evento se produce en el control interno, sin ser elevado hacia el formulario.&lt;/p&gt;

&lt;p&gt;Pero este problema tiene solución, y consiste en atrapar los eventos de los controles internos del User Control que se esta desarrollando y elevar el evento hacia afuera:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:52818d8d-c7fa-4216-96a9-3c6e5a087643" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public new event MouseEventHandler MouseMove;

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (this.Estado == EnumEstado.Inactivo)
        return;

    if (this.MouseMove != null)
        this.MouseMove(this, e);
}

private void label1_MouseMove(object sender, MouseEventArgs e)
{
    if (this.Estado == EnumEstado.Inactivo)
        return;

    if (this.MouseMove != null)
        this.MouseMove(this, e);
}

protected override void OnMouseMove(MouseEventArgs e)
{
    if (this.Estado == EnumEstado.Inactivo)
        return;

    if (this.MouseMove != null)
        this.MouseMove(this, e);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La primer línea de este código define el evento MouseMove, pero hay que remarcar que el User Control de por si ya posee este evento lo cual trae conflicto al definir uno propio, es por eso que se usa el modificado “new” en la declaración, allí se esta forzando una redefinición del evento.&lt;/p&gt;

&lt;p&gt;El ultimo método donde se realiza un override del OnMouseMove, permite tomar la acción cuando el puntero del mouse se sitúa en una zona libre del User Control, y de esta forma agregarle lógica especializada, como es en este caso que al esta en un estado Inactivo no permita el movimiento.&lt;/p&gt;

&lt;p&gt;Los dos métodos restantes permiten capturar los eventos MouseMove de los controles internos, en este caso un PictureBox y un Label, también se la aplica cierta lógica de estados y se lanza el evento hacia el formulario, pero en este caso no se usa un override, sino que se adjunta el evento en la inicialización del User Control, asignado el handler del evento:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:79061b13-438e-467e-87e6-51539465883a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public TestUserControl()
 {
     InitializeComponent();

     pictureBox1.MouseMove += new MouseEventHandler(pictureBox1_MouseMove);
     lblMensaje.MouseDown += new MouseEventHandler(label1_MouseMove);
     
     ModoEdicion(false);

     this.Estado = EnumEstado.Activo;
     this.Texto = this.Name;

 }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cambio del Texto&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La acción de cambio de Texto implica dos operaciones, manejo de visibilidad de los controles, el envió del evento informando del cambio al formulario.&lt;/p&gt;

&lt;p&gt;Para llevar a acabo esta operación se contara un con menú contextual que será desplegado al realizar click con el botón derecho del mouse sobre el User Control.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:1abd328a-8c9b-47c0-901d-834863f147a0" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void ModoEdicion(bool modo)
{
    txtMensaje.Visible = modo;
    mnuAceptar.Visible = modo;
    mnuAceptar.Visible = modo;
    mnuCancelar.Visible = modo;

    lblMensaje.Visible = !modo;
    mnuCambiarTexto.Visible = !modo;
}

private void mnuCambiarTexto_Click(object sender, EventArgs e)
{
    ModoEdicion(true);

    txtMensaje.Text = _texto;
    txtMensaje.Select();
    txtMensaje.SelectAll();
}

private void mnuAceptar_Click(object sender, EventArgs e)
{
    this.Texto = txtMensaje.Text;

    ModoEdicion(false);

    if (CambioMensaje != null)
        CambioMensaje(this, new MensajeEventArgs(_texto));
}

private void mnuCancelar_Click(object sender, EventArgs e)
{
    ModoEdicion(false);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El primer método simplemente cumple la función de implementar como el cambio de estado afecta a la visibilidad de los controles, inicialmente se visualiza lo escrito en el Label, y una modalidad de edición se despliega el TextBox para ingresar la nueva descripción.&lt;/p&gt;

&lt;p&gt;Los demás eventos pertenecen a los ítems del menú, que realizaran las acciones necesarias para el edición, por ejemplo la opción “mnuAceptar”, no solo asigna los escrito a la propiedad, sino que además lanza un evento informando al formulario que ha cambiado el mensaje asignado al control.&lt;/p&gt;

&lt;p&gt;El formulario por su parte se adjunta al evento expuesto:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b3256291-ec50-4df0-8a36-8be4a4de0cf8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void Form1_Load(object sender, EventArgs e)
{
    testUserControl1.CambioMensaje += new CambioMensajeEventHandler(testUserControl1_CambioMensaje);
}

void testUserControl1_CambioMensaje(object sender, MensajeEventArgs e)
{
    TestUserControl usrcontrol = sender as TestUserControl;

    if (usrcontrol != null)
        MessageBox.Show(string.Format(&amp;quot;El texto ha cambiado: {0}&amp;quot;, e.Texto));
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Algo interesante es como se hace uso del argumento “e” den método del evento para tomar el valor ingresado en el control y desplegarlo en el MessageBox, este se logra mediante la definición de un EventArgs creado especialmente para este fin.&lt;/p&gt;

&lt;p&gt;La declaración del evento se logra mediante dos simples líneas:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:056a4d63-7ba2-4bc0-b305-f1ca8ce403d1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public delegate void CambioMensajeEventHandler(object sender, MensajeEventArgs e);

public partial class TestUserControl : UserControl
{

	public event CambioMensajeEventHandler CambioMensaje;
		
	.
	.
	.
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Pero como comentamos el delegate hace uso de un argumento en el evento especial, el cual se define al final del control:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:7e78da60-92a7-4079-b55a-f2d20ae868da" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class MensajeEventArgs : EventArgs
{
    public MensajeEventArgs(string texto)
    {
        _texto = texto;
    }

    private string _texto;

    public string Texto
    {
        get { return _texto; }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de código &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El código fue desarrollado usando Visual Studio 2008. &lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/MoverUserControl/[csharp]UserControlDragDrop.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET]
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/WinForms/MoverUserControl/[vb.net]UserControlDragDrop.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7335086878908806624?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7335086878908806624/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7335086878908806624' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7335086878908806624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7335086878908806624'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/09/winforms-mover-usercontrol.html' title='[WinForms] - Mover UserControl'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7754195201107118032</id><published>2010-08-29T20:13:00.001-07:00</published><updated>2010-08-29T20:14:28.986-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='WinForm'/><title type='text'>[WinForms] - Abrir formulario modal en el evento Load</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En algunas situaciones puede ser necesario realizar la apertura de alguna ventana nueva en forma modal desde un lugar algo particular, como es el evento Load del formulario.&lt;/p&gt;  &lt;p&gt;Cualquier proceso que bloque la terminación completa de este evento impedirá que se muestre el formulario que se esta abriendo hasta tanto no se cierre el que se encuentra visualmente activo.&lt;/p&gt;  &lt;p&gt;Por ejemplo, si en el código utilizan algunas líneas como estas:&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:06e9ed14-0ac4-4e96-a4d4-38e349ddd621" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void Form2_Load(object sender, EventArgs e)
{
    Form3 frm = new Form3();
    frm.ShowDialog();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;notaran el efecto que se comenta, en donde el Form2 no se visualiza, pero si el Form3, recién cuando este ultimo es cerrado el evento Load termina y se despliega el Form2.&lt;/p&gt;

&lt;p&gt;Para evitar este efecto hay algunas técnicas que pueden aplicarse, pero básicamente lo que buscan es realizar la apertura del formulario modal un tiempo después de concluir el evento Load.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Utilizando las API de Windows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mediante el uso de un mensaje a las API se puede enviar un mensajes al proceso de Windows, atapándolo y lanzando allí la ventana. &lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b967eb92-6b04-4e6a-af3f-8858d69fddb9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class Form2 : Form
{
    
    [DllImport(&amp;quot;user32.dll&amp;quot;, SetLastError = true)]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);


    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
        PostMessage(this.Handle, 7000, 0, 0);
    }


    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 7000)
        {
            Form3 frm = new Form3();
            frm.ShowDialog();

            m.Result = (IntPtr)0;
            return;
        }

        base.WndProc(ref m);
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/AbrirFormEventoLoad/[csharp]AbrirFormEventoLoad^_WinAPI.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Utilizando el Timer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta otra alternativa declara en muy pocas líneas un objeto Timer, en donde el uso de métodos anónimos permite definir el cuerpo del evento Tick que será lanzado luego de 1 ms, mas que suficiente para que el evento Load termine de ejecutarse.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:a6c52a75-e81a-4f97-8489-714771b1cd81" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {

        Timer timer = new Timer();
        timer.Interval = 1;
        timer.Tick += delegate(object s, EventArgs eventarg)
        {
            ((Timer)s).Stop();
            Form3 frm = new Form3();
            frm.ShowDialog();
        };
        timer.Start();

    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;La idea es esta alternativa es mostrar además que no es necesario definir evento en métodos separados que harían engorrosa esta implementación, aquí en un bloque de unas pocas líneas se logra el efecto deseado.&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-b30e32da2150df24.office.live.com/embedicon.aspx/Foro/WinForm/AbrirFormEventoLoad/[csharp]AbrirFormEventoLoad^_Timer.zip" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El efecto que debe lograrse es el no bloqueo del evento Load del formulario, estas dos técnicas lo logran, pero seguramente se preguntaran porque no se han usando Thread.&lt;/p&gt;

&lt;p&gt;El problema surge porque lo Thread generan un hilo independiente evitando que los formularios actúen como modales, si el Form3 es abierto dentro de un nuevo hilo este ya no seria modal, sino que se abriría como una ventana común a la cual se le aplico el método Show()&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7754195201107118032?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7754195201107118032/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7754195201107118032' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7754195201107118032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7754195201107118032'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/08/winforms-abrir-formulario-modal-en-el.html' title='[WinForms] - Abrir formulario modal en el evento Load'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-6213864935112103855</id><published>2010-08-29T17:47:00.001-07:00</published><updated>2010-11-22T16:42:50.207-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='N-Tier'/><title type='text'>[N-Tier] Desarrollo en capas – Transformación de entidades en la Capa de Negocio – Parte 4</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Cuando se desarrolla aplicaciones muy centrada en los datos suele ocurrir que el ida y vuelta de los datos por las capas resulte muy directo, notando que la capa de negocio prácticamente no aporta un valor relevante.&lt;/p&gt;  &lt;p&gt;En la mayoría de los casos cuando se confeccionan ABM de entidades sin mayor complejidad, la capa de negocio suele convertirse en un simple pasamanos de entidades y listas sin aportar mayor utilidad, esto puede ser cierto en algunas situaciones, pero igualmente esta capa es necesaria para seguir con una arquitectura adecuada, ya que uno nunca sabe cuando será necesario adaptar la información para disponerlos en un formato distinto.&lt;/p&gt;  &lt;p&gt;En este articulo analizaremos la importante la capa de negocio y como puede ser útil para transformar la estructura de los datos, adecuándola a la necesidad de la presentación.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Ejemplo propuesto&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Para realizar la demostración se atacara un punto claramente poco practico a la hora de ser usada la aplicación.&lt;/p&gt;  &lt;p&gt;Este tiene que ver con la búsqueda de un determinado tema, en la grilla de compra.&lt;/p&gt;  &lt;p&gt;Actualmente se agrega una nueva línea y se dispone un combo en la celda para seleccionar un tema, pero esto es poco practico teniendo en cuenta la cantidad de temas disponibles.&lt;/p&gt;  &lt;p&gt;Una solución a este problema podrías ser representar la selección en forma de árbol, ya que los datos que disponemos implican que los temas se asocian a Álbumes y Artistas, otorgando una relación jerárquica.&lt;/p&gt;  &lt;p&gt;El problema presente aquí es que los datos, tan cual podrían tomarse de la base de datos, serán devueltos como registros sin estructura que permita una simple conversión para ser representada en el control TreeView en el formulario.&lt;/p&gt;  &lt;p&gt;Es aquí donde entra en jugo la capa de negocio como mediador / adaptador de la información, facilitando la integración entre las capas.&lt;/p&gt;  &lt;p&gt;La capa de datos devolverá los registros planos sin estructura, pero la presentación necesita de una jerarquía, es aquí donde la capa de negocio realizaría la transformación, con la ayuda de Linq.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Estructura de los datos&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En la base de datos se cuenta con la siguiente estructura de tablas:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/imagen1.jpg" target="_blank"&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/imagen1.jpg" width="360" height="328" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Recuperar los datos y armar la jerarquía&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Al obtener información proveniente de las tablas esta se estructura en un nivel simple de registros, la información viene de forma plana definida en campos, es por eso que en esta solución se han creado entidades adicionales para trabajar con la información en dos estadios distintos. &lt;/p&gt;  &lt;p&gt;El primero cuando se recupera la información directa de la consulta realizada, la cual mapeara a la entidad representada por TrackHierarchicalEntity, esta contiene las propiedades que definen la relación entre varias entidades:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:207e99d5-8156-4ebf-80f2-0af8dbc28310" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;
public class TrackHierarchicalEntity
{
    public int ArtistId { get; set; }
    public string ArtistName { get; set; }

    public int AlbumId { get; set; }
    public string AlbumTitle { get; set; }
    
    public int TrackId { get; set; }
    public string TrackName { get; set; }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Por otro la se tendrá una estructura diferente de entidades que conformaran la jerárquica de información, es por ello que estas cuentan con propiedades de lista&amp;#160; genérica de su entidad relacionada:&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:02d6b7a4-c2fb-432b-9193-710de9e4c1da" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class ArtistEntity
{
  public int ArtistId { get; set; }
  public string Name { get; set; }

  public List&amp;lt;AlbumEntity&amp;gt; Alumns { get; set; }
}

public class AlbumEntity
{
  public int AlbumnId { get; set; }
  public string Title { get; set; }

  public List&amp;lt;TrackEntity&amp;gt; Tracks { get; set; }
}

public class TrackEntity
{
  public int TrackId { get; set; }
  public string Name { get; set; }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capa de Datos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En la clase TrackDAL, se agrego un nuevos métodos que permitirán obtener la información de forma plana y directa mapeando al entidad uno a uno con la query que se utiliza:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:26fce7d8-2071-4b64-89cf-7b882fd44788" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;TrackHierarchicalEntity&amp;gt; GetAllHierarchical()
{
    List&amp;lt;TrackHierarchicalEntity&amp;gt; list = new List&amp;lt;TrackHierarchicalEntity&amp;gt;(); ;

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string sql = @&amp;quot;SELECT AR.ArtistId, 
                            AR.Name As ArtistName, 
                            A.AlbumId, 
                            A.Title As AlbumTitle, 
                            T.TrackId, 
                            T.Name 
                        FROM Track T 
                            INNER JOIN Album A ON A.AlbumId = T.AlbumId
                            INNER JOIN Artist AR ON AR.ArtistId = A.ArtistId
                        ORDER BY AR.Name, A.Title, T.Name&amp;quot;;

        SqlCommand cmd = new SqlCommand(sql, conn);

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            list.Add(LoadHierarchicalTrack(reader));
        }

    }

    return list;
}

private static TrackHierarchicalEntity LoadHierarchicalTrack(IDataReader reader)
{
    TrackHierarchicalEntity track = new TrackHierarchicalEntity();


    track.ArtistId = Convert.ToInt32(reader[&amp;quot;ArtistId&amp;quot;]);
    track.ArtistName = Convert.ToString(reader[&amp;quot;ArtistName&amp;quot;]);

    track.AlbumId = Convert.ToInt32(reader[&amp;quot;AlbumId&amp;quot;]);
    track.AlbumTitle = Convert.ToString(reader[&amp;quot;AlbumTitle&amp;quot;]);

    track.TrackId = Convert.ToInt32(reader[&amp;quot;TrackId&amp;quot;]);
    track.TrackName = Convert.ToString(reader[&amp;quot;Name&amp;quot;]);


    return track;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La consulta utilizada el INNER JOIN para unir la información de las tres tablas definidas en la base de datos, recuperando la información en una sola consulta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capa de Negocio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sera la responsable de adaptar la información plana devuelta por la Capa de Datos, otorgando una jerarquía.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:6821ccdc-8bab-46a1-8714-7dfb3ac842ba" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;ArtistEntity&amp;gt; GetAllHierarchical()
{
    List&amp;lt;TrackHierarchicalEntity&amp;gt; tracks = TrackDAL.GetAllHierarchical();

    var hierarchicalList = from item in tracks
                            group item by new { item.ArtistId, item.ArtistName } into artist
                            select new ArtistEntity()
                            {
                                ArtistId = artist.Key.ArtistId,
                                Name = artist.Key.ArtistName,
                                Alumns = (from artistitem in artist
                                          group artistitem by new { artistitem.AlbumId, artistitem.AlbumTitle } into album
                                          select new AlbumEntity()
                                          {
                                              AlbumnId = album.Key.AlbumId,
                                              Title = album.Key.AlbumTitle,
                                              Tracks = (from trackitem in album
                                                        select new TrackEntity()
                                                        {
                                                            TrackId = trackitem.TrackId,
                                                            Name = trackitem.TrackName  
                                                        }).ToList() 
                                          }).ToList()

                            };

    return hierarchicalList.ToList();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Linq ayuda mucho en esta operación de transformación, el uso de la sentencia “group by” es muy útil para definir los campos usados en cada nivel.&lt;/p&gt;

&lt;p&gt;En este caso, al posee dos propiedades para la cada entidad se hizo uso de un “group by” junto al “new” para definir una entidad anónima, lo cual permitió tomar la información de la propiedades Key he ir armando las nuevas entidades.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capa de Presentación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El contar con información jerárquicamente adaptada por la capa de negocio facilita enormemente la tarea de creación de los nodos en el TreeView, simplemente se recorre de forma anidada cada nivel y se van creando los nodos.&lt;/p&gt;

&lt;p&gt;Como verán es muy simple:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:866e792f-8fb0-4428-85c3-cab45d9ec391" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void CargarTree()
{
    List&amp;lt;ArtistEntity&amp;gt; lista = TrackBO.GetAllHierarchical();

    foreach (ArtistEntity artist in lista)
    {
        TreeNode nodeArtist = new TreeNode(artist.Name);
        treeTracks.Nodes.Add(nodeArtist);

        foreach (AlbumEntity album in artist.Alumns)
        {
            TreeNode nodeAlbum = new TreeNode(album.Title);
            nodeArtist.Nodes.Add(nodeAlbum);

            foreach (TrackEntity track in album.Tracks)
            {
                TreeNode nodeTrack = new TreeNode(track.Name);
                nodeTrack.Tag = track;
                nodeAlbum.Nodes.Add(nodeTrack);
            }
        }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="402"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/[csharp]Chinook3Capas.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/[vb.net]Chinook3Capas.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt;&amp;#160;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternativa en el trabajo de entidades&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En el ejemplo planteado hasta el momento la entidad &lt;em&gt;TrackHierarchicalEntity&lt;/em&gt; define dos propiedades por cada entidad que involucra en la jerarquía, pero que sucedería si se trata de objetos algo mas complejos.&lt;/p&gt;

&lt;p&gt;Esta es justamente la alternativa que se analizaría en esta sección.&lt;/p&gt;

&lt;p&gt;La entidad usada para definir la estructura con la cual se trabaja el linq cambiara su aspecto:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:03db61f8-4212-4fb9-be90-d415c92e2df1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class TrackHierarchicalEntity
{
    public ArtistEntity Artist { get; set; }

    public AlbumEntity Album { get; set; }

    public TrackEntity Track { get; set; }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Esto impactara también en la capa de datos, mas que nada en la funcionalidad que crea la entidad:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:aaec6a60-72ba-48d5-b98e-dbd91fcb11ba" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;TrackHierarchicalEntity&amp;gt; GetAllHierarchical()
{
    List&amp;lt;TrackHierarchicalEntity&amp;gt; list = new List&amp;lt;TrackHierarchicalEntity&amp;gt;(); ;

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
    {
        conn.Open();

        string sql = @&amp;quot;SELECT AR.ArtistId, 
                            AR.Name As ArtistName, 
                            A.AlbumId, 
                            A.Title As AlbumTitle, 
                            T.TrackId, 
                            T.Name 
                        FROM Track T 
                            INNER JOIN Album A ON A.AlbumId = T.AlbumId
                            INNER JOIN Artist AR ON AR.ArtistId = A.ArtistId
                        ORDER BY AR.Name, A.Title, T.Name&amp;quot;;

        SqlCommand cmd = new SqlCommand(sql, conn);

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            list.Add(LoadHierarchicalTrack(reader));
        }

    }

    return list;
}

private static TrackHierarchicalEntity LoadHierarchicalTrack(IDataReader reader)
{
    TrackHierarchicalEntity track = new TrackHierarchicalEntity();

    track.Artist = new ArtistEntity()
    {
        ArtistId = Convert.ToInt32(reader[&amp;quot;ArtistId&amp;quot;]),
        Name = Convert.ToString(reader[&amp;quot;ArtistName&amp;quot;])
    };

    track.Album = new AlbumEntity()
    {
        AlbumnId =  Convert.ToInt32(reader[&amp;quot;AlbumId&amp;quot;]),
        Title = Convert.ToString(reader[&amp;quot;AlbumTitle&amp;quot;])
    };

    track.Track = new TrackEntity()
    {
        TrackId = Convert.ToInt32(reader[&amp;quot;TrackId&amp;quot;]),
        Name = Convert.ToString(reader[&amp;quot;Name&amp;quot;])
    };

    return track;
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Si bien el query usado para extraer la información continua sin variante, la forma en como se procesa ha cambiado, teniendo que crear instancias por cada entidad que se necesites utilizar.&lt;/p&gt;

&lt;p&gt;En la clase TrackBO el linq usado para armar la estructura no ha sufrido cambios notables en el grueso de la lógica aplicada:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:bb58afb4-2282-4c7d-a9af-5f9cac74c5d0" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static List&amp;lt;ArtistEntity&amp;gt; GetAllHierarchical()
{
    List&amp;lt;TrackHierarchicalEntity&amp;gt; tracks = TrackDAL.GetAllHierarchical();

    var hierarchicalList = from item in tracks
                            group item by item.Artist into artist
                            select new ArtistEntity()
                            {
                                ArtistId = artist.Key.ArtistId,
                                Name = artist.Key.Name,
                                Alumns = (from artistitem in artist
                                          group artistitem by artistitem.Album into album
                                          select new AlbumEntity()
                                          {
                                              AlbumnId = album.Key.AlbumnId,
                                              Title = album.Key.Title,
                                              Tracks = (from trackitem in album
                                                        select new TrackEntity()
                                                        {
                                                            TrackId = trackitem.Track.TrackId,
                                                            Name = trackitem.Track.Name  
                                                        }).ToList() 
                                          }).ToList()

                            };

    return hierarchicalList.ToList();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Pero si hay un detalle no menos a remarcar, en el linq ahora se hacen uso de las propiedades con entidades en las funciones de agrupación, pero estas trabajan por referencia de los objetos, por lo tanto como sabrá linq que entidades son iguales para agruparlas, es aquí donde se genera el principal cambio con la primer parte del articulo.&lt;/p&gt;

&lt;p&gt;Las dos entidades involucradas en la agrupación dentro del linq deberás implementar una interfaz IEquatable&amp;lt;&amp;gt;, la cual permite definir los métodos que permitirán diferenciar una instancia con otra cuando se los necesita comparar.&lt;/p&gt;

&lt;p&gt;Tanto el método Equals() como el GetHashCode() serán invocados por linq en su operación de agrupación, dentro de estos métodos se hacen uso de la propiedades para poder determinar si se trata de la misma entidad o no, de esta forma se evita evita el método estándar cuando no se implemento la interfaz, en donde solo se comparan las referencias de los objetos.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:28110152-0fd0-4fba-9e71-761b259b7217" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class AlbumEntity : IEquatable&amp;lt;AlbumEntity&amp;gt;
{
    public int AlbumnId { get; set; }
    public string Title { get; set; }

    public List&amp;lt;TrackEntity&amp;gt; Tracks { get; set; }


    #region IEquatable&amp;lt;ArtistEntity&amp;gt; Members

    public bool Equals(AlbumEntity other)
    {
        if (Object.ReferenceEquals(other, null)) return false;

        if (Object.ReferenceEquals(this, other)) return true;

        return other.AlbumnId.Equals(this.AlbumnId);
    }

    public override int GetHashCode()
    {
        return this.AlbumnId.GetHashCode() ^ this.Title.GetHashCode();
    }

    #endregion

}


public class ArtistEntity : IEquatable&amp;lt;ArtistEntity&amp;gt;
{
    public int ArtistId { get; set; }
    public string Name { get; set; }

    public List&amp;lt;AlbumEntity&amp;gt; Alumns { get; set; }


    #region IEquatable&amp;lt;ArtistEntity&amp;gt; Members

    public bool Equals(ArtistEntity other)
    {
        if (Object.ReferenceEquals(other, null)) return false;

        if (Object.ReferenceEquals(this, other)) return true;

        return other.ArtistId.Equals(this.ArtistId);
    }

    public override int GetHashCode()
    {
        return this.ArtistId.GetHashCode() ^ this.Name.GetHashCode();
    }

    #endregion

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Si bien esta alternativa no parece muy útil al principio, cuando se trabaja con objetos es muy común encontrarse con situaciones como esta, en donde entidades se encuentren relacionados con otras mas complejas, las cuales hay que trabajarlas y conocer las técnicas que permiten su manipulación.&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/[csharp]Chinook3Capas%20-%20Objetos%20Complejos.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/NTier/Parte4/[vb.net]Chinook3Capas%20-%20Objetos%20Complejos.zip" target="_blank"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Si bien la capa de negocio puede resultar muy útil como intermediario para coordinar operaciones que requiere complejas actualizaciones de datos a distintas tablas, definiendo a su vez todo en una misma transacción, esta es solo la operación común para la cual se usaría esta capa.&lt;/p&gt;

&lt;p&gt;La transformación de información para adaptarla a la presentación es otra de las tantas responsabilidades que puede tener.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-6213864935112103855?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/6213864935112103855/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=6213864935112103855' title='18 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/6213864935112103855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/6213864935112103855'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/08/n-tier-desarrollo-en-capas.html' title='[N-Tier] Desarrollo en capas – Transformación de entidades en la Capa de Negocio – Parte 4'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-5071096274839447343</id><published>2010-08-15T21:00:00.001-07:00</published><updated>2011-06-26T20:13:17.874-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='N-Tier'/><title type='text'>[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 3</title><content type='html'>&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Hemos entrado en un momento clave en la evolución del presente del articulo, aquí es donde concretamente se verán las 3 capas interactuando entre si.&lt;/p&gt;  &lt;p&gt;El mismo fue evolucionado de artículos previos:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2010/06/n-tier-desarrollo-en-capas-ejemplo.html"&gt;[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 1&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2010/07/n-tier-desarrollo-en-capas-ejemplo.html"&gt;[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 2&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;En la ultima oportunidad se había reestructurado la aplicación para que soportara 2 capas, la presentación accedía directo a la de datos para trabajar con las entidades.&lt;/p&gt;  &lt;p&gt;Además se había agregado un proyecto que representa las entidades del negocio, el cual cruzaba todas las capas y era usado como medio de trasporte de datos entre las mismas. Este estaba implementado por medios de dataset tipado para representar las entidades.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Estructura del proyecto&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;La nueva arquitectura agrego un proyecto adicional del tipo “Class Library”, el cual se ubicara en medio de la capa de presentación y la de datos, desacoplándolas.&lt;/p&gt;  &lt;p&gt;Esta nueva capa representara la &lt;a href="http://es.wikipedia.org/wiki/Facade_(patr%C3%B3n_de_dise%C3%B1o)" target="_blank"&gt;fachada&lt;/a&gt; de entrada al dominio de la aplicación, mas adelante cuando se implementen servicio para distribuir la aplicación cumplirá un papel fundamental para aislar el dominio.&lt;/p&gt;  &lt;p&gt;También se reestructuro el proyecto de entidades, ahora ya no se usan dataset tipados para representar las entidades, estas fueron reemplazas por clases custom, es por eso que se verán&amp;#160; nombre como ser:&amp;#160; “CustomerEntity”, “InvoiceEntity”, etc.&lt;/p&gt;  &lt;p&gt;El cambio en las entidades afecto la capa de datos, ya no se usa el DataAdapter para cargar los datatable que representaban a la entidad, fue necesario reemplazarlos por DataReader, estos son óptimos para la lectura secuencial de los registros devueltos por la query, y el armado de las instancias de la entidad.&lt;/p&gt;  &lt;p&gt;La imagen representa los distintos proyectos y como se referencian entre si:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte3/imagen1.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;En esta nueva distribución de capas será imprescindible que la presentación se comunique siempre con la fachada de negocio, la cual abstraerá las operaciones transaccionales, y creara un único punto de entrada al sistema, si bien no se aprecia la importancia de lo dicho con este ejemplo, si a futuro fuera necesario cambiar la presentación, quizás por una web o con WPF, no se perdería todo el trabajo realizado, ya que las reglas de negocio y persistencia quedan intacticas.&lt;/p&gt;  &lt;p&gt;Algo que seguro traer molestia al desarrollar aplicando esta técnica es que la mayoría de las operaciones serán un pasamano por la capa de negocio, esta solo tomara lo que la presentación retorne y lo devolverá ala presentación, sin efectuar ninguna operación en medio, para la mayoría de las operaciones de consulta será así, pero en otras circunstancias se vera la importancia de esta capa, sobre todo al persistir entidades complejas.&lt;/p&gt;  &lt;p&gt;El uso de entidades con clases en lugar de dataset tipados, también aporta una mejora importante, las clases permiten extender funcionalidad y relacionar entidades fácilmente, como ser el caso del calculo de Total en la entidad de facturación.&lt;/p&gt;  &lt;p&gt;A continuación se analizarían las operaciones que han sufrido cambios durante la transformación a las 3 capas.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Grabar/Actualizar un Cliente&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Durante la operación de confirmación de la factura se notara el cambio en la técnica utilizada para persistir la información del cliente, anteriormente desde la presentación se decidía si se actualizaba o insertaba la entidad, ahora es la capa de negocio quien decide que operación debe realizarse.&lt;/p&gt;  &lt;p&gt;[Presentación]&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:d97c98ca-0c6c-4213-989a-9d577cbddbe0" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;if (cliente == null)
    cliente = new CustomerEntity();

cliente.FirstName = txtNombre.Text;
cliente.LastName = txtApellido.Text;
cliente.Company = txtCompa&amp;#195;&amp;#177;ia.Text;
cliente.Address = txtDireccion.Text;
cliente.Email = txtEmail.Text;

cliente = CustomerBO.Save(cliente);
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[Business Layer]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b06dfe13-9f15-4385-8217-d83bd5e8cbbf" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static CustomerEntity Save(CustomerEntity customer)
{

    if (CustomerDAL.Exists(customer.CustomerId))
        return CustomerDAL.Update(customer);
    else
        return CustomerDAL.Create(customer);

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[Data Access]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b1dcf5e5-a4ee-45d6-899b-3c2a3c4c4bf7" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class CustomerDAL
{

    public static bool Exists(int id)
    {
        int nrorecord = 0;

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            conn.Open();

            string sql = @&amp;quot;SELECT Count(*)
                            FROM Customer 
                            WHERE CustomerId = @customerId&amp;quot;;

            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddWithValue(&amp;quot;customerId&amp;quot;, id);

            nrorecord = Convert.ToInt32(cmd.ExecuteScalar());
        }

        return nrorecord &amp;gt; 0;

    }

    public static CustomerEntity Create(CustomerEntity customer)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            string sql = @&amp;quot;INSERT INTO Customer (FirstName, LastName, Company, Address, Email) 
                                VALUES (@firstName, @lastName, @company, @address, @email)
                           SELECT SCOPE_IDENTITY()&amp;quot;;

            SqlCommand cmd = new SqlCommand(sql, conn);

            cmd.Parameters.AddWithValue(&amp;quot;@firstName&amp;quot;, customer.FirstName);
            cmd.Parameters.AddWithValue(&amp;quot;@lastName&amp;quot;, customer.LastName);
            cmd.Parameters.AddWithValue(&amp;quot;@company&amp;quot;, customer.Address);
            cmd.Parameters.AddWithValue(&amp;quot;@address&amp;quot;, customer.Company);
            cmd.Parameters.AddWithValue(&amp;quot;@email&amp;quot;, customer.Email);

            customer.CustomerId = Convert.ToInt32(cmd.ExecuteScalar());
        }

        return customer;
    }

    public static CustomerEntity Update(CustomerEntity customer)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            conn.Open();

            string sql = @&amp;quot;UPDATE Customer SET  
                                        FirstName = @firstName, 
                                        LastName = @lastName, Company = @company, 
                                        Address = @address,
                                        Email = @email
                                WHERE CustomerId = @customerid&amp;quot;;

            SqlCommand cmd = new SqlCommand(sql, conn);

            cmd.Parameters.AddWithValue(&amp;quot;@firstName&amp;quot;, customer.FirstName);
            cmd.Parameters.AddWithValue(&amp;quot;@lastName&amp;quot;, customer.LastName);
            cmd.Parameters.AddWithValue(&amp;quot;@company&amp;quot;, customer.Address);
            cmd.Parameters.AddWithValue(&amp;quot;@address&amp;quot;, customer.Company);
            cmd.Parameters.AddWithValue(&amp;quot;@email&amp;quot;, customer.Email);
            cmd.Parameters.AddWithValue(&amp;quot;@customerid&amp;quot;, customer.CustomerId);


            cmd.ExecuteNonQuery();

        }

        return customer;
    }


}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La capa de negocio valida si la entidad existe o no, y procede a ejecutar la operación correcta para cada caso, usando como identificador el id de la entidad.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proceso de Facturación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Con respecto a la implementación del articulo previo el proceso de facturación sufrió un cambio importante, ya no se envía dos entidades separadas para procesar, las cuales representaban al encabezado de la factura y sus líneas, sino que una única entidad posee una colección o lista genérica vinculada que permite cargar los datos de la asociación.&lt;/p&gt;

&lt;p&gt;[Presentación]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:b1e9bcb8-af1a-47ae-8801-5b2fbbdbb0b1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;#region Creo el Encabezado\Linea de la Factura

InvoiceEntity invoice = new InvoiceEntity();

invoice.CustomerId = cliente.CustomerId;
invoice.InvoiceDate = DateTime.Now.Date;
invoice.BillingAddress = txtDireccion.Text;


foreach (DataGridViewRow row in dgvLineaCompra.Rows)
{
    InvoiceLinesEntity invoiceLine = new InvoiceLinesEntity();

    invoiceLine.TrackId = Convert.ToInt32(row.Cells[&amp;quot;Track&amp;quot;].Value);
    invoiceLine.UnitPrice = Convert.ToDecimal(row.Cells[&amp;quot;PrecioUnitario&amp;quot;].Value);
    invoiceLine.Quantity = Convert.ToInt32(row.Cells[&amp;quot;Cantidad&amp;quot;].Value);

    invoice.Lineas.Add(invoiceLine);
}

InvoiceBO.RegistrarFacturacion(invoice);

#endregion
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;[Business Layer]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:02f3949d-dd95-46d6-973a-70a3386b691c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class InvoiceBO
{
    public static void RegistrarFacturacion(InvoiceEntity invoice)
    {
        //
        // inicializo la transacciones
        //
        using (TransactionScope scope = new TransactionScope())
        {
            //
            // Creo la factura y sus lineas
            //
            InvoiceDAL.Create(invoice);

            //
            // Actualizo el total
            //
            InvoiceDAL.UpdateTotal(invoice.InvoiceId, invoice.Total);
            
            scope.Complete();
        }

    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;[Data Access]&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:4b550726-c864-467e-ad9d-e9274148d754" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class InvoiceDAL
{

    public static void Create(InvoiceEntity invoice)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            conn.Open();
            //
            // Creacion de la Factura
            //
            string sqlFactura = @&amp;quot;INSERT INTO Invoice (CustomerId, InvoiceDate, BillingAddress, Total) VALUES (@customerId, @date, @address, @total)
                       SELECT SCOPE_IDENTITY()&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlFactura, conn))
            {

                cmd.Parameters.AddWithValue(&amp;quot;@customerId&amp;quot;, invoice.CustomerId);
                cmd.Parameters.AddWithValue(&amp;quot;@date&amp;quot;, invoice.InvoiceDate);
                cmd.Parameters.AddWithValue(&amp;quot;@address&amp;quot;, invoice.BillingAddress);
                cmd.Parameters.AddWithValue(&amp;quot;@total&amp;quot;, 0);

                invoice.InvoiceId = Convert.ToInt32(cmd.ExecuteScalar());
            }


            string sqlLineaFactura = @&amp;quot;INSERT INTO InvoiceLine (InvoiceId, TrackId, UnitPrice, Quantity) 
                                        VALUES (@invoiceid, @trackid, @unitprice, @quantity)
                                        SELECT SCOPE_IDENTITY()&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlLineaFactura, conn))
            {

                foreach (InvoiceLinesEntity invoiceLine in invoice.Lineas)
                {
                    //
                    // como se reutiliza el mismo objeto SqlCommand es necesario limpiar los parametros
                    // de la operacion previa, sino estos se iran agregando en la coleccion, generando un fallo
                    //
                    cmd.Parameters.Clear();

                    cmd.Parameters.AddWithValue(&amp;quot;@invoiceid&amp;quot;, invoice.InvoiceId);
                    cmd.Parameters.AddWithValue(&amp;quot;@trackid&amp;quot;, invoiceLine.TrackId);
                    cmd.Parameters.AddWithValue(&amp;quot;@unitprice&amp;quot;, invoiceLine.UnitPrice);
                    cmd.Parameters.AddWithValue(&amp;quot;@quantity&amp;quot;, invoiceLine.Quantity);

                    //
                    // Si bien obtenermos el id de linea de factura, este no es usado
                    // en la aplicacion
                    //
                    invoiceLine.InvoiceLineId = Convert.ToInt32(cmd.ExecuteScalar());
                }

            }

        }

        

    }

    /// &amp;lt;summary&amp;gt;
    /// Actualizacion del Total de la Factura
    /// &amp;lt;/summary&amp;gt;
    public static void UpdateTotal(int idInvoice, decimal total)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            conn.Open();

            string sqlUpdateTotal = @&amp;quot;UPDATE Invoice SET Total = @total WHERE InvoiceId = @InvoiceId&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlUpdateTotal, conn))
            {
                cmd.Parameters.AddWithValue(&amp;quot;@total&amp;quot;, total);
                cmd.Parameters.AddWithValue(&amp;quot;@InvoiceId&amp;quot;, idInvoice);

                cmd.ExecuteNonQuery();
            }
        }
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Un punto importante a remarcar es que ahora la capa de negocio orquesta todas las operaciones y por ende es esta la que lleva la transacción de las entidades, en el artículo anterior se había comentado este punto, justamente porque la presentación no debía ser responsable de asegurar la operación. Es mas a la presentación se le ha quitado la referencia a la librería System.Transactions.&lt;/p&gt;

&lt;p&gt;Durante la transformación se separo una de las funcionalidades correspondiente a al actualización del total de la factura, en el ejemplo previo se hacia la sumatoria mientras se creaba cada línea, y al final se actualizaba la entidad “invoice” con el valore resultante. Ahora esta se realiza en una operación separada y coordinada por la capa de negocio, se toma el id de factura devuelto por la operación anterior, y se sumando los valores de las línea internamente en la propia entidad que representa la factura de cliente, ya que ahora esta posee la colección relacionada, una operación lambda en el método de extensión de suma fue mas que suficiente.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:f60243e3-a06b-4939-a591-608e44fd9c9e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class InvoiceEntity
{
    public InvoiceEntity()
    {
        this.Lineas = new List&amp;lt;InvoiceLinesEntity&amp;gt;();
    }

    public int InvoiceId { get; set; }
    public int CustomerId { get; set; }
    public DateTime InvoiceDate { get; set; }
    public string BillingAddress { get; set; }

    public List&amp;lt;InvoiceLinesEntity&amp;gt; Lineas { get; set; }

    public decimal Total
    {
        get { return this.Lineas.Sum(x =&amp;gt; x.UnitPrice * x.Quantity); }
    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si bien aun quedan muchos puntos por explorar, este simple ejemplo puede servir de guía para comenzar con futuros desarrollos.&lt;/p&gt;

&lt;p&gt;Es cierto que algunos aspectos que podrían haberse implementado, como ser:&lt;/p&gt;

&lt;p&gt;- La entidad InvoiceEntity aun conserva la propiedad CustomerId, cuando debería reemplazarse por una propiedad del tipo CustomerEntity, lo cual no se realizo para no aumentar la complejidad.&lt;/p&gt;

&lt;p&gt;- No se implementaron controles fuerte de errores y como comunicarlos hacia la presentación para ser tratados, este punto se vera en un articulo futuro.&lt;/p&gt;

&lt;p&gt;- No se agregaron reglas de negocio restrictivas, como podría ser, por ejemplo, si el cliente supera tiene un monto determinado de facturas impagas no se permita la facturación en curso.&lt;/p&gt;

&lt;p&gt;En posteriores artículos tratare estos tema con mas detalle, y otros como ser: &lt;/p&gt;

&lt;p&gt;-la utilización de reportadores como Crystal Reports o Reporting Service en aplicación con capas, &lt;/p&gt;

&lt;p&gt;-la creación de repositorios de acceso a datos que soporten distintas base de datos, &lt;/p&gt;

&lt;p&gt;-el cambio de la presentación a un proyecto web para marcar la reutilización, así como también al desconexión de la capas de presentación y negocio mediante servicios para poder hacer uso de un ambiente distribuido.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El proyecto fue desarrollado con Visual Studio 2008&lt;/p&gt;

&lt;p&gt;Se debe tener presente Sql Server 2008 Express para poder acceder a la db integrada al proyecto.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte3/[csharp]Chinook3Capas.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte3/[vb.net]Chinook3Capas.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-5071096274839447343?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/5071096274839447343/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=5071096274839447343' title='30 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5071096274839447343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/5071096274839447343'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/08/n-tier-desarrollo-en-capas-ejemplo.html' title='[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 3'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>30</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7038693224054604832</id><published>2010-08-08T22:05:00.001-07:00</published><updated>2011-06-26T20:32:06.530-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='GridView'/><title type='text'>[GridView] ITemplate – Columnas definidas en runtime</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Suelen darse los escenarios en donde se necesite editar distintas entidades pero haciendo uso de un único control gridview.&lt;/p&gt;  &lt;p&gt;Una respuesta rápida a este problema seria hacer uso de la propiedad AutoGenerateColumns en true, para que los datos que se le proporciona al control defina las columnas que debe mostrar, lo malo de esta opción es que se pierde control sobre la grilla.&lt;/p&gt;  &lt;p&gt;Otra alternativa interesante podría ser el uso de la clase &lt;a href="http://msdn.microsoft.com/es-es/library/system.web.ui.webcontrols.boundfield(VS.80).aspx" target="_blank"&gt;BoundField&lt;/a&gt; con esta seria posible definir columnas en tiempo de ejecución, si bien podría ser la solución en la mayoría de los caso, esta no permite un control total del témplate que se debe usar en al edición de las columnas.&lt;/p&gt;  &lt;p&gt;La solución definitiva al problema esta en la implementación de témplates de columnas, estas clases especializadas contendrán el código del témplate que define, para que esto se posible se necesitara implementar la interfaz &lt;a href="ITemplate " target="_blank"&gt;ITemplate&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;El modelo del ejemplo de código planteado hace referencia a dos listados, uno de notebooks y otro de televisores, ambos con distintas columnas por mostrar, pero haciendo uso de un solo control de grilla y la definición de las columnas de forma explicita en runtime.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Uso del BoundField&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;La definición de las columnas mediante esta clase podrá apreciarse en el formulario de nombre “GridViewBoundField.aspx”&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:5e105b15-6f83-4f49-a031-6440fbdfcbf1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public partial class GridViewBoundField : System.Web.UI.Page
{
    protected void Page_Init(object sender, EventArgs e)
    {
        DefinirColumnasNotebook();
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            GridView1.DataSource = DataAccess.ObtenerListaNotebook();
            GridView1.DataBind();
        }
    }

    private void DefinirColumnasNotebook()
    {
        //
        // Se define el campo dentro de la grilla, 
        // para poder identificar cada item 
        //
        GridView1.DataKeyNames = new string[] { &amp;quot;Id&amp;quot; };

        GridView1.Columns.Clear();

        BoundField tempDesc = new BoundField();
        tempDesc.HeaderText = &amp;quot;Descripcion Producto&amp;quot;;
        tempDesc.DataField = &amp;quot;Descripcion&amp;quot;;
        GridView1.Columns.Add(tempDesc);

        BoundField tempPrecio = new BoundField();
        tempPrecio.HeaderText = &amp;quot;Precio&amp;quot;;
        tempPrecio.DataField = &amp;quot;Precio&amp;quot;;
        GridView1.Columns.Add(tempPrecio);

    }
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Se notara en el código que las columnas son definidas en el evento Page_Init, mientras que los datos son cargadas en el Page_Load&lt;/p&gt;

&lt;p&gt;Esto es porque la definición de las columnas al ser dinámicas se deberán crear cada vez que se realice un postback, mientras que los datos pueden volver a bindear a la grilla, o no, eso dependerá de la funcionalidad que se quiera lograr&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definición de ITemplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La definición de las columnas por medio de la creación de templetes de columnas, podrá apreciarse en el formulario de nombre “GridViewITemplate.aspx”.&lt;/p&gt;

&lt;p&gt;Para poder hacer uso de template de columnas en el control GridView, será necesario la implementación de a interfaz ITemplate, a continuación se vera el código de estas clases:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:9044284e-27d5-47c3-9f08-5389f4038e43" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class GridViewHeaderTemplate : ITemplate
{
    string text;

    public GridViewHeaderTemplate(string text)
    {
        this.text = text;
    }

    public void InstantiateIn(System.Web.UI.Control container)
    {
        Literal lc = new Literal();
        lc.Text = text;

        container.Controls.Add(lc);

    }
}

public class GridViewEditTemplate : ITemplate
{
    private string columnName;

    public GridViewEditTemplate(string columnName)
    {
        this.columnName = columnName;
    }

    public void InstantiateIn(System.Web.UI.Control container)
    {
        TextBox tb = new TextBox();
        tb.ID = string.Format(&amp;quot;txt{0}&amp;quot;, columnName);
        tb.EnableViewState = false;
        tb.DataBinding += new EventHandler(tb_DataBinding);

        container.Controls.Add(tb);
    }

    void tb_DataBinding(object sender, EventArgs e)
    {
        TextBox t = (TextBox)sender;

        GridViewRow row = (GridViewRow)t.NamingContainer;

        string RawValue = DataBinder.Eval(row.DataItem, columnName).ToString();

        t.Text = RawValue;
    }
}

public class GridViewItemTemplate : ITemplate
{
    private string columnName;

    public GridViewItemTemplate(string columnName)
    {
        this.columnName = columnName;
    }
    
    public void InstantiateIn(System.Web.UI.Control container)
    {
        Literal lc = new Literal();

        lc.DataBinding += new EventHandler(lc_DataBinding);

        container.Controls.Add(lc);

    }

    void lc_DataBinding(object sender, EventArgs e)
    {
        Literal l = (Literal)sender;

        GridViewRow row = (GridViewRow)l.NamingContainer;

        string RawValue = DataBinder.Eval(row.DataItem, columnName).ToString();

        l.Text = RawValue;
    }
}


public class GridViewItemCheckTemplate : ITemplate
{
    private string columnName;

    public GridViewItemCheckTemplate(string columnName)
    {
        this.columnName = columnName;
    }

    public bool CanEdit { get; set; }

    public void InstantiateIn(System.Web.UI.Control container)
    {
        CheckBox check = new CheckBox();
        check.ID = string.Format(&amp;quot;chk{0}&amp;quot;, columnName);
        check.Enabled = this.CanEdit;
        check.DataBinding += new EventHandler(check_DataBinding);

        container.Controls.Add(check);

    }

    void check_DataBinding(object sender, EventArgs e)
    {
        CheckBox check = (CheckBox)sender;

        GridViewRow row = (GridViewRow)check.NamingContainer;

        string value = DataBinder.Eval(row.DataItem, columnName).ToString();

        check.Checked = bool.Parse(value);
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Cada una representa un témplate de visualización y edición dentro del Gridview.&lt;/p&gt;

&lt;p&gt;El método principal que debe implementarse es &lt;em&gt;InstantiateIn()&lt;/em&gt;, dentro de este se definirá el o los control que conformen el témplate de columna para el estado especifico.&lt;/p&gt;

&lt;p&gt;Algo que seguramente llamara la atención es el uso del evento DataBinding, el cual es usada para tomar los datos al momento de bindear cada fila de la grilla, este evento será ejecutado tantas veces como filas tenga.&lt;/p&gt;

&lt;p&gt;Estas clases serán usadas para definir cada témplate de columna:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:ff194288-cc73-4fd4-8c7b-3595f156c5dc" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void DefinirColumnasNotebook()
{
    //
    // Se define el campo dentro de la grilla, 
    // para poder identificar cada item 
    //
    GridView1.DataKeyNames = new string[] { &amp;quot;Id&amp;quot; };

    GridView1.Columns.Clear();

    //
    // Columna Descripcion
    //
    TemplateField tempDesc = new TemplateField();
    tempDesc.HeaderTemplate = new GridViewHeaderTemplate(&amp;quot;Descripcion Producto&amp;quot;);
    tempDesc.ItemTemplate = new GridViewItemTemplate(&amp;quot;Descripcion&amp;quot;);
    tempDesc.EditItemTemplate = new GridViewEditTemplate(&amp;quot;Descripcion&amp;quot;);
    GridView1.Columns.Add(tempDesc);

    //
    // Columna Precio
    //
    TemplateField tempPrecio = new TemplateField();
    tempPrecio.HeaderTemplate = new GridViewHeaderTemplate(&amp;quot;Precio&amp;quot;);
    tempPrecio.ItemTemplate = new GridViewItemTemplate(&amp;quot;Precio&amp;quot;);
    tempPrecio.EditItemTemplate = new GridViewEditTemplate(&amp;quot;Precio&amp;quot;);
    GridView1.Columns.Add(tempPrecio);
}

private void DefinirColumnasTelevisores()
{
    GridView1.DataKeyNames = new string[] { &amp;quot;Id&amp;quot; };

    GridView1.Columns.Clear();

    //
    // Columna Descripcion
    //
    TemplateField tempDesc = new TemplateField();
    tempDesc.HeaderTemplate = new GridViewHeaderTemplate(&amp;quot;Descripcion Televidor&amp;quot;);
    tempDesc.ItemTemplate = new GridViewItemTemplate(&amp;quot;Descripcion&amp;quot;);
    tempDesc.EditItemTemplate = new GridViewEditTemplate(&amp;quot;Descripcion&amp;quot;);
    GridView1.Columns.Add(tempDesc);

    //
    // Columna PrecioUnitario
    //
    TemplateField tempPrecio = new TemplateField();
    tempPrecio.HeaderTemplate = new GridViewHeaderTemplate(&amp;quot;Precio Unitario&amp;quot;);
    tempPrecio.ItemTemplate = new GridViewItemTemplate(&amp;quot;PrecioUnitario&amp;quot;);
    tempPrecio.EditItemTemplate = new GridViewEditTemplate(&amp;quot;PrecioUnitario&amp;quot;);
    GridView1.Columns.Add(tempPrecio);

    //
    // Columna EsPlasma
    //
    TemplateField tempEsPlasma = new TemplateField();
    tempEsPlasma.HeaderTemplate = new GridViewHeaderTemplate(&amp;quot;Plasma&amp;quot;);

    GridViewItemCheckTemplate esPlasmaItem = new GridViewItemCheckTemplate(&amp;quot;EsPlasma&amp;quot;);
    tempEsPlasma.ItemTemplate = esPlasmaItem;

    GridViewItemCheckTemplate esPlasmaEdit = new GridViewItemCheckTemplate(&amp;quot;EsPlasma&amp;quot;);
    esPlasmaEdit.CanEdit = true;
    tempEsPlasma.EditItemTemplate = esPlasmaEdit;

    GridView1.Columns.Add(tempEsPlasma);
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Se define tanto el témplate del Ítem, como el de edición y encabezado, usando para ello el témplate que corresponda, se debe tener presente que también el tipo de dato a mostrar influye en la decisión de que témplate utilizar, un ejemplo muy claro lo representa el checkbox que marca si el televisor es de plasma o no, representado por un témplate que justamente dibuja un check en la celda.&lt;/p&gt;

&lt;p&gt;También hay que comentar que no hay una forma única de crear las clases de témplate, estas podrían tomar la info mediante propiedades o pasarlas en el constructor. Un ejemplo de esto es la clase “GridViewItemCheckTemplate” la cual asigna el nombre del campo al cual vincula los datos, pero si debe permitir la edición o no, es asignada mediante una propiedad, tomando un valor por defecto en caso de no asignar valor.&lt;/p&gt;

&lt;p&gt;La edición de un registro en la grilla implica todo un tema:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c2b3334f-4f1f-456a-a907-b12715607062" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
    GridView1.EditIndex = e.NewEditIndex;
    DataBindGrid();
}

protected void GridView1_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
    GridView1.EditIndex = -1;
    DataBindGrid();
}

protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    int Id = Convert.ToInt32(GridView1.DataKeys[e.RowIndex].Value);
    
    GridViewRow row = GridView1.Rows[e.RowIndex];

    if (Session[&amp;quot;datos&amp;quot;] is List&amp;lt;Notebook&amp;gt;)
    {
        Notebook notebookActualizada = (from item in (List&amp;lt;Notebook&amp;gt;)Session[&amp;quot;datos&amp;quot;]
                                         where item.Id == Id
                                         select item).FirstOrDefault();


        TextBox txtDescripcion = row.FindControl(&amp;quot;txtDescripcion&amp;quot;) as TextBox;
        notebookActualizada.Descripcion = Convert.ToString(txtDescripcion.Text);

        TextBox txtPrecio = row.FindControl(&amp;quot;txtPrecio&amp;quot;) as TextBox;
        notebookActualizada.Precio = Convert.ToInt32(txtPrecio.Text);

    }
    else if (Session[&amp;quot;datos&amp;quot;] is List&amp;lt;Televisor&amp;gt;)
    {
        Televisor televisorActualizado = (from item in (List&amp;lt;Televisor&amp;gt;)Session[&amp;quot;datos&amp;quot;]
                                            where item.Id == Id
                                            select item).FirstOrDefault();


        TextBox txtDescripcion = row.FindControl(&amp;quot;txtDescripcion&amp;quot;) as TextBox;
        televisorActualizado.Descripcion = Convert.ToString(txtDescripcion.Text);

        TextBox txtPrecio = row.FindControl(&amp;quot;txtPrecioUnitario&amp;quot;) as TextBox;
        televisorActualizado.PrecioUnitario = Convert.ToInt32(txtPrecio.Text);

        CheckBox chkEsPlasma = row.FindControl(&amp;quot;chkEsPlasma&amp;quot;) as CheckBox;
        televisorActualizado.EsPlasma = chkEsPlasma.Checked;
    }

    GridView1.EditIndex = -1;
    DataBindGrid();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Mediante los evento RowEditing y RowCancelingEdit, se controla que la fila este o no en estado de edición, esto indica a la grilla cuando debe cambiar el témplate de edición que se ha definido.&lt;/p&gt;

&lt;p&gt;El evento RowUpdating actuara cuando se acepta la edición, es en este momento donde controla que tipo de lista se esta visualizando, como primer paso se localiza la entidad dentro de la colección que se había usado para bindear la grilla, para esta tarea se hizo uso de Linq.&lt;/p&gt;

&lt;p&gt;Luego se toma la información de los controles que genero cada template, hay que remarcar en este punto que los template usan internamente el agregado de un prefijo con respecto al tipo de control que agregan, agregando este al nombre de la columna que se le asigno, por ejemplo:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tb.ID = string.Format(&amp;quot;txt{0}&amp;quot;, columnName);&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Esta línea agrega el prefijo “txt” al nombre del campo, es por esto que luego al buscar el control se uso&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TextBox txtDescripcion = row.FindControl(&amp;quot;txtDescripcion&amp;quot;) as TextBox;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;En donde “Descripcion” es el nombre del campo, y “txt” el prefijo.&lt;/p&gt;

&lt;p&gt;Al igual que se hizo con el BoundField, en este caso la definición de las columnas se realiza en el Page_Init&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c274e5b4-c683-474d-8a3e-0e633a059cdb" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;protected void Page_Init(object sender, EventArgs e)
{
    if (Session[&amp;quot;datos&amp;quot;] == null || Session[&amp;quot;datos&amp;quot;] is List&amp;lt;Notebook&amp;gt;)
    {
        DefinirColumnasNotebook();

    }
    else if (Session[&amp;quot;datos&amp;quot;] is List&amp;lt;Televisor&amp;gt;)
    {
        DefinirColumnasTelevisores();
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Session[&amp;quot;datos&amp;quot;] = DataAccess.ObtenerListaNotebook();
        DataBindGrid();
    }
}

private void DataBindGrid()
{
    GridView1.DataSource = Session[&amp;quot;datos&amp;quot;];
    GridView1.DataBind();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Hay un método adicional que por ahí no este tan claro, DataBindGrid(), este método simplemente toma la info de session y bindea la grilla, este cache de información en session me pareció importante ya que por cada accion que se realiza la grilla debe ser bindeada a los datos, lo cual podría producir una sobrecarga de comunicación si en todo momento debe ir controla la db para buscar la información.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplos de Código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El ejemplo fue confeccionado con Visual Studio 2008.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/asp.net/gridview/GridViewTemplateField/[csharp]WebGridViewTemplateField.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/asp.net/gridview/GridViewTemplateField/[vb.net]WebGridViewTemplateField.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7038693224054604832?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7038693224054604832/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7038693224054604832' title='13 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7038693224054604832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7038693224054604832'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/08/gridview-itemplate-columnas-definidas.html' title='[GridView] ITemplate – Columnas definidas en runtime'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-7192383767701688691</id><published>2010-07-18T20:56:00.001-07:00</published><updated>2011-06-26T20:35:58.230-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='N-Tier'/><title type='text'>[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 2</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Este articulo es la continuación de su predecesor:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ltuttini.blogspot.com/2010/06/n-tier-desarrollo-en-capas-ejemplo.html" target="_blank"&gt;[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 1&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Donde se había planteado el hecho de confeccionar todo el código dentro del formularios, sin capas, remarcando las desventajas que esto trae a la reutilización y el mantenimiento de la aplicación.&lt;/p&gt;  &lt;p&gt;En cambio en esta segunda parte se planteara la aplicación agregando una capa adicional, la de datos.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Estructura del proyecto&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En este nueva arquitectura se incluirán dos nuevos proyectos, ambos del tipo “Class Library”, estos compilaran como dll, para ser referenciadas por el proyecto de Presentación. En toda la solución solo habrá un proyecto que genere un ejecutable.&lt;/p&gt;  &lt;p&gt;En la siguiente imagen se grafica la relaciones entre los proyectos, las flechas marcan como se han realizado las referencia entre ellos.&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte2/imagen1.jpg" /&gt; &lt;/p&gt;  &lt;p&gt;El proyecto de entidades es un caso especial que debe analizarse,&amp;#160; en esta oportunidad se ha utilizado DataSet Tipados para crear las clases que representan al negocio.&lt;/p&gt;  &lt;p&gt;Se ha creado un único DataSet con varios DataSet tipados, uno por cada entidad.&lt;/p&gt;  &lt;p&gt;Se podría haber utilizado clases especiales, pero esto se hará en la próxima iteración cuando se separen verdaderamente en 3 capas, ya que el uso de clase requiere cambiar la lógica en como se carga la información, o sea no se podrá usar mas el DataAdapters, sino que se deberá pasar al DataReader, los cuales son óptimos para la carga secuencial de la información.&lt;/p&gt;  &lt;p&gt;La capa de Datos esta formada por una clases estáticas, una por cada entidad que conforma el negocio, se definieron como estática para facilitar el acceso a los métodos, que claramente solo procesan de los datos de la instancias con las cuales trabaja.&lt;/p&gt;  &lt;p&gt;En esta capa hay algo interesante para remarcar, como las líneas de facturación de por si solas no existen sin una factura, las líneas no tienen funcionalidad especifica en la cada de datos, o sea no se persisten por si solas, sino que lo hacen cuando se crea una factura. Esto es interesante ya que remarca fuertemente el modelo del negocio, impidiendo que una línea quede disociada del encabezado de factura, que le da sentido de existencia.&lt;/p&gt;  &lt;p&gt;Las líneas de facturación son persistidas en la misma operación en que se crea la factura, es por eso que en la clase InvoiceDAL, solo esta el método Create(), cuyo objetico es añadir tanto la factura, como sus líneas.&lt;/p&gt;  &lt;p&gt;Este tipo de razonamiento tiene mucho que ver con la notación UML, en donde existe una relación denominada de agregación. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.dcc.uchile.cl/~psalinas/uml/modelo.html" target="_blank"&gt;Modelo de Clases&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;La relación entre las facturas y sus líneas es del tipo Agregación por Valor, o también llamada Composición, en donde el tiempo de vida de las líneas esta condicionado al tiempo en que dure la factura, si esta ultima se elimina las líneas también serán borradas.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Creación de un nuevo ítem en la grilla&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;En el artículo previo se había utilizado uso de un datatable sin tipo para crear dinámicamente una nueva línea de facturación, en cambio esto se ha cambiado adaptando al uso de una clase.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:66234fcc-bee4-46e5-a0fc-05963f3d1471" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;/// &amp;lt;summary&amp;gt;
/// Crea un item de compra en la grilla
/// &amp;lt;/summary&amp;gt;
private void NuevaLinea()
{

    List&amp;lt;DGVLine&amp;gt; newlist = new List&amp;lt;DGVLine&amp;gt;();

    if (dgvLineaCompra.DataSource != null)
    {
        List&amp;lt;DGVLine&amp;gt; list = (dgvLineaCompra.DataSource as List&amp;lt;DGVLine&amp;gt;);

        list.ForEach((item) =&amp;gt;
        {
            newlist.Add((DGVLine)item.Clone());
        });
    }

    newlist.Add(new DGVLine());

    dgvLineaCompra.AutoGenerateColumns = false;
    dgvLineaCompra.DataSource = newlist;

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:2471607a-a5f3-4572-9898-408346fc7db6" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public class DGVLine : ICloneable
{
    public int? Track { get; set; }
    public decimal PrecioUnitario { get; set; }
    public int Cantidad { get; set; }

    #region ICloneable Members

    public object Clone()
    {
        DGVLine item = new DGVLine();

        item.Track = this.Track;
        item.PrecioUnitario = this.PrecioUnitario;
        item.Cantidad = this.Cantidad;

        return item;
    }

    #endregion
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Un detalle importante que implico el cambio del datatable por la clase es la necesidad de clonar los ítems previos que tenia el DataGridView, de no hacerlo el control no detecta las nueva instancia y por lo tanto no se visualizan las nuevas filas agregadas a la grilla.&lt;/p&gt;

&lt;p&gt;Por esta razón se implementa de la interfaz ICloneable. El ForEach() aplicado a cada ítem previo de la grilla es pasado a una nueva lista de ítems que al final es asignada como origen de datos nuevamente.&lt;/p&gt;

&lt;p&gt;El método ForEach() esta usado una expresión lambda para recorrer cada ítem y facilitar la transformación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Carga de la datos en la Presentación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El uso de la capa de acceso a datos permite que la presentación quede muy prolija, con una línea se resuelva la toma de los datos necesarios para desplegar la información al usuario.&lt;/p&gt;

&lt;p&gt;Un ejemplo claro de esta interactividad es presente en la carga de los ítem del combo de la grilla:&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:1def14f0-9077-43cb-9dd0-47124374e56b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void frmCompra_Load(object sender, EventArgs e)
{

    //
    // Cargo los items del combo
    //
    DataGridViewComboBoxColumn comboCol = dgvLineaCompra.Columns[&amp;quot;Track&amp;quot;] as DataGridViewComboBoxColumn;
    comboCol.ValueMember = &amp;quot;TrackId&amp;quot;;
    comboCol.DisplayMember = &amp;quot;Name&amp;quot;;
    comboCol.DataSource = TrackDAL.GetAll();

    //
    // 
    //
    NuevaLinea();
}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;Es rápidamente visible la mejora que implica usar capas, anteriormente en este misma acción se tenia una buena cantidad de código, ahora solo es una llamada a un método GetAll(), de la clase TrackDAL, el cual devolverá un DataTable especializado del tipo TrackDataTable.&lt;/p&gt;

&lt;p&gt;Otro punto importante que recibió una mejora notable es la búsqueda del cliente, tanto al momento de cargar la grilla en el formulario frmBuscarCliente:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:846d94a8-92ff-42c2-9b04-f442df626ad7" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void frmBuscarCliente_Load(object sender, EventArgs e)
{
    //
    // Se carga la lista de clientes
    //
    dgvClientes.AutoGenerateColumns = false;
    dgvClientes.DataSource = CustomerDAL.GetAll();

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Como así también al momento del retorno de la información, al cargar por id la información completa de ese cliente en especial:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:dfdb1d12-5133-4029-a4a1-703a6e3c793c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnBuscarCliente_Click(object sender, EventArgs e)
{
    frmBuscarCliente frm = new frmBuscarCliente();

    if (frm.ShowDialog() == DialogResult.OK)
    {
        //
        // Mantengo la entidad cliente seleccionada global al formulario
        //
        cliente = CustomerDAL.GetById(frm.IdCliente); 

        //
        // muestro en pantalla la info del cliente
        //
        txtId.Text = Convert.ToString(cliente.CustomerId);
        txtNombre.Text = cliente.FirstName;
        txtApellido.Text = cliente.LastName;
        txtDireccion.Text = cliente.Address;
        txtCompa&amp;#195;&amp;#177;ia.Text = cliente.Company;
        txtEmail.Text = cliente.Email; 
    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;El uso de entidades devueltas por la Capa de Datos agrega prolijidad y facilidad en el acceso a los datos, por ejemplo, la funcionalidad de CustomerDAL.GetById() devuelve una objeto de tipo CustomerRow, o sea un solo registro del CustomerDataTable, lo cual permite acceder directo a los campos de la fila, asignando los valores a los controles del formulario, no hace falta recorrer registros, o controlar si hay datos en un datatable, la capa de datos devuelve directamente la entidad filtrando por el Id.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proceso de Facturación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Del proceso de Facturación se destaca la creación de las entidades del encabezado y de las líneas dentro del evento de la presentación. Como se habrá observado, en el código la presentación ha perdido todas las consultas sql que anteriormente se ejecutaban, pasando a formar parte de la capa de datos como especialista en procesar la información.&lt;/p&gt;

&lt;p&gt;La comunicación entre las capas de Presentación y DataAccess se realiza por medio de entidades definidas en el proyecto que lleva el mismo nombre.&lt;/p&gt;

&lt;p&gt;En el proceso de facturación dos entidades son creadas y pasadas por parámetros a la capa de datos para ser procesadas.&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:76856ab9-5f99-4c61-a7ea-9c9e7932cbca" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;private void btnConfirmar_Click(object sender, EventArgs e)
        {
            if (!Validaciones())
                return;

            //
            // inicializo la transacciones
            //
            using (TransactionScope scope = new TransactionScope())
            {

                #region Creo\Actualizo la informacion del cliente

                //
                // si el cliente se ha seleccionado lo actualizo, sino se crea uno nuevo
                //
                if (cliente == null)
                {
                    cliente = (new ChinookEntity.CustomerDataTable()).NewCustomerRow();

                    cliente.FirstName = txtNombre.Text;
                    cliente.LastName = txtApellido.Text;
                    cliente.Company = txtDireccion.Text;
                    cliente.Address = txtCompa&amp;#195;&amp;#177;ia.Text;
                    cliente.Email = txtEmail.Text;

                    cliente = CustomerDAL.Create(cliente);

                }
                else
                {
                    cliente.FirstName = txtNombre.Text;
                    cliente.LastName = txtApellido.Text;
                    cliente.Company = txtCompa&amp;#195;&amp;#177;ia.Text;
                    cliente.Address = txtDireccion.Text;
                    cliente.Email = txtEmail.Text;

                    cliente = CustomerDAL.Update(cliente);
                }
                #endregion

                #region Creo el Encabezado\Linea de la Factura

                ChinookEntity.InvoiceRow invoice = (new ChinookEntity.InvoiceDataTable()).NewInvoiceRow();

                invoice.CustomerId = cliente.CustomerId;
                invoice.InvoiceDate = DateTime.Now.Date;
                invoice.BillingAddress = txtDireccion.Text;


                ChinookEntity.InvoiceLineDataTable invoiceLine = new ChinookEntity.InvoiceLineDataTable();

                foreach (DataGridViewRow row in dgvLineaCompra.Rows)
                {
                    ChinookEntity.InvoiceLineRow invoiceLineRow = invoiceLine.NewInvoiceLineRow();
 
                    invoiceLineRow.TrackId = Convert.ToInt32(row.Cells[&amp;quot;Track&amp;quot;].Value);
                    invoiceLineRow.UnitPrice = Convert.ToDecimal(row.Cells[&amp;quot;PrecioUnitario&amp;quot;].Value);
                    invoiceLineRow.Quantity = Convert.ToInt32(row.Cells[&amp;quot;Cantidad&amp;quot;].Value);

                    invoiceLine.AddInvoiceLineRow(invoiceLineRow);
                }

                InvoiceDAL.Create(invoice, invoiceLine);

                #endregion
                
                scope.Complete();

            }

            InicializarControles();

        }
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:c7a9e4ea-de3d-4ce0-af68-751958f6c5a6" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;public static class InvoiceDAL
{

    public static void Create(ChinookEntity.InvoiceRow invoice, ChinookEntity.InvoiceLineDataTable invoiceLine)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[&amp;quot;default&amp;quot;].ToString()))
        {
            conn.Open();
            //
            // Creacion de la Factura
            //
            string sqlFactura = @&amp;quot;INSERT INTO Invoice (CustomerId, InvoiceDate, BillingAddress, Total) VALUES (@customerId, @date, @address, @total)
                       SELECT SCOPE_IDENTITY()&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlFactura, conn))
            {

                cmd.Parameters.AddWithValue(&amp;quot;@customerId&amp;quot;, invoice.CustomerId);
                cmd.Parameters.AddWithValue(&amp;quot;@date&amp;quot;, invoice.InvoiceDate);
                cmd.Parameters.AddWithValue(&amp;quot;@address&amp;quot;, invoice.BillingAddress);
                cmd.Parameters.AddWithValue(&amp;quot;@total&amp;quot;, 0);

                invoice.InvoiceId = Convert.ToInt32(cmd.ExecuteScalar());
            }

            //
            // Creacion de cada Liena de Factura
            //
            decimal total = 0;

            string sqlLineaFactura = @&amp;quot;INSERT INTO InvoiceLine (InvoiceId, TrackId, UnitPrice, Quantity) 
                                        VALUES (@invoiceid, @trackid, @unitprice, @quantity)
                                        SELECT SCOPE_IDENTITY()&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlLineaFactura, conn))
            {

                foreach (ChinookEntity.InvoiceLineRow row in invoiceLine.Rows)
                {
                    //
                    // como se reutiliza el mismo objeto SqlCommand es necesario limpiar los parametros
                    // de la operacion previa, sino estos se iran agregando en la coleccion, generando un fallo
                    //
                    cmd.Parameters.Clear();

                    cmd.Parameters.AddWithValue(&amp;quot;@invoiceid&amp;quot;, invoice.InvoiceId);
                    cmd.Parameters.AddWithValue(&amp;quot;@trackid&amp;quot;, row.TrackId );
                    cmd.Parameters.AddWithValue(&amp;quot;@unitprice&amp;quot;, row.UnitPrice);
                    cmd.Parameters.AddWithValue(&amp;quot;@quantity&amp;quot;, row.Quantity);

                    //
                    // Si bien obtenermos el id de linea de factura, este no es usado
                    // en la aplicacion
                    //
                    row.InvoiceLineId = Convert.ToInt32(cmd.ExecuteScalar());

                    total += row.UnitPrice * row.Quantity;
                }

            }

            //
            // Actualizacion del Total de la Factura
            //
            string sqlUpdateTotal = @&amp;quot;UPDATE Invoice SET Total = @total WHERE InvoiceId = @InvoiceId&amp;quot;;

            using (SqlCommand cmd = new SqlCommand(sqlUpdateTotal, conn))
            {
                cmd.Parameters.AddWithValue(&amp;quot;@total&amp;quot;, total);
                cmd.Parameters.AddWithValue(&amp;quot;@InvoiceId&amp;quot;, invoice.InvoiceId);

                cmd.ExecuteNonQuery();
            }


        }

        

    }

}
&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;

&lt;p&gt;La creación de la factura implica unos cuantos pasos, pero al contar con entidades el procesamiento es mas simple.&lt;/p&gt;

&lt;p&gt;Dentro del método Create() ahora solo es necesario iterar por las rows definidas en el datatable InvoiceLineDataTable, cuya información proviene de la presentación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas y Desventajas de este nueva implementación&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si bien es cierto que esta nueva implementación requiere codificar un poco mas, ya que es necesario definir clases y proyectos nuevo, también es cierto que estos mejora la reutilización del código, a partir de ahora cualquier otro formulario que necesite, por ejemplo, listar los clientes no será necesario copiar y pegar el fragmento de código usado para conectarse a la base de datos y recuperar los registro, solo se agrega una línea invocando al método GetAll() de la clase CustomerDAL y eso será todo.&lt;/p&gt;

&lt;p&gt;Esto implica una gran ventaja que centraliza la funcionalidad, separando todo lo que tenga que ver con la persistencia de los datos, es importante notar que a los métodos de la capa de datos en ningún momento se le paso como parámetro un control de presentación, o sea no se paso una referencia del DataGridView para que tomara las líneas de la factura, sino que se desacopla las capas usando las entidades. &lt;/p&gt;

&lt;p&gt;Es por esta razón que en la presentación donde se recorre los ítems del DataGridView y se arman filas del datatable InvoiceLineDataTable, el cual es usado para reflejar las línea de facturación. Las entidades son en todo momento el vinculo de transporte de información entre las capas, y esta regla no se puede romper si es que se quiere mantener las capas desacopladas.&lt;/p&gt;

&lt;p&gt;Un punto que aun queda como detalle esta relacionado al manejo de transacciones, el cual aun es controlado por la presentación, pero esto no es correcto, aunque solucionarlo requiere de algún intermediario entre estos componentes que permitan interceder en la transacción, es aquí donde entra en juego la capa de negocio, que se implementara en la próxima iteración.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El mismo fue desarrollado con Visual Studio 2008, y Sql Server 2008 Express.&lt;/p&gt;

&lt;p&gt;Durante el desarrollo del articulo no se ha insertado todo el código de los ejemplos, ya que resultaría demasiado extenso, la idea es que puedan descargar el código, ejecutarlo y analizarlo detenidamente, viendo como interactúan y comunican las distintas capas. &lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;[C#] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte2/[csharp]Chinook2Capas.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;

      &lt;td valign="top" width="200"&gt;[VB.NET] 
        &lt;br /&gt;&lt;a href="http://www.ltuttini.com.ar/blogfiles/ntier/DesarrolloCapas/parte2/[vb.net]Chinook2Capas.zip" target="_blank"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" src="http://www.ltuttini.com.ar/blogfiles/folder.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7361892840793499128-7192383767701688691?l=ltuttini.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ltuttini.blogspot.com/feeds/7192383767701688691/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7361892840793499128&amp;postID=7192383767701688691' title='10 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7192383767701688691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7361892840793499128/posts/default/7192383767701688691'/><link rel='alternate' type='text/html' href='http://ltuttini.blogspot.com/2010/07/n-tier-desarrollo-en-capas-ejemplo.html' title='[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 2'/><author><name>Leandro Tuttini</name><uri>https://profiles.google.com/100876873577560513844</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-3rqrvUe7LvQ/AAAAAAAAAAI/AAAAAAAAAIE/tQLkvEA_SzI/s512-c/photo.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7361892840793499128.post-3164969106416876166</id><published>2010-07-16T20:51:00.001-07:00</published><updated>2011-06-26T20:52:34.504-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>[ASP.NET] Mantener la Session Activa Indefinidamente</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;El objetivo que persigue el articulo es demostrar como mediante llamadas asíncronas realizadas por medio de la librería de jquery se puede mantener la Session activa en el servidor de forma indefinida sin que este expire en un tiempo determinado.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Mantener la Session Activa&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Asp.net expira la Session de un usuario cuando un tiempo prolongado de inactividad indicaría que ya no se esta usando la pagina, pero a veces es necesario permitir que el usuario tome varias horas sin actividad, aunque en la configuración del servidor el timeout este definido en un tiempo reducido.&lt;/p&gt;  &lt;p&gt;La idea del ejemplo es realizar de forma transparente para el usuario, y sin que se refresque la pagina, una invocación al servidor, para que este registre dicha actividad.&lt;/p&gt;  &lt;p&gt;Para lograr el objetivo se hará uso de dos librerías de jquery:&lt;/p&gt;  &lt;p&gt;- la principal realizará las invocaciones a los WebMethod definidos en la pagina, usando la funcionalidad de $.ajax&lt;/p&gt;  &lt;p&gt;- la otra librería permitirá especificar desde javascript el intervalo de tiempo en que debe invocarse al método expuesto en el servidor web, &lt;a href="http://plugins.jquery.com/project/timers" target="_blank"&gt;jQuery Timers&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;[javascript]&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:DFDE9937-D816-47f4-A306-7B60D5CE5AC0:755ba3ac-f8c5-4020-b5c7-b9e41f30f37d" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: jscript; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "&gt;&amp;lt;script language=&amp;quot;javascript&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;

        $().ready(function() {

        $(document).everyTime(3000, function() {
        
                $.ajax({
                    type: &amp;quot;POST&amp;quot;,
                    url: &amp;quot;ValidarSession1.aspx/KeepActiveSession&amp;quot;,
                    data: {},
                    contentType: &amp;quot;application/json; charset=utf-8&amp;quot;,
 
