Requerimiento
Se solicitó desarrollar una calculadora que permita realizar operaciones básicas como suma, resta, multiplicación y división. Esta debe estar desarrollada en Windows Forms, una conjunto de librerías de .NET que permite crear aplicaciones de escritorio con interfaces gráficas.
Además, se requiere que la calculadora tenga una interfaz gráfica amigable para el usuario en base a lo aprendido en UX/UI de primer año.
En este ejemplo se realizará lo requerido, pero se hará con un enfoque completo, es decir, se aplicarán los conceptos de Programación Orientada a Objetos para estructurar el código de manera eficiente y mantenible.
Contexto
Para mantener la simplicidad en esta instancia inicial, el proyecto se mantendrá en una estructura mínima. Evitaremos separar el código en capas (como Modelos, Vistas y Controladores) para enfocarnos exclusivamente en la lógica de los objetos.
Diseño
Se optó por el siguiente diseño, basado en la calculadora KCalc de KDE, que es una calculadora de código abierto y muy utilizada en sistemas operativos basados en Linux.
Este diseño es simple, intuitivo y fácil de usar, lo que lo hace ideal para este proyecto.
KCalc

Nuestra Calculadora

Creación
Con el fin de intentar utilizar los mismos colores, empezamos por establecer algunas de las propiedades del Form, como el BackColor y el ForeColor, para que se asemeje al diseño de KCalc.
Propiedades del Form:
Padding: 20; 20; 20; 20
Espaciado interno del Form, para que los controles no estén pegados a los bordes.
Size: 550;520
Tamaño del Form.
Icon: Un ícono de nuestra preferencia.
Esto es opcional, pero le da un toque más profesional a la aplicación.
Text: Calculadora
El título del Form, que se muestra en la barra de título.
ForeColor: White
Color blanco para el texto, para que contraste con el fondo oscuro.
BackColor: 44; 44; 44
Un color oscuro para el fondo, similar al de KCalc.
Name: frmCalc
Nombre descriptivo para identificar el Form.
TableLayoutPanel
Un TableLayoutPanel es un control que permite organizar otros controles en una cuadrícula de filas y columnas. Es ideal para este proyecto, ya que nos permite organizar los botones de la calculadora de manera ordenada y consistente.
Si observamos el diseño de KCalc, podemos ver que los botones están organizados en una cuadrícula de 6 columnas (la 5 y la 6 unidas) y 5 filas:

Por lo tanto, crearemos un TableLayoutPanel con 6 columnas y 5 filas para organizar los botones de nuestra calculadora.
Para esto, agregamos un TableLayoutPanel al Form y configuramos sus propiedades:
Anchor: Top, Bottom, Left, Right
Esto hace que el TableLayoutPanel se ajuste al tamaño del Form, lo que es importante para mantener un diseño consistente cuando el usuario redimensiona la ventana (Diseño Responsivo).
Rows: Collection -> Agregar 5 filas y que todas usen el 50%.

Columns: Collection -> Agregar 6 columnas y que todas usen el 50%.

Name: tlpButtons
Nombre descriptivo para identificar el TableLayoutPanel.
Debería quedar algo así hasta ahora:

Panel de Pantalla
En el diseño de KCalc, hay dos controles en la parte superior que muestran la operación actual y el resultado. Para esto, agregaremos dos TextBox en la parte superior del Form, justo arriba del TableLayoutPanel, para mostrar la operación actual y el resultado, o bien, podríamos crear un nuevo TableLayoutPanel (el cual en este caso llamamos tlpDisplays) con dos filas y una sola columna para organizar estos TextBox de manera ordenada.

