Mostrando las entradas con la etiqueta C#. Mostrar todas las entradas
Mostrando las entradas con la etiqueta C#. Mostrar todas las entradas

2010-06-23

StringWriter y con distintos Encodings

Recientemente me topé con un pequeño problema al armar una consigna de un trabajo práctico del colegio: La idea era armar un helper para que los chicos puedan bajar facilmente archivos XML.

El problema fue que yo me encargaba de bajar el stream del archivo, lo retocaba un poco y luego lo pasaba desde un XDocument a un string utilizando un StringWriter, entonces al escribir el string, lo hacía agregando el header de XML de encoding="utf-16". Todo esto no sería un problema si no fuese porque, la idea detrás de esto era que asi como les llegaba el string, lo pudiesen guardar a disco con StreamWriter, utilizando los constructores mas simples, sin tener que especificar encodings.

Dado que StringWriter no permite modificar el encoding que utiliza, la solución fue crear una subclase de StringWriter y hacer override de la propiedad Encoding.

La pequeña clase quedó asi:


class UTF8StringWriter : StringWriter
{
public override System.Text.Encoding Encoding
{
get
{
return System.Text.Encoding.UTF8;
}
}
}

Espero que les sea útil!

Zaiden


2010-06-21

Problemas al querer cargar un XML de un string Unicode

Hace poco, por cierta necesidad me encontré con un error bastante extraño. Al intentar leer un XML con un XmlDocument obtenía el siguiente error: "Data at the root level is invalid. Line 1, position 1".

El string obtenido provenía de un stream, el cual se iba leyendo de a partes.

Luego de varias idas y vueltas, encontré finalmente la casua del problema: al escribirse el stream original, se estaba haciendo lo siguiente:

var writer = new StreamWriter(stream, Encoding.Unicode);
writer.Write(text);

El problema que traía esto es que, por la forma en la que se estaba leyendo el stream del otro lado (codigo externo, revisado gracias al amigo Lutz!) se estaba interpretando mal la marca de uso de Unicode, al comienzo del string.

Para poder solucionar esto, bastó con reemplazar las lineas en la que se crea y usa el StreamWriter, por la siguiente:

var writer = new StreamWriter(stream, new UnicodeEncoding(false, false));
writer.Write(text);

Esta forma de crear el encoding, le indica que no se deberá incluir la marca de Unicode al comienzo del string, solucionando el problema antes mencionado

Espero poder ayudar a alguien con esto.
Saludos!

Zaiden


2009-12-31

Nueva (y por ahora última) versión de TweetMyFace

Ya está la versión Release (1.1) de TweetMyFace.

La pueden bajar de acá: http://tweetmyface.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=37990

Saludos!
Zaiden


2009-12-23

Recibí tus notificaciones de Facebook por Twitter: TweetMyFace

En estos días quise hacer un pequeño experimento con el API de Facebook para C# y el resultado fue TweetMyFace

La idea de esta aplicación crear un nuevo usuario de Twitter que se utilizará para publicar las notificaciones que recibe un usuario de Facebook.

Idealmente, el único seguidor de este usuario de Twitter será el mismo dueño de la cuenta de Facebook que se está monitoreando, por lo que podrá recibir en su cliente de Twitter favorito todas las notificaciones de Facebook al instante.

Si, ya se... no tiene mucha utilidad, pero fue lo que salió.

Saludos!
Zaiden

2009-10-14

Nuevo proyecto: zInject, o "Cómo hacer Inyección de Código para cualquier hijo de vecino"

Hace tiempo que tenía la idea de hacer un programa para poder inyectar código sobre assemblies ya compiladas dando vueltas por la cabeza. Finalmente, hoy fue el día de crear su versión 0.1

Les presento zInject!

Cual es la idea detrás de esto? Simple...
Supongamos que yo tengo un proyecto hecho y derecho, con una clase cuyos métodos son M1, M2 y M3.


public class M
{
public void M1()
{
...
}

public void M2()
{
...
}

public void M3()
{
...
}
}


Un día viene el jefe y me dice: "De ahora en mas, tenemos que verificar cuanto demora la ejecución de los siguientes métodos: M1, M2 y M3.
En ese momento uno piensa: "Bien, tendría que hacer nuevos métodos que se ejecuten antes y después de M1, M2 y M3 y que los mismos calcules cuanto tardó cada ejecución.


public class M
{
public void M1()
{
Empezo("M1");
...
Termino("M1");
}

public void M2()
{
Empezo("M2");
...
Termino("M2");
}
    public void M3()
{
Empezo("M3");
...
Termino("M3");
}
}

Esto trae una serie de problemas. Agregar esto ensucia el código de M1, M2 y M3 por las llamadas a los nuevos métodos al principio y al final. Peor aún, si tengo que agregar nuevos "comportamientos" al principio o al final del método, el código termina siendo incomprensible! Ni hablar en caso de que cada comportamiento (como seguridad o auditoria) a agregar implique mas de una sola linea de código...

Para solucionar llega zInject.
Para usarlo, definimos un atributo que hereda de InjectionBaseAttribute


    
public class TiempoAttribute : InjectionBaseAttribute
{
public TiempoAttribute()
{
}
        public override void BeforeExecute(string method, object target)
{
Empezo(method);
}
        public override void AfterExecute(string method, object target)  
{
Termino(method);
}
}

Después, simplemente agregamos el atributo a los métodos que se desea:


public class M
{
[Tiempo]
public void M1()
{
...
}

[Tiempo]
public void M2()
{
...
}

[Tiempo]
public void M3()
{
...
}
}

