Ir al contenido principal

Introducción a Windows Forms

Windows Forms es la tecnología tradicional de Microsoft para crear aplicaciones de escritorio con interfaz gráfica de usuario (GUI) en el ecosistema .NET. Aunque han surgido tecnologías más modernas como WPF y WinUI, Windows Forms sigue siendo ampliamente utilizado debido a su simplicidad, madurez y la gran cantidad de aplicaciones existentes que lo emplean.

Esta tecnología permite crear aplicaciones nativas para Windows con una interfaz rica e interactiva, utilizando un modelo de programación orientada a eventos. Windows Forms proporciona un conjunto completo de controles visuales, desde botones y cajas de texto hasta componentes más complejos como grillas de datos y menús, todos ellos totalmente integrados con el sistema operativo Windows.

En este artículo exploraremos los fundamentos de Windows Forms, desde la creación de un proyecto básico hasta la comprensión de los conceptos esenciales que necesitas dominar para desarrollar aplicaciones de escritorio efectivas.

Arquitectura y conceptos fundamentales

Modelo de aplicación Windows Forms

Windows Forms se basa en un modelo de programación orientada a eventos donde la aplicación responde a las acciones del usuario y del sistema. Esta arquitectura se compone de varios elementos fundamentales:

Componente Descripción Propósito
Application Clase principal que gestiona el ciclo de vida de la aplicación Controlar inicio, cierre y bucle de mensajes
Form Ventana principal o secundaria de la aplicación Contenedor visual para controles y lógica de interfaz
Controls Elementos visuales interactivos (botones, etiquetas, etc.) Permitir interacción del usuario con la aplicación
Events Mecanismo de comunicación basado en acciones Responder a clicks, cambios de texto, etc.
Designer Herramienta visual para diseñar interfaces Crear interfaces gráficamente sin escribir código

El bucle de mensajes y eventos

Windows Forms utiliza el sistema de mensajes de Windows para manejar la comunicación entre la aplicación y el sistema operativo:

using System;
using System.Windows.Forms;

namespace IntroduccionWindowsForms
{
    // Clase principal de la aplicación
    static class Program
    {
        /// <summary>
        /// Punto de entrada principal para la aplicación.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // Configuración de la aplicación
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            // Iniciar el bucle de mensajes con el formulario principal
            Application.Run(new FormularioPrincipal());
        }
    }
    
    // Ejemplo de manejo del bucle de mensajes personalizado
    public partial class FormularioPrincipal : Form
    {
        public FormularioPrincipal()
        {
            InitializeComponent();
            
            // Suscribirse a eventos del formulario
            this.Load += FormularioPrincipal_Load;
            this.FormClosing += FormularioPrincipal_FormClosing;
        }
        
        private void FormularioPrincipal_Load(object sender, EventArgs e)
        {
            Console.WriteLine("Formulario cargado y listo para interacción");
        }
        
        private void FormularioPrincipal_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Confirmar cierre de aplicación
            var resultado = MessageBox.Show(
                "¿Está seguro que desea cerrar la aplicación?",
                "Confirmar cierre",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);
            
            if (resultado == DialogResult.No)
            {
                e.Cancel = true; // Cancelar el cierre
            }
        }
    }
}

Creación del primer proyecto Windows Forms

Configuración inicial del proyecto

Para crear un proyecto Windows Forms en .NET 8, podemos utilizar tanto Visual Studio como la línea de comandos. El proceso implica configurar correctamente las referencias y la estructura básica:

<!-- Archivo de proyecto (.csproj) para Windows Forms -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

Estructura básica de un formulario

Un formulario en Windows Forms hereda de la clase Form y puede crearse tanto por código como mediante el diseñador visual:

using System;
using System.Drawing;
using System.Windows.Forms;

public partial class MiPrimerFormulario : Form
{
    // Componentes de la interfaz
    private Button botonSaludar;
    private TextBox cajaTexto;
    private Label etiquetaResultado;
    private MenuStrip menuPrincipal;
    private StatusStrip barraEstado;
    
    public MiPrimerFormulario()
    {
        InitializeComponent();
        ConfigurarFormulario();
        CrearControles();
        ConfigurarEventos();
    }
    
