Páginas

Traducir

Miniprograma que apuesta quinielas de futbol en C# + WPF

En un anterior artículo se desarrollo un miniprograma escrito en Java que hacia apuestas de quinielas. En este artículo se pretende hacer lo mismo pero programandolo en C# + desarrollo de interfaces con WPF ( Windows Presentation Foundation ) y Visual Studio 2015. El aspecto sera el mismo para que pueda compararse el codigo escrito en Java del anterior articulo con este. Veremos como el codigo que no depende del interface gráfico es muy parecido.

Cada boleto esta compuesto de 8 columnas con 14 partidos. Los partidos de cada columna son apuestas con tres valores 1, X, 2. Si un equipo gana en casa es un 1, si empata es una X y si pierde es un 2. La ultima apuesta o pleno al 15 son los números de goles marcados por cada equipo que juega ese partido 0, 1, 2, M. Donde M es 3 o mas goles.

Lenguaje

Escribiremos el código en C# usando ademas lenguaje de marcas XAML (WPF) una variante de XML introducida por microsoft para dibujar la interface gráfica. Como entorno de desarrollo se usa Visual Studio 2015

Diseño

Nada mas empezar mostraremos un mensaje de bienvenida y el menú con las opciones de pronosticar apuestas, configuración e información sobre la versión del programa.

Sobre las opciones: Apuesta, 1x2%, ?.

  • Apuesta: Elegir 'Empezar' que será la que nos calcule el pronostico y salga por pantalla.
  • 1X2 %: Configuraciones que nos calculara pronósticos al azar según porcentaje elegido.
  • ?: Información sobre la versión del programa.

Código

Iniciado Visual Studio 2015, creamos un nuevo proyecto eligiendo la plantilla "WPF Application" y le pondremos de nombre "WpfQuiniela2000". El siguiente paso con ayuda del entorno "Visual" que aparece en la pestaña "MainWindow.xaml" sera diseñar o dibujar el aspecto gráfico de la aplicación con el raton y retocar si hace falta el código que ira escribiendo en lenguaje XAML que aparece en la ventana inferior.

En la zona vertical izquierda del entorno tenemos varios desplegables, entre ellos el "Toolbox" que seran los objetos graficos con los que construiremos la interface. Algunos objetos usados son "Label" como etiquetas, "Grid" contenedor de otros objetos, "Menu" para diseñar el menu de opciones... A medida que vamos posicionando, redimensionando en la zona "Design" y rellenando los atributos de los objetos que aparece en la ventana "Properties" iremos viendo como aparece el codigo XAML en la zona "XAML".

Diseño de la interfaz gráfica

En la ventana con el código en XAML tendremos esta plantilla que nos crea Visual Studio a la que le iremos agregando o modificando algunas cosas.

En la zona inferior derecha vamos a "Properties" y cambiamos alguna de las propiedades de la ventana "Window" del código. El titulo de la ventana en "Title" lo dejamos como "Quinielas 2000", el ancho y alto lo modificamos con las variables "Width" 220 y "Height" 300. La ventana no sera redimensionable pero si minimizable esto se cambia en la propiedad "ResizeMode" desplegando CanMinimize. Si ahora miramos el código XAML veremos una línea con lo siguiente:

 Title="Quinielas 2000" Height="300" Width="220" ResizeMode="CanMinimize">

En el menú "Toolbox" elegimos el objeto "Menu" y con el ratón lo posicionamos en el diseño en la parte superior de la ventana de la aplicación que estamos construyendo.

Lo siguiente será posicionar el ratón encima del menú donde queremos la primera lista de opciones "Apuesta" hacer click en botón derecho y seleccionaremos la opción "Add MenuItem". En la ventana "Properties - Common - Header" le pondremos el nombre de la opción "Apuesta". Con las opciones "1X2%" y "?" procedemos a hacer los mismos pasos.

Para crear los submenús posicionamos el ratón encima de la opción principal y seleccionamos también "Add MenuItem". En el caso de "Apuesta" crearemos un submenú al que llamaremos "Empezar". Con los siguientes submenús de las opciones que quedan haremos tambien el mismo proceso. El código de una opción con submenú debe ser parecido a este:

  <MenuItem Header="Apuesta" Height="24">
    <MenuItem Header="Empezar" HorizontalAlignment="Left" Width="145">
  </MenuItem>

