lunedì 31 gennaio 2011

TFS 2010 Object Model: recupero dei progetti

Nei precedenti post (link, link) abbiamo visto come utilizzare l’object model di TFS 2010 per connettersi ad un server TFS e recuperare le project collection ivi presenti.

In questo post vedremo i deiversi modi per recuperare le proprietà dei progetti presenti all’interno delle project collections.

Query sui nodi del server

Il primo modo è quello di sfruttare un meccanismo analogo a quello visto nei precedenti post per il recupero delle collections che si basa sull’utilizzo del metodo QueryChildren della proprietà CatalogNode:

  1. Dim tfs = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                                                New UICredentialsProvider())
  3. Dim allProjects = tfs.CatalogNode.QueryChildren({CatalogResourceTypes.TeamProject},
  4.                                           True,
  5.                                           CatalogQueryOptions.IncludeParents)

In questo caso otteniamo una collezione in sola lettura di CatalogNode nella cui proprietà Resource sono presenti le proprietà del progetto (nome, descrizione, etc., etc.).
Poichè abbiamo utilizzato l’opzione IncludeParents, otteniamo che, per ogni catalogNode, la proprietà Parent contiene il nodo relativo alla collection di appartenenza.
Il parametro recurse=true nella precedente query è fondamentale in quanto i progetti non sono direttamente contenuti nel server.
Se mettiamo recurse=false otteniamo una collezione vuota.

Se volessimo ottenere i progetti contenuti in una specifica collection, potremmo utilizzare una query LINQ del tipo:

  1. Dim tfs = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                                                New UICredentialsProvider())
  3. Dim allProjects = tfs.CatalogNode.QueryChildren({CatalogResourceTypes.TeamProject},
  4.                                           True,
  5.                                           CatalogQueryOptions.IncludeParents)
  6.  
  7. Dim collProjects = From n In allProjects.OfType(Of CatalogNode)()
  8.                     Where n.ParentNode.Resource.DisplayName = "DomusDotNet"
  9.                     Select n

Query sui nodi della Project Collection

Un altro modo per ottenere i progetti presenti all’interno di una collection è quello di eseguire una query sui nodi contenenti le risorse direttamente su un’istanza di TfsTeamProjectCollectionNel precedente post della serie, abbiamo visto i tre modi possibili per ottenere un’istanza della classe TfsTeamProjectCollection. Supponiamo, quindi di averne una e di voler recuperare le proprietà dei progetti in essa contenuti:

  1. Dim collProjects = tfsColl1.CatalogNode.QueryChildren({CatalogResourceTypes.TeamProject},
  2.                                                         False,
  3.                                                         CatalogQueryOptions.None)

In questo caso non serve la recursione e il recupero dei nodi padre perchè i progetti sono gerarchicamente disposti sotto la collection.

 

martedì 25 gennaio 2011

Evento online per lo sviluppo di applicazioni WP7 con VB.NET a cura di WPF Tips & Tricks

Vi segnalo l’evento online che si terrà il giorno 27 Gennaio alle ore 21:00.

Speakers dell’evento saranno Alessandro Del Sole e Renato Marzaro entrambi MVP per Visual Basic, quindi una garanzia di qualità.

Per maggiori info ed iscrizioni all’evento fate riferimento all’indirizzo:

http://www.wpfitalia.it/Eventionline.aspx

lunedì 24 gennaio 2011

TFS 2010 Object Model: recuperare le informazioni delle project collections

In questo post vorrei mostrare come sia possibile recuperare  le informazioni delle project collection contenute nel nostro Team Foundation Server utilizzando l’Object Model messoci a disposizione dall’SDK.

Nel precedente post della serie abbiamo visto come sia possibile eseguire la connessione al server TFS, quindi diamo per assodato il fatto di essere in grado di accedere al server.

