Introducción
Continuando con el planteo en la primer artículo
Archivos de Configuración - Una introducción (1/3)
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.
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.
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.
Definición de la configuración
Definiremos la configuración con la cual nos basaremos en el ejemplo
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="MediosPagoSection" type="WinformsConfigSeccionesPropias.Configuration.MediosPagoConfigurationSection, WinformsConfigSeccionesPropias" /> </configSections> <MediosPagoSection> <MediosPago> <MedioPago id="1" descripcion="Efectivo" provider="WinformsConfigSeccionesPropias.EfectivoProvider" /> <MedioPago id="2" descripcion="Tarjeta Credito" provider="WinformsConfigSeccionesPropias.TarjetaCreditoProvider"/> <MedioPago id="3" descripcion="Tarjeta Debito" provider="WinformsConfigSeccionesPropias.TarjetaDebitoProvider"/> <MedioPago id="4" descripcion="Cheque" provider="WinformsConfigSeccionesPropias.ChequeProvider"/> <MedioPago id="5" descripcion="Transferencia Bancaria" provider="WinformsConfigSeccionesPropias.TransferenciaBancariaProvider"/> </MediosPago> </MediosPagoSection> </configuration>
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.
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.
Definición de las clase de configuración
En el siguiente diagrama
Se define la estructura de clases utilizadas para poder mapear los tag del .config con clases que permitan manipular esta información.
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.
public class Config { private static Config _config; private Config() { this.MediosPago = (MediosPagoConfigurationSection)ConfigurationManager.GetSection("MediosPagoSection"); } public static Config Instance() { if (_config == null) _config = new Config(); return _config; } public MediosPagoConfigurationSection MediosPago { get; private set; } }
El resto de los archivos si son parte del mapeo y requieren unirse uno con otro para armar la estructura.
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”.
public class MediosPagoConfigurationSection : ConfigurationSection { [ConfigurationProperty("MediosPago")] public MediosPagoCollection MedioPagoItems { get { return ((MediosPagoCollection)(base["MediosPago"])); } } }
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.
[ConfigurationCollection(typeof(MedioPagoElement), AddItemName = "MedioPago")] 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); } } }
Cada elemento representado por la clase “MedioPagoElement” solo tiene el mapeo a las propiedades del tag.
public class MedioPagoElement : ConfigurationElement { [ConfigurationProperty("id", DefaultValue = "", IsKey = true, IsRequired = true)] public string Id { get { return ((string)(base["id"])); } set { base["id"] = value; } } [ConfigurationProperty("descripcion", DefaultValue = "", IsKey = false, IsRequired = true)] public string descripcion { get { return ((string)(base["descripcion"])); } set { base["descripcion"] = value; } } [ConfigurationProperty("provider", DefaultValue = "", IsKey = false, IsRequired = true)] public string provider { get { return ((string)(base["provider"])); } set { base["provider"] = value; } } }
Definición de las clase de calculo de pago
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.
interface ICalculoImpuesto { decimal Calcular(decimal importe); }
Cada clase concreta implementa la interfaz y aplica el calculo.
public class ChequeProvider : ICalculoImpuesto { /// <summary> /// El cheque recarga un 10% /// </summary> /// <param name="importe"></param> /// <returns></returns> public decimal Calcular(decimal importe) { return importe + (importe * (decimal)0.10); } } public class EfectivoProvider : ICalculoImpuesto { /// <summary> /// En Efectivo se descuenta un 10% /// </summary> /// <param name="importe"></param> /// <returns></returns> public decimal Calcular(decimal importe) { return importe - (importe * (decimal)0.10); } } public class TarjetaCreditoProvider : ICalculoImpuesto { /// <summary> /// La tarjeta de Credito recarga un 10% /// </summary> /// <param name="importe"></param> /// <returns></returns> public decimal Calcular(decimal importe) { return importe + (importe * (decimal)0.10); } } public class TarjetaDebitoProvider : ICalculoImpuesto { /// <summary> /// La tarjeta de Debito recarga un 5% /// </summary> /// <param name="importe"></param> /// <returns></returns> public decimal Calcular(decimal importe) { return importe + (importe * (decimal)0.05); } } public class TransferenciaBancariaProvider : ICalculoImpuesto { /// <summary> /// El Trsnaferencia Bancaria no afecta al importe /// </summary> /// <param name="importe"></param> /// <returns></returns> public decimal Calcular(decimal importe) { return importe; } }
Aplicación de todo lo definido
Bien, ahora llego el momento de poner manos a la obra y hacer uso de todo lo configurado en los pasos anteriores.
Empezaremos por cargar el combo de medios de pago, tomando la información de esta nueva estructura.
private void Form2_Load(object sender, EventArgs e) { var result = (from config in Config.Instance().MediosPago.MedioPagoItems.Cast<MedioPagoElement>() select new { key = config.Id, value = config.descripcion }).ToList(); cmbMediosPago.DisplayMember = "value"; cmbMediosPago.ValueMember = "key"; cmbMediosPago.DataSource = result; cmbMediosPago.SelectedIndex = -1; }
Como se observa no ha cambiado mucho con respecto al artículo anterior, solo que esta vez se cuenta con la ayuda de
Config.Instance().MediosPago.MedioPagoItems
el cual nos abstrae de la operación de carga de config en las clases.
El próximo punto involucra al calculo de impuesto.
private void btnCalcular_Click(object sender, EventArgs e) { errProvider.Clear(); if (cmbMediosPago.SelectedIndex == -1) { errProvider.SetError(cmbMediosPago, "Debe seleccionar un medio de pago"); return; } decimal importe = 0; if (!decimal.TryParse(txtImporte.Text, out importe)) { errProvider.SetError(txtImporte, "El importe ingresado es invalido"); 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("{0:N2}", calculo.Calcular(importe)); }
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.
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).
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.
Código de ejemplo
[C#]
|
[VB.NET]
|
hola leandro... me has ayudado muchisimo con tus aportes en la web..
ResponderEliminarestoy desarrollando un sistema de gestion comercial... y estoy trancado con un datagrid.... y como tu eres el experto en datagrd, para mi seria un honor, que me ayudaras. si recibes este pequeño comentario porfavor devuelve a mi correo, y asi coordinamos la manera de interactuar... gracias por inticipado.... edward arias...
mi email es. couldcountry@gmail.com
hola edward arias
ResponderEliminarno te animas a tratar el tema por medio del foro ?
digo de esta forma si por alguna razon me retraso en responder alguien mas estaria dispuesto a colaborar, seguramente yo participe, pero no te quedas con mi uncia opinion sobre el tema
foro msdn
saludos
Tengo un sistema en c#. El sistema tiene que ser capaz de soportar 10 PCs más en red.Tiene que tener en cuenta que la información de cada pc debe actualizarse Instantáneamente y automáticamente, si un usuario de una pc modifica , da de alta o elimina algún registro.La base de datos debe residir en un equipo que a su vez actúa de cliente.Si dos usuarios o más al mismo tiempo desean tocar registros diferentes en una misma tabla, que puedan hacerlo.Si dos o más usuarios desean justo hacer operaciones sobre el mismo registro en la misma tabla que el sistema del mensaje pertinente.El sistema es un sistema de factura y nomina clientes, el sistema esta hecho pero me falta esto (ponerlo en red) hacerlo multi-usuario, creo que así se los llama a los sistemas que actúan en red.Pensé en trabajar con winsock pero me dijeron que para esto no es necesario.
ResponderEliminarAdemás me dijeron que necesito al estar la aplicación en red, realizar n control de perfiles de usuarios.
Actualmente la base de datos que uso es sql server 2008.
atencion mi hermano tuttini..... !!
Por favor ayuda
Gracias
hola leandro veo que eres un muy buen experto en la programacion , tengo un problema estoy empezando en esto y estoy desarrollando una aplicacion donde cuento con 6 listbox y quiero que la informacion que esta en ellas se guarde en mi base de datos access 2003 trabajo con visual basic te paso algo de lo que estoy intentando hacer en el codigo como ves quize pasar la informacion a textbox pero aun asi me sale error que no coiciden los criterios, ayudame por favor
ResponderEliminarSub GUARDAR()
For i = 0 To LB1.Items.Count - 1
PRUEBA1.Text &= LB1.Items(i) & vbCrLf
Next
For i = 0 To LB2.Items.Count - 1
PRUEBA2.Text &= LB2.Items(i) & vbCrLf
Next
For i = 0 To LB3.Items.Count - 1
PRUEBA3.Text &= LB3.Items(i) & vbCrLf
Next
For i = 0 To LB4.Items.Count - 1
PRUEBA4.Text &= LB4.Items(i) & vbCrLf
Next
For i = 0 To LB5.Items.Count - 1
PRUEBA5.Text &= LB5.Items(i) & vbCrLf
Next
For i = 0 To LB6.Items.Count - 1
PRUEBA6.Text &= LB6.Items(i) & vbCrLf
Next
Dim CMD As New OleDb.OleDbCommand("INSERT INTO VENTAS VALUES ('" & NUMERO.Text & "','" & FECHA.Value & "','" & MONEDA.Text & "','" & TC.Text & "','" & MODALIDAD.Text & "','" & DIAS.Text & "','" & FV.Value & "','" & CLIENTE.Text & "','" & DIRECCION.Text & "','" & RUC.Text & "','" & VENDEDOR.Text & "','" & OBSERVACIONES.Text & "','" & PRUEBA1.Text & "','" & PRUEBA2.Text & "','" & PRUEBA3.Text & "','" & PRUEBA4.Text & "','" & PRUEBA5.Text & "','" & PRUEBA6.Text & "','" & SUBTOTAL.Text & "','" & IMP.Text & "','" & IGV.Text & "','" & OTRO.Text & "','" & OTROS.Text & "','" & TOTAL.Text & "','" & SON.Text & "','" & CAMBIO.Text & "','" & CONTROL.Text & "')", CN)
CN.Open()
CMD.ExecuteNonQuery()
CN.Close()
MsgBox("Registro Agregado")
End Sub
hola edward
ResponderEliminarimagino esta respuesta fue solucionada en el foro
http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/b007cb63-628a-45ef-9085-798bdb19b157
saludos
hola JuancaPeru
ResponderEliminaralgo que puedo ver como primer problema es que no usas parametros en la query que define
como lo explico aqui
ADO.NET – Parte 4 – Actualización Información Ms Access
es muy importante como primer medida usar parametros en las queries y no concatenar el string
saludos
hola leandro... me has ayudado muchisimo con tus aportes en la web..
ResponderEliminarTe comento, estoy desarrollando un sistema para una empresa, en el que se logean los usuarios y pueden acceder a su historial de cuentas, movimientos, etc... necesitaría saber como hacer para mantener abierta una sesión con una cuenta particular y que al cerrar sesión no se pueda acceder a ningún tipo de información?
Desde ya muchas gracias....
hola nakiotio
ResponderEliminarpor cerrar la session te refieres a que la persona hace un log off ?
solo sale de la aplicacion
o seria inhabilitar esa cuenta de usuario
saludos
Hola Leandro, muchas gracias por todas las respuesta oportunas. Tengo una preguntica, que aunque no tiene que ver con este post, me tiene quemando neuronas!!. Esta relacionado con herencia, quiero saber si existe alguna forma de tener acceso a las propiedades de la clase hijo desde la clase padre? Gracias de nuevo..
ResponderEliminarleandro, mi problema es como mantener un session de login.
ResponderEliminarLa aplicacion que estoy realizando tiene un form de LOGEO y luego otros formularios q dependen de la persona q se logea....
Como hago para mantener activa la cuenta (utilizando SESSION????), asi mismo me gustaria saber como hago para cuando el usuario quiera salir de su cuenta....
desde ya muchas gracias!!!!
hola Pierina Joplin
ResponderEliminaren principio aplicando conceptos de POO, no puedes
ahora si la clase padre puede acceder a uan instancia de un hijo, mediente uan relacion de asociacion, en ese caso podrias
public class PersonaBase{
private void Metodo1(){
PersonaClass2 class2 =(PersonaClass2)this;
}
}
public class PersonaClass1: PersonaBase {
}
public class PersonaClass2: PersonaBase {
}
igualmente no creo que esto sea muy bueno hacerlo
porque recuerda que clases hijas puede haber miles, con eso estarias generando un acoplamiento no saludable para el modelo
saludos
hola nakiotio
ResponderEliminarno habias revisado este articulo
Login – Usando Password con Hash
saludos
Hola, tengo una duda: Quiero leer datos específicos de un archivo de texto y mostrarlos en un DataGridView.
ResponderEliminarPor ejemplo:
Servicio de Administración Tributaria
Acuse de cancelación de CFDI
Fecha y hora de solicitud: 07/02/2012 15:27:48
Fecha y hora de cancelación: 07/02/2012 15:27:48
RFC Emisor: ASDFG
Folio Fiscal: Estado CFDI
00FDA0DC-9BBF-E20A-CB6A-500448C93351 Cancelado
Lo que quiero leer es la fecha y hora de cancelación
(07/02/2012 15:27:48) y el folio fiscal (00FDA0DC-9BBF-E20A-CB6A-500448C93351) y mostrarlos en una columna diferente en mi DataGridView. Espero que puedas ayudarme
hola Juan
ResponderEliminarel tema que veo es que ese txt no tene mucha estructura para procesarse
podrias usar el
string[] lineas = File.ReadAllLines("ruta");
foreach(string linea in lineas){
string[] partes= linea.Split(':');
if(parte[0]== "Fecha y hora de cancelación"){
txtFecha.Text = parte[1];
}
if(parte[0]== "Folio Fiscal"){
txtFolio.Text = parte[1];
}
}
por supuesto es solo una idea, habria que probarla y refinarla un poco mas
saludos
Disculpa, pero no me quedó del todo claro. Este es mi código:
ResponderEliminarprivate void button2_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
string caption = "ERROR";
MessageBox.Show("No hay ningún elemento seleccionado", caption);
}
if (textBox1.Text != "")
{
string texto;
int count = 3;
string[] split = null;
try
{
StreamReader sr = new StreamReader(openFileDialog1.FileName);
while ((texto = sr.ReadLine()) != null)
{
split = texto.Split(new char[] {',', ';', '*'}, count);
dataGridView1.Rows.Add(split[0], split[1], split[2]);
split = null;
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
Mi DataGridView consta de 3 columnas, y para poder mostrar los campos que quiero en cada columna, es necesario que copie, separado por comas, dichos campos al inicio de mi archivo de texto, para que me lea esa línea. Así:
02/02/2012, 17:16:11, FEB2D7A5-D677-E788-F46A-F8B09FC8E90B
Servicio de Administración Tributaria
Acuse de cancelación de CFDI
Fecha y hora de solicitud: 02/02/2012 17:16:11
Fecha y hora de cancelación: 02/02/2012 17:16:11
RFC Emisor: xxxxxxxxxxx
Folio Fiscal: Estado CFDI
FEB2D7A5-D677-E788-F46A-F8B09FC8E90B Cancelado
Sello digital SAT : xxxxxxxxxxxxxxx
Lo que no quiero es seguir haciendo eso, ya que son aproximadamente 3000 archivos, y darle ése formato a todos los archivos, sería demasiado tardado.
De nuevo, gracias por tu tiempo.
Saludos
hola
ResponderEliminarpero cual es el forma real del archivo? porque al principio tienes unso valores separados por coma en un sola linea
pero despues tienes un dato por linea
lo que tenenes que haces es ir leyendo las linea y procesar, no hay mucho secreto, en lugar de usar el StreamReader usa el File.ReadAllLinea() asi sera directo volcar el archivo en memoria y procesarlo
saludos