Ahora, dirán... Todo muy lindo, pero... ¿Cómo se ejecuta el código del atributo?

Para poder hacer esto hay que ejecutar el injector propiamente dicho. Esto se puede hacer tanto ejecutándolo desde linea de comandos una vez compilado el assembly, como usando una Post Build Action dentro de Visual Studio. Esta utilidad agregará dentro del IL del assembly las llamadas necesarias a los metodos ExecuteBefore y ExecuteAfter

To Do:

  • Evitar totalmente el uso de Reflection. Actualmente, el código inyectado utiliza reflection para ejecutar las acciones de los atributos, simplemente por simplicidad del desarrollo. A futuro, se inyectará directamente la llamda al atributo concreto.
  • Poder customizar cuales atributos aplicar y cuales no
  • UI para Windows/OS X/Linux
  • Quitar dependencias contra librerías externas

Les dejo la dirección del proyecto en CodePlex para bajar los binarios compilados y el código completo para bajarlo con sus tests para bajarlo y ver como se usa.

http://zInject.codeplex.com

Cualquier sugerencia es bienvenida!

Saludos!
Zaiden


2009-09-26

Yapa

Como yapa, dejo también la presentación de la charla introductoria a ASP.NET MVC que dimos con RodoF en la UTN de Rosario el viernes

ASP.NET MVC


2009-07-23

.NET Tip: Validación de requests con SSL usando System.Net.WebClient

¿Cuantas veces han querido hacer un request a una página por HTTPS y su certificado no era válido? Posiblemente nunca, pero en caso de querer hacerlo se encontrarán con que, por default, System.Net.WebClient rechazará el pedido por motivos de seguridad. Para poder pasar esta validación por arriba hay que hacer el pequeño triquiñuelo de agregar un callback a ServicePointManager.ServerCertificateValidationCallback antes de hacer el request utilizando el WebClient:

public void RequestSSL(string url)
{
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
using (WebClient client = new WebClient())
{
client.DownloadString(url);
}
}
private static bool ValidateRemoteCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors policyErrors)
{
return true;
}

Espero que a alguien le sirva!
Z


2009-07-14

Monos y Manzanas: Key Value Coding y Configuración

Hacia el final de la entrega anterior de Monos y Manzanas estuve contando sobre como utilizar los bindings de Cocoa con Monobjc. En el capítulo final de esta serie de posts veremos como es posible agilizar la implementación de clases que utilicen Key Value Coding (KVC de ahora en mas) desde .NET y como agregar un panel de preferencias para modificar la configuración de la aplicación y que la misma sea persistida de forma automática.

Ante todo, fe de erratas: una persona del equipo de MonobjC con la cual me contacté, me acalró que no es necesario crear una instancia que nadie usa de NSAutoReleasePool en el bootstrapping de la aplicación, ya que eso se hace automáticamente.

En el último ejemplo mostrado, cada vez que se quería implementar una propiedad que pudiese ser utilizada para los bindings con KVC era necesario crear los setters y getters, indicarles que iban a ser llamados como mensajes de ObjectiveC y llamar a las notificaciones de cambios de propiedad.
Para no tener que hacer esto por cada propiedad que queramos exponer al runtime de ObjectiveC, crearemos un nuevo atributo el cual indique que esa propiedad podrá ser utilizada con KVC:

using System;

namespace zPod.Objc
{
public class KeyValueCodingAttribute : Attribute
{
public KeyValueCodingAttribute (string propertyName)
{
PropertyName = propertyName;
}

public string PropertyName
{
get;
set;
}
}
}

Teniendo esto, las propiedades que querramos exponer se verán así:


[KeyValueCoding("modelo")]
public BrowserModel Modelo
{
get;
set;
}

Muy lindo hasta ahora pero... ¿Como le indicamos al runtime de ObjectiveC que deberá tomar la propiedad "modelo" al intentar llamar al mensaje "modelo" o "setModelo:"? Simple, sobreescribiendo el llamado a los mensajes "setValue:forUndefinedKey:" y "valueForUndefinedKey"


[ObjectiveCMessage("setValue:forUndefinedKey:")]
public override void SetValueForUndefinedKey (Id val, NSString key)
{
this.ResolveSetValueForUndefinedKey(val, key);
}

[ObjectiveCMessage("valueForUndefinedKey:")]
public override Id ValueForUndefinedKey (NSString key)
{
return this.ResolveValueForUndefinedKey(key);
}

Al no encontrar una clave definida, Cocoa avisa a la clase que se intento leer o dar valor a la clave utilizando estos mensajes.


En estas implementaciones se está llamando a los métodos ResolveSetValueForUndefinedKey y ResolveValueForUndefinedKey. Los mismos son extension methods creados para la clase NSObject que resuelven cual es la propiedad que deberá ser devuelta o modificada utilizando reflection para saber si tienen el atributo definido anteriormente. El código de estos métodos se incluye dentro de los fuentes de este ejemplo.




Hacia el final de la entrega anterior de Monos y Manzanas comenté que Cocoa proveía un controller especial llamado NSUserDefaultsController que permite guardar automáticamente las preferencias del usuario.
Ahora vamos a ver como crear un nuevo panel de preferencias donde configurar la URL Home de nuestro browser.


Volviendo al Interface Builder, agregamos un nuevo NSPanel y le agregamos un TextField para ingresar la URL Home


Picture 1


Para poder abrir el panel desde el menu de preferencias, conectamos la opción Preferences... del menú con el panel creado, asociándolo a la acción makeKeyAndOrderFront:.


Picture 3