Per recuperare le informazioni delle project collections possiamo utilizzare la proprietà CatalogNode della classe TfsConfigurationServer ed eseguire una query per ricavare i nodi di tipo Collection:

  1. Dim tfsInstance = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"))
  2. Dim collections = tfsInstance.CatalogNode.QueryChildren({CatalogResourceTypes.ProjectCollection},
  3.                                                         False, CatalogQueryOptions.None)

La variabile collections conterrà una collezione a sola lettura di CatalogNode nei quali sono contenuti i dati da noi ricercati.

In particolare CatalogNode ha una proprietà Resource di tipo CatalogResource.

Le più importanti proprietà della classe CatalogeResource sono:

  • Description :  descrizione della collection (in generale della risorsa). Si tratta della descrizione che abbiamo inserito in fase di creazione della collezione;
  • DisplayName : nome della collectio. E’ il nome che abbiamo inserito nel wizard di creazione della collezione;
  • Identifier : si tratta di un Guid che identifica univocamente la risorsa a livello di catalog;
  • Properties : è un oggetto che implementa IDictionary(Of String,String) in cui sono contenute delle proprietà del nodo. In particolare in questo dictionary troviamo la proprietà InstanceId utile quando vogliamo recuperare il riferimento ai servizi esposti dalla collezione;
  • ResourceType : contiene il tipo della risorsa. Nel caso in questione si tratta di TeamprojectCollection.

La proprietà CatalogNode di una TfsConnection, permette di ricavare tutte le informazioni relative ai nodi apparteneti alla connessione. Nel caso della TfsConfigurationServer si tratta di tuti gli oggetti contenuti nel server tra cui collection, progetti e via discorrendo.

Possiamo recuperare le informazioni contenute nella CatalogeNode anche avendo a disposizione l’istanza della Project Collection (cioè un’istannza della classe TfsTeamProjectCollection).

Prima di vedere come recuperare le informazioni di cui sopra vediamo come è possibile ottenere l’istanza della TFSTeamProjectCollection.

Se abbiamo a disposizione un’istanza della TfsConfigurationServer (vedi il post precedente), possiamo utilizzare il metodo GetTeamProjectCollection.

Il metodo in questione prevede, come argomento, l’instance identifier della collezione che si vuole recuperare. Si tratta di un Guid che è recuperabile all’interno della proprietà Properties dell’istanza CatalogeResource della collection con la chiave “InstanceId”:

  1. Dim tfs = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                                                New UICredentialsProvider())
  3. Dim collections = tfs.CatalogNode.QueryChildren({CatalogResourceTypes.ProjectCollection},
  4.                                                              False,
  5.                                                              CatalogQueryOptions.None)
  6. Dim collInstanceId = (From n In collections
  7.                      Where n.Resource.DisplayName = "nome collection"
  8.                      Select n.Resource.Properties("InstanceId")).FirstOrDefault()

E’ evidente che se non abbiamo l’instance identifier e dobbiamo recuperarlo con il precedente pezzo di codice, abbiamo anche tutte le informazioni culla collection senza dover utilizzare l’istanza di TfsTeamProjectCollection ottenuta con il metodo GetTeamProjectCollection.

Supponiamo, per un attimo, di avere l’instace id senza dover accedere ai nodi della CatalogeNode dell’istanza di TfsConfigurationServer. In questo caso, per ottenere l’istanza di TfsTeamProjectCollection è sufficiente:

  1. Dim tfs = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                                                 New UICredentialsProvider())
  3. Dim tfsColl = tfs.GetTeamProjectCollection(New Guid(collInstanceId))

dove InstanceId è la stringa che identifica l’instance Id della collezione.

Il metodo GetTeamProjectCollection si comporta in maniera analoga a quanto visto per la TfsConfigurationServerFactory nel precedente post:

  • se l’istanza di TfsConfigurationServer su cui è richiamato è stata generata tramite la TfsConfigurationServerFactory, allora l’istanza restituita dal metodo sarà sempre la stessa;
  • se l’istanza di TfsConfigurationServer su cui è richiamato è stata istanziata utilizzando il costruttore, allora l’istanza restituita dal metodo sarà diversa per ogni chiamata.