    private void ConfigurarFormulario()
    {
        // Propiedades básicas del formulario
        this.Text = "Mi Primera Aplicación Windows Forms";
        this.Size = new Size(400, 300);
        this.StartPosition = FormStartPosition.CenterScreen;
        this.MinimumSize = new Size(300, 200);
        this.MaximizeBox = true;
        this.MinimizeBox = true;
        this.FormBorderStyle = FormBorderStyle.Sizable;
        
        // Configurar icono (si está disponible)
        try
        {
            this.Icon = new Icon("icono.ico");
        }
        catch
        {
            // Icono no disponible, usar el predeterminado
        }
    }
    
    private void CrearControles()
    {
        // Crear y configurar la caja de texto
        cajaTexto = new TextBox
        {
            Location = new Point(20, 20),
            Size = new Size(200, 23),
            PlaceholderText = "Introduce tu nombre aquí...",
            Font = new Font("Segoe UI", 9F)
        };
        
        // Crear y configurar el botón
        botonSaludar = new Button
        {
            Location = new Point(230, 20),
            Size = new Size(100, 25),
            Text = "Saludar",
            UseVisualStyleBackColor = true,
            Font = new Font("Segoe UI", 9F)
        };
        
        // Crear y configurar la etiqueta de resultado
        etiquetaResultado = new Label
        {
            Location = new Point(20, 60),
            Size = new Size(300, 50),
            Text = "Haz clic en 'Saludar' para ver el mensaje",
            ForeColor = Color.Blue,
            Font = new Font("Segoe UI", 10F, FontStyle.Italic),
            AutoSize = false,
            TextAlign = ContentAlignment.MiddleLeft
        };
        
        // Crear barra de menú
        menuPrincipal = new MenuStrip();
        CrearMenu();
        
        // Crear barra de estado
        barraEstado = new StatusStrip();
        var etiquetaEstado = new ToolStripStatusLabel("Listo");
        barraEstado.Items.Add(etiquetaEstado);
        
        // Agregar controles al formulario
        this.Controls.Add(cajaTexto);
        this.Controls.Add(botonSaludar);
        this.Controls.Add(etiquetaResultado);
        this.Controls.Add(menuPrincipal);
        this.Controls.Add(barraEstado);
        
        // Configurar el menú y la barra de estado
        this.MainMenuStrip = menuPrincipal;
    }
    
    private void CrearMenu()
    {
        // Menú Archivo
        var menuArchivo = new ToolStripMenuItem("&Archivo");
        menuArchivo.DropDownItems.Add("&Nuevo", null, MenuNuevo_Click);
        menuArchivo.DropDownItems.Add("&Abrir", null, MenuAbrir_Click);
        menuArchivo.DropDownItems.Add(new ToolStripSeparator());
        menuArchivo.DropDownItems.Add("&Salir", null, MenuSalir_Click);
        
        // Menú Ayuda
        var menuAyuda = new ToolStripMenuItem("&Ayuda");
        menuAyuda.DropDownItems.Add("&Acerca de...", null, MenuAcercaDe_Click);
        
        menuPrincipal.Items.AddRange(new ToolStripMenuItem[] { menuArchivo, menuAyuda });
    }
    
    private void ConfigurarEventos()
    {
        // Evento del botón
        botonSaludar.Click += BotonSaludar_Click;
        
        // Evento de la caja de texto (Enter para activar)
        cajaTexto.KeyPress += CajaTexto_KeyPress;
        
        // Establecer el botón por defecto
        this.AcceptButton = botonSaludar;
        
        // Configurar el foco inicial
        this.Load += (sender, e) => cajaTexto.Focus();
    }
    
    // Manejadores de eventos
    private void BotonSaludar_Click(object sender, EventArgs e)
    {
        string nombre = cajaTexto.Text.Trim();
        
        if (string.IsNullOrEmpty(nombre))
        {
            MessageBox.Show(
                "Por favor, introduce tu nombre primero.",
                "Campo requerido",
                MessageBoxButtons.OK,
                MessageBoxIcon.Warning);
            cajaTexto.Focus();
            return;
        }
        
        etiquetaResultado.Text = $"¡Hola, {nombre}! Bienvenido a Windows Forms.";
        etiquetaResultado.ForeColor = Color.Green;
        
        // Actualizar barra de estado
        if (barraEstado.Items.Count > 0)
        {
            ((ToolStripStatusLabel)barraEstado.Items[0]).Text = 
                $"Último saludo: {DateTime.Now:HH:mm:ss}";
        }
    }
    