Dado que se necesita únicamente una instancia de este panel, lo configuramos como se muestra en la siguiente imagen. Lo mas importante es desmascar las opciones Released When Closed y Visible at launch para que no se muestre al incio, pero que al cerrar la ventana solamente se oculte y no se destruya el objeto.


Picture 2


Creamos el binding para el value del TextField, conectándolo con la instancia de SharedUserDefaultsController, utilizando la clave values y el model key path homePage.
Al utilizar este controller, los valores conectados serán persistidos automáticamente.


Picture 4


Como último paso dentro del Interface Builder, agregamos un nuevo boton para ir a la home, agregamos una nueva acción llamada goHome: al BrowserController y los asociamos de la misma manera que el boton Go.


Picture 5


Volviendo a C#, Dentro del código del BrowserController, modificamos el método AwakeFromNib y agregamos el método que responda al mensaje goHome:, para que al iniciar la aplicación vaya a la URL definida como home y, en caso de no haber nada, definirle un valor default (recuenden que en _userDefaults tenemos guardada una instancia de SharedUserDefaultsController):


[ObjectiveCMessage("goHome:")]
public void GoHome(Id sender)
{
NSString home = _userDefaults.ValueForKeyPath("values.homePage");
this.Modelo.WillChangeValueForKey("url");
this.Modelo.Url = home;
this.Modelo.DidChangeValueForKey("url");
Browse(sender);
}

[ObjectiveCMessage("browse:")]
public void Browse(Id sender)
{
NSString url = this.Modelo.Url;
browser.MainFrameURL = url;
_userDefaults.SetValueForKeyPath("CocoaBrowser - " + url, "values.title");
}

[ObjectiveCMessage("awakeFromNib")]
public void AwakeFromNib ()
{
NSString home = _userDefaults.ValueForKeyPath("values.homePage");
if (home == null)
{
_userDefaults.SetValueForKeyPath(new NSString("http://zPod.com.ar"), "values.homePage");
}

GoHome(null);
}

Listo! Ahora a compilar y a disfrutar de su nuevo browser con botón de Home para Mac hecho en .NET!


Picture 6


Con esto queda finalizada la serie de posts: Monos y Manzanas. El código completo lo pueden bajar de aca.



Hasta la próxima!
Zaiden

2009-07-12

Monos y Manzanas: Binding entre la UI y el Modelo

En el último post expliqué como hacer una aplicación sencilla para Mac cuya UI estaba hecha con Interface Builder, y el resto de la plomería de fondo utilizaba C#, gracias a Mono y el bridge Monobjc.

Dada la extrema simplicidad de nuestro CocoaBrowser, hubieron algunos conceptos que escaparon al ejemplo inicial.

En esta nueva entrega de Monos y Manzanas veremos como hacer uso del binding provisto por Cocoa y el Interface Builder para conectar nuestra interfaz gráfica al Modelo de nuestra aplicación.

Por ahora, definiremos una nueva clase BrowserModel, la cual representará el “modelo” de nuestra apliación de juguete.

public class BrowserModel
{
private string _url;
public string GetUrl()
{
return _url;
}

public void SetUrl(string newUrl)
{
_url = newUrl;
}
}


Posiblemente se pregunten: Por qué la propiedad esta definida con metodos Get y Set, en vez se ser una propiedad “normal” de C#, al estilo “public string Url { get; set'; }”?

Es por un requerimiento del bridge con Objective-C y el binding con la UI, pero vamos a verlo bien mas adelante.


La idea es que lo que usuario tipee dentro del TextField utilizado para ingresar la URL de nuestro browser actualice automáticamente nuestro modelo y, en caso de actualizar el modelo desde otro lugar, se actualice automáticamente el TextField.


Para poder hacer esto, agregaremos un nuevo campo a la clase BrowserController que nos de acceso al modelo, y lo haremos visible para el bridge, con los siguientes getter y setter:


[ObjectiveCClass]
public class BrowserController : NSObject
{
//
// Mucho codigo antes...
//

private BrowserModel _modelo = new BrowserModel();

[ObjectiveCMessage("modelo")]
public BrowserModel getModelo()
{
return _modelo;
}

[ObjectiveCMessage("setModelo:")]
public void setModelo(BrowserModel nuevoModelo)
{
WillChangeValueForKey("modelo");
_modelo = nuevoModelo;
DidChangeValueForKey("modelo");
}

//
// Mucho codigo despues...
//

}

Esta forma de crear los campos permite a ObjectiveC utilizar la clase con algo conocido como Key Value Coding, accediendo a ciertas propiedades de nuestra clase como si fuesen pares Clave - Valor, tanto para leer como para escribir.

Los métodos WillChangeValueForKey y DidChangeValueForKey permiten avisarle a la UI que hubo un cambio en el valor la propiedad.


Para que las propiedades de la clase BrowserModel sean accesibles de la misma manera, las decoramos de forma similar, quedando como resultado:


[ObjectiveCClass]
public class BrowserModel : NSObject
{
// Constructores requeridos por NSObject
public BrowserModel()
{
}

public BrowserModel (IntPtr nativePointer) : base(nativePointer)
{
}

private string _url;

[ObjectiveCMessage("url")]
public string GetUrl()
{
return _url;
}

[ObjectiveCMessage("setUrl:")]
public void SetUrl(string newUrl)
{
WillChangeValueForKey("url");
_url = newUrl;
DidChangeValueForKey("url");
}
}

Como pueden ver, es requisito agregar el atributo [ObjectiveCClass] y hacer heredar la clase de NSObject (o algún hijo) para que la misma sea visible desde ObjectiveC. Al hacer esto, deberán agregar los dos constructores indicados en el código.