Due altri modi per recuperare l’istanza della TfsTeamProjectCollection sono:

  • utilizzare il costruttore della classe TfsTeamProjectCollection:
  1. Dim tfsColl = New TfsTeamProjectCollection(New Uri("http://server:8080/tfs/nomecollection"),
  2.                                            New UICredentialsProvider())
  • utilizzare la classe factory TfsTeamProjectCollectionFatctory:
  1. Dim tfsColl = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://server:8080/tfs/nomecollection"),
  2.                                                                         New UICredentialsProvider())

Osserviamo che, l’url della collection è ottenuto concatenando il nome della colection all’url del server.

Se utilizziamo la factory, otterremo sempre la stessa istanza della TfsTeamProjectCollection, mentre il costruttore ci consente di avere ogni volta un’istanza differente.

Nel momento in cui abbiamo a disposizione l’istanza per accedere alla componente server della project collection, le informazioni della stessa sono recuperabili accedendo direttamente alla proprietà CatalogNode ed in particolare alla proprietà Resource:

  1. Dim collName = tfsColl.CatalogNode.Resource.DisplayName
  2. Dim collDescription = tfsColl.CatalogNode.Resource.Description
  3. Dim collInstanceId = tfsColl.CatalogNode.Resource.Properties("InstancreId")

Un ulteriore modo per recuperare le informazioni relative alle collection presenti nel nostro server è quello di richiedere, all’istanza di TfsConfigurationServer, il servizio ITeamProjectCollectionService tramite l’istruzione:

  1. Dim tfs = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://rma-mbnb01-dev:8080/tfs"),
  2.                                                                         New UICredentialsProvider())
  3.  
  4. Dim temProSrv = CType(tfs.GetService(Of ITeamProjectCollectionService)(), ITeamProjectCollectionService)

e, quindi, richiamare il metodo GetCollections:

  1. Dim collections = temProSrv.GetCollections()

Il risultato di questo è una IList(Of TeamProjectCollection). La TeamProjectCollection è una classe presente nel namespace Microsoft.TeamFoundation.Framework.Client e contiene delle informazioni relative alla project collection e poco di più.

 

venerdì 21 gennaio 2011

Ma da quando un’interfaccia semplice è noiosa!!!!

E’ un pò che non faccio post di “sfogo” ed è arrivato il momento di farne uno!!

Oggi mi è capitato di leggere alcuni commenti relativi a WP7 in cui si diceva che il sistema operativo non ha riscontrato il favore del pubblico perchè troppo semplice da risultare noioso!

Da quando la semplicità di utilizzo fa si che un prodotto non incontri il favore del pubblico?!?!?!?!

Ci siamo lamentati di Windows Mobile perchè complesso, lento, macchinoso, etc., etc. e, adesso, questo è troppo semplice da usare?!?!?!

Io ho dato in mano il telefono a mia moglie che non ama la tecnologia e che usa un telefono in cui il massimo dell’avveniristico è l’MMS ed è riuscita ad utilizzarlo senza una laurea in ingegneria nucleare! E non ha neanche chiesto il divorzio perchè me lo sono comperato!!!!

Se il WP7 non ha riscontrato il successo atteso non è certo dovuto al fatto che è semplice da utilizzare! Il fatto che la sua interfaccia sia minimale non può essere portato come lato negativo: può piacere o no, ma non è un lato negativo!!!

Detto questo, personalmente godo come un riccio a leggere le mail con pochissimi tap del dito, voi pensatela come vi pare!!!

Code Contracts su ioProgrammo di Febbraio

E’ in uscita, nel numero di febbraio di ioProgrammo, un mio articolo riguardante l’introduzione ai Code Contracts.

4-159g 

sabato 15 gennaio 2011

TFS 2010 Object Model: connettersi ad un server TFS