Como cada submenú nos debe llevar a ejecutar una acción cuando se pulse en ella, en el propio diseño hacemos "Click" en estos para que nos agregue automáticamente al código XAML el atributo "Click" con el nombre del evento que contendrá el código en C# que se ejecutará en cada caso. Ademas daremos un nombre identificador con el atributo "x:Name" a cada línea XAML que define el submenú.

  <MenuItem Header="Apuesta" Height="24">
    <MenuItem x:Name="mnEmpezar" Header="Empezar" HorizontalAlignment="Left" Width="145" Click="mnEmpezar_Click">
  </MenuItem>

En los tres submenús de la opción "1X2 %" además agregaremos un "RadioButton" a cada uno. El primero de la lista sera el seleccionado por defecto con el atributo "IsChecked='True'".

  <MenuItem Header="1X2 %">
    <RadioButton x:Name="mnOpcion1" Click="mnOpcion1_Click" IsChecked="True" >
      <MenuItem Header="55,56 - 33,33 - 11,11" HorizontalAlignment="Left" Width="206" Margin="0,0,-60,0"/>
    </RadioButton>
    <RadioButton x:Name="mnOpcion2" Click="mnOpcion2_Click">
      <MenuItem  Header="55,56 - 22,22 - 22,22" HorizontalAlignment="Left" Width="206" Margin="0,0,-60,0"/>
    </RadioButton>
    <RadioButton x:Name="mnOpcion3" Click="mnOpcion3_Click">
      <MenuItem Header="33,33 - 33,33 - 33,33" HorizontalAlignment="Left" Width="205" Margin="0,0,-60,0"/>
    </RadioButton>
  </MenuItem>

El mensaje de bienvenida lo escribiremos dentro del mismo contenedor "Grid" donde hemos creado el Menú. Seleccionando de "Toolbox" el objeto "Label" centrada en horizontal,vertical y con texto en negrita. Este contenedor será el mismo donde también visualizaremos los pronósticos de las apuestas.

El código XAML resultante seria este:

  <Label x:Name="lbBienvenido" Content="¡ Bienvenido a Quiniela 2000 !" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0" FontWeight="Bold"  />

La última parte del diseño será crear otro contenedor "Grid" dividido en 2 columnas y 17 filas para mostrar los pronósticos. Vamos a "Toolbox" elegimos el objeto "Grid" para posicionarlo y redimensionarlo dentro de la ventana. El código se vera como este:

  <Grid x:Name="grEmpezar"  VerticalAlignment="Bottom" Width="214" Height="239" HorizontalAlignment="Center" >
  </Grid>

dentro del contenedor se añadirán las 2 columnas con el atributo "Grid.ColumnDefinition" y 17 filas con "Grid.RowDefinition" rellenando el código que falta manualmente dentro de la zona XAML en la ventana inferior del editor de Visual Studio.

La primera fila "RowDefinition" donde pondremos las etiquetas "Partido" y "Pronostico" tendrá una altura de 2 celdas indicándoselo con "Height='2*'"

  <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="2*" />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

Introduciremos después el código manualmente para posicionar en la primera fila del contenedor "grEmpezar" las etiquetas "Partido" y "Pronostico" rodeadas por un borde de color negro. Al iniciar la aplicación estas dos etiquetas no pueden ser visibles ya que inicialmente se esta dando un mensaje de bienvenida. Asi que le damos el atributo de ocultar con "Visibility='Hidden'". Cuando en el menú elijamos la opción empezar con el código en C# cambiaremos este atributo a visible.

  <Border x:Name="bgPartido" Grid.Row="0" Grid.Column="0" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Center" Margin="5,4,0,0" Visibility="Hidden"> 
    <Label x:Name="lbPartido" Content="Partido"  VerticalAlignment="Top" FontWeight="Bold" Foreground="Black"  FontSize="9.333" HorizontalAlignment="Center" />
  </Border>
  <Border x:Name="bgPronostico" Grid.Row="0" Grid.Column="1" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Center" Margin="0,4,5,0" Visibility="Hidden">
    <Label x:Name="lbPronostico" Content="Pronostico" VerticalAlignment="Top" FontWeight="Bold" Foreground="Black" FontSize="9.333" HorizontalAlignment="Center"/>
  </Border>