    private void CajaTexto_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)Keys.Enter)
        {
            e.Handled = true; // Evitar el sonido de error
            BotonSaludar_Click(sender, EventArgs.Empty);
        }
    }
    
    // Manejadores de eventos del menú
    private void MenuNuevo_Click(object sender, EventArgs e)
    {
        cajaTexto.Clear();
        etiquetaResultado.Text = "Haz clic en 'Saludar' para ver el mensaje";
        etiquetaResultado.ForeColor = Color.Blue;
        cajaTexto.Focus();
    }
    
    private void MenuAbrir_Click(object sender, EventArgs e)
    {
        using (var dialogo = new OpenFileDialog())
        {
            dialogo.Filter = "Archivos de texto|*.txt|Todos los archivos|*.*";
            dialogo.Title = "Seleccionar archivo de texto";
            
            if (dialogo.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string contenido = System.IO.File.ReadAllText(dialogo.FileName);
                    MessageBox.Show($"Archivo cargado:\n{contenido.Substring(0, Math.Min(100, contenido.Length))}...",
                                  "Contenido del archivo",
                                  MessageBoxButtons.OK,
                                  MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Error al leer el archivo:\n{ex.Message}",
                                  "Error",
                                  MessageBoxButtons.OK,
                                  MessageBoxIcon.Error);
                }
            }
        }
    }
    
    private void MenuSalir_Click(object sender, EventArgs e)
    {
        this.Close();
    }
    
    private void MenuAcercaDe_Click(object sender, EventArgs e)
    {
        MessageBox.Show(
            "Mi Primera Aplicación Windows Forms\n" +
            "Versión 1.0\n\n" +
            "Una demostración básica de Windows Forms en C#",
            "Acerca de",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }
}

Controles básicos y sus propiedades

Catálogo de controles esenciales

Windows Forms proporciona una amplia gama de controles para diferentes propósitos. Aquí están los más utilizados organizados por categoría:

Categoría Control Propósito principal Propiedades clave
Entrada TextBox Entrada de texto simple Text, Multiline, PasswordChar
  RichTextBox Entrada de texto con formato Rtf, SelectionFont, SelectionColor
  NumericUpDown Entrada numérica con controles Value, Minimum, Maximum
  MaskedTextBox Entrada con formato específico Mask, ValidatingType
Selección ComboBox Lista desplegable de opciones Items, SelectedItem, DropDownStyle
  ListBox Lista de elementos seleccionables Items, SelectedItems, SelectionMode
  CheckBox Opción booleana Checked, ThreeState
  RadioButton Selección exclusiva Checked, GroupBox para agrupación
Navegación Button Acción mediante click Text, Image, FlatStyle
  MenuStrip Menú principal de aplicación Items, MdiWindowListItem
  ToolStrip Barra de herramientas Items, GripStyle
  TabControl Pestañas de navegación TabPages, SelectedTab
Visualización Label Mostrar texto estático Text, AutoSize, TextAlign
  PictureBox Mostrar imágenes Image, SizeMode
  ProgressBar Indicador de progreso Value, Maximum, Style
Contenedores Panel Agrupación visual de controles BorderStyle, AutoScroll
  GroupBox Agrupación lógica con borde Text como título del grupo
  SplitContainer División ajustable del espacio Orientation, SplitterDistance

Ejemplo práctico con múltiples controles

public partial class FormularioCompleto : Form
{
    // Controles de entrada
    private TextBox txtNombre;
    private TextBox txtApellidos;
    private NumericUpDown nudEdad;
    private ComboBox cmbPais;
    private CheckBox chkNewsletterr;
    private RadioButton rbMasculino, rbFemenino, rbOtro;
    
    // Controles de visualización
    private Label lblResultado;
    private ProgressBar progressBar;
    private PictureBox picAvatar;
    
    // Controles de navegación
    private Button btnGuardar;
    private Button btnLimpiar;
    private Button btnCancelar;
    
    // Contenedores
    private GroupBox grpDatosPersonales;
    private GroupBox grpGenero;
    private Panel pnlBotones;
    
