[Windows App Development] BusyIndicator personalizado para Windows Phone

Para algunos (incluyéndome) el ProgressBar no es suficiente si lo que queremos es evitar (de alguna forma elegante) que el usuario siga interactuando con la aplicación mientras se esta ejecutando algun proceso. Por eso en este post enseñare como crear un BusyIndicator.

C#

Empezamos creando una clase que extienda de Control con la siguiente definición:

XAML

Y terminamos definiendo la plantilla del control que acabamos de crear mediante un estilo implícito de la siguiente manera:

¿Cómo usar?

Agregar el namespace del BusyIndicator a la página o al control correspondiente y listo:

CodePlex

Código fuente disponible en: Windows Phone -> CustomBusyIndicator

[Xamarin] ¿Cómo usar recursos de colores Drawing en Xamarin.Android?

En esta publicación mostraré como usar un recurso de color del tipo Drawing de dos formas distintas. La primera forma responde a como usarlo desde otro XML y la segunda a como usarlo desde Código.

themes.xml

Para este ejemplo empezemos definiendo nuestro recurso en un nuevo archivo: ResourcesvaluesThemes.xml.

main.axml

En otro XML hacemos referencia a los recursos que definimos en Themes.xml con ayuda de @drawable. Si tienen algun error asegurence que Themes.xml tenga como Build action AndroidResource. Adicionalmente hay otros 2 TextView los cuales actualizaremos desde el código.

MainActivity.cs

Por último para usarlos desde código solo tenemos que hacer lo siguiente, aparte de agregar el using Android.Graphics.Drawables;

[WP8] ApplicationBar y MVVM

Este post sale a raíz de que las clases ApplicationBarIconButton y ApplicationBarMenuItem de Windows Phone Silverlight no tienen una propiedad Command como aquellos controles que heredan de ButtonBase.

Ahora que identificamos el problema prosigamos con una de las tantas soluciónes que me imagino que se le pueden dar:

MainPageVM.cs

Creamos un ViewModel para nuestro MainPage. Para implementar correctamente el RelayCommand revisen este enlace.

    public class MainPageVM : INotifyPropertyChanged
{
#region Properties

private string miTexto;

public string MiTexto
{
get { return miTexto; }
set { miTexto = value; RaisePropertyChanged(); }
}

#endregion Properties

#region Command Properties

private ICommand iconButtonCommand;

public ICommand IconButtonCommand
{
get
{
if (null == iconButtonCommand)
iconButtonCommand = new RelayCommand(IconButtonExecute);
return iconButtonCommand;
}
}

private ICommand menuItemCommand;

public ICommand MenuItemCommand
{
get
{
if (null == menuItemCommand)
menuItemCommand = new RelayCommand(MenuItemExecute);
return menuItemCommand;
}
}

#endregion Command Properties

#region Command Methods

private void MenuItemExecute()
{
this.MiTexto = "Acabas de presionar el MenuItem";
}

private void IconButtonExecute()
{
this.MiTexto = "Acabas de presionar el IconButton";
}

#endregion Command Methods

#region INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

#endregion INotifyPropertyChanged
}

MainPage.cs

Aquí usando Nuget instalemos un paquete llamado BindableApplicationBar.

Y luego prosigamos definiendo los enlaces de la siguiente manera:

<phone:PhoneApplicationPage
x:Class="ApplicationBarMVVMApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ApplicationBarMVVMApp.ViewModels"
mc:Ignorable="d"
xmlns:bar="clr-namespace:BindableApplicationBar;assembly=BindableApplicationBar"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">

<phone:PhoneApplicationPage.DataContext>
<vm:MainPageVM />
</phone:PhoneApplicationPage.DataContext>

<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="XAMLDEVELOPMENT.BLOGSPOT.COM" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="AppBar" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding MiTexto}" Style="{StaticResource PhoneTextExtraLargeStyle}" TextWrapping="Wrap"/>
</Grid>
</Grid>

<bar:Bindable.ApplicationBar>
<bar:BindableApplicationBar>
<bar:BindableApplicationBar.Buttons>
<bar:BindableApplicationBarButton Text="{Binding LocalizedResources.AppBarButtonText, Source={StaticResource LocalizedStrings}}" IconUri="/Assets/AppBar/add.png" Command="{Binding IconButtonCommand}"/>
</bar:BindableApplicationBar.Buttons>
<bar:BindableApplicationBar.MenuItems>
<bar:BindableApplicationBarMenuItem Text="{Binding LocalizedResources.AppBarMenuItemText, Source={StaticResource LocalizedStrings}}" Command="{Binding MenuItemCommand}" />
</bar:BindableApplicationBar.MenuItems>
</bar:BindableApplicationBar>
</bar:Bindable.ApplicationBar>

</phone:PhoneApplicationPage>

CodePlex

Código fuente disponible en: Windows Phone -> ApplicationBarMVVMApp

[EVENTO] 4ta Hackaton de Lima 2014

¿Qué es la Hackatón de Lima 2014?

La Hackatón es una competencia tecnológica donde participan equipos multidisciplinarios para crear aplicaciones destinadas a resolver problemas concretos de la ciudad. Los participantes usarán los datos abiertos dispuestos por el municipio capitalino.

Primer Puesto

#Hablavas ayudará a los ciudadanos a ahorrar tiempo buscando rutas, es un sistema que te indacará que líneas debes tomar para llegar al destino deseado.

Segundo Puesto

#LimaSalud busca que los ciudadanos conozcan su estado de salud, muestra ciclovías y parques donde los ciudadanos podrán realizar ejercicios.

Tercer Puesto

#Cooltura es una guía cultural de Lima que mostrará lugares de manera divertida.

Hackidea

Katherine Soto Torres, Ganadora del Concurso de la Hackaidea y de un Smartphone por su propuesta para la mejora de accesibilidad de personas con discapacidad.

[WP] ExpanderControl Personalizado

En este post compartire el código necesario para implementar un simple ExpanderControl. Ya dependera de tí agregar las animaciones que quieras para mejorar la experiencia de usuario.

XAML

Primero el XAML donde defino un par de estados visuales, un StackPanel y 2 Border que actuarán como contenedores.

<UserControl x:Class="XamlDev.Phone.UserControls.ExpanderControl"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">

<StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CustomGroup">
<VisualState x:Name="OpenedState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="brdExpandableContentContainer" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>Visible</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ClosedState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="brdExpandableContentContainer" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>Collapsed</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="brdContentContainter" Tap="brdContentContainter_Tap">

</Border>
<Border x:Name="brdExpandableContentContainer" >

</Border>
</StackPanel>
</UserControl>

C#

Luego registramos los DependencyProperty para el contenido, el contenido expandible y un flag que nos servira para ocultar o mostrar el contenido expandible.

    public partial class ExpanderControl : UserControl
{
public new UIElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}

public static readonly new DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(UIElement), typeof(ExpanderControl), new PropertyMetadata(OnContentChanged));

private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ExpanderControl)d).brdContentContainter.Child = (FrameworkElement)e.NewValue;
}

public FrameworkElement ExpandableContent
{
get { return (FrameworkElement)GetValue(ExpandableContentProperty); }
set { SetValue(ExpandableContentProperty, value); }
}

public static readonly DependencyProperty ExpandableContentProperty =
DependencyProperty.Register("ExpandableContent", typeof(FrameworkElement), typeof(ExpanderControl), new PropertyMetadata(OnExpandableContentChanged));

private static void OnExpandableContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ExpanderControl)d).brdExpandableContentContainer.Child = (FrameworkElement)e.NewValue;
}

public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}

public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(ExpanderControl), new PropertyMetadata(false, OnIsOpenChanged));

private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
VisualStateManager.GoToState((ExpanderControl)d, "OpenedState", false);
else
VisualStateManager.GoToState((ExpanderControl)d, "ClosedState", false);
}

public ExpanderControl()
{
InitializeComponent();
VisualStateManager.GoToState(this, "ClosedState", false);
}

private void brdContentContainter_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
e.Handled = true;
this.IsOpen = !this.IsOpen;
}
}

¿Como usar?

Agregar el namespace del ExpanderControl, por ejemplo: xmlns:userControls=”clr-namespace:XamlDev.Phone.UserControls” y listo.


<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<userControls:ExpanderControl Grid.Row="1">
<userControls:ExpanderControl.Content>
<TextBlock Style="{StaticResource PhoneTextLargeStyle}" Text="Hazme Tap!"/>
</userControls:ExpanderControl.Content>
<userControls:ExpanderControl.ExpandableContent>
<TextBlock Style="{StaticResource PhoneTextLargeStyle}" Text="Hola! Soy el contenido expandible."/>
</userControls:ExpanderControl.ExpandableContent>
</userControls:ExpanderControl>
</Grid>

[WP] UpdateSourceTrigger = PropertyChanged

En Windows Phone Silverlight simplemente no podemos hacer esto y en teoría tenemos que esperar hasta que un LostFocus ocurra. Felizmente solo se tienen que hacer un par de cosas para obtener el comportamiento esperado cuando hacemos nuestros Binding.

Primero

Definimos una clase con un Attached Property, en mi caso estoy llamando UpdateSourceOnPropertyChanged a la clase y Value al Attached Property.

namespace MyPhoneApp.Phone.Classes
{
public class UpdateSourceOnPropertyChanged
{
public static bool GetValue(DependencyObject obj)
{
return (bool)obj.GetValue(ValueProperty);
}

public static void SetValue(DependencyObject obj, bool value)
{
obj.SetValue(ValueProperty, value);
}

public static readonly DependencyProperty ValueProperty =
DependencyProperty.RegisterAttached("Value", typeof(bool), typeof(UpdateSourceOnPropertyChanged), new PropertyMetadata(false, Callback));

private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (d is TextBox)
((TextBox)d).TextChanged += TextBox_TextChanged;

if (d is PasswordBox)
((PasswordBox)d).PasswordChanged += PasswordBox_PasswordChanged;
}
else
{
if (d is TextBox)
((TextBox)d).TextChanged -= TextBox_TextChanged;

if (d is PasswordBox)
((PasswordBox)d).PasswordChanged -= PasswordBox_PasswordChanged;
}
}

private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((TextBox)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

private static void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
((PasswordBox)sender).GetBindingExpression(PasswordBox.PasswordProperty).UpdateSource();
}
}
}

Segundo

Por último agregar en nuestro View la referencia al namespace correspondiente a la clase creada.

<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:be="clr-namespace:MyPhoneApp.Phone.Classes"
...
>

Y agregar el Attached Property a nuestros controles y definir UpdateSourceTrigger=Explicit en los Binding.

            <StackPanel>
<TextBox x:Name="txb" Text="{Binding Hello, Mode=TwoWay, UpdateSourceTrigger=Explicit}" be:UpdateSourceOnPropertyChanged.Value="True"/>
<PasswordBox x:Name="pwd" Password="{Binding Hello, Mode=TwoWay, UpdateSourceTrigger=Explicit}" be:UpdateSourceOnPropertyChanged.Value="True"/>
<TextBlock Text="{Binding Hello}" />
</StackPanel>

PDT: Tener en cuenta que si se desea hacer el Attach o Deattach desde el código .cs se puede hacer lo siguiente:

            UpdateSourceOnPropertyChanged.SetValue(txb, false);
UpdateSourceOnPropertyChanged.SetValue(pwd, false);

[.NET] Integrando objetos de .NET en SQL

La integración CLR es una característica que nos permite ejecutar código manejado en SQL Server. El código manejado es más adecuado que Transact-SQL para los cálculos y la lógica de ejecución complicada y presenta una amplia compatibilidad para muchas tareas complejas, como son el tratamiento de cadenas, fechas, expresiones regulares, etc.

Primer paso

En un nuevo proyecto del tipo Class Library (o librería de clases) definimos una clase con un par de métodos estáticos:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IntegrandoCLR
{
public class SpecialMath
{
public static void Procesar(int a, int b, out int c)
{
if (a > b)
c = a + b;
else
c = a * a;
}

public static double Calcular(double a, double b)
{
return Math.Log(a, b);
}
}
}

Segundo paso

Una vez construida la librería y ubicada en algún lugar del disco, hacemos lo siguiente en SQL Server:

--Habilitamos la carácterística a nivel de servidor.
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

--Registro el ensamblado con algún nombre (este ensamblado queda disponible para todas las bases de datos en el servidor)
CREATE ASSEMBLY MiSuperEnsamblado
FROM 'D:IntegrandoCLR.dll'
GO