Dado que ahora el URL a navegar será indicado por las propiedades del modelo, podemos sacar la referencia al TextField del BrowserController y modificar el metodo Browse de la siguiente manera:


[ObjectiveCClass]
public class BrowserController : NSObject
{
//
// Mucho codigo antes...
//

[ObjectiveCMessage("browse:")]
public void Browse(Id sender)
{
string url = this.fModelo.getUrl();
browser.MainFrameURL = url;
}

//
// Mucho codigo despues...
//

}

Con esto listo, solo resta hacer los bindings propiamente dichos en el Interface Builder.


El primer paso será agregar un ObjectController desde la Library. Este tipo de controller nos permite hacer binding facilmente a un objeto concreto del modelo. En caso de querer hacer binding contra Arrays, Diccionarios y otros tipos de datos, existen controllers mas apropiados.


Picture 1


Dentro de los atributos del ObjectController, agregar una nueva clave llamada “url”, la cual usaremos como nexo entre el modelo y la UI.


Picture 2


Dentro de los bindings del ObjectController, indicamos que el Controller Content debe hacer binding con el BrowserController, utilizando como Model Key Path “modelo”. Esto hará referencia a la propiedad modelo que creamos – e hicimos visible con setter y getter para ObjectiveC – dentro de la clase creada en C#.


Picture 3


Finalmente, seleccionamos el TextField de la URL y dentro de la solapa de bindings indicamos que se deberá hacer el binding con el ObjectController, utilizando “selection” como Controller Key y “url” como Model Key Path. Para que el modelo se actualice cada vez que se modifica el TextField y no únicamente al terminar de editarlo cambiando el foco, deberemos marcar el check de “Continuosly Update Value”.


Picture 4


Como yapa, en esta versión de los fuentes, podrán ver como hacer binding contra una clase especial llamada NSUserDefaultsController, la cual nos permite mantener guardadas las preferencias del usuario haciendo binding contra las mismas. En este ejemplo se utiliza para guardar el titulo de la ventana de la aplicación, aunque su utilidad mas común es dentro de los paneles de preferencias.


El código lo pueden bajar de aca.


Hasta la próxima!
Zaiden

2009-07-08

Monos y Manzanas: Como hacer aplicaciones Cocoa con .NET

Desde que soy usuario de OS X he intentado varias veces desarrollar alguna aplicación que pueda aprovechar todo el poder de la plataforma .NET y las delicias gráficas de las interfaces gráficas creadas con Cocoa y sus frameworks para Mac.

La necesidad de utilizar .NET sobre el sistema operativo impulsado por Steve Jobs está cubierta desde hace un buen tiempo por la gente de Mono y su implementación multiplataforma del CLR y gran mayoría de las librerías incluidas en el .NET Framework.
El problema radicaba en como poder interactuar de forma simple y comoda con una UI hecha en Cocoa y no con el - bastante sovietico - port de Windows.Forms que incluye Mono, o con librerías graficas como GTK que se ven bastante alienigenas dentro de la Mac.

Investigando un poco, encontré que existen varios proyectos que hacen de bridge entre el mundo de Mono y .NET y los frameworks provistos por Cocoa.
Inicialmente, utilicé la librería publicada en el mismo sitio de Mono: Cocoa#. El problema fue que, pese a su simplicidad, tenía demasiados problemas de performance y estabilidad, por lo que quedó descartada.

Hace no mucho leí de otro bridge que, apartentemente, tiene bastante aceptación: Monobjc. Estas librerías permiten utilizar tanto una interfaz creada en Cocoa y sus frameworks desde una aplicación .NET como exponer - con ciertas restricciones - clases hechas en .NET dentro de una aplicación nativa hecha en Cocoa y ObjectiveC.

En este caso voy a explicar, paso a paso, como crear una nueva aplicación que nos permita navegar por la web cuya (única) View esté desarrollada con Cocoa utilizando el Interface Builder y el Controller y (ausente) Model estén hechos en C#

Requerimientos

Para empezar vamos a crear una nueva View utilizando Interface Builder (se recomienda tener al menos conocimientos básicos sobre como utilizar Interface Builder).

Al abrir Interface Builder seleccionamos el template de Cocoa Application

1. NuevaApp

Una vez creado el proyecto, podremos ver la ventana principal creada para la aplicación, su menú, el listado de objetos existentes, el inspector donde podrémos modificar propiedades de los controles y la librería de controles disponibles desde Cocoa

2. Empezar

En la librería de controles buscamos los controles de WebView, TextField y Button y los agregamos a la ventana, como se ve en la imagen. Pueden modificar las propiedades de estos controles en el inspector para permitirles ajustar su tamaño de acuerdo al resizing de la ventana.

5. VentanaLista

Con la ventana lista, agregamos un nuevo Object a la ventana que contiene la lista de objetos disponibles, y cambiamos el nombre de su clase por ObjectController. Este será el objeto que represente al Controller que crearemos desde C# mas adelante.

6. NuevoObject

Dentro de las actions disponibles para el controller deberemos agregar "browse:" que corresponderá a la acción de iniciar la navegación del URL escrito en el TextField anterior.
De la misma manera, deberémos exponer los Outlets "address", "window" y "browser" como se muestra en la imagen, los cuales asociaremos al TextField, la ventana y el WebView.

7. PropiedadesController

El último paso de esta parte será hacer el link entre las acciones y outlets definidos en el BrowserController y los controles creados dentro de la UI. Para hacer esto mantenemos apretada la tecla control y dibujamos una linea hacia cada uno de los controles. De esta manera aparecerá un menú para vincularlos hacia los outlets disponibles en el Controller.
De forma análoga se debe dibujar una linea desde el botón hacia el BrowserController, para asociar el click a la action "browse:"