Con esto ya habremos acabado el diseño gráfico de la interfaz con ayuda XAML y del entorno Visual. Lo siguiente será acabar el resto del programa que calculará los pronósticos introduciendo código en C#

Código XAML final del diseño gráfico:

<Window x:Class="WpfQuiniela2000.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        Title="Quinielas 2000" Height="300" Width="220" ResizeMode="CanMinimize">
    <Grid>
        <Menu x:Name="menu" HorizontalAlignment="Left" Width="212" Margin="0,0,0,239">
            <MenuItem Header="Apuesta" Height="24">
                <MenuItem x:Name="mnEmpezar" Header="Empezar" HorizontalAlignment="Left" Width="145" Click="mnEmpezar_Click"/>
            </MenuItem>
            <MenuItem Header="1X2 %">
                <RadioButton x:Name="mnOpcion1" Click="mnOpcion1_Click" IsChecked="True" >
                    <MenuItem Header="55,56 - 33,33 - 11,11" HorizontalAlignment="Left" Width="206" Margin="0,0,-60,0"/>
                </RadioButton>
                <RadioButton x:Name="mnOpcion2" Click="mnOpcion2_Click">
                    <MenuItem  Header="55,56 - 22,22 - 22,22" HorizontalAlignment="Left" Width="206" Margin="0,0,-60,0"/>
                </RadioButton>
                <RadioButton x:Name="mnOpcion3" Click="mnOpcion3_Click">
                    <MenuItem Header="33,33 - 33,33 - 33,33" HorizontalAlignment="Left" Width="205" Margin="0,0,-60,0"/>
                </RadioButton>
            </MenuItem>
            <MenuItem Header="?">
                <MenuItem x:Name="mnSobre" Header="Sobre ..." HorizontalAlignment="Left" Width="120" Margin="0,0,-48,0" Click="mnSobre_Click" />
            </MenuItem>
        </Menu>
        <Label x:Name="lbBienvenido" Content="¡ Bienvenido a Quiniela 2000 !" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0"  FontWeight="Bold"  />
        <Grid x:Name="grEmpezar"  VerticalAlignment="Bottom" Width="214" Height="239" HorizontalAlignment="Center" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="2*" />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Border x:Name="bgPartido" Grid.Row="0" Grid.Column="0" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Center" Margin="5,4,0,0" Visibility="Hidden"> 
                <Label x:Name="lbPartido" Content="Partido"  VerticalAlignment="Top" FontWeight="Bold" Foreground="Black"  FontSize="9.333" HorizontalAlignment="Center" />
            </Border>
            <Border x:Name="bgPronostico" Grid.Row="0" Grid.Column="1" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Center" Margin="0,4,5,0" Visibility="Hidden">
                <Label x:Name="lbPronostico" Content="Pronostico" VerticalAlignment="Top" FontWeight="Bold" Foreground="Black" FontSize="9.333" HorizontalAlignment="Center"/>
            </Border>
        </Grid>
    </Grid>
</Window>

Código de control del programa.

Visual Studio cuando empieza el proyecto crea también una plantilla inicial con el código básico en C# para ejecutar el programa. Ha este código en la plantilla con nombre MainWindow.xaml.cs se irá añadiendo el código necesario para los cálculos, control de los eventos del ratón,...

Por lo tanto tendremos un código inicial en C# lo mas parecido a este:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfQuiniela2000
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

En la parte anterior del diseño de la interfaz cuando hacíamos click en los menus para agregar los atributos al lenguaje de marcas XAML (MainWindow.xaml) también nos creaba en la pestaña de código C# (MainWindow.xaml.cs) el correspondiente de control de eventos.

Por lo tanto deberíamos tener además este código ya creado:

private void mnSobre_Click(object sender, RoutedEventArgs e)
{

}

private void mnOpcion1_Click(object sender, RoutedEventArgs e)
{
       
}

private void mnOpcion2_Click(object sender, RoutedEventArgs e)
{

}

private void mnOpcion3_Click(object sender, RoutedEventArgs e)
{

}

private void mnEmpezar_Click(object sender, RoutedEventArgs e)
{

}

Nos ha creado 5 métodos identificados con el nombre del atributo x:Name de la zona del diseño donde se había hecho Click con el ratón. Cada método será llamado por su evento correspondiente y ejecutará él código que se haya rellenado en su interior.

Así por ejemplo tenemos que cuando el programa este en ejecución y se hace Click en "?" del menú se crea un evento que llama al método mnSobre_Click donde nos ejecutará el código interno correspondiente. Para los demás eventos que llamaran a sus métodos _Click el proceso es el mismo.

Opción "Apuesta" evento mnEmpezar

Declaramos una variable global llamada 'modoApuesta' que vamos a necesitar en la subrutina del evento para saber que configuración tomar a la hora de representar el pronostico de la apuesta. También usamos un objeto llamado "rnd" para que nos de los pronosticos al azar.

static int modoApuesta=1;
Random rnd = new Random((int)DateTime.Now.Millisecond);

Rellenamos también el código del evento que llamará al metodo "mnEmpezar_Click".

private void mnEmpezar_Click(object sender, RoutedEventArgs e)
{
  lbBienvenido.Visibility= Visibility.Hidden;
  bgPartido.Visibility = Visibility.Visible;
  bgPronostico.Visibility = Visibility.Visible;
  grEmpezar.Children.RemoveRange(2,33);
  for (int i = 1, c; i <= 16; i++)
    {
      if (i < 15) c = i; else c = 15;
      TextBlock txtBlockCasilla = new TextBlock();
      txtBlockCasilla.Text = ""+c;
      txtBlockCasilla.FontSize = 10;
      txtBlockCasilla.HorizontalAlignment = HorizontalAlignment.Center;
      Grid.SetRow(txtBlockCasilla, i); Grid.SetColumn(txtBlockCasilla, 0);
      grEmpezar.Children.Add(txtBlockCasilla);
      TextBlock txtBlockApuesta = new TextBlock();
      txtBlockApuesta.Text = generaCasilla(modoApuesta, c);
      txtBlockApuesta.FontSize = 10;
      txtBlockApuesta.HorizontalAlignment = HorizontalAlignment.Center;
      Grid.SetRow(txtBlockApuesta, i); Grid.SetColumn(txtBlockApuesta, 1);
      grEmpezar.Children.Add(txtBlockApuesta);
    }
}

También creamos una función generaCasilla() que va a utilizar el método mnEmpezar_Click que nos da el resultado de la casilla apostada según la variable modoApuesta

private String generaCasilla(int modo, int casilla )
{ 
  int num;

  if (casilla < 15)
  {
    num = rnd.Next(0,9); // Devuelve 0 a 8 
    switch (modo)
    {
      case 1: // 55,56 - 33,33 - 11,11
        switch (num)
        {
          case 0: case 1: case 2: case 3: case 4: return "1";
          case 5: case 6: case 7: return "X";
          case 8: return "2";
        }
        break;
      case 2: // 55,56 - 22,22 - 22,22
        switch (num)
        {
          case 0: case 1: case 2: case 3: case 4: return "1";
          case 5: case 6: return "X";
          case 7: case 8: return "2";
        }
        break;
      case 3: // 33,33 - 33,33 - 33,33
        switch (num)
        {
          case 0: case 1: case 2: return "1";
          case 3: case 4: case 5: return "X";
          case 6: case 7: case 8: return "2";
        }
        break;
    }
  }
  else
  {
    num = rnd.Next(0,4); // Devuelve 0 a 3
    switch (num)
    {
      case 0: return "0";
      case 1: return "1";
      case 2: return "2";
      case 3: return "M";
    }
  }
  return "";
  }
}

Opción "1X2 %" eventos mnOpcion1, mnOpcion2, mnOpcion3

