Manual paso a paso · Peruvian Oil Company

Caso: Yacimientos y Pozos · Windows Forms con C# · solo entidades

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.

Lo que vas a construir:

Resumen del caso

Yacimiento
Pozo

Arquitectura del proyecto final

PeruvianOilCompany/
├── 📂 entities/ CAPA ENTIDADsolo propiedades
│ ├── Pozo.cs
│ └── Yacimiento.cs
├── FrmPrincipal.cs PRESENTACIONmenu + lista compartida
├── FrmYacimiento.cs PRESENTACIONregistro de yacimientos
├── FrmPozo.cs PRESENTACIONregistro de pozos del yacimiento
├── FrmReportes.cs PRESENTACIONlos 4 reportes
└── Program.cs
NOTA. Esta version del manual usa la estructura mas simple: solo 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.
📋 INDICE

Setup

  1. Crear el proyecto en Visual Studio
  2. Crear la carpeta entities

Capa Entidad

  1. Crear entities/Pozo.cs
  2. Crear entities/Yacimiento.cs

Form menu · FrmPrincipal

  1. Disenar FrmPrincipal y declarar la lista compartida
  2. Codigo de los 3 botones del menu
  3. Configurar Program.cs

Form · FrmYacimiento

  1. Disenar FrmYacimiento
  2. Cargar combo Region en Form_Load
  3. btnRegistrar_Click (registrar yacimiento)

Form · FrmPozo

  1. Disenar FrmPozo
  2. Cargar combos Yacimiento y Extraccion en Form_Load
  3. btnRegistrar_Click (registrar pozo en yacimiento)

Form · FrmReportes

  1. Disenar FrmReportes y cargar combo de reportes
  2. Reporte 1 · ordenar yacimientos por reserva
  3. Reporte 2 · yacimiento(s) con menor numero de pozos
  4. Reporte 3 · Selva con al menos un pozo No Convencional
  5. Reporte 4 · sin pozos con produccion mayor a 500 barriles

Cierre

  1. Probar el programa completo
  2. Errores comunes y como evitarlos

PASO 1 · Crear el proyecto en Visual Studio

PASO 1Punto de partida
  1. Abre Visual Studio y elige Create a new project.
  2. Busca y selecciona Windows Forms App (.NET Framework) con C#.
  3. Nombre del proyecto: PeruvianOilCompany. Framework: .NET Framework 4.7.2.
  4. Visual Studio te abre el proyecto con Form1.cs, Program.cs y la carpeta Properties.

PASO 2 · Crear la carpeta entities

PASO 2Estructura del proyecto

En el Solution Explorer, click derecho sobre el proyecto PeruvianOilCompany y elige Add → New Folder:

PASO 3 · Crear entities/Pozo.cs

PASO 3Entidad Pozo ENTIDAD
📍 Donde: click derecho en entitiesAdd → 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; }
    }
}
NOTA. El Codigo del pozo es string porque trae letras (CST-, SRA-, SLV-). La ProduccionDiaria es entera (barriles por dia).

PASO 4 · Crear entities/Yacimiento.cs

PASO 4Entidad Yacimiento (con lista anidada de Pozos) ENTIDAD
📍 Donde: click derecho en entitiesAdd → 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; }
    }
}
NOTA. La Reserva es double porque puede tener decimales (millones de barriles). La lista Pozos es la multilista que vamos a usar para los reportes.

PASO 5 · Disenar FrmPrincipal y declarar la lista compartida

PASO 5Form menu del programa PRESENTACION
📍 Donde: renombra Form1.cs a FrmPrincipal.cs (click derecho → Rename, acepta el cambio en todas las referencias).

Arrastra desde el Toolbox y configura:

TipoNombre (Name)Texto (Text)
FormFrmPrincipalPeruvian Oil Company
ButtonbtnYacimientoYacimiento
ButtonbtnPozoPozo
ButtonbtnReportesReportes

Distribuye los 3 botones uno debajo del otro, centrados:

📺 Vista previa: FrmPrincipal
Peruvian Oil Company - Menu×
Peruvian Oil Company
Yacimiento

Pozo

Reportes

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();
        }
    }
}
Por que 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.

PASO 6 · Codigo de los 3 botones del menu

PASO 6Eventos de FrmPrincipal PRESENTACION
📍 Donde: doble clic sobre cada boton para que se cree el handler. Pega el codigo dentro.
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();
}
NOTA. Visual Studio te dira que FrmYacimiento, FrmPozo y FrmReportes no existen aun. Los crearemos en los siguientes pasos. Ignora los errores rojos por ahora.

PASO 7 · Configurar Program.cs

PASO 7Punto de entrada PRESENTACION
📍 Donde: abre Program.cs y reemplaza la linea Application.Run(new Form1()); por:
Application.Run(new FrmPrincipal());

