Como suscribir eventos de objetos Silverlight en JavaScript

En las aplicaciones que desarrollamos con SilverLight, rara es la vez en la que no tenemos nada de JavaScript y además queremos relacionarlo con lo que ocurre en nuestos controles de SilverLight, por ejemplo que cuando el usuario haga Click en determinado elemento de nuestro control SilverLight este evento sea capturado por el javascript y lanze tambien su propia función.

 

   1:  <Button x:Name="Button1" Height="35" Width="120" Content="Do Something" Click="Button1_Click" />

 

Tenemos el botón anterior definido en nuestro control SilverLight y nuestro code-behind es

 

   1:  public partial class Page : UserControl
   2:  {
   3:      [ScriptableMember]
   4:      public event EventHandler ButtonClicked;
   5:   
   6:      public Page()
   7:      {
   8:          InitializeComponent();
   9:          HtmlPage.RegisterScriptableObject("ScriptablePageObject", this);
  10:      }
  11:   
  12:      private void Button1_Click(object sender, RoutedEventArgs e)
  13:      {
  14:          if (ButtonClicked != null)
  15:          {
  16:              ButtonClicked(sender, e);
  17:          }
  18:      }
  19:  }

Como podeis observar ponemos el atributo [ScriptableMember] en el vento que queremos que se comunique con el Javascript y en la creación del control lo registramos con la instrucción

9: HtmlPage.RegisterScriptableObject(“ScriptablePageObject“, this);

Ahora desde Javascript solo nos queda

 

  1:  <asp:Silverlight 
   2:      ID="Xaml1" 
   3:      runat="server" 
   4:      Source="~/ClientBin/CallHtmlDom.xap" 
   5:      OnPluginLoaded="pluginLoaded"
   6:      Version="2.0" 
   7:      Width="100%" Height="100%" />
   8:  </div>
   9:  </form>
  10:  <script type="text/javascript">
  11:   
  12:      function pluginLoaded(sender,args) {
  13:          var slCtl = document.getElementById("Xaml1"); 
  14:          slCtl.Content.ScriptablePageObject.addEventListener("ButtonClicked", jsEventHandler);
  15:      } 
  16:      
  17:      function jsEventHandler(sender, args) {
  18:          alert("Pulso el botón");
  19:      }
  20:      
  21:  </script>

Una vez que el control SilverLight se ha cargado se llama a la función javascript pluginLoaded, que se encarga de crear el manejador para el evento Buttonclicked

Como saber el usuario conectado en SilverLight?

Esta es la pregunta que me ha surgido, lo primero que pense fue hacer un servicio WCF que me devolviera el usuario autenticado, pero aplicando KISS (hazlo simple estupido) me di cuenta que los objetos SilverLight tienen la propiedad InitParameters en la cual se puede indicar un string de los parametros con el formato nombreparametro=valorparametro y separados por comas, asi que era tan sencillo como en la pagina Web indicarle la instrucción

sl.InitParameters += "user=" + HttpContext.Current.User.Identity.IsAuthenticated + ",username=" + HttpContext.Current.User.Identity.Name;
El segundo paso es en el objeto SilVerlight crear un constructor nuevo con los parametros que queremos tener, en este caso si el usuario esta autenticado y su nombre
Luego en el fichero App.xaml.cs que realiza la llamada nuestro objeto SilverLight con los nuevos parametros
 this.RootVisual = new Page(e.InitParams["user"], e.InitParams["user"], username);
Y ya tenemos si el usuario esta autenticado sin ningún servicio
 

Animaciones Dinamicas en SilverLight

Muchas veces por exigencias de lo que estamos haciendo necesitamos crear una animación en tiempo de ejecución y realizarlo es bastante sencillo, aqui teneis un codigo de ejemplo que realiza la animación de un rectangulo

 

public void Create_And_Run_Animation(object sender, EventArgs e)
{
// Crear un rectangulo rojo

Rectangle myRectangle = new Rectangle();
myRectangle.Width = 200;
myRectangle.Height = 200;
Color myColor = Color.FromArgb(255, 255, 0, 0);
SolidColorBrush myBrush = new SolidColorBrush();
myBrush.Color = myColor;
myRectangle.Fill = myBrush;

// añadir el rectangulo
LayoutRoot.Children.Add(myRectangle);

// La duración es de 2 segundos
Duration duration = new Duration(TimeSpan.FromSeconds(0.2));

// Crear Dos Doubleanimation junto a sus propiedades
DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();

myDoubleAnimation1.Duration = duration;
myDoubleAnimation2.Duration = duration;

Storyboard sb = new Storyboard();
sb.Duration = duration;

sb.Children.Add(myDoubleAnimation1);
sb.Children.Add(myDoubleAnimation2);

Storyboard.SetTarget(myDoubleAnimation1, myRectangle);
Storyboard.SetTarget(myDoubleAnimation2, myRectangle);

// Enviamos las propiedades atachadas Canvas.Left y Canvas.Top
// que seran las target properties de las animaciones
Storyboard.SetTargetProperty(myDoubleAnimation1, "(Canvas.Left)");
Storyboard.SetTargetProperty(myDoubleAnimation2, "(Canvas.Top)");

myDoubleAnimation1.To = 200;
myDoubleAnimation2.To = 200;

// Crear la StoryBoard como un recurso.
LayoutRoot.Resources.Add(sb);

// Lanzar la animacion.
sb.Begin();
}