Para asegurarse de haber vinculado todo de forma correcta, al hacer click derecho sobre el BrowserController se deberá ver un panel similar a este:

8. BindingController

Con la interfaz lista, guardamos la ventana como un nuevo archivo NIB (ojo! guardar como NIB y NO como XIB!).

Ahora, a programar el controller y el bootstrap de la aplicación en C#
Para hacerlo, pueden utilizar cualquier IDE de su agrado. En particular, yo uso MonoDevelop.

Deberémos crear un proyecto el cual referencie a las assemblies de Monobjc, Monobjc.Cocoa y Monobjc.WebKit ya que son las que utilizamos dentro de la interfaz.
Dentro del proyecto crearemos dos clases: la primera será el bootstrap de la aplicación y la segunda sera el BrowserController

Para hacer el bootstrap deberémos crear una clase muy similar al siguiente ejemplo:

using Monobjc;
using Monobjc.Cocoa;
using System;

namespace zPod.CocoaBrowser
{
public class Program
{
public Program ()
{
}

public static void Main (String[] args)
{
ObjectiveCRuntime.LoadFramework ("Cocoa");
ObjectiveCRuntime.LoadFramework ("WebKit");

ObjectiveCRuntime.Initialize ();
NSApplication.Bootstrap ();

NSAutoreleasePool pool = new NSAutoreleasePool();

NSApplication.LoadNib ("MainWindow.nib");
NSApplication.RunApplication ();
}
}
}


Las primeras lineas del método Main cargan los frameworks de ObjectiveC que utilizaremos. En este caso "Cocoa" y "WebKit".


Luego de cargar los frameworks se inicializa el bridge con el runtime de ObjectiveC y se dispara la aplicación.


Como verán, antes de cargar el NIB se crea un nuevo NSAutoreleasePool. Esto sirve para evitar leaking por parte de ObjectiveC.



Finalmente llamamos al metodo LoadNib, el cual cargará la interfaz grafica creada anteriormente y lanzamos la aplicacion con el método RunApplication.



La creación del BrowserController es muy sencilla: en la misma hay que crear propiedades que corresponderán a los outlets definidos en la interfaz y metodos para atrapar las actions disparadas desde la misma.



Las clases marcadas con el atributo [ObjectiveCClass] serán expuestas al runtime de ObjectiveC. Las propiedades decoradas con el atributo [ObjectiveCField] representan los outlets y los métodos decorados con [ObjectiveCMessage] sirven para atrapar las actions disparadas por la View.



using System;
using Monobjc;
using Monobjc.Cocoa;
using Monobjc.WebKit;

namespace zPod.CocoaBrowser
{
[ObjectiveCClass]
public class BrowserController : NSObject
{
[ObjectiveCField]
public NSTextField address;

[ObjectiveCField]
public WebView browser;

[ObjectiveCField]
public NSWindow window;

public BrowserController ()
{
}

public BrowserController (IntPtr nativePointer) : base(nativePointer)
{
}

[ObjectiveCMessage("browse:")]
public void Browse (Id sender)
{
string url = address.Cell.Title;

browser.MainFrameURL = url;
}

[ObjectiveCMessage("awakeFromNib")]
public void AwakeFromNib ()
{
window.Title = "CocoaBrowser - zPod";
}
}
}


En este ejemplo, se ve el método AwakeFromNib, el cual se ejecutará al finalizar de cargar la vista. En el mismo, seteamos el título de la ventana.

Para atrapar la accion "browse", creamos un metodo con su correspondiente atributo y dentro del mismo cambiamos la URL del MainFrame del WebView.



Una vez listo esto, solo queda probar la aplicación.

Según la documentación de Monobjc, se tendría que poder ejecutar un script de NAnt que proveen ellos y crear una estructura de directorios específica. Dado que eso no me está funcionando (por algún motivo NAnt no encuentra a mono) armé un pequeño script de bash que arma todo lo necesario para tener nuestra App empaquetada y lista para ejecutar.



Aca dejo para bajar esta aplicación de ejemplo como proyecto de MonoDevelop, el cual al compilarse dejará en el directorio bin/Debug todos los archivos necesarios para armar el paquete. Lo único que hay que hacer es, desde la consola, ir al directorio /bin/Debug del proyecto y ejecutar:



./MakePackage CocoaBrowser


Con esto se generará un nuevo paquete de aplicación ejecutable, cuyo resultado es:



9. App



Ahora, a divertirse!



Saludos!

Zaiden

2009-05-17

MTGDeck Version 0.5

Paso a presentarles la primera version Beta (0.5) del MTGDeck!

Este programa/juego permite jugar al conocido juego de trading cards "Magic: The Gathering" desde la computadora a través de la red, sin la necesidad de comprar los no-tan-baratos set de cartas, pudiendo probar distintos mazos, combinaciones, estrategias, etc...

El juego esta separado en dos aplicaciones distintas:
- Servidor: programa encargado de almacenar la información de las manos de los jugadores y de comunicarlos entre si. El mismo puede servir varias partidas distintas y para jugar siempre será necesario acceso al servidor donde se aloje la partida a la cual se desea ingresar.
- Cliente: programa desde el cual - conectándose a un servidor - permite jugar partidas de Magic contra el resto de los jugadores conectados.


Que NO hace el programa:

- NO valida las (complicadas) reglas del juego y cada una de sus cartas. El propósito de este programa es el de reemplazar el set de cartas, y nada mas. El hecho de "jugar" propiamente dicho todavía queda a cargo de los humanos.

- NO permite jugar contra la computadora. El único modo de jugar solo es, simplemente, no tener contrincante.

- NO te va a ayudar a ganar minas (aceptémoslo, es un juego bien nerd)


Que SI hace el programa:

- Permite jugar desde uno (practica solo) hasta varios jugadores dentro de la misma partida

- Permite armar mazos, utilizar cualquier carta que haya existido para Magic: The Gathering, referenciándolas por su nombre en ingles
- ¡Te ahorra dinero! (¡no hoy que ir a comprar cartas!)
- ¡Te proporciona horas y horas de diversión nerd!

Requerimientos:
- Windows XP o superior con todos sus Service Packs.
- .NET Framework 3.5 con todos sus Service Packs.
- Algun tipo de conexión de red (y al menos la primera vez que se usa cada mazo, conexión a internet).

Info sobre el proyecto:
- UI hecha con .NET 3.5 en Winforms.
- Para la comunicación entre los clientes y el server se puede utilizar tanto un cliente/server de WCF funcionando con basicHttpBinding - proximamente esto será configurable - y otro cliente/server hecho con old-school Remoting.
Esto tiene dos motivos: por un lado, hay algunas computadoras en los cuales el server de WCF no funciona. Todavía no pude encontrar el problema, pero voy a seguir buscando. El otro motivo es que las librerías de Remoting en Mono estas mucho mas maduras que las de WCF y, pese a que todavía me topo con algunos errores del lado de Mono, pienso que dentro de poco voy a poder tener la app mas cross-platform (si me vuelvo suficientemente loco me pongo a hacer una interfaz nativa para Mac y todo!).
Por default, se utiliza el Client de WCF, pero esto se puede modificar por configuración (y ya que es levantado por reflection... ¡Pueden hacer sus propios clientes y servers en casa!)

FAQ:

- ¿De dónde bajo el juego?
Esta versión la pueden bajar de aca. El código fuente y las futuras versiones las podrán encontrar en el sitio de MTGDeck en Google Code

- ¿Cómo empiezo?
El paquete incluye un pequeño manual de usuario. Ahí esta toda la info necesaria para salir andando.

- Todo muy lindo pero... ¡Cómo armo mi mazo?
El formato del archivo de mazos es muy simple: cada línea tiene la cantidad de cartas de un tipo, seguida por el nombre de la carta. Las lineas que comienzan con "#" son ignoradas. Junto con el paquete se incluye un ejemplo.

Para armar mazos, recomiendo la siguiente página: http://www.essentialmagic.com/decks/

- ¡No puedo conectarme al servidor! ¿Qué pasa?
Posiblemente, el problema sea que el firewall de Windows (o algún otro) no este permitiendo conectarse al puerto 1234 (o el que esté configurado) en la computadora donde el servidor esté ejecutando. Para solucionarlo, habilitar el puerto en el firewall o desactivarlo por completo.
Durante las pruebas me ha ocurrido que hay máquinas donde el server indica que levantó correctamente, sin embargo es imposible conectarse, incluso desde la máquina local. Cuando tenga identificado este error y lo solucione, avisaré.

- Encontré un error. ¿Cómo lo reporto?
Pueden contactarse conmigo por twitter (http://twitter.com/pablozaiden), por mail o dejando un comentario en el blog.

- Me gustaría que el juego tuviese tal o cual feature. ¿Qué puedo hacer?
Nuevamente, podes contactarme por alguno de los medios nombrados anteriormente y si sos desarrollador podes contribuir con el desarrollo (proximamente)


A continuación dejo unos screenshots del juego en acción:



Saludos!
Z

2009-01-21

Material de la conferencia sobre C# 4

Ya está disponible para bajar el material que usamos ayer con RodoF en la conferencia "Agregando dinamismo al código: C# 4 entra en escena" sobre las nuevas características que van a venir con C# 4 y la próxima versión del .NET Framework.

Link



Hasta la próxima!
Z

2008-10-06

Charla de Voice Recognition y Synthesis con .NET 3.x

Este sabado dimos con RodoF una charla en el CodeCamp sobre Voice Recognition y Synthesis utilizando .NET 3.x.
La presentación la pueden bajar de aca.

Durante la charla, estuvimos mostrando el proyecto de KeySimulator - VoiceCommander, el cual ya estuve comentando en el blog. Pueden obtener el código desde Codeplex.



Saludos!

Z

2008-01-20

Intellisense de bolsillo (y multiplataforma): ZSense!

Dado que la gran mayoría de mis amigos están disfrutando de sus vacaciones, ya sea en la costa, el sur o en el exterior, este fin de semana fue bastante aburrido: me quedé en casa practicamente sin hacer nada. Todo parecía seguir asi hasta que me vino una idea a la cabeza...

Desde que instalé OS X en la laptop estoy queriendo desarrollar aplicaciones .NET usando el port de Mono que hay para OS X. El gran problema que tengo es que al no haber port de MonoDevelop (o al menos, no hay sin tener que recompilar medio universo y bajar nosecuantas librerías extra...) termino levantando una VM con Windows XP y el Visual Studio. No pido tener un IDE que se integre con el debugger ni mucho menos, sino que pido algun editor que tenga IntelliSense para no tener que estar programando con la ayuda abierta constantemente.

Aqui es donde aparece la idea que me vino el día de hoy: lo que hay que hacer es tener el IntelliSense dentro de una aplicacion hecha en .Net, asi, cuando quiera desarrollar, la puedo usar como referencia.

Sin mas presentación, les entrego a... ZSense!

Este es un proyectito bastante chico que hice hoy (pero planeo seguir mejorando con el tiempo) que recibe una linea de código con tipos de .Net y va mostrando en una lista las opciones disponibles para seguir escribiendo la linea.

Esta primera versión tiene algunos detalles ásperos: el tipo inicial debe estar escrito entre comillas dobles. Por ej, una linea válida sería: "System.Int32".GetHashCode().CompareTo(System.Int32)
Como pueden ver, la manera de indicar que sobrecarga de un método se quiere usar es escribiendo el tipo de dato de cada parámetro, separados por comas.

Si se quieren usar mas assemblies aparte de las predeterminadas, se pueden cargar en la lista de la izquierda.

Para mas detalle, vean el código! (si, ya se... hay unas cuantas cosas que podrían estar mejor - como el parsing - pero lo quería sacar andando rápido. Ya tendré tiempo para mejorarlo).

Les dejo aca la versión inicial. En un rato lo subo a Codeplex asi voy dejando ahi las últimas actualizaciones.



ZSenseMultiPlatform
Screenshot, corriendo el mismo ejecutable en OS X con Mono (todavía hay algunos problemas, pero anda...) y en Windows con VMWare Fusion


Un saludo!

Z

2008-01-18

Problema con parametros de tipo string de longitud 0 en Oracle usando System.Data.OracleClient

Haciendo unas pruebas para una migración a Enterprise Library, nos encontramos con un problema (no se si intencional o no) utilizando las clases de acceso a datos contra Oracle que trae .NET Framework 1.1.

Supongamos que uno quiere hacer la siguiente consulta:

SELECT * FROM Countries WHERE Country_id = :c_id



Si usamos el siguiente código, la aplicacion generará una excepcion por tener un parametro de tipo string con longitud 0:
using (IDbConnection conn = new OracleConnection("Password=hr;Persist Security Info=True;User ID=hr;Data Source=localhost"))
{
    conn.Open();
    using (IDbCommand command = conn.CreateCommand())
    {
        command.CommandType = CommandType.Text;
        command.CommandText = "SELECT * FROM Countries WHERE Country_id = :c_id";
 
        IDbDataParameter param = command.CreateParameter();
        param.ParameterName = "c_id";
        param.DbType = DbType.AnsiStringFixedLength;
        param.Value = String.Empty;
        command.Parameters.Add(param);
        using (IDataReader reader = command.ExecuteReader())
        {
            DumpReader(reader);
        }
    }
    conn.Close();
}


El error es: Parameter 'c_id': No size set for variable length data type: String.

Para solucionar esto tenemos dos opciones:

1. Usar la librería provista por Oracle para .NET para acceder a los datos. Esta librería maneja de forma correcta los Strings vacios. (al menos es asi usando la última version de la librería. Me falta confirmar que con versiones anteriores tambien funcione)

2. Enviar DbNull.Value como valor del parametro, en vez de String.Empty. En otro blog leí que Oracle maneja de la misma manera tanto a los strings vacíos como a los NULL. Sin embargo yo no estoy 100% seguro de que esto sea asi, asi que de tener confirmación o refutación de esto, por favor haganmelo saber! De todas maneras, mi recomendación es usar ODP.Net

Hasta la próxima!

Z

2008-01-05

Conversor de Cbr a Cbz

Hace unos dias me baje unos comics de X-Men y la mayoría estaban con formato Cbr (formato para comics, comprimido con rar). El problema es que el programa que uso en OS X para abrir comics suele tener problemas con algunos archivos comprimidos con rar. Sin embargo, esto no sucede con su contraparte en formato zip (archivos Cbz). Por lo tanto, hice un pequeño programa en C# que agarra todos los archivos CBR en un directorio y los recomprime usando ZIP, creando asi la version CBZ de los mismos. Les dejo aca el programa y el fuente por si a alguien le es útil o le interesa.

Z

2007-11-05

Accediendo dinamicamente a WebServices

Hoy estábamos viendo con uno de los chicos del laburo que necesitaba poder acceder a distintos WebServices (desconocidos para el en tiempo de compilación) y queríamos encontrar una forma cómoda de hacerlo.

Después de buscar un poco, encontramos una solución que pareció bastante piola: usando el WSDL del WebService, generar la clase proxy en runtime y usar los WebMethods por Reflection. Está claro que no es la solución óptima en términos de velocidad, pero con unas clases helper que hice, resulta muy cómodo para usar.

El siguiente código lee el WSDL y genera la nueva clase en runtime:




try

{

    //Get the web service description

    Uri uri = new Uri(wsdlUrl);

 

    WebRequest webRequest = WebRequest.Create(uri);

    System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();

    ServiceDescription sd = ServiceDescription.Read(requestStream);

    string sdName = sd.Services[0].Name;

 

    //Create the ServiceDescriptionImporter

    ServiceDescriptionImporter sdImporter = new ServiceDescriptionImporter();

    sdImporter.AddServiceDescription(sd, String.Empty, String.Empty);

    sdImporter.ProtocolName = "Soap";

    sdImporter.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;

 

    CodeNamespace codeNamespace = new CodeNamespace(DefaultNamespace);

    CodeCompileUnit compileUnit = new CodeCompileUnit();

    compileUnit.Namespaces.Add(codeNamespace);

 

    //Import the ServiceDescription

    ServiceDescriptionImportWarnings warnings = sdImporter.Import(codeNamespace, compileUnit);

 

    if (warnings == 0) //No warnings

    {

        CSharpCodeProvider codeProvider = new CSharpCodeProvider();

        codeProvider.GenerateCodeFromNamespace(codeNamespace, new StringWriter(), new CodeGeneratorOptions());

 

        //Compile the assembly

        string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };

        CompilerParameters param = new CompilerParameters(assemblyReferences);

        param.GenerateExecutable = false;

        param.GenerateInMemory = true;

        param.TreatWarningsAsErrors = false;

        param.WarningLevel = 4;

 

        CompilerResults results = codeProvider.CompileAssemblyFromDom(param, compileUnit);

        Assembly assembly = results.CompiledAssembly;

 

        Type[] types = assembly.GetExportedTypes();

        //Get the generated Type

        return new DynamicProxyClass(types[0], GetMethodNames(sd.PortTypes[0].Operations));

    }

    else

    {

        throw new Exception(String.Format("Importing has warnings: {0}", warnings.ToString()));

    }

}