PASO 8 · Disenar FrmYacimiento

PASO 8Form de registro de yacimientos PRESENTACION
📍 Donde: click derecho en el proyecto → Add → Windows Form. Nombre: FrmYacimiento.cs.
TipoNamePara
TextBoxtxtCodigoCodigo del yacimiento
TextBoxtxtNombreNombre
ComboBoxcboRegionCosta / Sierra / Selva
TextBoxtxtReservaReserva en millones de barriles
ButtonbtnRegistrarRegistrar yacimiento
DataGridViewdgvYacimientosMostrar la lista
📺 Vista previa: FrmYacimiento
Yacimiento×
Datos
Operaciones
Registrar

PASO 9 · Cargar combo Region en Form_Load

PASO 9FrmYacimiento_Load PRESENTACION
📍 Donde: doble clic sobre el Form (no sobre un control) para crear el evento Load.
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;
}

PASO 10 · btnRegistrar_Click (registrar yacimiento)

PASO 10Registrar yacimiento con validaciones PRESENTACION
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.");
}
Patron clave: al final de las validaciones siempre va un return; para que la ejecucion no continue creando el objeto si algo no cuadro.

PASO 11 · Disenar FrmPozo

PASO 11Form de registro de pozos PRESENTACION
📍 Donde: click derecho en el proyecto → Add → Windows Form. Nombre: FrmPozo.cs.
TipoNamePara
ComboBoxcboYacimientoSelecciona el yacimiento destino
TextBoxtxtCodigoCodigo del pozo (CST-001, etc.)
TextBoxtxtNombreNombre tecnico
ComboBoxcboExtraccionConvencional / No Convencional
TextBoxtxtBarrilesBarriles/dia
ButtonbtnRegistrarRegistrar pozo
DataGridViewdgvPozosPozos del yacimiento seleccionado
📺 Vista previa: FrmPozo
Pozo×
Datos del Pozo
Operaciones
Registrar

PASO 12 · Cargar combos Yacimiento y Extraccion en Form_Load

PASO 12FrmPozo_Load PRESENTACION
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;
}

PASO 13 · btnRegistrar_Click (registrar pozo en yacimiento)

PASO 13Registrar pozo con validaciones PRESENTACION
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.");
}

PASO 14 · Disenar FrmReportes y cargar combo de reportes

PASO 14Form de reportes PRESENTACION
📍 Donde: click derecho en el proyecto → Add → Windows Form. Nombre: FrmReportes.cs.
TipoNamePara
ComboBoxcboReportesLista los 4 reportes
ButtonbtnEjecutarEjecuta el reporte seleccionado
DataGridViewdgvReportesMuestra el resultado
📺 Vista previa: FrmReportes
Reportes×
Reporte
Ejecutar
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;
    }
}

PASO 15 · Reporte 1 · ordenar yacimientos por reserva

PASO 15Listar yacimientos ordenados ascendentemente segun su reserva PRESENTACION
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;
}

PASO 16 · Reporte 2 · yacimiento(s) con menor numero de pozos

PASO 16Encontrar minimo + listar empates PRESENTACION
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;
}
Patron clave: en cualquier reporte que pida "el / los con menor / mayor X", primero recorres para encontrar el valor extremo y despues recorres de nuevo para incluir todos los que empatan en ese valor.

PASO 17 · Reporte 3 · Selva con al menos un pozo No Convencional

PASO 17foreach anidado + bandera PRESENTACION
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;
}

PASO 18 · Reporte 4 · sin pozos con produccion mayor a 500 barriles

PASO 18Negacion del foreach anidado PRESENTACION
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;
}
NOTA. Los reportes 3 y 4 son casi simetricos. El 3 dice "al menos uno cumple", el 4 dice "ninguno cumple". Se invierte la condicion final con ! en la bandera.

PASO 19 · Probar el programa completo

PASO 19Caso de prueba sugerido

Presiona F5 y prueba con estos datos para verificar los 4 reportes:

YacimientoRegionReservaPozos
Y001 · LobitosCosta120.5CST-001 (Convencional, 600), CST-002 (Convencional, 450)
Y002 · AguaytiaSelva85.0SLV-001 (No Convencional, 320)
Y003 · CamiseaSelva200.0SLV-002 (Convencional, 200), SLV-003 (No Convencional, 800)
Y004 · TalaraCosta50.0(sin pozos)

Resultados esperados:

PASO 20 · Errores comunes y como evitarlos

PASO 20Cierre del manual
ErrorCausaSolucion
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
Listo. Si llegaste hasta aqui ya tienes la solucion completa del parcial Tema A con la estructura mas simple posible. Repasa los 4 patrones de reporte (ordenar, minimo+empates, foreach anidado con bandera, negacion) porque son los que se repiten en cualquier ejercicio de multilistas.