Per questioni di lavoro mi sono occupato recentemente di interagire, via codice, utilizzando l’object model, con TFS. Vorrei iniziare una mini serie dedicata a trucchi e brevi articoli sull’utilizzo di alcune classi presenti all’interno dello stesso object model. La serie non ha la pretesa di essere esaustiva ma ha lo scopo, come mia consuetudine, di condividere il know-how.

Questo post riguarda la parte iniziale di ogni interazione con TFS: la connessione al server.

Se vogliamo creare applicativi client che interagiscono con Team Foundation Server 2010, possiamo utilizzare l’object model messo a disposizione dalle librerie Microsoft.TeamFoundation.*.dll.

image

In particolare per la connessione al server è necessario referenziare le librerie Microsoft.TeamFoundation.Client.dll e Microsoft.TeamFoundation.Common.dll.

La connessione al server TFS (allo scopo di ottenere servizi per la gestione delle entità proprie del server) può essere eseguita mediante la classe TFSConfigurationServer contenuta all’interno del namespace Microsoft.TeamFoundation.Client (nell’assembly Microsoft.TeamFoundation.Client.dll).

E’ possibile creare un’istanza della TFSConfigurationServer in due modalità:

  • Utilizzando il costruttore:
  1. Dim tfsInstance1 = New TfsConfigurationServer(New Uri("http://server:8080/tfs"), New UICredentialsProvider())
  • Utilizzando la classe factory TFSConfigurationServerFactory:
  1. Dim tfsInstance1 = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"), New UICredentialsProvider())

In entrambi i casi l’indirizzo http://server:8080/tfs deve essere un indirizzo valido per la connessione a TFS. Possiamo ottenere l’indirizzo sfruttando il Team Explorer. E’ sufficiente aprire le properties di una collection del Team Explorer per avere l’indirizzo da inserire nelle nostre classi:

image

Sia costruttore che factory mettono a disposizione overload per specificare le credenziali di accesso ma, in questo momento, non ce ne preoccupiamo e come provider di credenziali utilizziamo la classe UICredentialProvider che permette, se TfsConfigurationServer non trova delle credenziali in cache, di richiederle all’utente con un prompt.

Quello che ci interessa in questo post è mettere in evidenza la sostanziale differenza tra costruttore e factory e cioè il fatto che il costruttore permette di ottenere differenti istanze di conessione  (una per ogni New) mentre la factory crea una nuova istanza la prima volta che viene richiamato il metodo GetConfigurationServer e restituisce la stessa istanza le volte successive.

Per verificare questo non possimao utilizzare il metodo Equals delal classe Object perchè tale metodo e ridefinito nella classe TfsConnection da cui la TfsConfigurationServer deriva per confrontare due istanze della classe e definirle uguali quando hanno lo stesso Url. Allora scriviamo il seguente codice:

  1. Dim tfsInstance1 = New TfsConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                               New UICredentialsProvider())
  3. Dim tfsInstance2 = New TfsConfigurationServer(New Uri("http://server:8080/tfs"),
  4.                                               New UICredentialsProvider())
  5. tfsInstance1.Dispose()

A seguito della chiamata al metodo Dispose, se le istanze sono distinte, tfsInstance1 sarà “disposata” mentre la tfsInstance2 no.

Per verificare questo inseriamo dei QuickWatch nel nostro progetto in modo da mettere in evidenza i valori delle proprietà Disposed (che non sono visibili direttamente perchè Friend) e osserviamo che, dopo il Dispose si ha:

image

Facciamo la stessa cosa per le istanze generate dalla Factory:

  1. Dim tfsInstance1 = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  2.                                                                     New UICredentialsProvider())
  3. Dim tfsInstance2 = TfsConfigurationServerFactory.GetConfigurationServer(New Uri("http://server:8080/tfs"),
  4.                                                                     New UICredentialsProvider())
  5.  
  6. tfsInstance1.Dispose()

In questo caso, i QuickWatch ci segnalano:

image

Se questo non bastasse possiamo analizzare come viene implementato il metodo GetConfigeurationServer della TfsConfigurationServerFactory utilizzando Reflector:

image

La classe implementa una sorta di cache (si tratta di un Dictionary(Of Uri,TfsConfigurationServer) ) in cui viene ricercata un eventuale istanza già creata (punto 1) e, in caso in cui sia presente restituita. Se non esiste un’istanza per l’Uri passato come argomento, la factory utilizza il costruttore per istanziare una nuova TfsConfigurationServer e la memorizza nella propria cache (punto 2).

 

martedì 11 gennaio 2011

Esempi di codice in VB.NET

Il team di Visual Basic, aderendo alle richieste fatte dalla community degli sviluppatori VB.NET (che, a dispetto dei C-sharpisti è ampia e vitale Sorriso), ha pubblicato una serie di esempi che vanno da Windows Phone 7 a Azure.

Potete trovare l’elenco completo a questo link.

Nella pagina indicata trovate anche la VB code wishlist, ovvero la possibilità di segnalare al team di VB un articolo o uno snippet di codice che volete compaia negli esempi.

 

Tag di Technorati: ,,,,

lunedì 10 gennaio 2011

VB for Dummies: La serializzazione – parte 2

Questo post è la continuazione del precedente post sulla serializzazione.

In particolare vedremo la serializzazione SOAP e quella JSON.

 

Serializzazione SOAP

La serializzazione SOAP è demandata alla classe SOAPFormatter contenuta nel namespace System.Runtime.Serialization.Formatters.Soap contenuto nella libreria omonima.

Il formatter SOAP risale alle primissime versioni del framework e, purtroppo, da un certo punto in poi, pur non essendo stato dichiarato obsoleto, non è stato portato avanti nello sviluppo e non supporta alcuni tipi di dati usatissimi nel mondo .NET quali i generici e i nullable.

Per questo motivo, non possiamo serializzare in formato SOAP (utilizzando il SOAPFormatter) la nostra Fattura (vedi post precedente) poichè questa ha una proprietà di tipo List(Of DettagliFattura) (generico).

 

Serializzazione JSON

Il formato di serializzazione JSON (maggiori info qui) è un formato testuale molto in voga nelle applicazioni AJAX. Si trata di un modo per serializzare oggetti anche complessi con un formato molto compatto e facilmente interoperabile con Javascript che, quindi, si adatta partoicolarmente bene per gli scenari web di tipo AJAX.

La serializzazione JSON può essere eseguita utilizzando la classe DataContractJsonSerializer che nel framework 4.0 è contenuta nel namespace System.Runtime.Serialization.Json all’interno dell’omonimo assembly.

Fate attenzione perchè, se state sviluppando per il framework 3.5 (prima di questo la classe DataContractJsonSerialization non era presente), trovate la classe nello stesso namespace ma l’assembly è System.ServiceModel.Web.

La classe DataContractJsonSerializer prevede, tra i vari metodi, WriteObject e ReadObject che permettono , rispettivamente, di serializzare e deserializzare un’oggetto in formato JSON.

Entrambi i metodi sfruttano uno stream per serializzare l’oggetto. In base alle nostre esigenze tale stream potrebbe essere in memoria (come nell’esempio riportato in seguito), su file o su un canale web (ad esempio come risposta ad una richiesta di un client remoto).

I metodi per la serializzazione e deserializzazione dell’oggetto Fattura sono:

  1. Public Shared Function SerializzaJSON(ByVal fattura As Fattura) As String
  2.     If fattura Is Nothing Then Throw New ArgumentNullException("Fattura")
  3.     Dim strJSON As String = Nothing
  4.     Dim serializer = New DataContractJsonSerializer(GetType(Fattura))
  5.     Using memStream = New MemoryStream()
  6.         serializer.WriteObject(memStream, fattura)
  7.         strJSON = Encoding.Default.GetString(memStream.ToArray())
  8.     End Using
  9.     Return strJSON
  10. End Function
  11.  
  12. Public Shared Function DeserializzaJSON(ByVal strFattura As String) As Fattura
  13.     Dim retObj As Fattura = Nothing
  14.     Dim serializer = New DataContractJsonSerializer(GetType(Fattura))
  15.     Using memStream = New MemoryStream(Encoding.Default.GetBytes(strFattura))
  16.         retObj = CType(serializer.ReadObject(memStream), Fattura)
  17.     End Using
  18.     Return retObj
  19. End Function