Carga Dinámica de un Ensamblado en SilverLight

Cuando desarrollamos en Silverlight, no nos tenemos que olvidar de que estamos trabajando en Web aunque nos de la sensación que estamos desarrollando en WPF, y que tenemos que tener cuidado con el peso de nuestro ensamblado, muchas veces en aplicaciones Silverlight hacemos que se bajen todos los componentes y puede que el usuario no llegue a utilizarlos, entonces porque bajarlos?, no es mejor que estos componentes se bajen cuando el usuario vaya a utilizarlos?, es verdad, que con esta técnica el usuario sufrira un pequeño retardo la primera vez que demande el control Silverlight, pero mejoraremos la primera presentación de nuestra aplicación.

La verdad que es bastante sencillo realizar la carga dinámica en SilverLight, lo primero es copiar el ensamblado en la carpeta ClientBin de nuestra aplicación Web, lo segundo es en nuestro código realizar la petición de bajada de ensamblado, esto lo conseguimos con estas líneas de código

 

              WebClient client = new WebClient();
               client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
               client.OpenReadAsync(new Uri(“MiEnsamblado.dll”, UriKind.Relative));

Con estas lineas realizamos la petición y en el evento  OpenReadcompleted

 

void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
       {
           AssemblyPart assemblyPart = new AssemblyPart();
           Assembly assembly = assemblyPart.Load(e.Result);

           UserControl controlsilverlight= assembly.CreateInstance(“MiEnsamblado.MiControl”) as UserControl;
           if (controlsilverlight != null)
           {
               controlsilverlight.SetValue(Canvas.LeftProperty, 100);
               MiCanvas.Children.Add(editor);
           }

       }

Cargamos el ensambldo mediante reflection y lo añadimos al  contenedor donde queremos que se muestre y ya esta.

Cosillas del TextBox de Silverlight 2.0

Desarrollando en SilverLigth 2.0 me he encontrado con unos problemillas dentro del control TextBox. El primero es que en el modo full-screen no permiten introducir datos en ellos, según el quipo de desarrollo es por temas de seguridad, no he querido profundizar mas al estar trabajando en una Beta y que mucha gente se ha quejado, ignoro si en la RTM esta característica estará habilitada.

La segunda es que no tiene la característica de Password para que no se ve alo que se esta tecleando, debes de crearte un control tuyo que herede de Textbox y manejando los eventos de  TextChanged y KeyDown implementarlo tu.

Otra cosilla es que no se pueden introducir @ ni #, es decir caracteres en los que intervenga la tecla Alt Gr, lo mismo que para la password debes de implementarlo tu controlando los eventos KeyDown y KeyUp.

supongo que estas dos ultimas vendrán implementadas en la RTM porque si las he implementado yo sin mucho esfuerzo que no va hacer el equipo de SilverLight

Excepciones en SilverLight 2

Cuando estamos debugeando en Silverlight 2 y ocurre una excepcion, esta nos muestra el siguiente texto

 

An exception of type 'System.ServiceModel.ProtocolException' occurred in System.ServiceModel.dll but was not handled in user code
Additional information: [UnexpectedHttpResponseCode]
Arguments:Not Found
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=2.0.30226.2&File=System.ServiceModel.dll&Key=UnexpectedHttpResponseCode

Un texto nada descriptivo sobre el error que se ha producido, que no se nos muestre l texto completo de la excepción tiene su explicación, es para que el runtime de SilverLight pese menos a la hora de bajarlo, pero podemos configurar en nuestra máquina de desarrollo para que nos muestre el texto completo de la excepción.

 

Los assemblies del runtime de SilverLight estan instalados en el directorio C:Program FilesMicrosoft Silverlight2.0.30226.2 Estos assemblies no contienen ningún string para las excepciones, de manera que cuando una excepción es lanzada y no es capturada se muestra el mensaje generico que hemos visto antes.

 

Silverlight runtime with debug resources installed

Como podemos ver en el directorio en-us aparece el assembly debug.resources.dll en este assembly es donde se encuentran los mensajes completos de las excepciones para activarlos debemos de modificar el manifiesto de silverlight, concretamente el archivo C:Program FilesMicrosoft Silverlight2.0.30226.2slr.dll.managed_manifest con lo siguiente para cada assembly

<localizedassembly>
   <name>System.ServiceModel.debug.resources</name>
   <version>2.0.5.0</version>
   <publickeytoken>31bf3856ad364e35</publickeytoken>
   <relfolder>.</relfolder>
   <file>System.ServiceModel.debug.resources.dll</file>
</localizedassembly>

Despues de esta modificación el mensaje de la excepcion sera

 

An exception of type 'System.ServiceModel.ProtocolException' occurred in System.ServiceModel.dll but was not handled in user code
Additional information: The remote server returned an unexpected response: (404) Not Found