Threading Considerations for Binding and Change Notification in XAML

I read a very similar article about how to work with MVVM and multithreading aspects. The problem is well known; in XAML’s Binding features, if you want to communicate any change to UI through Binding object, you need to implement INotifyPropertyChanged on model class, this interface has an event named PropertyChanged that is needed to update the UI, however in multithreading scenarios, you could have secondary threads modifying the model and this implies that the PropertyChanged event will be caught on a secondary thread and then you will the following exception:

Threads other than the UI thread are not allowed to access or manipulate UI objects

In this article you can read how to solve the problem in Silverlight (for WPF and WP as well) but it is not suitable for Portable Library scenarios. In Portable Library we have neither the Deployment class or even the Dispatcher class, so we have to take the SynchronizationContext class.

Implementation is really easy, you need to declare a global class field as follows

private SynchronizationContext _context = SynchronizationContext.Current;

Now, you have in _context the thread which created the class, you need to ensure that this is the same as controls’ thread: Thread UI

The next step is :


protected virtual void OnPropertyChanged(string propertyName)

{

PropertyChangedEventHandler handler = this.PropertyChanged;

if (handler != null)

{

var e = new PropertyChangedEventArgs(propertyName);

if (SynchronizationContext.Current == _context)

{

handler(this, e);

}

else

{

_context.Post(obj =>

{

handler(this, e);

}, null);

}

}

}

_context.Post is called only if the current thread is not the same as the creator thread.

System.Runtime error en WP8 y librerías portables

Un caso raro es cuando usamos librerías portables y Microsoft.Bcl no da un error de compilación System.Runtime. En un primer momento no tenemos muy claro de que se trata y tampoco nos da ninguna pista de que puede ser.

Después de buscar y buscar resulta ser un bug de NuGet en que te pone una redirección en el archivo app.config

 

<dependentAssembly>

<assemblyIdentity name=”System.Runtime” publicKeyToken=”b03f5f7f11d50a3a” culture=”neutral” />

        <bindingRedirect oldVersion=”0.0.0.0-2.6.3.0″ newVersion=”2.6.3.0″ />

</dependentAssembly>

 

De modo que debes eliminar esa línea para que todo funcione correctamente otra vez.

<dependentAssembly>

<assemblyIdentity name=”System.Runtime” publicKeyToken=”b03f5f7f11d50a3a” culture=”neutral” />

</dependentAssembly>

Llibreries portables i Thread UI

Amb .NET 4 apareixen les llibreries portables que penso que són fantàstiques per poder fer una programació completament reutilitzable en les diferents tecnologies que tenim disponibles amb Microsoft .NET : Windows Apps, Windows 8 Store, Windows Phone i Silverlight.

Hem de saber però que aquest tipus de llibreries utilitzen un subconjunt de les característiques que poden tenir en comú les diferents tecnologies i abans de posar-nos a fer res hem de consultar quines funcionalitats ens permeten fer les llibreries portables.

Hi ha un aspecte que sempre ens trobem que és com fem referència al Thread UI de l’aplicació. El Dispatcher no el podem fer servir des de les llibreries portables. Tenim la clase SynchronizationContext per fer-ho.

De fet, l’ús és molt senzill, declarem una variable a nivell de classe com aquesta

#región Fields

private SynchronizationContext _context = SynchronizationContext.Current;

#endregion

 

I llavors sempre que vulguem assegurar-nos que una funció s’executi al mateix thread que ha creat la classe llavors:

if (SynchronizationContext.Current == _context)

{

/*Do something */

}

else

{

/*El thread actual no és el mateix */

_context.Post(obj =>

{

/*Do something */

},null);

}

Fixa’t que el que mira és si el thread actual és el mateix que ha creat la classe, per tant l’únic que ens hem de preocupar és que el Thread UI creí la classe.
Més informació:

Portable Class Library and ObservableCollection, updating UI Thread

Portable class library equivalent of Dispatcher.Invoke or Dispatcher.RunAsync

 

 

 

Navegación a paginas XAML de otros ensamblados

Tanto en WPF como en Silverlight como en Windows Phone trabajamos con ventanas escritas en XAML que son XML que definen la interface de usuario.

Puede ser muy habitual tener definidas estas ventanas en otros ensamblados, como por ejemplo librerías, y en el momento de querer navegar hasta esta ventana tenemos la duda de como se puede hacer ya que el servicio de navegación en WPF funciona con Uri.

Así que, cual es la Uri de una ventana definida en otro ensamblado?

/{assemblyName};component/{pathToResource}

Como por ejemplo : “/SharedPages;component/TestPage.xaml” donde SharedPages es el nombre del ensamblado y /TestPage.xaml la ruta a partir de la raíz del proyecto para encontrar la ventana.