Il risultato è il seguente:

image

Osserviamo che, negli esempi, abbiamo utilizzato l’encoding di default per le stringhe. Evidentemente, all’occorrenza, l’encoding può essere quello da noi desiderato.

Se la nostra classe (come accade nella Fattura) è composta da oggetti .NET serializzabili, possiamo utilizzare immediatamente la serializzazione JSON.

In alternativa possiamo decorare la nostra classe (e tutte quelle eventualmente utilizzate come proprietà) con l’attributo Serializable o con l’attributo DataContract.

Nel primo caso è sufficiente decorare le sole classi e la serializzazione JSON avrà le proprietà espresse con il nome dell’attributo privato che viene incapsulato dalle proprietà. Nel caso delle proprietà definite in modo compatto (come accade nella Fattura) il compilatore crea, dietro le quinte un attributo per ogni proprietà con lo stesso nome della proprietà a cui viene anteposto il carattere “_”. In questo caso il JSON risultante vedrà le proprietà espresse con il nome della proprietà preceduta da “_”.

Nel caso dell’attributo DataContract, invece, è necessario decorare anche ogni proprietà con l’attributo DataMember (se una proprietà non viene decorata con DataMember, questa non finisce nella serializzazione).

L’attributo DataMember permette di rinominare la proprietà all’interno del JSON risultante:

  1. <DataMember(Name:="numeroDocumento")>
  2. Public Property NumeroDocumento As String

In questo caso la proprietà, all’interno del JSON, si chiamerà “numeroDocumento” e non “NumeroDocumento”.

Utilizzando DataContract e DataMember possiamo intervenire nel JSON risultante in modo da personalizzare il risultato a nostro piacimento (utile soprattutto quando ci viene dato il tracciato e dobbiamo ricreare la classe).

Serializzazion personalizzata

Solo un accenno alla possibilità di creare una serializzazione custom di oggetti.

Per fare questo, il framework ci mette a disposizione l’interfaccia IFormatter che dispone dei due metodi Serialize e Deserialize che dobbiamo ridefinire implementando la nostra logica di serializzazione/deserializzazione:

Un esempio può essere il seguente:

  1. Imports System.Runtime.Serialization
  2.  
  3. Public Class MioSerializzatore
  4.     Implements IFormatter
  5.  
  6.     Public Property Binder As System.Runtime.Serialization.SerializationBinder Implements System.Runtime.Serialization.IFormatter.Binder
  7.         Get
  8.  
  9.         End Get
  10.         Set(ByVal value As System.Runtime.Serialization.SerializationBinder)
  11.  
  12.         End Set
  13.     End Property
  14.  
  15.     Public Property Context As System.Runtime.Serialization.StreamingContext Implements System.Runtime.Serialization.IFormatter.Context
  16.         Get
  17.  
  18.         End Get
  19.         Set(ByVal value As System.Runtime.Serialization.StreamingContext)
  20.  
  21.         End Set
  22.     End Property
  23.  
  24.     Public Function Deserialize(ByVal serializationStream As System.IO.Stream) As Object Implements System.Runtime.Serialization.IFormatter.Deserialize
  25.         ' qui ci va la logica di deserializzazione
  26.     End Function
  27.  
  28.     Public Sub Serialize(ByVal serializationStream As System.IO.Stream, ByVal graph As Object) Implements System.Runtime.Serialization.IFormatter.Serialize
  29.         ' qui ci va la logica di serializzazione
  30.     End Sub
  31.  
  32.     Public Property SurrogateSelector As System.Runtime.Serialization.ISurrogateSelector Implements System.Runtime.Serialization.IFormatter.SurrogateSelector
  33.         Get
  34.  
  35.         End Get
  36.         Set(ByVal value As System.Runtime.Serialization.ISurrogateSelector)
  37.  
  38.         End Set
  39.     End Property
  40. End Class

