C# 7.0: Out variables, Tuples, Local Functions

Les mostrare algunas mejoras en C# 7.0 que me han gustado:

Out Variables
Al usar variables de salida, en versiones anteriores de C# se necesitaba declarar antes las variables de salida que se utilizarían en el método.

 public void PrintCoordinates(Point p)
 {
    int x, y; // have to "predeclare"
    p.GetCoordinates(out x, out y);
    WriteLine($"({x}, {y})");
 }
 

ahora ya no es necesario:

 public void PrintCoordinates(Point p)
 {
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
 }
 

Tuples(retornar mas de un valor en los métodos)
¿Cuantas veces has necesitado retornar mas de un valor? generalmente esto lo suplimos con diccionarios,System.Tuple<>, creamos una estructura o clase para usarla solo una vez.

Ahora podemos hacerlo de esta manera:

 (string first, string middle, string last) LookupName(long id)
{
   // lógica para obtener los tres valores...
   return (first: first, middle: middle, last: last); // nombrar valores...
}

var names = LookupName(id);
WriteLine($"found {names.first} {names.last}.");

// otro ejemplo:
var tup1 = ("Hello", "world", "!", true); // 3 strings, 1 bool
 

Funciones Locales:

En ocasiones es necesario crear funciones pequeñas que posiblemente llamaremos varias veces dentro de otra función. en lugar de crearla como una función mas de una clase, podemos usar una función local (dentro de otra función) con las mismas capacidades de cualquier función pero solo visible en el scope de la función padre.

public int Calculate(int someInput)
{
    int Factorial(int i)
    {
        if (i <= 1)
            return 1;
        return i * Factorial(i - 1);
    }
    var input = someInput + ... // Other calcs
 
    return Factorial(input);

Espero les sea de utilidad y ayude en sus próximos desarrollos!

blogs.msdn.microsoft.com

Enviar archivos por bluetooth con Python

Lightblue, Python & Linux…

Division por cero

Manejar dispositivos con bluetooth es bastante fácil utilizando Lightblue.

Lightblue proporciona un acceso sencillo a:

  • La detección de dispositivos y de servicios (con y sin GUI)
  • Interfaz de sockets estándar para RFCOMM y L2CAP
  • Envío y recepción de archivos a través de OBEX
  • La publicación de los servicios de RFCOMM y OBEX
  • Información sobre el dispositivo.

Consulta la documentación de la API para más detalles.

Pueden buscar esta librería en los repositorios de su distribución de linux o bajar los binarios de la pagina oficial.

El código básico para enviar una imagen es el siguiente.

import lightblue # mac y nombre de los dispositivos detectados lightblue.finddevices() #devolverá la mac, canal y protocolo de comunicacion disponibles de los dispositivos, para el envio de archivos el mio es el 8, OBEX Object Push lightblue.findservices() #se crea la variable con la direccion mac y el canal del dispositivo cliente = lightblue.obex.OBEXClient('xx:xx:xx:xx:xx:xx',8) #despues de conectarse correctamente se enviara un mensaje…

Ver la entrada original 30 palabras más

Juegos WPF: Cuadro mágico / magic square C# WPF

juego-cuadromagico

Programé  el conocido juego de cuadro mágico en C# con WPF,  ahorita solo tiene el conteo de movimientos realizados hasta acabar el juego con los números ordenados de forma horizontal. Los números se generan aleatoria mente para hacer el juego diferente cada vez que se abre, no es la forma mas optima de generar la cuadrilla pero el punto aquí es manejar el juego.

Próximamente agregare niveles para terminar el juego en diferentes figuras y posiciones, también falta agregar animaciones para el deslizamiento de los cuadros.

les dejo el Código fuente (cambiar la extension de .doc a .zip)…

<Window x:Class="WPFCuadroMagico.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Width="430" Height="500" MinWidth="420" MinHeight="500" Background="LightGray" Loaded="Window_Loaded">
    <DockPanel>
        <Label HorizontalContentAlignment="Center" BorderBrush="Blue" BorderThickness="1" Content="Cuadro Mágico" DockPanel.Dock="Top" FontSize="14" FontWeight="Bold">
            <Label.Background>
                <RadialGradientBrush>
                    <GradientStop Offset="1" Color="#FF146AE6" />
                    <GradientStop Color="#FF59E5F3" />
                </RadialGradientBrush>
            </Label.Background>
        </Label>
        <StatusBar DockPanel.Dock="Bottom">
            <StatusBarItem>
                <StackPanel Orientation="Horizontal">
                    <TextBlock FontSize="13" FontWeight="Bold" Text="Movimientos: " />
                    <TextBlock Name="movimientosText" FontSize="13" FontWeight="SemiBold" Foreground="Red" Text="0" />
                </StackPanel>
            </StatusBarItem>
        </StatusBar>
        <Canvas Name="tableroCanvas" Width="400" Height="400" Margin="5" Background="Black" />
    </DockPanel>
</Window>
<UserControl x:Class="WPFCuadroMagico.Numero" 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" Width="100" Height="100" Background="Transparent" mc:Ignorable="d">
    <Grid Background="Transparent">
        <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="4">
            <Border.Background>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Offset="0" Color="#FFE0EBFF" />
                    <GradientStop Offset="1" Color="#FFC9D7F0" />
                    <GradientStop Offset="0.5" Color="#FF759ADC" />
                </LinearGradientBrush>
            </Border.Background>
            <TextBlock Name="numText" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25" FontWeight="Bold" Text="1" />
        </Border>
    </Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WPFCuadroMagico
{
    ///
<summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class MainWindow : Window
    {
        private int[,] TableroArray = new int[4, 4];

        private int movimientos;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            CrearTablero();
        }

        private void CrearTablero()
        {
            // generar lista random ... puede tardar un poco, no es lo mas optimo para generarla.
            var listNum = new List<int>();
            do
            {
                var random = new Random().Next(1, 30);
                if (!listNum.Contains(random) && random <= 15)
                    listNum.Add(random);
            } while (listNum.Count() != 15);

            var contar = 0;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (contar < 15)
                    {
                        TableroArray[i, j] = listNum[contar];
                        var numero = new Numero();
                        numero.numText.Text = listNum[contar].ToString();
                        numero.i = i;
                        numero.j = j;
                        Canvas.SetTop(numero, i * 100);
                        Canvas.SetLeft(numero, j * 100);
                        tableroCanvas.Children.Add(numero);
                        numero.MouseLeftButtonUp += numero_MouseLeftButtonUp;
                    }
                    else
                    {
                        TableroArray[i, j] = 0;
                    }
                    contar++;
                }
            }
        }

        private void numero_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var numero = sender as Numero;
            if (HayEspacioVacio(numero.i - 1, numero.j))
            {
                MoverAEspacioVacio(numero, numero.i - 1, numero.j);
            }
            else if (HayEspacioVacio(numero.i + 1, numero.j))
            {
                MoverAEspacioVacio(numero, numero.i + 1, numero.j);
            }
            else if (HayEspacioVacio(numero.i, numero.j + 1))
            {
                MoverAEspacioVacio(numero, numero.i, numero.j + 1);
            }
            else if (HayEspacioVacio(numero.i, numero.j - 1))
            {
                MoverAEspacioVacio(numero, numero.i, numero.j - 1);
            }
        }

        private void MoverAEspacioVacio(Numero cuadro, int i, int j)
        {
            TableroArray[cuadro.i, cuadro.j] = 0;
            TableroArray[i, j] = Convert.ToInt32(cuadro.numText.Text);
            cuadro.i = i;
            cuadro.j = j;
            Canvas.SetTop(cuadro, i * 100);
            Canvas.SetLeft(cuadro, j * 100);

            movimientos++;
            movimientosText.Text = movimientos.ToString();

            if (Terminado())
            {
                MessageBox.Show("Terminaste el juego en " + movimientos.ToString() + " movimientos!", "Juego Terminado", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                tableroCanvas.IsEnabled = false;
            }
        }

        private bool HayEspacioVacio(int i, int j)
        {
            if (i < 0 || i > 3 || j < 0 || j > 3)
                return false;
            if (TableroArray[i, j] == 0)
                return true;
            return false;
        }

        private bool Terminado()
        {
            bool si = true;
            int contador = 1;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (contador < 16)
                    {
                        if (TableroArray[i, j] != contador)
                        {
                            si = false;
                            break;
                        }
                    }
                    contador++;
                }
            }

            return si;
        }
    }
}
using System.Windows.Controls;

namespace WPFCuadroMagico
{
    ///
<summary>
    /// Interaction logic for Numero.xaml
    /// </summary>

    public partial class Numero : UserControl
    {

        public int i { get; set; }
        public int j { get; set; }
        public Numero()
        {
            InitializeComponent();
        }
    }
}

Popcorn Time: Alternativa gratuita a Netflix

pelis

PopCorn Time, es una de las mejores alternativas gratuitas del popular servicio de streaming de video Netflix.

Este servicio se vale de archivos torrents y se reproducen online, es decir no tienes que esperar a que se descargue completamente para poder disfrutar de tu película favorita, lo unico que tienes que hacer es bajar la app correspondiente a tu equipo y empezar a disfrutar del contenido disponble.

Ahora estoy viendo “The 100”, donde a diferencia de netflix ya tienen las 3 temporadas!

Net Fiddle: Escribir y compilar código c# en tu navegador

netfiddle

.Net Fiddle es una herramienta online, útil si queremos probar algún código o mostrar algún ejemplo y no tenemos visual studio o el compilador de .NET.

Esta app nos permite correr codigo de C#, VB.NET y F# ademas posee otras funciones como:

  • Tipo de proyecto a elegir.
  • Posibilidad de agregar un paquete NuGet.
  • Crear una cuenta y guardar tus códigos para mostrarlos después.
  • Colaboración en linea. Codificar junto con personas de tu equipo y poder intercambiar mensajes de voz o texto dentro de la aplicación.
  • Compartir el código en blogs y redes sociales y que puedan interactuar.
  • Convertir codigo C# <-> VB .NET

Espero les sea de utilidad.

Ejemplo:
https://dotnetfiddle.net/Widget/UTYktD

Parallel LINQ y Parallel Task en C#

Hace algunos años cuando queríamos agilizar alguna tarea que requería bastante tiempo de procesamiento todos pensaban en usar Hilos(Threads), en C# ahora tenemos algo llamado Parallel Task y Parallel LINQ, que no son lo mismo, los hilos se ejecutan en un solo core, siempre dentro del hilo principal, esto no permite utilizar al máximo el rendimiento de nuestros servidores que traen una cantidad considerable de Cores. A continuación presento un ejemplo usando Parallel Linq y Tasks con una lista de 2500 elementos. cada elemento tiene una tarea de 1 segundo simulada con un Thread.Sleep(1000) y como podemos ver el tiempo en que termina todas las tareas es de 90 segundos dado que internamente el CRL gestiona y asigna automáticamente las tareas a cada core. Si esto lo hubieramos hecho con Hilos tan solo la creación de los hilos nos consumiría bastante tiempo.

Espero les sirva.

parallellinqparallellinq2

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ParallelLinq
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var list = Enumerable.Range(1000, 5000);
            var time = DateTime.Now;
            Console.WriteLine("Get list ... {0}", time.ToString());
            var query = from value in list.AsParallel()
                        where value % 2 == 0
                        select value;
            Console.WriteLine("do something for {0} items ... seconds {1}", query.Count(), DateTime.Now.Subtract(time).TotalSeconds);

            Console.ReadLine();
            var timetask = DateTime.Now;
            Parallel.ForEach(query, item =>
            {
                taskPerItem(item);
            });
            Console.WriteLine("do something  finished in... {0} seconds", DateTime.Now.Subtract(timetask).TotalSeconds);
            Console.ReadLine();
        }

        private static void taskPerItem(int currentItem)
        {
            Thread.Sleep(1000);
            Console.WriteLine("{0} - task executed for item = {1}", DateTime.Now.ToString(), currentItem);
        }
    }
}

Editar Celdas en Datagrid con WPF y MVVM

Al usar MVVM en conjunto con WPF y datagrid´s la edición es un poco diferente de lo que estamos acostumbrados con Windows Form. La diferencia radica en que los datos del viewmodel deben actualizarse de acuerdo a los bindings que se tengan configurados en el datagrid. Para esto se simula una lista que tiene el precio real de un producto y el nuevo precio que se desea actualizar, una vez declaradas las columnas del datagrid usaremos un DataGridTemplateColumn,  ya que necesitamos un datatemplate de lectura(CellTemplate) y uno de modo escritura(CellEditingTemplate) a la propiedad de cada objeto de la lista. Al final es un TextBlock y un TextBox en cada template.

No profundizare en la explicacion del ViewModel, solo se simula una actualización si es que hubo un cambio en el precio si se da click en “Save changes” y con ayuda de la propiedad IsEditable declarada en la clase Product validamos el DataTrigger para pintar el fondo rojo de la celda editada.

Codigo fuente: EditableDataGrid.zip (doc renombrar a zip)

edit2

ViewModel:

 public class MainViewModel
    {
        private ObservableCollection<Product> products;
        private ICommand savecommand;

        public MainViewModel()
        {
            Products = new ObservableCollection<Product>();
            Products.Add(new Product("Product 1", "Type 1", 100));
            Products.Add(new Product("Product 2", "Type 2", 200));
            Products.Add(new Product("Product 3", "Type 3", 300));
            Products.Add(new Product("Product 4", "Type 4", 400));

            SaveCommand = new RelayCommand(c => SaveData(), b => true);
        }

        public ObservableCollection<Product> Products
        {
            get { return products; }
            set { products = value; }
        }

        public ICommand SaveCommand { get; set; }

        private void SaveData()
        {
            foreach (var item in products)
            {
                if (item.RealPrice != item.EditablePrice)
                {
                    //here save to service or DB
                    item.RealPrice = item.EditablePrice;
                }
            }
        }
    }

 public class Product : NotifyProperyChangedBase
    {
        private string name;
        private string type;
        private double readonlyPrice;
        private double editablePrice;
        private bool isEdited;

        public string Name
        {
            get { return name; }
        }

        public string TypeProduct
        {
            get { return type; }
        }

        public double RealPrice
        {
            get { return readonlyPrice; }
            set
            {
                readonlyPrice = value;
                FirePropertyChanged("RealPrice");
                FirePropertyChanged("IsEdited");
            }
        }

        public double EditablePrice
        {
            get { return editablePrice; }
            set
            {
                editablePrice = value;
                FirePropertyChanged("EditablePrice");
                FirePropertyChanged("IsEdited");
            }
        }

        public bool IsEdited
        {
            get
            {
                return editablePrice != readonlyPrice;
            }
        }

        public Product(string name, string type, double price)
        {
            this.name = name;
            this.type = type;
            this.readonlyPrice = price;
            this.EditablePrice = price;
        }
    }

Datagrid:

<tk:DataGrid AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" ItemsSource="{Binding Products}">
            <tk:DataGrid.Columns>
                <tk:DataGridTextColumn Width="100" Binding="{Binding Name}" Header="Name" IsReadOnly="True"/>
                <tk:DataGridTextColumn Width="100" Binding="{Binding TypeProduct}" Header="Type" IsReadOnly="True"/>
                <tk:DataGridTextColumn Width="100" Binding="{Binding RealPrice}" Header="RealPrice" IsReadOnly="True"/>
                <tk:DataGridTemplateColumn Width="100" IsReadOnly="False" Header="Edit Price">
                    <tk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding EditablePrice}" Margin="1,1,4,1" HorizontalAlignment="Right"/>
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellTemplate>
                    <tk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding EditablePrice}" Margin="1,1,1,1" HorizontalContentAlignment="Center" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellEditingTemplate>
                    <tk:DataGridTemplateColumn.CellStyle>


<Style TargetType="tk:DataGridCell">
                            <Setter Property="IsEditing" Value="True" />
                            <Style.Triggers>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Background" Value="#fcf1b6" />
                                    <Setter Property="Foreground" Value="Black" />
                                    <Setter Property="BorderBrush" Value="Transparent" />
                                    <Setter Property="BorderThickness" Value="0" />
                                </Trigger>
                                <DataTrigger Binding="{Binding IsEdited}" Value="True">
                                    <Setter Property="Background" Value="Red" />
                                </DataTrigger>
                                <!--<DataTrigger Binding="{Binding IsEdited}" Value="False">
                                    <Setter Property="Background" Value="White" />
                                </DataTrigger>-->
                            </Style.Triggers>
                            
                        </Style>


                    </tk:DataGridTemplateColumn.CellStyle>
                </tk:DataGridTemplateColumn>
            </tk:DataGrid.Columns>
        </tk:DataGrid>

Optimizar el frontend de tus aplicaciones en WPF con C# Parte 1

Mucho se ha hablado sobre el  rendimiento de las aplicaciones en WPF y muchas veces es por que desconocemos el core del framework o algunas cuestiones muy técnicas en cuanto a esta tecnología integrada al .Net Framework.

Mencionare algunos puntos que he usado en mis desarrollos y que pueden mejorar el performance de tus apps:

Usar TextBlock en lugar de Label cada vez que sea posible. Esto especialmente al tener bindings en la vista que requieren una actualización constante. Es mucho mas costoso un binding de un  Label que de un TextBlox.

  • Label.Content Binding: 835 ms
  • TextBlock.Text = 242 ms

Usar VirtualizingStackPanel con colecciones grandes de objetos en la vista. Imagina que tienes 5000 objetos en un itemscontrol que mostrar en la vista, usando VirtualizingStackPanel te aseguras que solo se hace el render de los objetos mostrados en pantalla y conforme se hace el scroll se van a renderizar los demás objetos en tiempo de ejecución.