Propiedades de los TextBox:
Dock: Fill
Esto hace que los TextBox usen todo el espacio disponible en su celda del TableLayoutPanel
TextAlign: Right
Esto alinea el texto a la derecha, lo que es común en las calculadoras para que los números se alineen a la derecha a medida que se ingresan.
ReadOnly: True
Esto hace que el usuario no pueda escribir directamente en los TextBox, ya que solo se mostrarán los resultados de las operaciones.
ForeColor: White
Color del texto en los TextBox, para que contraste con el fondo oscuro.
BackColor: 49; 49; 49
Color de fondo de los TextBox.
Font: Arial, 20pt para tbOperacion y Arial, 35pt para tbResultado
Fuente y tamaño del texto en los TextBox.
Name: tbOperacion y tbResultado
Nombres descriptivos para identificar cada TextBox.
Botones
Ahora, agregaremos los botones a cada celda del TableLayoutPanel, siguiendo el diseño de KCalc. Para esto, arrastramos y soltamos botones desde la caja de herramientas a cada celda del TableLayoutPanel.
Recomendamos agregar y configurar uno, luego copiarlo y pegarlo para mantener la consistencia en el diseño y las propiedades de los botones, modificando únicamente el texto y el nombre de cada botón.
Propiedades de los botones:
FlatAppearance -> BorderSize: 0
Esto elimina el borde del botón
FlatAppearance tiene otras propiedades que recomendamos echar un vistazo, como el MouseOverBackColor, que permite cambiar el color del botón cuando el usuario pasa el mouse por encima, lo que mejora la experiencia del usuario (UX).
FlatStyle: Flat
Esto hace que el botón tenga un estilo plano, sin bordes ni sombras
Dock: Fill
Esto hace que el botón use todo el espacio disponible en su celda del TableLayoutPanel
Name: Un nombre descriptivo para cada botón (ejemplo: btnSuma, btnResta, etc.).
Esto es importante para identificar cada botón en el código y asignarles eventos de clic.
Text: El texto que corresponda a cada botón.
El texto que se muestra en cada botón, para que el usuario sepa qué función tiene cada uno.
ForeColor: White
Color del texto en los botones, para que contraste con el fondo oscuro.
BackColor: 49; 49; 49
Color de fondo de los botones.
Font: Arial, 15pt
Fuente y tamaño del texto en los botones.
Botones largos
En el diseño de KCalc, los botones C, AC, (, ) y +/- son más largos que los demás, ya que ocupan el espacio de dos columnas. Para lograr esto, debemos configurar la propiedad ColumnSpan de estos botones para que ocupen dos columnas en el TableLayoutPanel en lugar de una. Para esto, seleccionamos cada uno de estos botones, vamos a la propiedad ColumnSpan y la configuramos a 2.
Con ColumnSpan = 1:

Con ColumnSpan = 2:

Con todo esto realizado, deberíamos tener una interfaz gráfica similar a la de KCalc, con un diseño limpio y profesional, y lista para ser funcionalizada con código en C#.
Código
Estructura del proyecto
Debemos comprender antes de agregar código, que el framework de Windows Forms ya nos proporciona una estructura básica para nuestra aplicación:
Program.cs: Es el punto de entrada de la aplicación (El código que se ejecuta al iniciar la aplicación)frmCalc.Designer.cs: Contiene el código generado automáticamente por el diseñador de Windows Forms para crear la interfaz gráfica.
Form1.cs: Archivo que contiene la clase del formulario principal de la calculadora. En este caso cambiaremos también el nombre del archivo a frmCalc.cs, para que sea más descriptivo y fácil de identificar (igual que la propiedad Name del Form).

Estructura de la clase frmCalc
Podremos observar el siguiente código en frmCalc.cs:
public partial class frmCalc : Form
public frmCalc()
Si lo desglosamos, vemos que:
public: Es un modificador de acceso que indica que la clasefrmCalces accesible desde cualquier parte del programa.partial: Indica que la definición de la clasefrmCalcestá dividida en varios archivos. En este caso, la definición de la clase está dividida entrefrmCalc.csyfrmCalc.Designer.cs. Esto es común en Windows Forms, ya que el código generado por el diseñador se mantiene separado del código que escribimos manualmente, lo que facilita la organización y el mantenimiento del código.class frmCalc: Define una clase llamadafrmCalc, que es el formulario principal de nuestra calculadora. Esta clase hereda deForm, lo que significa quefrmCalces un tipo de formulario de Windows Forms y puede utilizar todas las funcionalidades que ofrece la claseForm.public frmCalc(): Es el constructor de la clasefrmCalc. El constructor es un método especial que se ejecuta automáticamente cuando se crea una instancia de la clase. En este caso, el constructor se utiliza para inicializar el formulario y configurar cualquier lógica adicional que necesitemos al iniciar la aplicación.
El código
Constructor
El constructor se ejecuta al crear una instancia del formulario.
public frmCalc(): constructor principal del formulario.InitializeComponent(): inicializa y dibuja en pantalla todos los controles creados en el diseñador de Windows Forms.
Constantes
Comenzaremos abriendo el archivo frmCalc.cs. Este será el archivo donde escribiremos la mayoría del código de nuestra calculadora, ya que el archivo frmCalc.Designer.cs está destinado únicamente para el código generado por el diseñador de Windows Forms, y no es recomendable modificarlo manualmente, ya que cualquier cambio que hagamos allí podría ser sobrescrito por el diseñador. Por otra parte, el archivo Program.cs solo contiene el código de inicio de la aplicación, y no es necesario modificarlo para nuestro proyecto.
Lo primero que haremos es crear algunas constantes en frmCalc.cs que nos van a servir para validar posteriormente, como una lista de operaciones y una lista de números, para asegurarnos de que el usuario solo ingrese caracteres válidos en la calculadora.

