Maestro – Detalle

En esta oportunidad vamos a realizar una app Maestro – Detalle, usando dos DataGridView, ADO.NET y LINQ to DataSet.

Vamos a trabajar con la DB Northwind, tablas Customers(Maestro) y Orders(Detalle), tenemos que fijarnos el campo en común que tienen estas dos tablas que en le Maestro es el PK y en el Detalle es el FK, por esos campos es que relacionamos ambas tablas.

public class CustomersOrdersRepository
    {
        public static DataSet MaestroDetalle()
        {
            // Agregamos la key que define al config
            using (SqlCeConnection cn = Conexion.Conectar("NorthwindConnectionString"))
            {
                cn.Open();
                using (SqlCeCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT [Customer ID], [Company Name], City, Country FROM Customers";
                    using (SqlCeDataAdapter da = new SqlCeDataAdapter(cmd))
                    {
                        DataSet ds = new DataSet();
                        // Llenar DataTable en el DataSet
                        da.Fill(ds, "Customers");
                        cmd.CommandText = "SELECT [Order ID], [Customer ID], [Employee ID], [Order Date]," +
                                          " [Required Date] FROM Orders";
                        da.Fill(ds, "Orders");

                        // Establecer relaiones
                        DataColumn colPadre = ds.Tables["Customers"].Columns["Customer ID"];
                        DataColumn colHijo = ds.Tables["Orders"].Columns["Customer ID"];
                        DataRelation relation = new DataRelation("Detalle de Pedidos", colPadre, colHijo);
                        ds.Relations.Add(relation);

                        return ds;
                    }
                }

            }
        }
    }

 

Llamaremos al método MaestroDetalle desde el Load del formulario frmMaestroDetalle, para esto vamos a declarar dos variables a nivel de formulario ds y código, para ser llamadas desde cualquier parte del formulario para nuestros propósitos.

public partial class frmMaestroDetalle : Form
    {
        // Declaramos variables a nivel de formulario
        DataSet ds = new DataSet();
        private string codigo;

        public frmMaestroDetalle()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            CargarGrillas();
        }

        private void dgvMaestro_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            ObtenerID();
            // Aplicamos las bondades de LINQ to DataSet.
            DataTable DetalleItem = ds.Tables["Orders"];
            EnumerableRowCollection<datarow> query = from _detalle in DetalleItem.AsEnumerable()
                                                     where _detalle.Field<string>("Customer ID") == codigo
                                                     select _detalle;
            DataView view = query.AsDataView();
            dgvDetalle.DataSource = view;

        }

        private  void CargarGrillas()
        {
            // Le asignamos al DataSet el método que hace la consula a la DB.
            ds = CustomersOrdersRepository.MaestroDetalle();
            dgvMaestro.DataSource = ds;
            dgvMaestro.DataMember = "Customers";

            dgvDetalle.DataSource = ds;
            dgvDetalle.DataMember = "Orders";
        }

       private void ObtenerID()
       {
           codigo = Convert.ToString(dgvMaestro.CurrentRow.Cells[0].Value);
       }

    }

Para poder cargar los datos en las grillas estoy creando un método de nombre CargarGrillas, el cual lo llamo en el evento Load. En el cual utilizo la variable ds que esta declarada a nivel del formulario para poder ser llamada desde cualquier lado que se le necesite, a la variable ds nos representa un DataSet, le asignamos el método MaestroDetalle que es el encargado de hacer la persistencia contra la DB y traernos los datos solicitados de Maestro y Detalle, una vez que el DataSet tiene los datos solicitados, se lo asignamos al DataSource del DataGridView, utilizamos el DataMember para darle el nombre de la tabla en este caso como estamos trabajando con la grilla de Maestro le ponemos el nombre “Customers”, de igual manera hacemos para Detalle.

Hasta allí ya tenemos cargadas las dos grillas, ahora vamos a trabajar en el evento dgvMaestro_CellClick del DataGridView, para poder hacer el filtro, en el cual llamamos al método ObtenerID el cual nos obtiene la primera posición de una fila, obteniendo la clave del campo en comúm, teniendo el código hacemos el filtro mediante LINQ to DataSet.

El resultado final que debemos de obtener es como muestra la imagen.

MaestroDetalle

Descargar Proyecto C#

PropertyGrid

Hace poco conocí este control, por que me mandaron en mi trabajo a implementarlo en el desarrollo de una aplicación que estaba haciendo y bueno no encontré mucha información, me puse a investigar y logre utilizarlo, les mostrare como implementarlo.

PropertyGrid

PropertyGrid, es el mismo control que se usa para ver las propiedades de un control en el IDE de VS.NET, muestra las propiedades de cualquier objeto o tipo, y recupera las propiedades del elemento, principalmente mediante la reflexión. (La reflexión es una tecnología que proporciona información de tipo en tiempo de ejecución). El control PropertyGrid se compone de dos columnas y, como mínimo, tantas filas como propiedades editables contiene el objeto. La columna de la izquierda identifica la propiedad por su nombre, y la de la derecha ofrece un espacio para ver y/o editar la propiedad. Es muy útil para el uso del usuario final que le permite editar las propiedades de un objeto,  lo cual, sin duda, añade elegancia y valor a la aplicación. Sin mas preámbulo manos a la obra.

Se utiliza una clase para establecer las propiedades del objeto, algo muy importante hay que declarar el namespace ComponentModel, de la siguiente manera using System.ComponentModel.