    public FormularioCompleto()
    {
        InitializeComponent();
        CrearInterfazCompleta();
    }
    
    private void CrearInterfazCompleta()
    {
        // Configuración del formulario
        this.Text = "Registro de Usuario - Demostración Completa";
        this.Size = new Size(500, 450);
        this.StartPosition = FormStartPosition.CenterScreen;
        this.FormBorderStyle = FormBorderStyle.FixedDialog;
        this.MaximizeBox = false;
        
        CrearControles();
        ConfigurarLayout();
        ConfigurarEventos();
        CargarDatosIniciales();
    }
    
    private void CrearControles()
    {
        // Grupo de datos personales
        grpDatosPersonales = new GroupBox
        {
            Text = "Datos Personales",
            Location = new Point(10, 10),
            Size = new Size(460, 150)
        };
        
        // Campos de texto
        var lblNombre = new Label { Text = "Nombre:", Location = new Point(15, 25), Size = new Size(60, 23) };
        txtNombre = new TextBox { Location = new Point(80, 22), Size = new Size(150, 23) };
        
        var lblApellidos = new Label { Text = "Apellidos:", Location = new Point(250, 25), Size = new Size(60, 23) };
        txtApellidos = new TextBox { Location = new Point(315, 22), Size = new Size(130, 23) };
        
        var lblEdad = new Label { Text = "Edad:", Location = new Point(15, 55), Size = new Size(60, 23) };
        nudEdad = new NumericUpDown 
        { 
            Location = new Point(80, 52), 
            Size = new Size(60, 23),
            Minimum = 0,
            Maximum = 120,
            Value = 18
        };
        
        var lblPais = new Label { Text = "País:", Location = new Point(160, 55), Size = new Size(40, 23) };
        cmbPais = new ComboBox 
        { 
            Location = new Point(205, 52), 
            Size = new Size(120, 23),
            DropDownStyle = ComboBoxStyle.DropDownList
        };
        
        chkNewsletterr = new CheckBox 
        { 
            Text = "Suscribirse al boletín de noticias",
            Location = new Point(15, 85),
            Size = new Size(250, 23)
        };
        
        // Avatar
        picAvatar = new PictureBox
        {
            Location = new Point(350, 52),
            Size = new Size(80, 80),
            BorderStyle = BorderStyle.FixedSingle,
            SizeMode = PictureBoxSizeMode.Zoom,
            BackColor = Color.LightGray
        };
        
        // Agregar controles al grupo
        grpDatosPersonales.Controls.AddRange(new Control[] 
        {
            lblNombre, txtNombre, lblApellidos, txtApellidos,
            lblEdad, nudEdad, lblPais, cmbPais, chkNewsletterr, picAvatar
        });
        
        // Grupo de género
        grpGenero = new GroupBox
        {
            Text = "Género",
            Location = new Point(10, 170),
            Size = new Size(200, 90)
        };
        
        rbMasculino = new RadioButton { Text = "Masculino", Location = new Point(15, 25), Checked = true };
        rbFemenino = new RadioButton { Text = "Femenino", Location = new Point(15, 45) };
        rbOtro = new RadioButton { Text = "Otro/Prefiero no decir", Location = new Point(15, 65) };
        
        grpGenero.Controls.AddRange(new RadioButton[] { rbMasculino, rbFemenino, rbOtro });
        
        // Barra de progreso y etiqueta de resultado
        lblResultado = new Label
        {
            Location = new Point(220, 180),
            Size = new Size(250, 60),
            Text = "Complete el formulario y presione 'Guardar'",
            ForeColor = Color.Blue,
            Font = new Font("Segoe UI", 9F, FontStyle.Italic)
        };
        
        progressBar = new ProgressBar
        {
            Location = new Point(220, 245),
            Size = new Size(250, 15),
            Style = ProgressBarStyle.Continuous
        };
        
        // Panel de botones
        pnlBotones = new Panel
        {
            Location = new Point(10, 280),
            Size = new Size(460, 50),
            BorderStyle = BorderStyle.FixedSingle
        };
        
        btnGuardar = new Button
        {
            Text = "Guardar",
            Location = new Point(10, 10),
            Size = new Size(80, 30),
            BackColor = Color.LightGreen
        };
        
        btnLimpiar = new Button
        {
            Text = "Limpiar",
            Location = new Point(100, 10),
            Size = new Size(80, 30),
            BackColor = Color.LightYellow
        };
        
        btnCancelar = new Button
        {
            Text = "Cancelar",
            Location = new Point(190, 10),
            Size = new Size(80, 30),
            BackColor = Color.LightCoral
        };
        
        pnlBotones.Controls.AddRange(new Button[] { btnGuardar, btnLimpiar, btnCancelar });
        
        // Agregar controles principales al formulario
        this.Controls.AddRange(new Control[] 
        {
            grpDatosPersonales, grpGenero, lblResultado, progressBar, pnlBotones
        });
    }
    