Eventos de los botones
Lo normal es que cada botón tenga un evento de clic asociado. Que hagamos doble clic en cada botón en el diseñador de Windows Forms y que genere un método para cada uno de ellos.
El problema de esto es que vamos a terminar con demasiados métodos, uno para cada botón, y el código va a quedar muy desorganizado y difícil de trabajar. Además, muchos de estos métodos van a tener código repetido, lo que no es una buena práctica de programación.
Para evitar esto, podemos asignar el mismo evento de clic a todos los botones que tengan una función similar.
En este caso lo que queremos es que se escriban en la pantalla de la calculadora los números y las operaciones que el usuario ingrese, por lo que podemos asignar el mismo evento de clic a todos los botones numéricos, a los paréntesis, la coma y a las operaciones, ya que todos estos botones van a tener una función similar: agregar su texto al TextBox de la operación actual.
Para esto, seleccionamos los botones mencionados manteniendo presionada la tecla Shift y haciendo clic en cada uno de ellos.

Luego, vamos a la ventana de propiedades, buscamos el evento Click y asignamos el mismo método para todos los botones seleccionados, por ejemplo, btnAppendTextClick:

Esto hará que cuando el usuario haga clic en cualquiera de estos botones, se ejecute el mismo método btnAppendTextClick, lo que nos permitirá manejar la lógica de agregar el texto al TextBox de manera centralizada y evitar código repetido.
Método btnAppendTextClick
El método btnAppendTextClick se ejecutará cada vez que el usuario haga clic en cualquiera de los botones numéricos, de operaciones, paréntesis o coma. Este método se encargará de agregar el texto del botón al TextBox de la operación actual.
Para lograr esto, deberíamos agregar el siguiente código al método btnAppendTextClick:

Desglosemos el código:
private void btnAppendTextClick(object sender, EventArgs e): Este es el método que se ejecuta cuando se hace clic en cualquiera de los botones seleccionados. Es un método privado, lo que significa que solo puede ser llamado desde dentro de la clasefrmCalc.
El método no devuelve ningún valor (void) y recibe dos parámetros:sender, que es el objeto que generó el evento (en este caso, el botón que se hizo clic), ye, que contiene información adicional sobre el evento (en este caso, no se utiliza).Button btn = (Button)sender;: Este código convierte el objetosender(que es el botón que se hizo clic) a un tipoButton, lo que nos permite acceder a sus propiedades, como el texto del botón.tbOperacion.Text += btn.Text;: Este código agrega el texto del botón al TextBoxtbOperacion, que es donde se muestra la operación actual. El operador+=se utiliza para concatenar el texto del botón al texto existente en el TextBox, lo que permite que el usuario vea la operación completa a medida que ingresa los números y las operaciones.
Hasta ahora, con este código, cada vez que el usuario haga clic en un botón numérico, de operación, paréntesis o coma, el texto de ese botón se agregará al TextBox de la operación actual, permitiendo al usuario construir la operación que desea calcular.
Aunque este método es funcional, no tiene ninguna validación, lo que significa que el usuario podría ingresar una operación con una sintaxis incorrecta, como ++ o 1..2, lo que causaría un error al intentar calcular el resultado. Para evitar esto, es necesario agregar validaciones adicionales para asegurarnos de que la operación ingresada por el usuario tenga una sintaxis correcta antes de intentar calcular el resultado. Esto se puede hacer en el mismo método btnAppendTextClick o en un método separado que se llame antes de calcular el resultado.
Validaciones en btnAppendTextClick
Para evitar sintaxis inválida al escribir operaciones, agregaremos algunas validaciones dentro del mismo método.