public class Producto
    {
        // Se implementan los enum para poder cargar los combobox
        public enum Colors
        {
            AZUL = 1,
            VERDE = 2,
            AMARILLO = 3
        }

        public enum Medida
        {
            SMALL = 1,
            MEDIUM = 2,
            LARGE = 3
        }

        [Category("Producto"), DescriptionAttribute("Código del producto")]
        public string Código { get; set; }

        [Category("Producto"), DescriptionAttribute("Color que tiene el producto")]
        public Colors Color { get; set; }

        [Category("Producto"), DescriptionAttribute("Tamaño, medida"), TypeConverter(typeof(ConvertTalla))]
        public string Talla { get; set; }

        [Category("Producto"), DescriptionAttribute("Modelo del cuello")]
        public string Cuello { get; set; }

        [Category("Producto"), DescriptionAttribute("Indica si se encuentra activo")]
        public bool Estado { get; set; }

    }

Ahora vamos al load del formulario para instanciar la clase producto para asignársela al control PropertyGrid.

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Instanciamos la clase Producto
            Producto producto = new Producto();
            
            // Le pasamos la instancia al objeto
            propertyGrid1.SelectedObject = producto; 
        }
    }

Ahora les paso a explicar de los atributos que se aplicaron a la propiedades de la clase producto, estos atributos se pueden usar gracias al namespace que declaramos en la clase. Los atributos son etiquetas declarativas que se aplican a la propiedades que se pueden recuperar en tiempo de ejecución mediante la reflexión.

  • DescriptionAttribute . Establece el texto de la propiedad que se muestra en el panel de ayuda siguiente descripción de las propiedades. Esta es una manera útil para proporcionar texto de ayuda de la propiedad activa (la propiedad que tiene el foco).
  • CategoryAttribute . Establece la categoría que la propiedad se encuentra en la red. Esto es útil cuando se desea una propiedad agrupados por un nombre de categoría. Al Misc categoría. Aplique este atributo a todas las propiedades.
  • BrowsableAttribute – Indica si la propiedad se muestra en la cuadrícula. Esto es útil cuando se quiere ocultar una propiedad de la red. De forma predeterminada,
  • ReadOnlyAttribute – Indica si la propiedad es de sólo lectura. Esto es útil cuando se desea mantener la propiedad de ser editable en la red. De forma predeterminada, una propiedad pública con obtener y establecer funciones de acceso se puede editar en la cuadrícula.
  • DefaultValueAttribute – Identifica el valor predeterminado de la propiedad. Esto es útil cuando se desea proporcionar un valor predeterminado para una propiedad y luego determinar si el valor de la propiedad es diferente al predeterminado. Aplique este atributo a todas las propiedades.
  • DefaultPropertyAttribute – Identifica la propiedad predeterminada de la clase. La propiedad predeterminada de una clase obtiene el foco por primera vez cuando se selecciona la clase en la cuadrícula.

Segunda parte Acceso a Datos

Ahora la talla que se carga con el enum Medida, lo cargamos con datos reales que están en la Base de datos, para eso vamos a comentar el enum Medida que esta en la Clase Producto, hacemos la consulta SQL contra la base de datos, de la siguiente manera.

public class TallaRepository
    {
        public static List<tallaentity> GetTalla()
        {
            using(SqlCeConnection cn = Conexion.Conectar("PruebaConnectionString"))
            {
                cn.Open();
                using(SqlCeCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT idTalla, descripcion FROM Tallas";
                    List<tallaentity> talla = new List<tallaentity>();
                    using(SqlCeDataReader reader = cmd.ExecuteReader())
                    {
                        while(reader.Read())
                        {
                            TallaEntity t = new TallaEntity()
                                {
                                    idTalla = Convert.ToInt32(reader["idTalla"]),
                                    descripcion = Convert.ToString(reader["descripcion"])
                                };
                            talla.Add(t);
                        }
                    }
                    return talla;
                }
            }
        }

Ahora vamos a crear una clase donde se le asigna los datos al ComboBox a través de una lista y en donde se le asigna propiedades.

internal class ConvertTalla : StringConverter
    {
        // Permite alternar dinámicamente entre el ComboBox, donde el usuario debe elegir entre 
        // las opciones disponible, y TextBox, donde el usuario introduce libremente un valor.
        public override bool
           GetStandardValuesSupported(
           ITypeDescriptorContext context)
        {
            //True - means show a Combobox
            //and False for show a Modal 
            return true;
        }

        // Determina si el usuario debe limitarse a los valores de la lista o si, aun disponiendo de ella,
        // Puede introducir un valor nuevo.
        public override bool
            GetStandardValuesExclusive(
            ITypeDescriptorContext context)
        {
            //False - a option to edit values 
            //and True - set values to state readonly
            return true;
        }

        // Entrega la lista de miembros del ComboBox.
        public override StandardValuesCollection
           GetStandardValues(
           ITypeDescriptorContext context)
        {
            List<tallaentity> lista = TallaRepository.GetTalla(); 
            return new StandardValuesCollection(lista.Select(x =&gt; x.descripcion).ToArray());

        }
    }

Ahora solo nos toca modificar la propiedad de la Clase Producto

[Category("Producto"), DescriptionAttribute("Tamaño, medida"), TypeConverter(typeof(ConvertTalla))]
        public string Talla { get; set; }

Esta clase reimplementa tres funciones:

GetStandardValuesSupported permite alternar dinámicamente entre el ComboBox, donde el usuario debe elegir entre las opciones disponibles, y el TextBox, donde el usuario introduce libremente un valor.

GetStandardValuesExclusive determina si el usuario debe limitarse a los valores de la lista o si, aun disponiendo de ella, puede introducir un valor nuevo.

GetStandardValues entrega la lista de miembros del Combobox.

Proyecto C#

NOTA : El proyecto esta desarrollado en VS 2012 y se esta usando SQL CE 4.0 la base de datos ya viene incrustada en el proyecto, solo tienen que correr la aplicación.