    private void ConfigurarLayout()
    {
        // Configurar anclajes para redimensionamiento (aunque esté deshabilitado)
        grpDatosPersonales.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
        pnlBotones.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
        
        // Configurar orden de tabulación
        txtNombre.TabIndex = 0;
        txtApellidos.TabIndex = 1;
        nudEdad.TabIndex = 2;
        cmbPais.TabIndex = 3;
        chkNewsletterr.TabIndex = 4;
        rbMasculino.TabIndex = 5;
        btnGuardar.TabIndex = 6;
        
        // Establecer botón por defecto
        this.AcceptButton = btnGuardar;
        this.CancelButton = btnCancelar;
    }
    
    private void ConfigurarEventos()
    {
        // Eventos de botones
        btnGuardar.Click += BtnGuardar_Click;
        btnLimpiar.Click += BtnLimpiar_Click;
        btnCancelar.Click += BtnCancelar_Click;
        
        // Eventos de validación
        txtNombre.TextChanged += ValidarFormulario;
        txtApellidos.TextChanged += ValidarFormulario;
        nudEdad.ValueChanged += ValidarFormulario;
        cmbPais.SelectedIndexChanged += ValidarFormulario;
        
        // Evento para seleccionar avatar
        picAvatar.Click += PicAvatar_Click;
    }
    
    private void CargarDatosIniciales()
    {
        // Cargar países
        string[] paises = { "España", "México", "Argentina", "Colombia", "Chile", "Perú", "Venezuela", "Uruguay" };
        cmbPais.Items.AddRange(paises);
        cmbPais.SelectedIndex = 0; // España por defecto
        
        // Imagen por defecto para el avatar
        CrearImagenAvatar();
        
        ValidarFormulario(null, null);
    }
    
    private void CrearImagenAvatar()
    {
        // Crear una imagen simple por código
        var bitmap = new Bitmap(80, 80);
        using (var g = Graphics.FromImage(bitmap))
        {
            g.Clear(Color.LightBlue);
            g.FillEllipse(Brushes.Gray, 10, 10, 60, 60);
            g.DrawString("Avatar", new Font("Arial", 8), Brushes.White, 20, 35);
        }
        picAvatar.Image = bitmap;
    }
    
    private void ValidarFormulario(object sender, EventArgs e)
    {
        bool formularioValido = !string.IsNullOrWhiteSpace(txtNombre.Text) &&
                               !string.IsNullOrWhiteSpace(txtApellidos.Text) &&
                               cmbPais.SelectedItem != null;
        
        btnGuardar.Enabled = formularioValido;
        
        // Actualizar progreso
        int progreso = 0;
        if (!string.IsNullOrWhiteSpace(txtNombre.Text)) progreso += 25;
        if (!string.IsNullOrWhiteSpace(txtApellidos.Text)) progreso += 25;
        if (nudEdad.Value > 0) progreso += 25;
        if (cmbPais.SelectedItem != null) progreso += 25;
        
        progressBar.Value = progreso;
        
        if (formularioValido)
        {
            lblResultado.Text = "Formulario listo para guardar";
            lblResultado.ForeColor = Color.Green;
        }
        else
        {
            lblResultado.Text = "Complete todos los campos requeridos";
            lblResultado.ForeColor = Color.Red;
        }
    }
    