¿Qué validamos?
- Si el botón presionado es un número, lo agregamos directamente al TextBox y terminamos el método con
return. - Si el botón presionado es una operación, verificamos si ya hay texto en pantalla.
- Si ya hay texto, revisamos el último carácter escrito.
- Si el último carácter también es una operación, reemplazamos ese último carácter por el nuevo operador en lugar de agregar otro.
Con esto evitamos casos como 5++3, 9--2, 4//2, etc.
¿Qué es DataTable.Compute y por qué lo usamos?
DataTable.Compute es un método de .NET que permite evaluar expresiones escritas como texto (por ejemplo, "(3+2)*4") y devolver el resultado.
En otras palabras: recibe una cadena con una operación matemática y la calcula sin que tengamos que programar un parser matemático desde cero.
¿Por qué lo usamos en esta calculadora?
- Será nuestra herramienta principal para evaluar las operaciones que el usuario ingresa en la calculadora.
- Simplificará mucho la implementación del botón
=. - Evita tener que desarrollar manualmente la precedencia de operadores (
*,/,+,-, paréntesis, etc.). - Es una solución rápida y eficiente para el propósito de esta calculadora básica.
Botón de igual (=)
Ahora vamos a crear la lógica del botón igual para evaluar la expresión.
Como en el resto de los casos, asignamos el evento Click del botón = a un método, por ejemplo: btnEqualClick.

Método btnEqualClick
Este método se debe encargar de:
- Limpiar el resultado anterior.
- Preparar la expresión para que tenga una sintaxis válida.
- Transformar formatos que
DataTable.Computeno entiende directamente (como ciertos porcentajes). - Evaluar la expresión.
- Mostrar el resultado o un mensaje de error de sintaxis.

Desglosemos los puntos importantes:
- Bloque
try/catchEncerramos la evaluación en untry/catchporque cualquier error de formato o sintaxis lanzará una excepción. Así evitamos que la app se cierre inesperadamente. - Paréntesis sin cerrarAntes de evaluar, verificamos si existe
(y no existe). En ese caso:- Si la expresión termina en
(, se limpia el último carácter usando el botón de borrado. - Si no termina en
(, se agrega automáticamente)para cerrar la expresión.
- Si la expresión termina en
- Conversión de porcentaje
DataTable.Computeno interpreta directamente una expresión como50%100con el significado "50 por ciento de 100".Por eso usamosRegex.Replacepara transformarlo a:Ese formato sí puede evaluarse correctamente.- Entrada:
50%100 - Salida:
(100*50)/100
- Entrada:
- Decimales con comaEl evaluador espera punto decimal en lugar de comas, por lo que reemplazamos
,por.antes de calcular. - Evaluación de la expresiónSe crea un
DataTabley se usa el métodoComputepara evaluar el string matemático. - Manejo de erroresSi ocurre una
DataException, escribimos el error técnico en consola de depuración conDebug.WriteLiney mostramos al usuario el texto definido en la constanteSYNTAX_ERROR.
Botón de borrado (C)
Este botón elimina el último carácter de la operación actual.
Asignamos su evento Click al método btnClearClick.

Método btnClearClick

Lógica:
- Si no hay texto en pantalla, no hace nada.
- Si hay texto, recorta el último carácter con
Substring.
Esto permite corregir rápidamente la operación sin limpiar todo.
Botón de limpieza total (AC)
Este botón reinicia la calculadora visualmente, limpiando operación y resultado.
Asignamos su evento Click al método btnClearAllClick.

Método btnClearAllClick

Lógica:
tbCalc.Text = string.Empty;tbResult.Text = string.Empty;
Con esto dejamos la calculadora en estado inicial.
Botón de negación (+/-)
Este botón cambia el signo del resultado actual.
Asignamos su evento Click al método btnNegateClick.

Método btnNegateClick

Lógica:
- Primero llama a
btnEqualClickpara asegurarse de que la expresión sea válida y obtener un resultado actualizado. - Si el resultado es
Error de sintaxis, no hace nada. - Si el resultado no empieza con
-, le agrega-al inicio. - Si el resultado sí empieza con
-, elimina ese primer carácter para volverlo positivo.
Asignación final de eventos
Al terminar, deberíamos tener asignados los eventos de esta forma:
- Botones numéricos, coma, paréntesis y operadores:
btnAppendTextClick - Botón
=:btnEqualClick - Botón
C:btnClearClick - Botón
AC:btnClearAllClick - Botón
+/-:btnNegateClick
Resultado final
Con esto, la calculadora ya permite:
- Escribir operaciones matemáticas básicas.
- Corregir errores de ingreso rápidamente.
- Evaluar expresiones y mostrar resultados.
- Manejar errores de sintaxis sin romper la aplicación.
- Negar el resultado cuando sea necesario.

Cerramos con esto el tutorial de la calculadora basada en KCalc, utilizando Windows Forms y aplicando conceptos de Programación Orientada a Objetos para estructurar el código de manera eficiente y mantenible.