catch (Exception ex)

{

    throw new Exception("Couldn't generate proxy class (see inner exception)", ex);

}



Acá se puede ver que lo que devuelvo es una DynamicProxyClass. Esta clase lo que hace es wrappear la clase generada y exponer solo los métodos del WebService, sus tipos de retorno y sus parámetros.

Si les interesa jugar con esto, dejo para bajar el código completo de la solución.


Z

2007-09-02

Key Simulator – Voice Commander

Gente, para quienes quieran tener la última versión del Voice Commander que estoy haciendo, pueden bajárselo de http://www.codeplex.com/KeySimVoiceCommander

Saludos!

Z

2007-08-31

Feedback sobre los posts anteriores

Al parecer, lo que mostre en el post anterior funciona "como viene" nada mas con Windows Vista. Tendría que sentarme a ver bien que pasa sobre XP.


Saludos!

Z

2007-08-29

Cool & Useless: Creando nuestra propia aplicación para manejar la PC por medio de la voz, (casi) todo desde código manejado (Parte 2)

En el post anterior vimos hace para usar el motor de reconocimiento de voz usando el .Net Framework 3.0 para reconocer las frases que nosotros queremos. Sin embargo, para hacer esto estábamos usando el motor de reconocimiento predeterminado del sistema. Haciendo un pequeño retoque, veremos que es posible usar el motor en cualquier otro idioma.

El primer requerimiento será bajar el MUI Pack del idioma que queremos usar. El Windows Vista Ultimate que yo tengo instalado trae ya instalado el motor para inglés americano y británico. Desde Windows Update, baje el MUI Pack de español y con esto, se instaló el motor de reconocimiento en español.

Como comenté en el post anterior, Windows no permite el uso del reconocimiento de voz en un idioma distinto al que se está usando en el sistema. Sin embargo, con las siguientes líneas de código es posible inicializar la clase SpeechRecognitionEngine con cualquier otro motor instalado.

Este ejemplo busca algún motor que esté en español y empieza a reconocer voz con el idioma elegido:



static void Main(string[] args)
{
SpeechRecognitionEngine sre = null;
foreach (RecognizerInfo config in SpeechRecognitionEngine.InstalledRecognizers())
{
if (config.Culture.TwoLetterISOLanguageName == "es")
{
sre = new SpeechRecognitionEngine(config);
}
}

Choices choises = new System.Speech.Recognition.Choices(
new string[] { "hola", "como", "estas", "mi nombre es Pablo" });

GrammarBuilder gb = choises.ToGrammarBuilder();
gb.Culture = sre.RecognizerInfo.Culture;
Grammar grammar = new Grammar(gb);


sre.LoadGrammar(grammar);
sre.SpeechRecognized += new EventHandler(sre_SpeechRecognized);
sre.SetInputToDefaultAudioDevice();
sre.RecognizeAsync(RecognizeMode.Multiple);
}

Debemos recordar que al inicializar el SpeechRecognitionEngine con un idioma distinto al predeterminado, también debemos cambiar la cultura del GrammarBuilder usado para generar la gramática a cargar. El resto del ejemplo es igual a lo ya visto.

Volviendo a nuestro objetivo de manejar cualquier cosa con la voz, lo que necesitamos hacer ahora es poder simular el uso de teclado.

Si bien el .Net Framework trae la clase System.Windows.Forms.SendKeys con el método Send, el cual recibe un String y simula presionar esas teclas, veremos que esto no nos va a servir para simular es uso del teclado en juegos que usen DirectX.

Para poder hacer esto, usé la función SendInput() desde C++ la cual está disponible al incluir el header windows.h . Esta función sirve para simular desde un nivel mas bajo el input desde teclado, mouse o algún otro dispositivo de hardware (más información en http://msdn2.microsoft.com/en-us/library/ms646310.aspx).

A continuación un pequeño ejemplo de cómo usarlo para simular la letra 'A'



void SimulateKeyboard()
{
KEYBDINPUT kb={0};
INPUT Input={0};

kb.wVk = 'A';
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
::SendInput(1,&Input,sizeof(Input));
}


Teniendo esto, creé un nuevo proyecto de DLL en C++ para poder exportarla y usarla desde código manejado.

Una vez obtenida la DLL que exporta el método creé un proyecto de DLL en C# para poder wrappear esta función y usarla cómodamente desde código manejado.

Con esto ya terminé de armar todas las herramientas necesarias para mi aplicación. Solo restó armar una aplicación que levante una configuración desde un archivo XML con las frases que quería decir, asociadas a las combinaciones de teclas a simular y listo! Ya tenía armado mi simulador de teclado manejado por voz.

Acá les dejo un link al código del proyecto entero para que lo bajen. Tengan en cuenta que esto lo hice medio a los apurones así que puede estar bastante buggeado, pero, como diría el Bambino Veira, La Base Está J


Saludos!


Z