Fins ara, BiblioTech ha viscut sencer a la consola: cada préstec, cada alta de soci, cada consulta al catàleg s'ha mostrat amb Console.WriteLine i s'ha rebut amb Console.ReadLine. Aquesta lliçó fa el primer pas cap a una interfície real, amb Windows Forms (sovint abreujat WinForms), el framework d'interfície gràfica més antic de .NET que encara es fa servir. Tot i que avui existeixen alternatives més modernes (WPF, Blazor, MAUI, que veuràs a les properes lliçons), Windows Forms continua sent una opció legítima i molt productiva per a un tipus concret d'aplicació: eines internes d'escriptori a Windows, sense pretensions visuals sofisticades, on la velocitat de desenvolupament importa més que l'estètica. Construiràs un formulari que mostra el catàleg de BiblioTech i permet prestar un llibre amb un clic, reutilitzant exactament la mateixa lògica de domini dels mòduls anteriors.

Contingut

  1. Què és Windows Forms i quan té sentit fer-lo servir avui
  2. Crear un projecte amb dotnet new winforms
  3. El dissenyador de formularis i els controls bàsics
  4. El model d'esdeveniments dels controls: Click i els delegats del Mòdul 4
  5. Exemple complet: un formulari de préstecs per a BiblioTech
  6. Bones pràctiques en connectar la interfície amb el domini

  1. Què és Windows Forms i quan té sentit fer-lo servir avui

Windows Forms va aparèixer l'any 2002, juntament amb la primera versió de .NET, i va ser durant anys la forma estàndard de construir aplicacions d'escriptori en C#. El seu model és senzill: un formulari (Form) és una finestra, sobre la qual es col·loquen controls (botons, caixes de text, llistes...) mitjançant un dissenyador visual d'arrossegar i deixar anar. Cada control exposa propietats (posició, text, color) i esdeveniments (Click, TextChanged) als quals el programador hi enganxa codi.

Continua tenint sentit el 2026? Sí, per a un perfil d'aplicació molt concret:

Característica Windows Forms
Plataforma Només Windows (no és multiplataforma)
Corba d'aprenentatge Molt baixa: el dissenyador visual genera gairebé tot el codi de la interfície
Aspecte visual Funcional, amb l'estil natiu de Windows; no està pensat per a dissenys sofisticats
Separació UI/lògica Feble per defecte (el codi d'esdeveniments sol barrejar-se amb la lògica)
Cas d'ús típic Eines internes d'empresa, utilitats administratives, aplicacions de gestió senzilles
Alternativa moderna recomanada per a una UI més acurada WPF (lliçó següent)

Per a BiblioTech, Windows Forms encaixa perfectament en un escenari: una aplicació interna que fa servir el personal de la biblioteca als ordinadors de taulell, sense necessitat d'instal·lar res més que .NET, i sense que l'aspecte visual sigui una prioritat davant la rapidesa de tenir alguna cosa funcionant.

  1. Crear un projecte amb dotnet new winforms

Igual que en mòduls anteriors vas crear projectes de consola amb dotnet new console, Windows Forms té la seva pròpia plantilla:

dotnet new winforms -n BiblioTech.Escriptori
cd BiblioTech.Escriptori

Això genera una estructura mínima:

BiblioTech.Escriptori/
├── Program.cs
├── Form1.cs
├── Form1.Designer.cs
└── Form1.resx
  • Program.cs: el punt d'entrada, que arrenca l'aplicació i mostra el primer formulari.
  • Form1.cs: el codi del formulari que tu escrius (esdeveniments, lògica).
  • Form1.Designer.cs: codi generat automàticament pel dissenyador visual; no s'edita a mà en circumstàncies normals.
  • Form1.resx: recursos del formulari (icones, textos incrustats).
// Program.cs generat per la plantilla
ApplicationConfiguration.Initialize();
Application.Run(new Form1());

Application.Run(new Form1()) crea una instància del formulari principal i entra en el bucle de missatges de Windows: un bucle intern, gestionat pel framework, que espera esdeveniments del sistema operatiu (clics de ratolí, pulsacions de teclat, repintats de pantalla) i els envia al formulari corresponent. Aquest bucle és exactament el que fa que un clic en un botó acabi executant el teu codi: no és màgia, és el mateix model d'esdeveniments que ja coneixes del Mòdul 4, aplicat ara a la interacció de l'usuari amb el ratolí i el teclat.

  1. El dissenyador de formularis i els controls bàsics

El dissenyador de formularis (disponible a Visual Studio a Windows) permet arrossegar controls des d'una caixa d'eines fins al formulari, i ajustar-ne les propietats des d'un panell lateral, sense escriure codi a mà. Els controls més habituals:

Control Per a què serveix Propietat principal
Label Mostrar text no editable (títols, etiquetes) Text
TextBox Entrada de text d'una línia Text
Button Disparar una acció en fer clic Text (el text del botó)
ListBox Llista simple d'elements seleccionables Items, SelectedItem
DataGridView Taula amb files i columnes, ideal per a col·leccions d'objectes DataSource

Quan arrossegues un Button al formulari i li canvies la propietat Text a "Prestar", el dissenyador escriu automàticament alguna cosa així a Form1.Designer.cs (codi generat, no manual):

// Form1.Designer.cs (fragment generat pel dissenyador)
private Button botoPrestar;

private void InitializeComponent()
{
    botoPrestar = new Button();
    botoPrestar.Location = new Point(120, 200);
    botoPrestar.Size = new Size(100, 30);
    botoPrestar.Text = "Prestar";
    botoPrestar.Click += botoPrestar_Click; // connexio de l'esdeveniment, veure apartat 4
    // ...
    Controls.Add(botoPrestar);
}

Controls.Add(botoPrestar) afegeix el botó a la col·lecció Controls del formulari —el conjunt de controls fills que es dibuixen sobre seu—; sense aquesta línia, el botó existiria com a objecte en memòria però mai apareixeria en pantalla.

  1. El model d'esdeveniments dels controls: Click i els delegats del Mòdul 4

La lliçó de Delegats i Esdeveniments (Mòdul 4) va explicar que un esdeveniment és, en essència, una llista de mètodes (delegats) que s'executen quan alguna cosa passa —allà l'exemple va ser Biblioteca.PrestecRegistrat—. Els controls de Windows Forms fan servir exactament el mateix mecanisme: Button.Click és un esdeveniment de tipus EventHandler, al qual et subscrius amb +=, igual que vas fer amb PrestecRegistrat:

public partial class FormulariPrincipal : Form
{
    public FormulariPrincipal()
    {
        InitializeComponent(); // generat pel dissenyador: crea i col·loca els controls

        botoPrestar.Click += BotoPrestar_Click; // subscripcio a l'esdeveniment, igual que al Modul 4
    }

    private void BotoPrestar_Click(object? remitent, EventArgs arguments)
    {
        // codi que s'executa cada vegada que l'usuari fa clic a "Prestar"
    }
}

La signatura (object? remitent, EventArgs arguments) és el patró estàndard d'esdeveniments de .NET: remitent és l'objecte que ha disparat l'esdeveniment (aquí, botoPrestar) i arguments transporta informació addicional (buida a Click, però no en altres esdeveniments com KeyPress, que inclou la tecla premuda). Reconèixer aquesta signatura és clau: és la mateixa estructura object, EventArgs que fan servir pràcticament tots els esdeveniments de la biblioteca estàndard de .NET, no només els de Windows Forms.

  1. Exemple complet: un formulari de préstecs per a BiblioTech

Es reutilitza el domini ja construït —Biblioteca, MaterialBibliotecari, Llibre, Soci— sense modificar ni una sola classe. El formulari només afegeix una capa visual a sobre:

// FormulariPrincipal.cs
public partial class FormulariPrincipal : Form
{
    private readonly Biblioteca _biblioteca;

    public FormulariPrincipal(Biblioteca biblioteca)
    {
        InitializeComponent();
        _biblioteca = biblioteca;

        CarregarCatalegEnListBox();

        botoPrestar.Click += BotoPrestar_Click;
    }

    private void CarregarCatalegEnListBox()
    {
        llistaCataleg.Items.Clear();

        foreach (MaterialBibliotecari material in _biblioteca.Cataleg)
        {
            // ToString() no esta sobreescrit a MaterialBibliotecari, aixi que el text es compon aqui
            llistaCataleg.Items.Add($"{material.Titol} ({material.Autor})");
        }
    }

    private void BotoPrestar_Click(object? remitent, EventArgs arguments)
    {
        int indexSeleccionat = llistaCataleg.SelectedIndex;

        if (indexSeleccionat < 0)
        {
            MessageBox.Show("Selecciona un llibre de la llista abans de prestar.", "Avis");
            return;
        }

        MaterialBibliotecari materialSeleccionat = _biblioteca.Cataleg[indexSeleccionat];

        if (!materialSeleccionat.Disponible)
        {
            MessageBox.Show($"'{materialSeleccionat.Titol}' ja esta prestat.", "Avis");
            return;
        }

        materialSeleccionat.Prestar(); // logica de domini ja existent, Modul 2

        MessageBox.Show($"Prestec registrat: {materialSeleccionat.Titol}", "Prestec realitzat");

        CarregarCatalegEnListBox(); // refresca la llista per reflectir el nou estat
    }
}
// Program.cs
Biblioteca biblioteca = new Biblioteca();
biblioteca.AfegirMaterial(new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4"));
biblioteca.AfegirMaterial(new Llibre("Ficciones", "Jorge Luis Borges", "978-84-376-0496-1"));

ApplicationConfiguration.Initialize();
Application.Run(new FormulariPrincipal(biblioteca));

Cap concepte de Biblioteca, MaterialBibliotecari o Llibre ha canviat: el formulari es limita a llegir _biblioteca.Cataleg per pintar la ListBox, i a cridar materialSeleccionat.Prestar() quan l'usuari fa clic —exactament el mateix mètode que ja feies servir des de la consola al Mòdul 2. MessageBox.Show(...) mostra un quadre de diàleg emergent, la forma més simple de comunicar un missatge a l'usuari en Windows Forms, l'equivalent visual del Console.WriteLine dels mòduls anteriors.

Per a un catàleg més gran, un DataGridView amb llistaCataleg.DataSource = _biblioteca.Cataleg (en lloc d'una ListBox emplenada manualment) mostraria automàticament una taula amb una columna per propietat pública, tot i que caldria ajustar quines columnes mostrar per no exposar Disponible com a text pla sense format.

  1. Bones pràctiques en connectar la interfície amb el domini

  • El formulari no ha de contenir lògica de negoci: BotoPrestar_Click crida materialSeleccionat.Prestar(), no reimplementa la comprovació de disponibilitat dins del formulari —aquesta lògica ja viu, correctament, a MaterialBibliotecari des del Mòdul 2.
  • Passar Biblioteca al constructor del formulari, com es fa aquí, en lloc de crear-la dins del mateix formulari: manté el formulari desacoblat de com es construeix o configura Biblioteca (amb persistència en text, JSON, SQLite... segons el que s'ha vist al Mòdul 5), un principi que es retomarà amb més profunditat al Mòdul 8 en parlar d'injecció de dependències.
  • Refrescar la interfície després de cada canvi d'estat: com a CarregarCatalegEnListBox() després de Prestar(), perquè el que veu l'usuari coincideixi sempre amb l'estat real de _biblioteca.

Errors Comuns i Consells

  • Editar Form1.Designer.cs a mà: és codi generat pel dissenyador visual; qualsevol canvi fet a mà es pot perdre la propera vegada que s'editi el formulari des del dissenyador. Els canvis de comportament van al fitxer .cs "normal" (Form1.cs), no al .Designer.cs.
  • Oblidar comprovar SelectedIndex < 0: si l'usuari fa clic a "Prestar" sense haver seleccionat res a la ListBox, SelectedIndex val -1; accedir al catàleg amb aquest índex llançaria una excepció en temps d'execució.
  • Barrejar lògica de negoci dins del mètode d'esdeveniment: duplicar aquí la comprovació de Disponible en lloc de delegar-la a Prestar() (que ja la fa, Mòdul 2) generaria dues fonts de veritat que podrien desincronitzar-se amb el temps.
  • Consell: Windows Forms només compila i s'executa a Windows; si el teu equip de desenvolupament fa servir sistemes operatius diferents, tingues-ho en compte abans de triar-lo per a un projecte nou.

Exercicis

  1. Afegeix a FormulariPrincipal un TextBox anomenat quadreTitol i un segon Button anomenat botoCercar. A l'esdeveniment Click de botoCercar, filtra _biblioteca.Cataleg per títol (fent servir Contains, ja vist amb cadenes al Mòdul 1) i mostra només els resultats a llistaCataleg.

  2. Modifica BotoPrestar_Click perquè, en lloc de MessageBox.Show, mostri el missatge de confirmació en un Label anomenat etiquetaEstat situat a la part inferior del formulari.

Solucions

private void BotoCercar_Click(object? remitent, EventArgs arguments)
{
    string textCerca = quadreTitol.Text;

    llistaCataleg.Items.Clear();

    IEnumerable<MaterialBibliotecari> coincidencies = _biblioteca.Cataleg
        .Where(material => material.Titol.Contains(textCerca, StringComparison.OrdinalIgnoreCase));

    foreach (MaterialBibliotecari material in coincidencies)
    {
        llistaCataleg.Items.Add($"{material.Titol} ({material.Autor})");
    }
}
private void BotoPrestar_Click(object? remitent, EventArgs arguments)
{
    int indexSeleccionat = llistaCataleg.SelectedIndex;

    if (indexSeleccionat < 0)
    {
        etiquetaEstat.Text = "Selecciona un llibre de la llista abans de prestar.";
        return;
    }

    MaterialBibliotecari materialSeleccionat = _biblioteca.Cataleg[indexSeleccionat];

    if (!materialSeleccionat.Disponible)
    {
        etiquetaEstat.Text = $"'{materialSeleccionat.Titol}' ja esta prestat.";
        return;
    }

    materialSeleccionat.Prestar();
    etiquetaEstat.Text = $"Prestec registrat: {materialSeleccionat.Titol}";

    CarregarCatalegEnListBox();
}

Conclusió

En aquesta lliçó has construït la primera interfície gràfica real de BiblioTech amb Windows Forms: un projecte dotnet new winforms, controls bàsics (ListBox, Button, Label) col·locats amb el dissenyador visual, i el model d'esdeveniments dels controls (Click), que resulta ser exactament el mateix mecanisme de delegats i esdeveniments del Mòdul 4 aplicat a la interacció de l'usuari. Tot això sense tocar ni una línia de Biblioteca, MaterialBibliotecari o Llibre: la interfície és una capa nova a sobre del domini ja construït.

La lliçó següent presenta WPF, una alternativa més moderna a Windows Forms, amb una diferència de fons important: en lloc de col·locar controls imperativament des del dissenyador, WPF descriu la interfície de forma declarativa amb XAML, i separa amb més disciplina la interfície de la lògica mitjançant el patró MVVM i el data binding — veuràs el mateix catàleg de BiblioTech, però connectat a la interfície d'una manera força diferent.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats