[WP] TextBox Personalizado

C#

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

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace CustomTextBox.Controls
{
public class PhoneTextBox : Control
{
#region Members

private TextBlock _hintTextBlock;
private TextBox _controlTextBox;

#endregion Members

#region DependencyProperties

public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}

public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(PhoneTextBox), new PropertyMetadata(null));

public string Hint
{
get { return (string)GetValue(HintProperty); }
set { SetValue(HintProperty, value); }
}

public static readonly DependencyProperty HintProperty =
DependencyProperty.Register("Hint", typeof(string), typeof(PhoneTextBox), new PropertyMetadata(null));

public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(PhoneTextBox), new PropertyMetadata(string.Empty));

#endregion DependencyProperties

public PhoneTextBox()
{
DefaultStyleKey = typeof(PhoneTextBox);
}

public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this._hintTextBlock = (TextBlock)GetTemplateChild("HintTextBlock");
this._controlTextBox = (TextBox)GetTemplateChild("ControlTextBox");
}

protected override void OnKeyUp(System.Windows.Input.KeyEventArgs e)
{
base.OnKeyUp(e);
this._hintTextBlock.Visibility = string.IsNullOrWhiteSpace(this._controlTextBox.Text) ? Visibility.Visible : Visibility.Collapsed;
}
}
}

Y un Converter:

using System;
using System.Windows;
using System.Windows.Data;

namespace CustomTextBox.Converters
{
public class ObjectToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null != value ? Visibility.Visible : Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

XAML

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

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:CustomTextBox.Controls"
xmlns:converters="clr-namespace:CustomTextBox.Converters">

<converters:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter"/>

<Style TargetType="controls:PhoneTextBox">
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:PhoneTextBox">
<Border Background="{TemplateBinding Background}" CornerRadius="5" Margin="{StaticResource PhoneTouchTargetOverhang}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Margin="6" Source="{TemplateBinding ImageSource}" Width="32" Height="32" VerticalAlignment="Center" Visibility="{Binding ImageSource, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ObjectToVisibilityConverter}}"/>
<TextBlock Margin="6" x:Name="HintTextBlock" Grid.Column="1" Text="{TemplateBinding Hint}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" Opacity="0.54" />
<TextBox Margin="6" x:Name="ControlTextBox" Grid.Column="1" Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" >
<TextBox.Template>
<ControlTemplate TargetType="TextBox">
<ContentControl x:Name="ContentElement" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

</ResourceDictionary>

Uso

Agregar el namespace de PhoneTextBox a la página o al control correspondiente:

<phone:PhoneApplicationPage
x:Class="CustomTextBox.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"
mc:Ignorable="d"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
xmlns:controls="clr-namespace:CustomTextBox.Controls">

<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="WWW.REACHSOS.COM" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="xaml development" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<controls:PhoneTextBox Hint="efectivamente es el joven" Background="White" Foreground="Blue" ImageSource="/Assets/icon.png"/>
<controls:PhoneTextBox Grid.Row="1" Hint="y dile que ahorita no" Background="Brown" Foreground="White" ImageSource="/Assets/icon.png"/>
</Grid>
</Grid>

</phone:PhoneApplicationPage>

CodePlex

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

