Este manual recrea el parcial Tema A de Peruvian Oil Company y te lleva paso a paso de un proyecto vacio en Visual Studio a la solucion completa con multilistas. Version simple: solo entidades, toda la logica vive en los formularios.
entities/ con dos clases + 4 formularios WinForms.Codigo alfanumerico unicoNombre alfanumerico descriptivoRegion seleccionable entre Costa, Sierra, SelvaReserva decimal (millones de barriles estimados)Pozos lista de pozos asociadosCodigo con estructura CST-001 / SRA-010 / SLV-030NombreTecnico alfanumerico descriptivoTipoExtraccion seleccionable entre Convencional y No ConvencionalProduccionDiaria entero (barriles/dia)entities/. La lista de yacimientos vive como propiedad static en FrmPrincipal y los demas formularios la consultan directamente. Toda la validacion va en cada Form. Es lo minimo que pide la rubrica del parcial y es la forma mas comun en que se resuelve.
PeruvianOilCompany. Framework: .NET Framework 4.7.2.Form1.cs, Program.cs y la carpeta Properties.En el Solution Explorer, click derecho sobre el proyecto PeruvianOilCompany y elige Add → New Folder:
entitiesentities → Add → Class. Nombre: Pozo.cs.using System; namespace PeruvianOilCompany.entities { public class Pozo { public string Codigo { get; set; } public string NombreTecnico { get; set; } public string TipoExtraccion { get; set; } public int ProduccionDiaria { get; set; } } }
Codigo del pozo es string porque trae letras (CST-, SRA-, SLV-). La ProduccionDiaria es entera (barriles por dia).
entities → Add → Class. Nombre: Yacimiento.cs.using System; using System.Collections.Generic; namespace PeruvianOilCompany.entities { public class Yacimiento { public string Codigo { get; set; } public string Nombre { get; set; } public string Region { get; set; } public double Reserva { get; set; } public List<Pozo> Pozos { get; set; } } }
Reserva es double porque puede tener decimales (millones de barriles). La lista Pozos es la multilista que vamos a usar para los reportes.
Form1.cs a FrmPrincipal.cs (click derecho → Rename, acepta el cambio en todas las referencias).Arrastra desde el Toolbox y configura:
| Tipo | Nombre (Name) | Texto (Text) |
|---|---|---|
| Form | FrmPrincipal | Peruvian Oil Company |
| Button | btnYacimiento | Yacimiento |
| Button | btnPozo | Pozo |
| Button | btnReportes | Reportes |
Distribuye los 3 botones uno debajo del otro, centrados:
Abre el codigo de FrmPrincipal.cs y al inicio de la clase agrega la lista compartida que usaran todos los formularios:
using PeruvianOilCompany.entities; using System.Collections.Generic; namespace PeruvianOilCompany { public partial class FrmPrincipal : Form { // Lista global accesible desde todos los formularios public static List<Yacimiento> Yacimientos = new List<Yacimiento>(); public FrmPrincipal() { InitializeComponent(); } } }
static? Asi todos los formularios usan la misma lista en memoria. Lo accedes desde cualquier Form con FrmPrincipal.Yacimientos. Sin static cada Form tendria su propia lista vacia.
private void btnYacimiento_Click(object sender, EventArgs e) { FrmYacimiento frm = new FrmYacimiento(); frm.ShowDialog(); } private void btnPozo_Click(object sender, EventArgs e) { FrmPozo frm = new FrmPozo(); frm.ShowDialog(); } private void btnReportes_Click(object sender, EventArgs e) { FrmReportes frm = new FrmReportes(); frm.ShowDialog(); }
FrmYacimiento, FrmPozo y FrmReportes no existen aun. Los crearemos en los siguientes pasos. Ignora los errores rojos por ahora.
Program.cs y reemplaza la linea Application.Run(new Form1()); por:Application.Run(new FrmPrincipal());
FrmYacimiento.cs.| Tipo | Name | Para |
|---|---|---|
| TextBox | txtCodigo | Codigo del yacimiento |
| TextBox | txtNombre | Nombre |
| ComboBox | cboRegion | Costa / Sierra / Selva |
| TextBox | txtReserva | Reserva en millones de barriles |
| Button | btnRegistrar | Registrar yacimiento |
| DataGridView | dgvYacimientos | Mostrar la lista |
using PeruvianOilCompany.entities; private void FrmYacimiento_Load(object sender, EventArgs e) { cboRegion.Items.Add("Costa"); cboRegion.Items.Add("Sierra"); cboRegion.Items.Add("Selva"); MostrarYacimientos(); } private void MostrarYacimientos() { dgvYacimientos.DataSource = null; dgvYacimientos.DataSource = FrmPrincipal.Yacimientos; }
private void btnRegistrar_Click(object sender, EventArgs e) { // 1. Validar campos no vacios if (txtCodigo.Text == "" || txtNombre.Text == "" || cboRegion.SelectedIndex == -1 || txtReserva.Text == "") { MessageBox.Show("Debe completar todos los campos."); return; } string codigo = txtCodigo.Text; // 2. Validar codigo unico (en yacimientos y en pozos) foreach (Yacimiento ya in FrmPrincipal.Yacimientos) { if (ya.Codigo == codigo) { MessageBox.Show("El codigo ya existe."); return; } foreach (Pozo p in ya.Pozos) { if (p.Codigo == codigo) { MessageBox.Show("El codigo ya existe (esta usado por un pozo)."); return; } } } // 3. Crear el yacimiento e inicializar la lista de pozos Yacimiento y = new Yacimiento(); y.Codigo = codigo; y.Nombre = txtNombre.Text; y.Region = cboRegion.Text; y.Reserva = double.Parse(txtReserva.Text); y.Pozos = new List<Pozo>(); FrmPrincipal.Yacimientos.Add(y); MostrarYacimientos(); txtCodigo.Clear(); txtNombre.Clear(); txtReserva.Clear(); cboRegion.SelectedIndex = -1; MessageBox.Show("Yacimiento registrado."); }
return; para que la ejecucion no continue creando el objeto si algo no cuadro.
FrmPozo.cs.| Tipo | Name | Para |
|---|---|---|
| ComboBox | cboYacimiento | Selecciona el yacimiento destino |
| TextBox | txtCodigo | Codigo del pozo (CST-001, etc.) |
| TextBox | txtNombre | Nombre tecnico |
| ComboBox | cboExtraccion | Convencional / No Convencional |
| TextBox | txtBarriles | Barriles/dia |
| Button | btnRegistrar | Registrar pozo |
| DataGridView | dgvPozos | Pozos del yacimiento seleccionado |
using PeruvianOilCompany.entities; private void FrmPozo_Load(object sender, EventArgs e) { // Combo de yacimientos: muestra Nombre, identifica por Codigo cboYacimiento.DataSource = null; cboYacimiento.DataSource = FrmPrincipal.Yacimientos; cboYacimiento.DisplayMember = "Nombre"; // Combo de tipo de extraccion cboExtraccion.Items.Add("Convencional"); cboExtraccion.Items.Add("No Convencional"); } private void cboYacimiento_SelectedIndexChanged(object sender, EventArgs e) { if (cboYacimiento.SelectedItem == null) return; Yacimiento y = (Yacimiento)cboYacimiento.SelectedItem; dgvPozos.DataSource = null; dgvPozos.DataSource = y.Pozos; }
private void btnRegistrar_Click(object sender, EventArgs e) { // 1. Validar yacimiento seleccionado if (cboYacimiento.SelectedItem == null) { MessageBox.Show("Seleccione un yacimiento."); return; } // 2. Validar campos no vacios if (txtCodigo.Text == "" || txtNombre.Text == "" || cboExtraccion.SelectedIndex == -1 || txtBarriles.Text == "") { MessageBox.Show("Debe completar todos los campos."); return; } string codigo = txtCodigo.Text; // 3. Validar codigo unico global (yacimientos y pozos) foreach (Yacimiento ya in FrmPrincipal.Yacimientos) { if (ya.Codigo == codigo) { MessageBox.Show("El codigo ya existe."); return; } foreach (Pozo pp in ya.Pozos) { if (pp.Codigo == codigo) { MessageBox.Show("El codigo ya existe."); return; } } } // 4. Validar produccion no negativa int barriles = int.Parse(txtBarriles.Text); if (barriles < 0) { MessageBox.Show("La produccion no puede ser negativa."); return; } // 5. Crear el pozo y agregarlo al yacimiento seleccionado Yacimiento y = (Yacimiento)cboYacimiento.SelectedItem; Pozo p = new Pozo(); p.Codigo = codigo; p.NombreTecnico = txtNombre.Text; p.TipoExtraccion = cboExtraccion.Text; p.ProduccionDiaria = barriles; y.Pozos.Add(p); dgvPozos.DataSource = null; dgvPozos.DataSource = y.Pozos; txtCodigo.Clear(); txtNombre.Clear(); txtBarriles.Clear(); cboExtraccion.SelectedIndex = -1; MessageBox.Show("Pozo registrado."); }
FrmReportes.cs.| Tipo | Name | Para |
|---|---|---|
| ComboBox | cboReportes | Lista los 4 reportes |
| Button | btnEjecutar | Ejecuta el reporte seleccionado |
| DataGridView | dgvReportes | Muestra el resultado |
using PeruvianOilCompany.entities; using System.Collections.Generic; private void FrmReportes_Load(object sender, EventArgs e) { cboReportes.Items.Add("1. Yacimientos ordenados por reserva (asc)"); cboReportes.Items.Add("2. Yacimiento(s) con menor numero de pozos"); cboReportes.Items.Add("3. Selva con al menos un pozo No Convencional"); cboReportes.Items.Add("4. Sin pozos con produccion mayor a 500"); } private void btnEjecutar_Click(object sender, EventArgs e) { switch (cboReportes.SelectedIndex) { case 0: Reporte1(); break; case 1: Reporte2(); break; case 2: Reporte3(); break; case 3: Reporte4(); break; default: MessageBox.Show("Seleccione un reporte."); break; } }
private void Reporte1() { List<Yacimiento> ordenados = new List<Yacimiento>(); // Copiar la lista para no modificar la original foreach (Yacimiento y in FrmPrincipal.Yacimientos) { ordenados.Add(y); } // Bubble sort ascendente por Reserva for (int i = 0; i < ordenados.Count - 1; i++) { for (int j = 0; j < ordenados.Count - 1 - i; j++) { if (ordenados[j].Reserva > ordenados[j + 1].Reserva) { Yacimiento tmp = ordenados[j]; ordenados[j] = ordenados[j + 1]; ordenados[j + 1] = tmp; } } } dgvReportes.DataSource = null; dgvReportes.DataSource = ordenados; }
private void Reporte2() { List<Yacimiento> lista = FrmPrincipal.Yacimientos; if (lista.Count == 0) return; // 1) Buscar el menor numero de pozos int menor = lista[0].Pozos.Count; foreach (Yacimiento y in lista) { if (y.Pozos.Count < menor) menor = y.Pozos.Count; } // 2) Filtrar los que tienen ese minimo (puede haber empates) List<Yacimiento> resultado = new List<Yacimiento>(); foreach (Yacimiento y in lista) { if (y.Pozos.Count == menor) resultado.Add(y); } dgvReportes.DataSource = null; dgvReportes.DataSource = resultado; }
private void Reporte3() { List<Yacimiento> resultado = new List<Yacimiento>(); foreach (Yacimiento y in FrmPrincipal.Yacimientos) { if (y.Region != "Selva") continue; bool tieneNoConvencional = false; foreach (Pozo p in y.Pozos) { if (p.TipoExtraccion == "No Convencional") { tieneNoConvencional = true; break; } } if (tieneNoConvencional) resultado.Add(y); } dgvReportes.DataSource = null; dgvReportes.DataSource = resultado; }
private void Reporte4() { List<Yacimiento> resultado = new List<Yacimiento>(); foreach (Yacimiento y in FrmPrincipal.Yacimientos) { bool tieneAlguno = false; foreach (Pozo p in y.Pozos) { if (p.ProduccionDiaria > 500) { tieneAlguno = true; break; } } if (!tieneAlguno) resultado.Add(y); } dgvReportes.DataSource = null; dgvReportes.DataSource = resultado; }
! en la bandera.
Presiona F5 y prueba con estos datos para verificar los 4 reportes:
| Yacimiento | Region | Reserva | Pozos |
|---|---|---|---|
| Y001 · Lobitos | Costa | 120.5 | CST-001 (Convencional, 600), CST-002 (Convencional, 450) |
| Y002 · Aguaytia | Selva | 85.0 | SLV-001 (No Convencional, 320) |
| Y003 · Camisea | Selva | 200.0 | SLV-002 (Convencional, 200), SLV-003 (No Convencional, 800) |
| Y004 · Talara | Costa | 50.0 | (sin pozos) |
Resultados esperados:
| Error | Causa | Solucion |
|---|---|---|
| NullReferenceException al agregar un Pozo | El Yacimiento se creo sin inicializar la lista Pozos |
Antes de FrmPrincipal.Yacimientos.Add(y) hacer y.Pozos = new List<Pozo>(); |
| El combo de yacimientos en FrmPozo aparece vacio | La lista no es static o se declaro en otro lugar |
Asegurar que FrmPrincipal.Yacimientos sea public static |
| El DataGridView muestra columnas raras como "Capacity" | Esta enlazado a un objeto con propiedades publicas extras | Solo exponer las propiedades relevantes en la entidad. No agregar metodos publicos a la clase entidad |
| Despues de cerrar FrmYacimiento el combo del FrmPozo no muestra los nuevos | El Form_Load del FrmPozo solo se ejecuta una vez al abrirse | Cerrar y reabrir el FrmPozo, o recargar el combo en un boton "Refrescar" |
| FormatException al hacer int.Parse(txtBarriles.Text) | El usuario escribio letras o lo dejo vacio | Validar antes con int.TryParse, o validar que no sea vacio antes de parsear |
| El reporte 2 muestra cero filas | No hay yacimientos registrados o todos tienen pozos | Probar con los datos del paso 19, en particular incluir un yacimiento sin pozos |
| El codigo se repite entre yacimientos y pozos | La validacion de codigo unico solo revisa entre yacimientos o solo entre pozos | Usar el doble foreach del paso 10 para revisar tanto yacimientos como pozos |