Animar Visibility en Silverlight

Las animaciones es una de las características que encuentro más interesantes en WPF.

Si alguna vez se te plantea la animación de propiedades de un control de ventana tienes que usar el ObjectAnimationUsingKeyFrames.

Si tu caso es animar la propiedad Visibility aqui tienes un ejemplo que he encontrado en el link que tienes en más información pero mira bien porque no es la respuesta marcada como correcta.

<Storyboard x:Name="VisStory">
    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
     <DiscreteObjectKeyFrame KeyTime="00:00:00">
      <DiscreteObjectKeyFrame.Value>
       <Visibility>Collapsed</Visibility>
      </DiscreteObjectKeyFrame.Value>
     </DiscreteObjectKeyFrame>
     <DiscreteObjectKeyFrame KeyTime="00:00:01">
      <DiscreteObjectKeyFrame.Value>
       <Visibility>Visible</Visibility>
      </DiscreteObjectKeyFrame.Value>
     </DiscreteObjectKeyFrame>
    </ObjectAnimationUsingKeyFrames>
</Storyboard>

 

Más información

Visibility animation

 

Bing Maps Draggable Pushpin

Bing maps està completament integrat a les tecnologíes de .Net com ASP.NET, WPF, Silverlight i Windows Phone. L’ús de la API és molt senzill.

Per començar a treballar amb ella primer necessites descarregar-te l’SDK de Bing Maps i aconseguir la teva clau de desenvolupador gratuitament.

Tota la informació i els passos a seguir els trobeu aqui: Bing Maps for Developers

En el cas que m’ocupa aquesta entrada vull parlar del control Pushpin de l’espai de noms Microsoft.Phone.Controls.Maps en Windows Phone.

Des de la pàgina de Microsoft pots trobar la informació de com es treballa amb els Pushpins i creume que no és gens complicat. Aqui tens una part de codi amb XAML que et permet incloure una llista de Pushpins enllaçada a una font de dades.

<my:Map  Name="map1" ZoomBarVisibility="Visible" ZoomLevel="16" Center="{Binding Location}">
    <my:MapLayer Visibility="{Binding VisibilityLayer}">
       <my:MapItemsControl ItemsSource="{Binding LlistaPushpins}">
           <my:MapItemsControl.ItemTemplate>
              <DataTemplate>
                <my:Pushpin Content="{Binding Contingut}" Location="{Binding Location}" >
                </my:Pushpin>
              </DataTemplate>
           </my:MapItemsControl.ItemTemplate>
       </my:MapItemsControl>
    </my:MapLayer>
</my:Map>

El que no et fa la SDK estàndard del Bing és poder arrosegar un Pushpin de un punt a un altre del mapa. Per sort la comunitat ja ha resolt aquest problema i penso jo d’una manera molt nítida si ens fixem en la resolució proposada al següent enllaç:

Draggable PushPins

Simplement es tracta de crear una clase que hereda de Behavior<Pushpin> que s’adjunta a l’event MouseLeftButtonDown per accedir a la propietat Location del Pushpin i assignar-l’hi la posició del mouse respecte l’area que ocupa el mapa.

El codi en XAML que has d’utilitzar després és simplement:

<my:Map  Name="map1" ZoomBarVisibility="Visible" ZoomLevel="16" Center="{Binding Location}">
<my:MapLayer >
     <my:Pushpin Location="{Binding Location,Mode=TwoWay}" Style="{StaticResource PushpinStyle2}" >
          <interactivity:Interaction.Behaviors>
               <behavior:DraggablePushpin />
          </interactivity:Interaction.Behaviors>
     </my:Pushpin>
</my:MapLayer>
</my:Map>

El resultat és el següent si seguim el que indica el post que faig referència:

1) Punt inicial del Pushpin

2) Desplaçar per el mapa (Drag)

3) Avís de canvi de ubicació

4) Pushpin canviat

Més informació:

Draggable PushPins

Draggable Pushpins using Bing Maps Silverlight Control

Combobox Silverlight Error 4004

Amb Silverlight 4 el control ComboBox dóna un error particular – no ho he provat amb els altres -. Aquest error no es pot debugar i té el codi 4004 amb l’excepció XamlInvalid. Després de hores i hores trencant-me el cap la conclusió és la següent:

Quan des de codi omplim la propietat ItemsSource del control, si després actualitzem la llista, apareix aquest error.

myComboBox.ItemsSource = <<operació que retorna la llista>>;
(myComboBox.ItemsSource as List<Element>).Add(new Element {nom="nou valor", codi=1});