[C#] ¿Cómo quitar elementos repetidos en una lista?

Existen datos simples y complejos, y por ello hay que tratarlos de diferente manera.

Simples

Si queremos trabajar con los tipos de datos primitivos como el double, int, long, decimal y string no hay que hacer mucho esfuerzo.

            int[] numeros = new int[] { 1, 1, 2, 3, 8, 8, 9 };

foreach (var numero in numeros)
Console.Write(numero);

Console.WriteLine();

foreach (var numero in numeros.Distinct())
Console.Write(numero);

Console.WriteLine();

var numerosSinRepetir = new List<int>();
foreach (var numero in numeros)
if (!numerosSinRepetir.Contains(numero))
numerosSinRepetir.Add(numero);
foreach (var numero in numerosSinRepetir)
Console.Write(numero);

Console.WriteLine();
Console.WriteLine();

Complejos

Si queremos trabajar con clases o estructuras tenemos que implementar la interfaz IEqualityComparer<T> correctamente.


public class DatoComplejo
{
public int Codigo { get; set; }

public DatoComplejo(int codigo)
{
this.Codigo = codigo;
}

public override string ToString()
{
return string.Format("Código: {0}", this.Codigo);
}
}

public class DatoComplejoEqualityComparer : IEqualityComparer<DatoComplejo>
{

public bool Equals(DatoComplejo x, DatoComplejo y)
{
return x.Codigo == y.Codigo;
}

public int GetHashCode(DatoComplejo obj)
{
return obj.Codigo.GetHashCode();
}
}
            DatoComplejo[] datosComplejos = new DatoComplejo[] 
{
new DatoComplejo(1),
new DatoComplejo(1),
new DatoComplejo(2),
new DatoComplejo(3),
new DatoComplejo(8),
new DatoComplejo(8),
new DatoComplejo(9)
};

foreach (var datoComplejo in datosComplejos)
Console.Write(datoComplejo + ". ");

Console.WriteLine();

foreach (var datoComplejo in datosComplejos.Distinct())
Console.Write(datoComplejo + ". ");

Console.WriteLine();

foreach (var datoComplejo in datosComplejos.Distinct(new DatoComplejoEqualityComparer()))
Console.Write(datoComplejo + ". ");

Console.WriteLine();

var datosComplejosSinRepetir = new List<DatoComplejo>();
foreach (var datoComplejo in datosComplejos)
if (!datosComplejosSinRepetir.Contains(datoComplejo, new DatoComplejoEqualityComparer()))
datosComplejosSinRepetir.Add(datoComplejo);
foreach (var datoComplejo in datosComplejosSinRepetir)
Console.Write(datoComplejo + ". ");

Console.WriteLine();
Console.WriteLine();

CodePlex

Código fuente disponible en: CSharp -> LINQDistinct

[C#] ¿Cómo obtener la dirección IP?

Este tema lo abarcaremos desde 2 puntos de partida: Frontend y Backend.

Frontend

Simplemente quieres saber cual es tu IP Privada o Pública.

using System;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace GetIPAddress.Frontend
{
internal class Program
{
private static void Main(string[] args)
{
//1.
GetPrivateIP();

//2.
Console.WriteLine(GetPublicIP());

}

private static void GetPrivateIP()
{
foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
foreach (var unicastAddress in networkInterface.GetIPProperties().UnicastAddresses)
if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
Console.WriteLine(networkInterface.Name);
Console.WriteLine(unicastAddress.Address.ToString());
Console.WriteLine();
}
}

private static string GetPublicIP()
{
string address;
var webRequest = WebRequest.Create("http://checkip.dyndns.org/");
using (var webResponse = webRequest.GetResponse())
using (var streamReader = new StreamReader(webResponse.GetResponseStream()))
address = streamReader.ReadToEnd();

int startIndex = address.IndexOf("Address: ") + 9;
int lastIndex = address.LastIndexOf("</body>");
address = address.Substring(startIndex, lastIndex - startIndex);

return address;
}
}
}

Backend

Tienes una aplicación web y quieres saber la dirección IP de los usuarios que la usan. Para este ejemplo usare un proyecto ASP.NET MVC.

using System;
using System.Web.Mvc;

namespace GetIPAddress.Backend.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
ViewBag.ClientIPAddress = GetClientIPAddress();
return View();
}

private string GetClientIPAddress()
{
string clientIPAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

if (String.IsNullOrEmpty(clientIPAddress))
clientIPAddress = Request.ServerVariables["REMOTE_ADDR"];

if (String.IsNullOrEmpty(clientIPAddress))
clientIPAddress = Request.UserHostAddress;

if (String.IsNullOrEmpty(clientIPAddress) || clientIPAddress.Trim() == "::1")
clientIPAddress = "Unknown";

try
{
clientIPAddress = clientIPAddress.Remove(clientIPAddress.IndexOf(':'));
}
catch { }

return clientIPAddress;
}
}
}