--Creo el procedimiento almacenado asociándolo con el método estático en la clase
CREATE PROCEDURE ProcesarConSpecialMath_SP
@Num1 INT,
@Num2 INT,
@Num3 INT OUTPUT
AS EXTERNAL NAME MiSuperEnsamblado.[IntegrandoCLR.SpecialMath].Procesar
GO

--Creo la función asociándola con la función estática en la clase
CREATE FUNCTION ClacularConSpecialMath_FN(@Num1 FLOAT, @Num2 FLOAT)
RETURNS FLOAT
AS EXTERNAL NAME MiSuperEnsamblado.[IntegrandoCLR.SpecialMath].Calcular
GO

Fin

Ya hecho todo esto podemos probarlo de la siguiente manera:

DECLARE @Result INT
EXEC ProcesarConSpecialMath_SP 77,9, @Result OUT
PRINT @Result
GO

SELECT dbo.ClacularConSpecialMath_FN(3,9)
GO

[UA] ¿Como usar .ttf en Universal Apps?

En este post comparto como usar un archivo .ttf para trabajar con fuentes personalizadas o fuentes que simplemente no se pueden encontrar en el VS.

Primer paso

Agregar el archivo a la solución

Segundo paso

Abrir el archivo y copiar el nombre de la fuente

Último paso

Usarlo por cualquiera de las siguientes 2 maneras:

    <Grid Background="White">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="In XAML" FontSize="60" FontFamily="/MGNORMAL.TTF#MagistralC" Margin="22" Foreground="Black"/>
<TextBlock x:Name="txb" Text="From C#" FontSize="60" Margin="22" Foreground="Black"/>
</StackPanel>
</Grid>
    public sealed partial class MyTTFExample : UserControl
{
public MyTTFExample()
{
this.InitializeComponent();
Loaded += MyTTFExample_Loaded;
}

private void MyTTFExample_Loaded(object sender, RoutedEventArgs e)
{
this.txb.FontFamily = new FontFamily("/MGNORMAL.TTF#MagistralC");
}
}

[WP8] Redimensionar con PhotoChooserTask

Hay 2 formas de redimensionar una imagen, ya sea usando el crop pre-construido o haciéndolo por programación. A continuación las 2 formas en que se puede hacer:

Primera

Programándolo como buen Geek.

            PhotoChooserTask photoChooserTask = new PhotoChooserTask();
photoChooserTask.ShowCamera = true;
photoChooserTask.Completed += (s, a) =>
{
if (null == a.Error && a.TaskResult == TaskResult.OK)
{
if (a.ChosenPhoto.Length != 0)
{
try
{
BitmapImage img = new BitmapImage();
img.SetSource(a.ChosenPhoto);
WriteableBitmap writeableBmp = new WriteableBitmap(img);

int targetHeight = writeableBmp.PixelHeight;
int targetWidth = writeableBmp.PixelWidth;

while (true)
{
if (180 > targetHeight * 0.9 || 180 > targetWidth * 0.9)
break;

targetHeight = (int)(targetHeight * 0.9);
targetWidth = (int)(targetWidth * 0.9);
}

MemoryStream ms = new MemoryStream();
writeableBmp.SaveJpeg(ms, targetWidth, targetHeight, 0, 100);
ms.Seek(0, SeekOrigin.Begin);

//Ya esta todo en el MemoryStream
}
catch (Exception)
{
throw new ArgumentException("La foto que ha elegido no se puede redimensionar, por favor eliga otra.");
}
}
else
{
throw new ArgumentException("La foto que ha elegido no se puede redimensionar, por favor eliga otra.");
}
}
};
photoChooserTask.Show();

Segunda

PhotoChooserTask, lo hacemos juntos.

            PhotoChooserTask photoChooserTask = new PhotoChooserTask();
photoChooserTask.PixelHeight = 180;
photoChooserTask.PixelWidth = 180;
photoChooserTask.ShowCamera = true;
photoChooserTask.Completed += (s, a) =>
{
if (null == a.Error && a.TaskResult == TaskResult.OK)
{
if (a.ChosenPhoto.Length != 0)
{
try
{
//Ya esta todo en el a.ChosenFoto;
}
catch (Exception)
{
throw new ArgumentException("La foto que ha elegido no se puede redimensionar, por favor eliga otra.");
}
}
else
{
throw new ArgumentException("La foto que ha elegido no se puede redimensionar, por favor eliga otra.");
}
}
};
photoChooserTask.Show();