&lt;ItemsControl ItemsSource=&quot;{Binding MyList}&quot; 
              VirtualizingStackPanel.IsVirtualizing=&quot;True&quot;
              VirtualizingStackPanel.VirtualizationMode=&quot;Recycling&quot;&gt;
            
&lt;/ItemsControl&gt;

Usar IList en lugar de IEnumerable para “bindear” colecciones.  Si tienes oportunidad de usar un IList<T> o un IEnumerable, de preferencia escoge el IList, Al “bindear” un IEnumerable  a un ItemsControl se obliga a WPF internamente a hacer un wrapper para poder usar un IList<T>, esto es costo de procesamiento en cantidades considerables de datos.

Reducir los frames por segundo de una aplicación por uso excesivo de CPU. En ocasiones  las aplicaciones comienzan a usar demasiado CPU, esto debido a que WPF tiene un re dibujado de pantalla de 60 frames/s y si se usa una cantidad considerable de imágenes, animaciones o gráficos puede tonar a todo el sistema lento. Podemos bajar el valor por default con unas lineas de código y probar el render de la aplicación.

Timeline.DesiredFrameRateProperty.OverrideMetadata(
                typeof(Timeline),
                new FrameworkPropertyMetadata { DefaultValue = 10 }
);

Tratar de evitar valores auto o * para en propiedades width o height. Siempre que sea posible en controles como DataGrid’s, Grids u otros que permitan setear estos valores usar valores estáticos,  esto dependerá si la aplicación cliente requiera siempre re dimensionar todas sus partes. Podemos hacer un Datagrid con medidas estáticas y  dejar otros controles con auto ajuste.

Usar StaticResource en lugar de DynamicResource. Los staticResource se generan una sola vez mientras que DynamicResource se evalúa cada vez que este es usado en un control. Esto es costoso y ralentiza tu app considerablemente ya que el trabajo se realiza en el Thread del UI. Al mismo tiempo trata de usar ResourceDictionary para almacenar templates o styles.

Usar ObservableCollection sin crear nuevas instancias en cada actualización al UI. Al usar este tipo de colecciones con notificaciones permite que la vista se actualice sin hacer un propertyChanged de toda la colección ya que los cambios se monitorean por cada item. Evita hacer un new ObservableCollection<T> cuando necesites refrescar tu información y usa los métodos de Add o Remove y mantén una sola colección, estar haciendo una actualización al ItemSource de tu control constantemente con grandes cantidades de datos es costoso y tu aplicación se vuelve lenta.

Conclusión: antes de usar o programar una interfaz o código analiza si es necesario usarla o se puede suplir con algo menos costoso en performance, generalmente usamos lo que mas conocemos, lo que soluciona nuestro problema de momento o lo primero que vemos en ejemplos de paginas de internet sin saber como afecta esto a la larga o cuando nuestra aplicación lleva corriendo ya bastante tiempo.

Espero les sea útil en sus futuros desarrollos.

The given key was not present in the dictionary, SQL Dependency

Al usar SQL Dependency  con C# y SQL server al arrancar mi aplicación algunas veces marcaba error al ejecutar la siguiente linea de código:

SqlDependency.Start(stringCon, “MyNotificationQueue”);

con la siguiente excepción, y no se iniciaba el sql dependency:

“KeyNotFoundException: ‘The given key was not present in the dictionary.'”

En mi aplicación siempre hago el stop(); al iniciar y al cerrar, pero por alguna razón a veces quedan mensajes corruptos en la cola de mensajes de SQL server.

No hay mucha información en la web, pero encontré una forma de limpiar la Queue, dejo aquí el script, comenten si les sirve:


DECLARE item_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT s.conversation_handle FROM [dbo].[MyNotificationQueue] s
LEFT JOIN sys.conversation_endpoints e ON e.conversation_handle=s.conversation_handle WHERE e.conversation_handle IS NULL;
-- o limpia todo
--SELECT s.conversation_handle FROM Level2_RAWMAT.[dbo].[MyNotificationQueue] s;
OPEN item_cursor
DECLARE @conversation UNIQUEIDENTIFIER
FETCH NEXT FROM item_cursor INTO @conversation
WHILE @@FETCH_STATUS = 0
BEGIN
END CONVERSATION @conversation WITH CLEANUP
FETCH NEXT FROM item_cursor INTO @conversation
END