@{
Layout = null;
}

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Xaml Development | GetIPAddress</title>
</head>
<body>
<div>
<ip>@ViewBag.ClientIPAddress</ip>
<hr />
<footer>
<p><a href="http://xamldevelopment.blogspot.com/2015/01/c-como-obtener-la-direccion-ip.html" target="_blank">[C#] ¿Cómo obtener la dirección IP? - Xaml Development</a></p>
</footer>
</div>
</body>
</html>

Una vez publicada esta aplicación web, ya podríamos hacer lo siguiente en algún cliente:


using System;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace GetIPAddress.Frontend
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("http://getipaddress.azurewebsites.net/ : " + GetIPAddress());
Console.WriteLine();
}

private static string GetIPAddress()
{
string address;
var webRequest = WebRequest.Create("http://getipaddress.azurewebsites.net/");
using (var webResponse = webRequest.GetResponse())
using (var streamReader = new StreamReader(webResponse.GetResponseStream()))
address = streamReader.ReadToEnd();

int startIndex = address.IndexOf("<ip>") + 4;
int lastIndex = address.LastIndexOf("</ip>");
address = address.Substring(startIndex, lastIndex - startIndex);

return address;
}
}
}

CodePlex

Código fuente disponible en: CSharp -> GetIPAddress

[C#] ¿Cómo obtener el índice de la iteración actual de un bucle foreach?

Con este post quiero demostrar 5 casos en los cuales nos podemos encontrar al momento de intentar conocer en que índice nos encontramos cuando usamos un bucle foreach. Para ello definiremos primero una clase, una estructura, un par de variables y un par de métodos.

    public class Usuario
{
public string Nombre { get; set; }

public int Edad { get; set; }

public DateTime FechaNacimiento { get; set; }

public static List<Usuario> Listar()
{
return new List<Usuario>
{
new Usuario { Nombre = "Atom", FechaNacimiento = DateTime.Now.AddYears(-12) },
new Usuario { Nombre = "Bolt", FechaNacimiento = DateTime.Now.AddYears(-15) },
new Usuario { Nombre = "Carmen", FechaNacimiento = DateTime.Now.AddYears(-18) },
new Usuario { Nombre = "Phineas", FechaNacimiento = DateTime.Now.AddYears(-21) },
new Usuario { Nombre = "Lawliet", FechaNacimiento = DateTime.Now.AddYears(-24) },
};
}
}

public struct Cliente
{
public string Nombre { get; set; }

public int Edad { get; set; }

public DateTime FechaNacimiento { get; set; }

public static List<Cliente> Listar()
{
return new List<Cliente>
{
new Cliente { Nombre = "Atom", FechaNacimiento = DateTime.Now.AddYears(-12) },
new Cliente { Nombre = "Bolt", FechaNacimiento = DateTime.Now.AddYears(-15) },
new Cliente { Nombre = "Carmen", FechaNacimiento = DateTime.Now.AddYears(-18) },
new Cliente { Nombre = "Phineas", FechaNacimiento = DateTime.Now.AddYears(-21) },
new Cliente { Nombre = "Lawliet", FechaNacimiento = DateTime.Now.AddYears(-24) },
};
}
}

class Program
{
static void Main(string[] args)
{
var usuarios = Usuario.Listar();
var clientes = Cliente.Listar();

//...
}

static Usuario CalcularEdad(Usuario usuario)
{
usuario.Edad = (int)((DateTime.Now - usuario.FechaNacimiento).TotalDays / 365);
return usuario;
}

static Cliente CalcularEdad(Cliente cliente)
{
cliente.Edad = (int)((DateTime.Now - cliente.FechaNacimiento).TotalDays / 365);
return cliente;
}
}

1. USANDO UN CONTADOR

Es la forma más sencilla de hacer esto y es tan útil en el momento que usamos un List<T> como en el momento en que usamos un Dictionary<TKey, TValue>.

            int c = 0;
foreach (var usuario in usuarios)
{
Console.WriteLine(c);
c++;
}

2. USANDO SYSTEM.LINQ EN CLASS

Ahora filtremos la lista usando las extensiones que hay en el namespace de System.Linq y usemos List<T>.IndexOf(T item).

            foreach (var usuario in usuarios.Where(x => x.Nombre.ToLower().Contains("a")))
Console.WriteLine(usuarios.IndexOf(usuario));

3. USANDO SYSTEM.LINQ Y UNA FUNCION EN CLASS

            foreach (var usuario in usuarios.Where(x => x.Nombre.ToLower().Contains("a")))
{
var usuarioProcesado = CalcularEdad(usuario);
Console.WriteLine(usuarios.IndexOf(usuarioProcesado) + " - " + usuarioProcesado.Edad);
}

4. USANDO SYSTEM.LINQ EN STRUCT

            foreach (var cliente in clientes.Where(x => x.Nombre.ToLower().Contains("a")))
Console.WriteLine(clientes.IndexOf(cliente))

5. USANDO SYSTEM.LINQ Y UNA FUNCION EN STRUCT

En los últimos ejemplos no han aparecido problemas, ha sido muy sencillo. Pero con este caso os quiero hacer recordar del particular comportamiento de un struct cuando es pasado como argumento y del cual tenemos que tener mucho cuidado.

            foreach (var cliente in clientes.Where(x => x.Nombre.ToLower().Contains("a")))
{
var clienteProcesado = CalcularEdad(cliente);
Console.WriteLine(clientes.IndexOf(clienteProcesado) + " - " + clienteProcesado.Edad);
}

CodePlex

Código fuente disponible en: CSharp -> ForeachIterationIndex

[C#] Estandares de Codificacion y Convenciones de Nomenclatura

Correcto

Usar PascalCasing para nombrar clases y métodos. Así es consistente con .NET y fácil para leer.

    public class DeveloperAction
{
public void CodeSomething()
{
//...
}
public void CommentSomething()
{
//...
}
}

Correcto

Usar camelCasing para argumentos de métodos y variables globales.

    public class DeveloperAction
{
public void CodeSomething(Code code)
{
int instructionsCount = code.Instructions.Count;
//...
}
}

Incorrecto

Usar notación Hungariana o algún identificador de tipos en las variables o métodos.

            // Correcto
int contador;
string nombres;

// Incorrecto
int iContador;
string strNombres;

Incorrecto

Usar mayúsculas para constantes o variables de solo lectura. Las mayúsculas llaman mucho la atención.

            // Correcto
public static const string LoginProvider = "Live";

// Incorrecto
public static const string LOGINPROVIDER = "Live";

Incorrecto

Usar abreviaciones. Excepciones: abreviaciones usados comúnmente como nombres, por ejemplo: Id, Xml, Ftp, Uri. Asi es consistente con .NET y evita abreviaciones inconsistentes.

        // Correcto
DeveloperAction developerAction;
BlogWriter blogWriter;

// Incorrecto
DeveloperAction dvpAct;
BlogWritter writer;

// Excepciones
UserId userId;
XmlDocument xmlDocument;
FtpHelper ftpHelper;
UriPart uriPart;

Incorrecto

Usar subguion en identificadores. Excepción: Puedes prefijar variables estáticas privadas con un subguión. Asi es consistente con .NET


// Correcto
public DateTime fechaModificacion;
public TimeSpan tiempoTranscurrido;

// Incorrecto
public DateTime fecha_Modificacion;
public TimeSpan tiempo_Transcurrido;

// Excepción
private DateTime _fechaCreacion;

Correcto

Utilizar los alias de tipos primitivos en lugar de nombres de tipos de sistema como String, Int32, UInt64, Boolean, etc. Así es consistente con .NET y más natural para leer.


// Correcto
string nombres;
int edad;
bool esFacil;

// Incorrecto
String nombres;
Int32 edad;
Boolean esFacil;

Correcto

Utiliza sustantivo o frases nominales para nombrar una clase. Así es consistente con .NET y fácil de recordar.


public class Developer
{
}
public class ItemLocation
{
}
public class WorkItem
{
}

Correcto

Declara todas las variables miembro en la parte superior de una clase, con las variables estáticas en la parte superior. Esta es una práctica generalmente aceptada ya que evita la necesidad de cazar las declaraciones de variables.


// Correcto
public class User
{
public static string ModuleName;

public string Name { get; set; }
public string Code { get; set; }
public DateTime RegisterDate { get; set; }
public int Points { get; set; }

public User()
{
}
}

Correcto

Usar nombres en singular para los enumerados.


public enum Color
{
Red,
Green,
Blue,
Yellow,
Magenta,
Cyan
}

Incorrecto

Establecer como sufijo Enum a los enumerados.


public enum ColorEnum
{
Red,
Green,
Blue,
Yellow,
Magenta,
Cyan
}

[C#] String vs string

¿Quién no ha tenido esta pregunta antes? ¿Quién no ha encontrado opiniones que van desde gatos hasta alienígenas? Por eso en este preciso post echaré un poco de luz sobre qué es lo que ocurre aquí.. ¿Qué es un String?.

1. La clase String representa una cádena de caracteres Unicode.

2. El tipo String es una clase sellada que hereda directamente de Object, esto significa que no puedes heredar de String

3. La palabra clave string es simplemente un alias para la predefinida clase System.String de la misma forma en que int lo es de la estructura System.Int32

Console.WriteLine(typeof(string)); //System.String
Console.WriteLine(typeof(String)); //System.String
Console.WriteLine(typeof(int)); //System.Int32
Console.WriteLine(typeof(Int32)); //System.Int32
Console.WriteLine(typeof(long)); //System.Int64
Console.WriteLine(typeof(Int64)); //System.Int64
Console.WriteLine(typeof(double)); //System.Double
Console.WriteLine(typeof(Double)); //System.Double
Console.WriteLine(typeof(bool)); //System.Boolean
Console.WriteLine(typeof(Boolean)); //System.Boolean

4. El resultado de usar string.Concat() o String.Concat() es el mismo, sin embargo por un tema de estándares de codificación deberíamos usar String para los métodos y string para definir variables.

// Correcto
string nombre;
int edad;
bool esFacil;

// Incorrecto
String nombre;
Int32 edad;
Boolean esFacil;

// Correcto
Console.WriteLine(String.Concat("Hello! ", "xamldevelopment"));

// Incorrecto
Console.WriteLine(string.Concat("Hello! ", "xamldevelopment"));

5. Ya que la clase String implementa la interface IEnumerable<char> puedes hacer uso de las extensiones definidas en System.Linq.

string miCadena = "1. Xaml Development es lo máximo. Compartan http://xamldevelopment.blogspot.com";
miCadena.ToList().ForEach(c => Console.Write(c + "-"));
Console.WriteLine();

Console.Write("¿Existe alguna l en mi cadena de caracteres?: ");
Console.Write(miCadena.Any(c => c == 'l'));
Console.WriteLine();

Console.Write("¿Existe algun numero en mi cadena de caracteres?: ");
var regex = new Regex(@"d");
Console.Write(miCadena.Any(c => regex.IsMatch(c.ToString())));

CodePlex

Código fuente disponible en: CSharp -> StringVSstring

[C#] Structs vs Classes

Stack

  • Crece o se contrae mientras las funciones o métodos insertan y expulsan variables locales.
  • No hay necesidad de gestionar la memoria personalmente, las variables se asignan y liberan automáticamente.
  • Tiene límites de tamaño y las variables no se pueden redimencionar.
  • Las variables sólo existen mientras la función que los creó, está en marcha.

Heap

  • Se necesitan de punteros para acceder a la memoria.
  • La memoria no es gestionada automáticamente, pueden ocurrir las famosos memory leaks.
  • No tiene límites de tamaño.
  • Las variables son esencialmente globales.

Value types & Reference types

  • Los tipos de valor son tipos que mantienen los datos y la memoria en la misma ubicación
  • Un tipo de referencia tiene un puntero que apunta a la ubicación de memoria.

CodePlex

Código fuente disponible en: CSharp -> StructVSClass

[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>