Damos valor a la variable global modoApuesta según la opción del menú configura

private void mnOpcion1_Click(object sender, RoutedEventArgs e)
{
  modoApuesta = 1;    
}

private void mnOpcion2_Click(object sender, RoutedEventArgs e)
{
  modoApuesta = 2;
}

private void mnOpcion3_Click(object sender, RoutedEventArgs e)
{
  modoApuesta = 3;
}

Opción "?" evento mnSobre

Visualizamos una ventana con información del programa con el método MessageBox.Show()

private void mnSobre_Click(object sender, RoutedEventArgs e)
{
  MessageBox.Show("Quiniela 2000 Version 1.0", "Sobre Quiniela",MessageBoxButton.OK, MessageBoxImage.Information);
}

Código C# completo

Aquí esta el código completo del programa correspondiente a la parte de control de menú y calculos.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfQuiniela2000
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        static int modoApuesta=1;
        Random rnd = new Random((int)DateTime.Now.Millisecond);

        public MainWindow()
        {
            InitializeComponent();
        }

        private void mnSobre_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Quiniela 2000 Version 1.0", "Sobre Quiniela",MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void mnOpcion1_Click(object sender, RoutedEventArgs e)
        {
            modoApuesta = 1;
        }

        private void mnOpcion2_Click(object sender, RoutedEventArgs e)
        {
            modoApuesta = 2;
        }

        private void mnOpcion3_Click(object sender, RoutedEventArgs e)
        {
            modoApuesta = 3;
        }

        private void mnEmpezar_Click(object sender, RoutedEventArgs e)
        {
            lbBienvenido.Visibility= Visibility.Hidden;
            bgPartido.Visibility = Visibility.Visible;
            bgPronostico.Visibility = Visibility.Visible;
            grEmpezar.Children.RemoveRange(2,33);
            for (int i = 1, c; i <= 16; i++)
            {
                if (i < 15) c = i; else c = 15;
                TextBlock txtBlockCasilla = new TextBlock();
                txtBlockCasilla.Text = ""+c;
                txtBlockCasilla.FontSize = 10;
                txtBlockCasilla.HorizontalAlignment = HorizontalAlignment.Center;
                Grid.SetRow(txtBlockCasilla, i); Grid.SetColumn(txtBlockCasilla, 0);
                grEmpezar.Children.Add(txtBlockCasilla);
                TextBlock txtBlockApuesta = new TextBlock();
                txtBlockApuesta.Text = generaCasilla(modoApuesta, c);
                txtBlockApuesta.FontSize = 10;
                txtBlockApuesta.HorizontalAlignment = HorizontalAlignment.Center;
                Grid.SetRow(txtBlockApuesta, i); Grid.SetColumn(txtBlockApuesta, 1);
                grEmpezar.Children.Add(txtBlockApuesta);
            }
        }

        private String generaCasilla(int modo, int casilla )
        { 
            int num;

            if (casilla < 15)
            {
                num = rnd.Next(0,9); // Devuelve 0 a 8 
                switch (modo)
                {
                    case 1: // 55,56 - 33,33 - 11,11
                        switch (num)
                        {
                            case 0: case 1: case 2: case 3: case 4: return "1";
                            case 5: case 6: case 7: return "X";
                            case 8: return "2";
                        }
                        break;
                    case 2: // 55,56 - 22,22 - 22,22
                        switch (num)
                        {
                            case 0: case 1: case 2: case 3: case 4: return "1";
                            case 5: case 6: return "X";
                            case 7: case 8: return "2";
                        }
                        break;
                    case 3: // 33,33 - 33,33 - 33,33
                        switch (num)
                        {
                            case 0: case 1: case 2: return "1";
                            case 3: case 4: case 5: return "X";
                            case 6: case 7: case 8: return "2";
                        }
                        break;
                }
            }
            else
            {
                num = rnd.Next(0,4); // Devuelve 0 a 3
                switch (num)
                {
                    case 0: return "0";
                    case 1: return "1";
                    case 2: return "2";
                    case 3: return "M";
                }
            }
            return "";
        }
    }
}