sabato 8 gennaio 2011

WP7 Tip : Cambiare il background di un controllo in base al tema

A seguito di un thread apparso sul forum Microsoft per lo sviluppo in Windows Phone vorrei riportare una implementazione di un converter che ci consente di cambiare il background di un controllo nello XAML in base al tema attivo.

Per cominciare “rubiamo” il tip del Genio Del Male riguardante il modo con cui determinare il tema attivo (link) e lo convertiamo in VB.NET. In particolare creiamo un metodo shared (ma andrebbe bene anche una proprietà shared) nella classe App della nostra applicazione:

  1. Public Shared Function GetCurrentTheme() As Theme
  2.     Dim bgc = App.Current.Resources("PhoneBackgroundColor").ToString()
  3.     If bgc = "#FF000000" Then
  4.         Return Theme.Dark
  5.     Else
  6.         Return Theme.Light
  7.     End If
  8. End Function

L’enumerazione Theme è definita nel seguente modo:

  1. Public Enum Theme
  2.     Dark
  3.     Light
  4. End Enum

A questo punto possiamo creare il nostro conveter:

  1. Imports System.Windows.Data
  2. Imports System.Windows.Media
  3.  
  4. Public Class ThemeColorConverter
  5.     Implements IValueConverter
  6.  
  7.     Private Function GetColorFromName(ByVal strColor As String) As Color?
  8.         Dim retColor As Color? = Nothing
  9.         Try
  10.             Dim propColor = GetType(Colors).GetProperty(strColor)
  11.             If propColor IsNot Nothing Then
  12.                 Dim value = propColor.GetValue(Nothing, Nothing)
  13.                 If value IsNot Nothing Then
  14.                     retColor = CType(value, Color)
  15.                 End If
  16.             End If
  17.         Catch ex As Exception
  18.             retColor = Nothing
  19.         End Try
  20.         Return retColor
  21.     End Function
  22.  
  23.     Private Function GetColorFromRGB(ByVal strColor As String) As Color?
  24.         Dim retColor As Color? = Nothing
  25.         strColor = strColor.Replace("#", "")
  26.         Dim a As Byte = 255
  27.         Dim r As Byte = 255
  28.         Dim g As Byte = 255
  29.         Dim b As Byte = 255
  30.         Dim start = 0
  31.         If strColor.Length = 6 Or strColor.Length = 8 Then
  32.             If strColor.Length = 8 Then
  33.                 a = Byte.Parse(strColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber)
  34.                 start = 2
  35.             End If
  36.             r = Byte.Parse(strColor.Substring(start, 2), System.Globalization.NumberStyles.HexNumber)
  37.             g = Byte.Parse(strColor.Substring(start + 2, 2), System.Globalization.NumberStyles.HexNumber)
  38.             b = Byte.Parse(strColor.Substring(start + 4, 2), System.Globalization.NumberStyles.HexNumber)
  39.             retColor = Color.FromArgb(a, r, g, b)
  40.         End If
  41.         Return retColor
  42.     End Function
  43.  
  44.     Private Function GetColor(ByVal strColor As String) As Color
  45.         Dim retColor As Color
  46.  
  47.         Dim tmpColor = GetColorFromName(strColor)
  48.         If Not tmpColor.HasValue Then
  49.             tmpColor = GetColorFromRGB(strColor)
  50.             If tmpColor.HasValue Then
  51.                 retColor = tmpColor.Value
  52.             End If
  53.         Else
  54.             retColor = tmpColor.Value
  55.         End If
  56.  
  57.         Return retColor
  58.     End Function
  59.  
  60.     Public Function Convert(ByVal value As Object,
  61.                             ByVal targetType As System.Type,
  62.                             ByVal parameter As Object,
  63.                             ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
  64.         If value IsNot Nothing Then
  65.             Dim brush As SolidColorBrush = Nothing
  66.             Dim values = value.ToString().Split("|"c)
  67.             If values.Count = 2 Then
  68.                 If App.GetCurrentTheme() = Theme.Light Then
  69.                     brush = New SolidColorBrush(GetColor(values(0)))
  70.                 Else
  71.                     brush = New SolidColorBrush(GetColor(values(1)))
  72.                 End If
  73.             End If
  74.             Return brush
  75.         Else
  76.             Return Nothing
  77.         End If
  78.     End Function
  79.  
  80.     Public Function ConvertBack(ByVal value As Object,
  81.                                 ByVal targetType As System.Type,
  82.                                 ByVal parameter As Object,
  83.                                 ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
  84.         Throw New NotImplementedException()
  85.     End Function
  86. End Class

La funzione GetColor ci consente di ricavare un oggetto Color a partire da una stringa nei seguenti formati #AARRGGBB, #RRGGBB o “nome colore” (con il nome che può essere uno dei colori possibili della classe Colors).

Il metodo Convert del nostro converter recupera l’argomento value che per convenzione dovrà essere una stringa con il seguente formato:

colore tema light|colore tema dark

Il metodo splitta la stringa Value per recuperare i due colori e, in base al risultato del metodo GetCurrentTheme (visto in precedenza), recupera il colore utilizzando la GetColor e crea un SolidBrush che restituisce al chiamante.

Il converter così scritto si può utilizzare direttamente in binding all’interno dello XAML.

Per fare questo è sufficiente referenziare il namespace opportuno:

  1. xmlns:my="clr-namespace:WP7ThemeBackground"

Inserire il converter nelle risorse dell’applicazione o della pagina:

  1. <phone:PhoneApplicationPage.Resources >
  2.     <my:ThemeColorConverter x:Key="TCC"></my:ThemeColorConverter>
  3. </phone:PhoneApplicationPage.Resources>

E, infine, mettere il convertitore in binding con il background del controllo desiderato:

  1. <Grid x:Name="LayoutRoot" Background="{Binding Converter={StaticResource TCC}, Source=Red|#AA00FF00}">

In questo esempio, il colore per il tema light è il rosso (Red) mentre quello per il tema dark è il colore #AA00FF00 (un verde con della trasparenza).

Lo XAML completo dell’esempio è il seguente:

  1. <phone:PhoneApplicationPage
  2.     x:Class="WP7ThemeBackground.MainPage"
  3.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  6.     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  7.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  8.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  9.     xmlns:my="clr-namespace:WP7ThemeBackground"
  10.     mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
  11.     FontFamily="{StaticResource PhoneFontFamilyNormal}"
  12.     FontSize="{StaticResource PhoneFontSizeNormal}"
  13.     Foreground="{StaticResource PhoneForegroundBrush}"
  14.     SupportedOrientations="Portrait" Orientation="Portrait"
  15.     shell:SystemTray.IsVisible="True" >
  16.     <phone:PhoneApplicationPage.Resources >
  17.         <my:ThemeColorConverter x:Key="TCC"></my:ThemeColorConverter>
  18.     </phone:PhoneApplicationPage.Resources>
  19.     <!--LayoutRoot is the root grid where all page content is placed-->
  20.     <Grid x:Name="LayoutRoot" Background="{Binding Converter={StaticResource TCC}, Source=Red|#AA00FF00}">
  21.         <Grid.RowDefinitions>
  22.             <RowDefinition Height="Auto"/>
  23.             <RowDefinition Height="*"/>
  24.         </Grid.RowDefinitions>
  25.  
  26.         <!--TitlePanel contains the name of the application and page title-->
  27.         <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
  28.             <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
  29.             <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  30.         </StackPanel>
  31.  
  32.         <!--ContentPanel - place additional content here-->
  33.         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>
  34.     </Grid>
  35. </phone:PhoneApplicationPage>