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
El blog amigo del Nerd moderno
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
Como bien dice el título, hoy es un gran día para Mono!
Aca de publicarse la versión 2.6 del Mono Framework (implementación libre y multiplataforma de .NET) junto con la versión 2.2 de MonoDevelop (IDE)
Dentro de la extensa lista de features - la cual pueden ver en www.go-mono.com - dos de ellas llamaron poderosamente mi atención:
Esto quiere decir que la factibilidad de realizar proyectos importantes en .NET abstrayéndose de la plataforma acaba de dos pasos gigantescos!
Por lo poco que probé el debugger funcionó bastante bien, llevando al olvido mis incontables horas de PRINTF-Oriented-Debugging sobre .NET en OS X.
Los ejemplos básicos que pude probar de LINQ-2-SQL funcionaron perfectamente e incluso proyectos como NerdDinner se pueden ejecutar sin problemas con un simple click en el botón de Debug de MonoDevelop (siempre y cuando tengamos acceso a una base SQL Server, obviamente).
Intenté llevar un paso mas adelante el funcionamiento de la nueva implementación de esta API al intentar ejecutar el proyecto en el cual estoy trabajando actualmente, solo para encontrarme que aún no está implementado el uso de DataLoadOptions en el DataContext de SQL, por lo que eso tendrá que esperar un tiempo más.
Igualmente, muchas felicitaciones al equipo de Mono por el gran trabajo que hacen día a día!
Tanto Mono Framework como MonoDevelop pueden ser descargados desde el sitio oficial de mono: http://www.go-mono.com
Saludos!
Zaiden
Durante el día de hoy estuve dando una charla junto a Diego Gonzalez sobre las novedades de C# 4.0, .NET 4.0 Visual Studio 2010 en el marco del evento de Microsoft Run Reloaded.
La presentación la pueden bajar aca
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:
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.
Cualquier sugerencia es bienvenida!
Saludos!
Zaiden
Pese a que el "espíritu" del standard de WebServices, WS-Security, y demas yerbas planteadas en las RFC supondrían una facilidad para la interoperabilidad ente diferentes plataformas, al intentar llevarlas a la realidad las asperezas surgen... y explotan en nuestra cara.
Hace un tiempo, en Lagash tuvimos que implementar un TokenManager (entre otras cosas) para poder traducir los llamados hechos desde WSE 3 a Java, utilizando certificados de seguridad.
Luego de estar funcionando sin problemas por casi dos años, el servicio comenzó a fallar al momento de recibir la respuesta de un pedido, con un mensaje que decía "WSE590: Failed to resolve the following key info"... y mostraba uno de los nodos de Key Info, conteniendo supuestamente el Subject Key Identifier del certificado necesario para desencriptar el mensaje.
El problema resultó ser que, dado que el nuevo certificado que comenzaron a usar no contenía la extensión de Subject Key Identifier, para poder identificarlo, tanto Java como WSE generaban un hash del certificado. El gran problema gran era que los hashes eran calculados de forma distinta!
Para poder solucionarlo, dentro del input filter de los mensajes hubo que reemplazar la porción de SOAP que indica el certificado a utilizar por una creada a mano que indique el certificado concreto (que, de hecho, siempre es el mismo que se utiliza al enviar el mensaje original).
Para mas información: http://support.microsoft.com/kb/922779/en-us (el problema no es exactamente el mismo, pero el approach para solucionarlo es muy similar).
Saludos!
Zaiden
Si alguna vez tratan de usar WPF desde una maquina virtual en VMWare Fusion, notarán que la aplicación se torna inusable.
Para poder solucionar esto hay que desactivar la aceleración por hardware de WPF dentro de la VM. Esto se hacer creando el siguiente DWORD dentro del registro de windows:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\DisableHWAcceleration
y seteando su valor a 1.
Fuente: http://blog.joachim.at/?p=19
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
Para quienes no pudieron ir a alguna de las charlas del CodeCamp 2009 en las que participe, o quieren repasar el contenido, dejo disponible lo mostrado en el evento.
Saludos!
Monos y Manzanas - Programando para Mac en .NET
Conociendo al Dúo Dinámico: C# 4 y VS2010
En el sitio de Monobjc, dice que para compilar y armar el paquete de aplicación para un programa hecho con una interfaz Cocoa y Monobjc se debe utilizar un script de nant que ellos proveen.
¿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
Mr. Kus me pasó el link a este programa de blogging para Mac, frente a mi pedido de algún soft que pudiese reemplazar a mi querido Live Writer.
Por ahora parece bastante bueno. Cuando tenga un post mas largo para escribir, espero poder confirmarlo.
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
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:.
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.
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.
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.
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!
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
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.
Dentro de los atributos del ObjectController, agregar una nueva clave llamada “url”, la cual usaremos como nexo entre el modelo y la UI.
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#.
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”.
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
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
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
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.
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.
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.
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:
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:
Ahora, a divertirse!
Saludos!
Zaiden
Como ya mencioné en el post anterior, luego de solucionar los problemas severos de performance causados por la combinación WPF + Aceleración de VMWare pude empezar a probar el Beta 1 de Visual Studio 2010.
Por alguna razón oscura que no llego a comprender, en MS decidieron que sería una buena opción hacer que Visual Studio utilice WPF en la capa de presentación.
La ventaja de esto es que la posibilidad de tener cientos y cientos de efectos, transiciones, menúes voladores y demas yerbas, mas la posibilidad de hacer sumamente extensible la UI del afamado IDE.
Pero todo esto viene con un precio... la performance. Si Visual Studio 2008 ya era pesado, la nueva versión salta una o dos categorías mas: iniciar el IDE, abrir una solución chica y compilarla se lleva nada mas y nada menos que 238MB de ram (mas 14MB del PresetantionFontCache).
Igualmente, estos valores y la performance presentada son indicadores que deben ser tomados con pinzas, ya que al ser el Beta 1, todavía queda un largo camino para dar lugar a optimizaciónes (las cuales espero, por el bien de mis VMs, que aparezcan!).
Otro problema que encontré (también atribuible al estado de Beta del producto) fue la cantidad de crashes que tuve. Durante el coding del ejemplo que dejé al final (una tarde) me habra crasheado unas 5 veces... bastante mas de lo tolerable. Esperemos que la estabilidad sea un punto importante a mejorar en la proxima Beta o RC
Cerrado este tema, pasemos a los "chiches" nuevos que trae:
.NET 4 (beta) y C# 4
Nueva version del CLR y del lenguaje estrella de la plataforma .NET. En este otro post (http://zpodblog.blogspot.com/2009/01/material-de-la-conferencia-sobre-c-4.html) pueden bajar una presentación que dimos con RodoF sobre los features nuevos incluidos en el lenguaje (tipado dinámico, parametros opcionales y por nombre, co y contra varianza en parametros de tipos, y algunas cosas mas).
F#
Esta versión de Visual Studio es la primera en incluir este lenguaje funcional para la plataforma .NET.
Debo decir que todavía no me puse a ver mucho sobre el mismo, pero será cuestion de encontrar una excusa suficientemente buena como para incluir alguna librería que tenga que hacer en F# para algún proyecto a futuro.
Por ahora los dejo con un bonito ejemplo de como obtener el n-esimo termino de la suceción de fibonacci (no puedo evitar caer en los clásicos) en F#:
let rec fibonacci elem =
match elem with
| 1 -> 1
| 2 -> 1
| n -> fibonacci (n - 1) + fibonacci (n - 2)
Printf.printf "Ingrese numero de termino: "
let termino = System.Console.ReadLine();
Printf.printfn "%i" (fibonacci (System.Int32.Parse termino))
let k = System.Console.ReadKey();
Parallel Extensions
Quienes hayan tenido que hacer optimizaciones en algún proyecto sabrán que uno de los primeros puntos a mirar es la paralelización de tareas independientes. Las Parallel Extensions vienen a simplificar este tipo de tareas.
Por ejemplo, en caso de tener una colección y querer aplicar una funcion a cada uno de los elementos, en vez de hacer un viejo y querido "foreach", podemos hacer:
miColeccion.AsParallel().ForAll(elem =>
{
//codigo a ejecutar por cada elemento de la colección
});
La gran ventaja de esto es que nos abstraemos de todo el manejo de concurrencia, manteniendo la ganancia de performance.
Sequence Diagram
Si hacemos click derecho sobre un método, vamos a notar que hay una nueva opción llamada "Generate Sequence Diagram". Al seleccionarla nos pregunta la profundidad máxima para las llamadas del diagrama y si queremos excluir las llamadas a clases dentro de ciertos namespaces (como System, u otros personalizables). Finalmente, nos presenta un diagrama como este:
Este es un feature sumamente útil en caso de tener que comprender lo que hace un código ajeno, sin tener que empezar a navegar todo el código! (y pensar que yo casi me hago una herramienta para generar estos diagramas... por suerte nunca prosperó!)
Definitivamente, una gran adición al Visual Studio.
Bueno, por ahora esto es lo que pude ver. Me queda pendiente el "Architecture View" y varios features mas que ni siquiera llegué a ver.
Como extra, dejo mi primer proyectito hecho en C# 4 que permite acceder datos provenientes de una conexión de ADO.NET levantando los campos como propiedades dinámicas del resultado y pudiendo aplicar un pipeline de filtros que hagan transformaciones sobre el resultado al ser obtenido.
La idea es poder hacer este tipo de cosas
using (var conn = new DynamicConnection(realADOConnection, new ImageMapper()))
{
dynamic result = conn.Query("SELECT * FROM Customer WHERE IdCustomer = @0", 37);
/* O tambien
dynamic result = conn.Query("SELECT * FROM Customer WHERE IdCustomer = @IdCustomer",
DynamicConnection.CreateParameter("@IdCustomer", 37));
*/
foreach (dynamic row in result) //recorrer el resultado con un foreach
{
//lectura directa
int id = row.IdCustomer;
string name = row.Name;
/*
Aca, el ImageMapper se encarga de leer el campo
y mapearlo a un Image
*/
Image customerPicture = row.Picture;
grid.AddRow(id, name, customerPicture);
}
//Obtengo un solo valor
dynamic val = conn.QueryValue("SELECT TOP 1 Name FROM Customer WHERE Id = @0", 37);
MessageBox.Show(val);
}
Lo pueden bajar de aca: DynamicDataAccess
Hasta la próxima!
Z
Tratando de mantener la vida del blog y siguiendo lo comentado en la lista de mails ALT.NET Argentina, vamos a hablar un poco de lo que fue la primera reunión del grupo
(para quien no sepa de que trata esto, puede darse una vuelta por: http://altdotnet.org/ y/o http://altnet-argentina.pbworks.com/ )
Pese a la no-tan-feliz combinación de día/horario para el inicio de esta reunión (sábado a las 9 de la mañana!) el ambiente era muy bueno: muchas caras conocidas - bastante mas dormidas que de costumbre - y muchas ganas por empezar las sesiones pensadas para el día.
A eso de las 10.00, nos fuimos presentando en ronda (no pude evitar tirar un chiste) y pasamos a contar que temas habían propuestos para las sesiones del día y arreglar entre todos los horarios y las salas de cada uno.
Independientemente del tema a tratar en cada una, se utilizó la modalidad de "open space", donde cada uno es libre de opinar y plantear su punto de vista cuando le parezca - siempre y cuando se haga de forma ordenada - y no las clásicas charlas con un orador y muchos escuchas.
Durante el día hubieron 4 franjas donde se llevaron a cabo sesiones simultáneas. En particular, voy a hacer una pequeña reseña sobre las cuatro donde yo participé:
(no recuerdo los nombres concretos, pero con el título de fantasía que puse se entiende)
1. SOA: ¿Vivo o muerto? ¿Cáncer o salvación?
En esta charla hubieron 4 a 6 sillas adelante, dándole al usuario de las mismas el derecho a hablar, siguiendo estas reglas:
- Siempre debe haber una silla vacía
- Si alguien quiere hablar, debe sentarse en una de esas sillas
- Si quedan todas las sillas ocupadas, deberá retirarse quien esté hace mas tiempo (o quien considere que ya no está aportando a la conversación, pero esto queda a criterio de cada uno)
La misma empezó comentando la experiencia de distintas personas que llevaron (o intentaron llevar a cabo) proyectos con arquitecturas que implementaban el patrón SOA y a raíz de esto, muchos - me incluyo - comentamos donde nos parecía que estaban las falencias usuales en este tipo de emprendimientos, cuales eran los beneficios, que costos se pagaban por intentar hacer SOA "porque es la palabra de moda" y cuando valía la pena realmente tener componentes de un proyecto que siguieran este patrón.
2. Microsoft... ¿Está innovando?
Este es un tema que propuso Miguel Angel Saez y, pese a que no mucha gente se prendió, me pareció un tópico sumamente interesante para tratar.
Acá charlamos sobre las alternativas a distintos problemas, viendo la forma - o formas - en que lo encaran Microsoft, otras empresas como Sun, Oracle, Amazon, Google, etc. y por último "la comunidad open source".
Para cada uno hablamos sobre cuanto de "inspiración" y cuanto de creatividad hubo en su creación y hacia donde veíamos el rumbo de evolución de cada una de estas tecnologías o productos.
Hacia el final de la sesión estuvimos hablando sobre Windows 7, su comparación con OS X (o inspiración en...) y cual grande - o pequeño - era el salto desde Windows Vista.
-- Pizza Break --
El almuerzo fue una gran sesión en si misma: todos hablando con todos sobre lo discutido en las sesiones anteriores, tratando de ver que nos perdimos y contando - mordisco de muzzarella de por medio - lo que aprendimos de los demás en las charlas que estuvimos.
Y sigue la tarde con...
3. YARF: Yet-Another-Rodo-Framework, o ¿Cuándo debo meter qué cosa?
Esta vez, pese a ser unos cuantos usamos la vieja y querida ronda para hablarnos entre todos y no tuvimos ningún problema.
Acá se charlo sobre para que tipos de desarrollo pensábamos que era mejor utilizar tal o cual framework o tecnología, o si - como algunos preferimos - hacernos el nuestro propio framework a media.
Hablamos de ventajas y desventajas de pensar necesidades a futuro, cuándo conviene reutilizar y cuándo conviene tirar lo que tenía y volver a pensar desde cero una mejor solución. Tocamos temas como la sobreingeniería de las soluciones planteadas, problemas con la división en capas (ya sea por defecto o por exceso) y varias cosas mas.
4. .NET hace monerías
La idea de esta charla fue hablar sobre las experiencias que hayamos tenido utilizando Mono (implementación Open Source de .NET) en distintas plataformas y poder contarles a los que nunca habían escuchado de este proyecto, o no lo habían probado, cual era el estado del mismo y que se podía o no hacer en comparación a .NET.
Durante la charla se mostró un poco de Mono en acción desde una maquina con Linux y mi Mac. Mostramos el REPL de csharp que viene incluido con Mono, explotando el hecho de que el compilador es provisto como "una librería mas". También vimos el plugin de Moonlight - implementación de Mono de Silverlight - corriendo dentro de Firefox en Linux y un pequeño vistazo a la IDE multiplataforma Monodevelop.
Bueno, con esto cierro el post de hoy. Si alguien quiere, pregunte sobre algún tema mas en particular de las sesiones y, de acordarme, con gusto responderé.
Saludos y hasta la próxima (donde pienso que subiré la primer versión beta del MTGDeck)!
Z