Aquesta operació pot semblar molt normal però provoca aquest error. El que s’ha de fer és treballar amb la llista abans d’assignar-la a ItemsSource

List<Element> llista = <<operació que retorna la llista>>;
llista.Add(new Element {nom="nou valor", codi=1});
myComboBox.ItemsSource = llista;

Contar els caràcters escrits a una caixa de text amb Silverlight

Silverlight és una tecnologia completament integrada amb .NET que utilitza XAML com a llenguatge per definir la interficia. Aquest llenguatges està dins les llibreries que conformen WPF. XAML és un llenguatge estructurat com un XML que permet definir els components gràfics juntament amb els seus estils, events i triggers.

Una cosa tant senzilla com és contar els caràcters que escrivim a una caixa de text que amb Html hem de fer ús de una llibreria Javascript amb XAML utilitzem el concepte d’enllaç de dades (databinding).

El següent codi XAML mostra una caixa de text juntament amb una etiqueta que mostra el numero de caràcters escrits. La manera és enllaçar l’etiqueta amb la propietat Length del control TextBox.

<StackPanel Orientation="Vertical" Margin="50">
  <TextBlock Text="Tweet (max 140 characters)" />
  <TextBox x:Name="tweetText" MaxLength="140"  Text="Right now I'm writing a book" />   
<StackPanel Orientation="Horizontal">
    <TextBlock Text="{Binding Text.Length, ElementName=tweetText}" />
    <TextBlock Text="/" />
    <TextBlock Text="{Binding MaxLength, ElementName=tweetText}" />
  </StackPanel>
 </StackPanel>

Que fàcil oi? Aquest és el resultat.

La font està al blog http://10rem.net/blog/2009/06/24/from-silverlight-in-action-2nd-edition-ui-element-binding

Per què Dispatcher.CheckAccess no surt a Intellisense

Quan treballem amb multi-threading és molt habitual actualitzar controls d’usuari però amb Windows només el thread que crea un control pot actualitzar-lo (Thread affinity). És per això que existeix Dispatcher amb Wpf i Silverlight com a classe que representa el thread UI o thread del control si hi accedim per la propietat Dispatcher. Però quan treballem amb multi-threading necessitem comprovar si el thread que està a punt d’actualitzar el control és o no el creador del mateix. Per això tenim el CheckAccess com a mètode que comprova si el thread pot actualitzar el control o necessitem fer-ho per Dispatcher.

private delegate void AddTextDelegate(Panel p, String text);
private void AddText(Panel p, String text)
{
p.Children.Clear();
p.Children.Add(new TextBlock { Text = text });
}
private void TestBeginInvokeWithParameters(Panel p)
{
if (p.Dispatcher.CheckAccess()) AddText(p, "Added directly.");
else p.Dispatcher.BeginInvoke(
new AddTextDelegate(AddText), p, "Added by Dispatcher.");
}

Però CheckAccess no surt a Intellisense però si l’utilitzem no dóna un error de compilació. CheckAccess és un mètode amb l’atribut EditorBrowsable a Never. Microsoft diu que no és per programadors normals. Però crec que ho solucionaran ja que han rebut moltes queixes.

Thread.CurrentPrincipal

El Client Application Services que podem implementar a la nostre aplicació WPF ens permet validar a un usuari d’una manera molt senzilla. Aquesta validació es guarda a una propietat CurrentPrincipal del thread. El problema és que la autentificació es guarda al thread que ha provocat la verificació. Però què passa quan es creen threads secundaris? Per internet hi ha articles que es diu que els threads secundaris hereden el CurrentPrincipal del Thread principal però no ho he provat, el que si que he provat és que els threads creats per la crida de funcions asíncrones per Beginxxxx no hereden aquesta propietat. El problema que hi ha és que si llavors executem alguna crida a algun servei web aquest no està amb autentificació.

Una de les solucions que ens podem buscar és la d’utlitzar el mètode SetThreadPrincipal de la classe AppDomain. Aquest mètode serveix per indicar quin és el CurrentPrincipal per defecte que tindran tots els threads del domini. És correcte i útil però té el problema que només es pot assignar una sola vegada. Quan es vol assignar una segona vegada provoca una excepció PolicyException, per tant, per els escenaris on es fa login/logout per després poder fer un altre login no ens serveix.

try
{
AppDomain.CurrentDomain.SetThreadPrincipal(Thread.CurrentPrincipal);
}
catch (System.Security.Policy.PolicyException) { }

Indagant per el .Net no m’ha quedat una altre alternativa que crear una classe estàtica ApplicationContext on per una propietat CurrentPrincipal l’hi assigno el IPrincipal que tinc en cada moment que es fa el login.