    private void BtnGuardar_Click(object sender, EventArgs e)
    {
        string genero = rbMasculino.Checked ? "Masculino" : 
                       rbFemenino.Checked ? "Femenino" : "Otro";
        
        string mensaje = $"Datos guardados:\n\n" +
                        $"Nombre: {txtNombre.Text} {txtApellidos.Text}\n" +
                        $"Edad: {nudEdad.Value} años\n" +
                        $"País: {cmbPais.SelectedItem}\n" +
                        $"Género: {genero}\n" +
                        $"Newsletter: {(chkNewsletterr.Checked ? "Sí" : "No")}";
        
        MessageBox.Show(mensaje, "Registro Completado", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    
    private void BtnLimpiar_Click(object sender, EventArgs e)
    {
        txtNombre.Clear();
        txtApellidos.Clear();
        nudEdad.Value = 18;
        cmbPais.SelectedIndex = 0;
        chkNewsletterr.Checked = false;
        rbMasculino.Checked = true;
        txtNombre.Focus();
    }
    
    private void BtnCancelar_Click(object sender, EventArgs e)
    {
        this.Close();
    }
    
    private void PicAvatar_Click(object sender, EventArgs e)
    {
        using (var dialogo = new OpenFileDialog())
        {
            dialogo.Filter = "Archivos de imagen|*.jpg;*.jpeg;*.png;*.gif;*.bmp";
            dialogo.Title = "Seleccionar avatar";
            
            if (dialogo.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    picAvatar.Image = Image.FromFile(dialogo.FileName);
                }
                catch
                {
                    MessageBox.Show("Error al cargar la imagen", "Error", 
                                  MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
    }
}

Manejo de eventos esenciales

Tipos de eventos más utilizados

En Windows Forms, los eventos son el mecanismo principal de interacción. Los eventos más importantes se clasifican en varias categorías:

Categoría Evento Cuándo se produce Uso típico
Mouse Click Click simple del mouse Activar acciones principales
  DoubleClick Doble click del mouse Edición rápida o apertura
  MouseEnter/MouseLeave Mouse entra/sale del control Efectos visuales, tooltips
  MouseDown/MouseUp Presionar/soltar botón del mouse Arrastrar elementos
Teclado KeyPress Tecla presionada (carácter) Validación de entrada
  KeyDown/KeyUp Tecla presionada/liberada (cualquiera) Atajos de teclado
Formulario Load Formulario se ha cargado Inicialización
  FormClosing Antes de cerrar el formulario Validación antes del cierre
  Resize Tamaño del formulario cambia Ajustar layout
Controles TextChanged Texto de un control cambia Validación en tiempo real
  SelectedIndexChanged Selección en lista cambia Actualizar controles relacionados
  CheckedChanged Estado de checkbox cambia Habilitar/deshabilitar opciones

Implementación práctica de eventos

public partial class FormularioEventos : Form
{
    private Button btnEjemplo;
    private TextBox txtMonitoreo;
    private Label lblEstado;
    private ListBox lstEventos;
    
    public FormularioEventos()
    {
        InitializeComponent();
        CrearInterfazEventos();
        ConfigurarEventosDetallados();
    }
    
    private void CrearInterfazEventos()
    {
        this.Text = "Demostración de Eventos en Windows Forms";
        this.Size = new Size(600, 400);
        
        // Botón para demostrar eventos del mouse
        btnEjemplo = new Button
        {
            Text = "Botón de Ejemplo",
            Location = new Point(20, 20),
            Size = new Size(120, 40),
            BackColor = Color.LightBlue
        };
        
        // TextBox para eventos de teclado
        txtMonitoreo = new TextBox
        {
            Location = new Point(20, 80),
            Size = new Size(200, 23),
            PlaceholderText = "Escribe aquí para ver eventos..."
        };
        
        // Label para mostrar estado actual
        lblEstado = new Label
        {
            Location = new Point(20, 120),
            Size = new Size(400, 30),
            Text = "Estado: Listo",
            Font = new Font("Segoe UI", 10F, FontStyle.Bold),
            ForeColor = Color.Blue
        };
        
        // ListBox para mostrar historial de eventos
        lstEventos = new ListBox
        {
            Location = new Point(20, 160),
            Size = new Size(540, 150),
            Font = new Font("Consolas", 8F)
        };
        
        // Agregar controles al formulario
        this.Controls.AddRange(new Control[] { btnEjemplo, txtMonitoreo, lblEstado, lstEventos });
    }
    
    private void ConfigurarEventosDetallados()
    {
        // Eventos del botón
        btnEjemplo.Click += (s, e) => RegistrarEvento("Botón: Click");
        btnEjemplo.DoubleClick += (s, e) => RegistrarEvento("Botón: DoubleClick");
        btnEjemplo.MouseEnter += (s, e) => 
        {
            btnEjemplo.BackColor = Color.LightGreen;
            lblEstado.Text = "Estado: Mouse sobre el botón";
            RegistrarEvento("Botón: MouseEnter");
        };
        btnEjemplo.MouseLeave += (s, e) => 
        {
            btnEjemplo.BackColor = Color.LightBlue;
            lblEstado.Text = "Estado: Listo";
            RegistrarEvento("Botón: MouseLeave");
        };
        btnEjemplo.MouseDown += (s, e) => 
        {
            btnEjemplo.BackColor = Color.Orange;
            RegistrarEvento($"Botón: MouseDown - {e.Button}");
        };
        btnEjemplo.MouseUp += (s, e) => 
        {
            btnEjemplo.BackColor = Color.LightGreen;
            RegistrarEvento($"Botón: MouseUp - {e.Button}");
        };
        
        // Eventos del TextBox
        txtMonitoreo.TextChanged += (s, e) => 
        {
            lblEstado.Text = $"Estado: Texto cambiado - Longitud: {txtMonitoreo.Text.Length}";
            RegistrarEvento($"TextBox: TextChanged - '{txtMonitoreo.Text}'");
        };
        
        txtMonitoreo.KeyPress += (s, e) => 
        {
            RegistrarEvento($"TextBox: KeyPress - Carácter: '{e.KeyChar}' (ASCII: {(int)e.KeyChar})");
            
            // Ejemplo: bloquear números
            if (char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
            {
                e.Handled = true; // Cancelar el carácter
                RegistrarEvento("TextBox: Número bloqueado");
            }
        };
        
        txtMonitoreo.KeyDown += (s, e) => 
        {
            RegistrarEvento($"TextBox: KeyDown - Tecla: {e.KeyCode}");
            
            // Ejemplo: atajo de teclado Ctrl+A para seleccionar todo
            if (e.Control && e.KeyCode == Keys.A)
            {
                txtMonitoreo.SelectAll();
                RegistrarEvento("TextBox: Seleccionar todo (Ctrl+A)");
            }
        };
        
        txtMonitoreo.Enter += (s, e) => 
        {
            txtMonitoreo.BackColor = Color.LightYellow;
            RegistrarEvento("TextBox: Enter (foco obtenido)");
        };
        
        txtMonitoreo.Leave += (s, e) => 
        {
            txtMonitoreo.BackColor = SystemColors.Window;
            RegistrarEvento("TextBox: Leave (foco perdido)");
        };
        
        // Eventos del formulario
        this.Load += (s, e) => RegistrarEvento("Formulario: Load");
        this.Activated += (s, e) => RegistrarEvento("Formulario: Activated");
        this.Deactivate += (s, e) => RegistrarEvento("Formulario: Deactivate");
        this.Resize += (s, e) => RegistrarEvento($"Formulario: Resize - {this.Size}");
        
        // Evento de cierre con confirmación
        this.FormClosing += (s, e) => 
        {
            RegistrarEvento("Formulario: FormClosing");
            var resultado = MessageBox.Show(
                "¿Desea cerrar la aplicación de demostración?",
                "Confirmar cierre",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);
            
            if (resultado == DialogResult.No)
            {
                e.Cancel = true;
                RegistrarEvento("Formulario: Cierre cancelado");
            }
        };
        
        // Eventos globales del mouse en el formulario
        this.MouseMove += (s, e) => 
        {
            lblEstado.Text = $"Estado: Mouse en ({e.X}, {e.Y})";
            // No registrar MouseMove para evitar spam en el log
        };
    }
    
    private void RegistrarEvento(string mensaje)
    {
        string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
        string eventoCompleto = $"[{timestamp}] {mensaje}";
        
        lstEventos.Items.Insert(0, eventoCompleto);
        
        // Limitar a 100 elementos para evitar consumo excesivo de memoria
        if (lstEventos.Items.Count > 100)
        {
            lstEventos.Items.RemoveAt(lstEventos.Items.Count - 1);
        }
        
        // Mostrar el evento más reciente
        lstEventos.TopIndex = 0;
    }
}