Technology Experience

.NET World

Programmazione, libri, snippet di codice, articoli tecnici

.NET World

Una classe DragService per gestire il drag’n’drop in ambito WPF & MVVM

Il titolo non potrebbe essere più esplicativo. Per lo stesso progetto che ho accennato nel mio post di ieri ho dovuto cercare una soluzione per:

  1. sfruttare il drag’n’drop nella UI dell’applicazione WPF
  2. fare in modo che tale meccanismo non sia troppo invasivo, e non mi costringa a riscrivere da zero i miew ViewModel già pronti
  3. fare in modo che tale meccanismo utilizzi i Command già predisposti nei miei ViewModel
  4. il drag’n’drop deve poter utilizzare gli stessi Command già funzionanti per la parte non drag’n’drop

Mi spiego meglio sul punto (4). Ho sempre preferito fare in modo di adattare la stessa UI, in modo tale che possa essere usabile in diversi modi, facendo in modo che l’utente possa scegliere istintivamente quello che preferisce. Quindi, supponiamo di avere questa interfaccia utente:

E’ una cosa piuttosto semplice: sulla sinistra abbiamo un elenco di nomi, a destra abbiamo una TextBox che mostrerà la persona che abbiamo scelto. In questo caso voglio dare la possibilità all’utente di scegliere una persona dall’elenco in due modi differenti:

  • selezionandola dall’elenco e poi cliccando sul bottoncino con la freccia “—>”
  • trascinandola dall’elenco sopra la TextBox

Sebbene si trattino di due interazioni diverse, vorrei poter riciclare gli stessi Command (sia per quanto riguarda la parte Execute, che la parte CanExeCute)

Sul Web, googlando, ho trovato un po’ di soluzioni, ma lo ho scartate tutte quante, un po’ perchè invasive (soprattutto per essere inserite su un progetto già avviato), un po’ perchè complesse (capire decine, decine e decine di linee di codice non tue può rivelarsi poco piacevole). Quindi mi sono messo di buona lena ed ho realizzato una classe DragService (283 linee di codice ben formattate) che soddisfa tutti i miei requisiti.

Vediamo quindi come si utilizza questa mia classe.

Come utilizzare VivendoByte.WPF.DragService
Lo screenshot qui sopra è ovviamente un’applicazione WPF, implementata secondo i soliti metodi richiesti dal pattern MVVM:

xmlns:l="clr-namespace:DragAndDropDemo"
xmlns:drag="clr-namespace:VivendoByte.WPF.DragService;assembly=VivendoByte.WPF.DragService"

 

Innanzitutto, ho dichiarato i namespace che mi servono: “l” è l’assembly locale, mentre “drag” fa riferimento all’assembly VivendoByte.WPF.DragService. Il ViewModel viene inserito fra le risorse della Window corrente; il DataContext della Grid prende questo ViewModel e fa in modo di governare la view.

<Window.Resources>
    <l:MainViewModel x:Key="vm" />
</Window.Resources>
    
<Grid DataContext="{StaticResource vm}">
...
</Grid>

 

La prima parte interessante riguarda la ListBox posizionata a sinistra sulla finestra:

<ListBox Grid.Row="0" Margin="4" ItemsSource="{Binding Persons}"
            Background="LightGoldenrodYellow"
            SelectedItem="{Binding DraggingPerson}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock
                    Text="{Binding Fullname}" FontSize="24"
                    drag:DragService.SourceCommand="{Binding
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Grid},
                        Path=DataContext.DragCommand}"
                    drag:DragService.SourceCommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

 

La ListBox è bindata ad un elenco di persone (proprietà Persons del ViewModel); l’elemento selezionato è bindato sulla proprietà DraggingPerson. All’interno della ListBox ho definito un semplice DataTemplate, per permettere di mostrare ogni elemento in modo più furbo: usando la proprietà Fullname esposta da Person. E qui arriva il barbatrucco.

Sulla TextBlock ho bindato un Command, comprensivo di parametro. Siccome ci si trova all’interno di un DataTemplate, ho dovuto utilizzare RelativeSource per permettere al motore di WPF di navigare il logical tree della Window, raggiungere la Grid su cui è impostato il DataContext e – finalmente – prendere il Command interessato (nel nostro caso DragCommand). La cosa interessante è questa:

  • Nel momento in cui parte la nostra operazione di drag su un elemento della ListBox viene eseguita la CanExecute del Command associato; se la CanExecute fallisce (ritorna false) il drag non parte nemmeno
  • Se invece la CanExecute ritorna true, parte l’operazione di dragging

La stessa cosa bene o male accade per la TextBox, che è la destinazione del drag’n’drop. Invece di utilizzare SourceCommand e SourceCommandParameter, devono essere utilizzate TargetCommand e TargetCommandParameter.

<StackPanel Grid.Column="1" Margin="4">
    <TextBlock Text="Trascina una persona" FontSize="24" Height="40" />
    <DockPanel>
        <Button Content="-->" Margin="8" Width="32" Command="{Binding DropCommand}" />
        <TextBox Background="LightGoldenrodYellow" Text="{Binding SelectedPerson.Fullname,Mode=OneWay}"
                    FontSize="24" Height="40"
            drag:DragService.TargetCommand="{Binding DropCommand}" />
    </DockPanel>
    <Button Content="Rimuovi persona" Margin="8" Command="{Binding ClearPersonCommand}" FontSize="20" />
</StackPanel>

 

La TextBox è bindata al comando DropCommand. Questa volta il binding è diretto, dal momento che non ci si trova più dentro un DataTemplate, e quindi il riferimento al DataContext della Grid è diretto. Anche in questo caso:

  • Nel momento in cui passiamo sopra la TextBox durante il drag’n’drop, viene eseguita la CanExecute. Se ritorna false, l’icona del mouse diventa il classico divieto. Se ritorna true, tutto bene: sul controllo appare l’Adorner ed il drag’n’drop può concludersi
  • Se rilasciamo il tasto del mouse sopra il controllo, viene eseguito il command

Notare che il Button è bindato allo stesso comando DropCommand. Difatti, l’utente può draggare una persona dalla ListBox, o semplicemente selezionarla e poi cliccare il bottone.

Ultime piccole osservazioni

Provate a selezionare o a draggare l’elemento “Sabrina Pedretti”. E’ minorenne, e siccome la CanExecute del DragCommand impone che la persone sia maggiorenne, ecco che su questo elemento l’operazione di drag’n’drop non avviene. Davvero molto comodo: possiamo annullare il drag’n’drop nel momento in cui l’utente tenta di cominciarlo, sfruttando i Command e quindi rispettando appieno il paradigma M-V-VM. Il button “—>” anche in questo caso di spegne, per lo stesso motivo: lo stesso metodo CanExecute che impedisce il drag’n’drop fa spegnere il Button, perchè il command DragCommand non è disponibile.

I Command del ViewModel sono implementati attraverso la libreria Galasoft, di Laurent Bignon. Sono supportati sia il RelayCommand normale, che quello tipizzato. Nel progettino demo, il comando DragCommand è tipizzato sulla classe Person (nello XAML è stato specificato il SourceCommandParameter), mentre il comando DropCommand no (per capire quale elemento sto draggando utilizzo la proprietà DraggingPerson del ViewModel, impostata all’inizio del drag’n’drop).

Il Button indicato come “Rimuovi persona” svuota semplicemente la TextBox per ricominciare un’altra operazione di drag’n’drop. E lo fa agendo sulla proprietà SelectedPerson esposta dal ViewModel.

Disponibile attraverso NuGet!

Se utilizzate NuGet per la gestione degli assembly all’interno delle vostre solution VS2010, sappiate che potete trovare la libreria VivendoByte.WPF.DragService, scaricabile gratuitamennte e pronta all’uso. Ringrazio Alessandro Melchiori per la sua sessione presso l’ultimo workshop UGIdotNET, dove ha illustrato NuGet da diversi punti di vista, compreso quello del developer, ovvero come pubblicare proprie librerie sul repository pubblico.

Download del progetto demo

Se invece siete interessati al progetto demo, piccola applicazione WPF da cui è stato tratto lo screenshot sopra, è scaricabile dal mio account SkyDrive.

Send to Kindle
.NET World

MSBuild e la compilazione massiva di app Windows Phone 7

Oggi riprendo in mano Windows Live Writer per parlarvi di un’esigenza nata nei giorni scorsi, e che ho risolto (almeno tecnicamente) questa notte, anche se ho dovuto fare le ore piccole.

In questo momento, sul marketplace per Windows Phone 7 ci sono 21 applicazioni I Love Qualcosa (dove “Qualcosa” sta per Milan, Juventus, Roma, Palermo, Inter, etc. etc – il numero è disparsi perchè ho creato anche I Love Foppa, dedicata alla Foppapedretti Bergamo, squadra femminile di pallavolo). Le app sono ovviamente già disponibili da parecchio tempo (qualche mese) e nei giorni scorsi mi son posto la domanda: cosa dovrei fare se volessi ricompilarle tutte in modo massivo? Dal punto di vista tecnologico:

  1. E’ ovviamente un progetto di tipo Windows Phone 7
  2. E’ organizzata come unica solution ed unico project
  3. Esiste un file AssemblyInfo*.cs specifico per ogni squadra (quindi AssemblyInfoMilan.cs, AssemblyInfoInter.cs, e così via)
  4. Esiste un file WMAppManifest*.xml specifico per ogni squadra (quindi WMAppManifestMilan.xml, WMAppManifestInter.xml, e così via)
  5. I files dei punti (3) e (4) sono inclusi nel progetto (per maggiore facilità nella loro gestione) in folders dedicati, ed impostati come Build Action a None (in modo tale che non finiscono nel file .xap, il compilato finale)

Quindi, vi chiederete voi, come si fa ad ottenere I Love Milan piuttosto che I Love Inter?

Fino ad oggi questi passaggi li ho fatti manualmente. Supponiamo di voler generare ILoveMilan.xap.

  1. Copiavo il file AssemblyInfoAssemblyInfoMilan.cs in PropertiesAssemblyInfo.cs
  2. Copiavo il file WMAppManifestWMAppManifestMilan.xml in PropertiesWMAppManifest.xml
  3. Modificavo il file MainPage.xaml.cs per modificare una variabile stringa contenente il nome della squadra
  4. Entravo nelle proprietà del progetto per specificare il nome del file di output (in questo caso ILoveMilan.xap)

Tutto qua: poi lanciavo la compilazione e mi trovavo il file da inviare sul marketplace. Da notare che i files indicati ai punti (1) e (2) contengono una serie di importanti informazioni per l’app: titolo, descrizione, quali icone usare nelle varie dimensioni, etc. etc. Di fatto, sostituendoli, vengono sostituite informazioni essenziali dell’app stessa.

Quello che ho fatto stanotte è quindi automatizzare i quattro passaggi, inizialmente tentando di fare tutto con MSBuild.exe, poi rinunciando (causa scarsa conoscenza dello strumento) per passare ad una più “tradizionale” console application. C’è da dire questo: penso che non ci sia una soluzione unica e che vada bene per tutti, perchè ovviamente il tutto dipende da come avete strutturato la vostra solution. Vi racconto come ho fatto io.

Qualche dettaglio sulla console application
Il discorso è molto semplice. Ho creato un metodo generateTeam:

   1:  static void generateTeam(string team)
   2:  {
   3:      string path1;
   4:      string path2;
   5:   
   6:      path1 = baseDir + @"AssemblyInfoAssemblyInfo" + team + ".cs";
   7:      path2 = baseDir + @"PropertiesAssemblyInfo.cs";
   8:      File.Copy(path1, path2, true);
   9:   
  10:      path1 = baseDir + @"WMAppManifestWMAppManifest" + team + ".xml";
  11:      path2 = baseDir + @"PropertiesWMAppManifest.xml";
  12:      File.Copy(path1, path2, true);
  13:   
  14:      string mainPage = getResource("ConsoleApplication1.MainPage.xaml.cs1") + team + getResource("ConsoleApplication1.MainPage.xaml.cs2");
  15:      path1 = baseDir + "MainPage.xaml.cs";
  16:      File.WriteAllText(path1, mainPage);
  17:   
  18:      string csproj = getResource("ConsoleApplication1.ILoveFootballTeam.csproj1") + team + getResource("ConsoleApplication1.ILoveFootballTeam.csproj2");
  19:      path1 = baseDir + "ILoveFootballTeam.csproj";
  20:      File.WriteAllText(path1, csproj);
  21:   
  22:      msbuildClean();
  23:      msbuildCompile();
  24:   
  25:      path1 = baseDir + @"binReleaseILove" + team + ".xap";
  26:      if (File.Exists(path1))
  27:      {
  28:          path2 = @"X:OutputILove" + team + ".xap";
  29:          File.Copy(path1, path2, true);
  30:      }
  31:  }

 

Scusate la qualità del codice, ma alle 2 di notte non sono granchè! Sorriso

Alle righe 6-8 aggiorno e sovrascrivo il file AssemblyInfo.cs.

Alle righe 10-12 aggiorno e sovrascrivo il file WMAppManifest.xml.

Alle righe 14-16 sovrascrivo il file MainPage.xaml.cs, sfruttando un meccanismo “balordo” (ma che funziona!) con files Embedded Resource.

Alle righe 18-20 aggiorno il file .csproj, anche qui usando come template dei files inclusi come Embedded Resource.

Alla fine chiamo due metodi, msbuildClean() e msbuildCompile().

   1:  static void msbuildClean()
   2:  {
   3:      ProcessStartInfo start = new ProcessStartInfo();
   4:      start.FileName = @"C:WindowsMicrosoft.NETFrameworkv4.0.30319msbuild.exe";
   5:      start.Arguments = @" /p:Configuration=Release /t:Clean ILoveFootballTeam.csproj";
   6:      start.UseShellExecute = false;
   7:      start.WorkingDirectory = baseDir;
   8:      start.WindowStyle = ProcessWindowStyle.Normal;
   9:   
  10:      Process process = new Process();
  11:      process.StartInfo = start;
  12:      process.Start();
  13:      process.WaitForExit();
  14:  }

 

   1:  static void msbuildCompile()
   2:  {
   3:      ProcessStartInfo start = new ProcessStartInfo();
   4:      start.FileName = @"C:WindowsMicrosoft.NETFrameworkv4.0.30319msbuild.exe";
   5:      start.Arguments = @" /p:Configuration=Release ILoveFootballTeam.csproj";
   6:      start.UseShellExecute = false;
   7:      start.WorkingDirectory = baseDir;
   8:      start.WindowStyle = ProcessWindowStyle.Normal;
   9:   
  10:      Process process = new Process();
  11:      process.StartInfo = start;
  12:      process.Start();
  13:      process.WaitForExit();
  14:  }

 

IMPORTANTE: attenzione a NON usare l’msbuidl.exe contenuto in C:WindowsMicrosoft.NETFramework64v4.0.30319, perchè NON compila. Io stesso ho perso un sacco di tempo nel cercare di capire il perchè. Evidentemente, e probabilmente, il .NET Framework a 64 bit non c’entra nulla con wp7.

Eseguo msbuild.exe in due step separati: il primo fa la Clean della nostra binRelease, il secondo per effettuare la compilazione vera e propria.

Se tutto è andato a buon fine, prendo il file .xap generato e lo copio dove voglio io (righe 25-29 del primo blocco di codice).

Questo è il meccanismo che sta alla base. A questo punto è possibile chiamare generateTeam(“Milan”), generateTeam(“Cesena”), generateTeam(“Inter”), generateTeam(“Fiorentina”) per ottenere in una directory tutti gli .xap generati.

E adesso che abbiamo gli xap, come faccio ad inviarli sul marketplace?

Qui arrivano le note dolenti. E’ vero che in un battito di ciglia abbiamo le nuove versioni delle nostre app, ma per inviarle al marketplace occorre fare tutto manualmente, e qui non ci sono storie. Bisogna avere le icone nelle varie dimensioni, gli screenshots della nostra app, i testi e le descrizioni, etc. etc. E se avete 70 applicazioni (come l’amico Matteo), siete proprio fregati! Sorriso

Domande? Sono tutt’orecchi.

Send to Kindle
.NET WorldSoftware

Catture screenshot dall’emulator WP7! Ecco il tool!

Se siete sviluppatori di app per Windows Phone 7, non potete perdere questa utility free liberamente scaricabile da questo url:

http://www.innovativetechguy.com/?p=13

Questo tool permette semplicemente di catturare screenshot dall’emulatore WP7, senza dover far tutto in manuale. Mi spiego meglio. Per poter pubblicare un’applicazione sul marketplace, è assolutamente obbligatorio avere almeno uno screenshot (fino ad un massimo di 8) della vostra app: questo screenshot apparirà su Zune o direttamente sul telefono, e consente all’utente finale di dare un veloce sguardo all’applicazione prima di scaricarla ed acquistarla.

Prima dell’arrivo di WP7 Simulator Cropper questa operazione andava fatta a mano, cioè: ALT+PrintScreen per catture il desktop, poi un bel Paste nel vostro programma di grafica preferito, poi un ritaglia della porzione di schermo relativa alla vostra app nell’emulatore, poi su Resize in 480×800, ed infine salvare l’immagine finale da qualche parte sul vostro PC.

Con WP7 Simulator Cropper avviene in pratica tutto in automatico. Avviate l’emulatore, deployate l’app, lanciare il tool e cliccate sul pulsante Take Screenshot per ottenere direttamente un bel .png. Fantastico! L’unica cosa a cui prestare attenzione è assicurarsi di aver attivato l’opzione “Marketplace” e di aver settato lo zoom level dell’emulatore a 100%, perchè il crop dell’immagine – ovviamente – deve avvenire con determinate dimensioni.

Insomma, secondo me un tool di questo tipo accelera notevolmente l’operazione di “cattura screenshots per il marketplace” Sorriso e – visto che Microsoft non ci ha pensato – thanks to Cory Smith per il suo lavoro.

Send to Kindle
.NET World

Windows Phone 7? Un ecosistema che cresce sempre di più

In uno dei miei precedenti post ricordo di aver assegnato un punto di vantaggio a WP7 rispetto ai concorrenti (Android e iPhone), quando dicevo che il contributo non solo di Microsoft, ma anche di tutti gli altri developers esterni avrebbe prodotto in poco tempo un gran numero di apps, ma anche di librerie, che avrebbe permesso a sviluppatori come me (che sono in fondo alla pipeline, il developer finale) di produrre applicazioni sempre più interessanti ed appetibili al consumer.

Ora, se teniamo a mente che WP7 è arrivato – almeno per noi in Italia – neanche sei mesi fa (eravamo a metà settembre), tutto questo sinceramente lo trovo stupefacente e meraviglioso. Ecco un breve elenco di varie componenti/librerie riguardanti l’ecosistema WP7 che sono disponibili in questo momento (e sicuramente, al 100% me ne sto dimenticando qualcuna). L’ordine è ovviamente sparso, e dipende da come le cose mi vengono in mente.

  1. Si parla già di un update dell’OS – che inizialmente ci si aspettava a Gennaio, mentre dovrebbe arrivare fra qualche settimana
  2. Nokia ed Acer hanno annunciato un loro telefono WP7 entro il 2011
  3. Libreria su CodePlex : Silverlight Toolkit
  4. Libreria su CodePlex : Windows Phone 7 Isolated Storage Explorer
  5. Libreria su CodePlex : My WP7 Brand (che include classi per leggere i feed Twitter degli utenti)
  6. Libreria : Windows Phone Shake Gestures Library
  7. Documentazione : Building User Interfaces for Windows Phone 7
  8. Libreria : Windows Phone 7 Status Indicator Control
  9. Libreria su CodePlex : Windows Phone Controls
  10. Libreria su CodePlex : Windows Phone 7 Extension Framework
  11. Libreria su CodePlex : Caliburn Micro- A Micro-Framework for WPF, Silverlight and WP7
  12. Libreria su CodePlex : MVVM Light Toolkit
  13. Libreria : Windows Phone GPS Emulator (segnalato da Matteo Pagani)
  14. Libreria su CodePlex : WP7 GPS Simulator (utile per sviluppare applicazioni gps-enabled senza dover scorrazzare per la città)
  15. Libreria su CodePlex : Windows Phone Cryptographic Storage
  16. Libreria su CodePlex : Coding4Fun Tools
  17. Libreria su CodePlex : Windows Phone 7 database
  18. Un gran numero di librerie non comunque dedicate specificatamente a WP7, ma comunque utilizzabili senza problema: PlayMe, Beintoo, e non solo

Ho la completa e sicura certezza di aver elencato un numero esiguo di tutto il materiale disponibile. Se qualcuno ha qualcosa da segnalare, e mi mette un commento, mi impegno a mantenere nel corso del tempo l’elenco qui sopra: sarebbe proprio bello avere una raccolta complessiva in cui trovare questo o quello, a seconda delle proprie necessità.

Un piccolo consiglio? Indipendentemente dal client Twitter, che utilizzate (io uso TweetDeck), tenete sempre attiva la ricerca sugli hashtag #wp7 e #wp7dev: a costo zero e senza alcun impegno verrete in pratica notificati di nuove app disponibili, e più in generale di tutto ciò che riguarda WP7.

Per concludere: tutto questo in meno di 6 mesi di vita effettiva. Cavolo! Cosa volere di più, in ambito WP7?

Ecco, un’idea ce l’avrei – forse un marketing migliore (ma qui noi developer, CodePlex, librerie possiamo fare poco o nulla), perchè se la gente non conosce il prodotto, non lo acquista (o è più restio a farlo). E se nessuno lo acquista, nessuno scaricherà ed acquisterà le nostre app, e quindi…è il cane che si morde la coda!

Insomma, lunga vita a Windows Phone 7!

Send to Kindle
.NET World

Occhio se usate il MediaElement nelle vostre app wp7

Seguendo le ultime specifiche per la pubblicazione delle vostre app per Windows Phone 7 – disponibili a questo url – si legge una cosa molto importante. Il succo è questo:

se l’utente sta ascoltando qualcosa attraverso il lettore multimediale del telefono (quindi podcast o qualche canzone), l’avvio della vostra app NON deve MAI interrompere questo stream audio (sezione 6.5.1)

In cosa si traduce tutto ciò? In una bella rottura di scatole, perchè nel momento stesso in cui definite uno XAML come questo:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <MediaElement Visibility="Collapsed" x:Name="MediaPlayer" />
</Grid>

 

allora tutto bene, ma se lo XAML diventa questo:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <MediaElement Visibility="Collapsed" x:Name="MediaPlayer" Source="Start.mp3" />
</Grid>

 

l’eventuale stream audio dell’utente, l’mp3 o il podcast che dir si voglia, si interrompe e siete fregati. La vostra app viene inesorabilmente bocciata. Come risolvere? Qualche consiglio.

  1. Nello XAML non preimpostate la proprietà Source – lo farete da codice solo se serve
  2. quando serve? Potete controllare il valore di Microsoft.Xna.Framework.Media.MediaPlayer.GameHasControl; se vale false, significa che c’è già un audio che sta andando, e quindi all’interno della vostra app
  3. Ricordo che NON si può usare la classe SoundEffect per la background music: quest’ultima è specifica per gli effetti sonori, dal momento che qualsiasi cosa mandiate sullo stream viene miscelato comunque con altri suoni già presenti

Ecco un url che spiega questo problema: http://blogs.msdn.com/b/oren/archive/2010/12/02/wp7-dev-tip-detecting-whether-or-not-the-user-is-playing-music-in-the-background.aspx.

Send to Kindle
.NET World

Chiusura della propria app dopo la scadenza della trial

Qualche dettaglio sulla gestione della modalità trial delle proprie applicazioni per Windows Phone 7 la trovate in questo mio post.

Saltiamo subito all’argomento di questo post. Immaginiamo che l’App.xaml sia configurato così:

<trial:TrialManager RunAsTrial="True" Expired="TrialManager_Expired" TimerInterval="00:01:00">
    <trial:TrialManager.ApplicationPolicy>
        <trial:TimeExpirationPolicy TrialDuration="00:03:00" Mode="Lifetime" />
    </trial:TrialManager.ApplicationPolicy>
</trial:TrialManager>

 

Questo blocco XAML configura la libreria SlickThought.Phone in modo tale che la modalità trial della nostra app basi il proprio funzionamento sul tempo. In breve: dopo tre minuti di utilizzo (proprietà TrialDuration della classe TimeExpirationPolicy), viene scatenato l’evento Expired. I tre minuti sono relativi alla sessione – e non complessivamente. All’interno di questo evento possiamo fare quello che vogliamo.

Supponiamo di voler mostrare un messaggio all’utente che dica qualcosa tipo “Spiacente, la modalità trial è scaduta!”, e poi chiudiamo l’applicazione. Sfortunatamente non abbiamo a disposizione un metodo sulla classe Application di Silverlight, per cui dobbiamo escogitare il solito barbatrucco. Questo barbatrucco è raccontato a questo indirizzo, e funziona a dovere.

In breve, l’evento Expired è gestito così:

private void TrialManager_Expired(object sender, SlickThought.Phone.TrialExpirationEventArgs e)
{
    string message = LocalizationManager.GetString("TrialExpiredMessage");
    MessageBox.Show(message, "Trial", MessageBoxButton.OK);
    App.Quit();
}

 

Il metodo Quit è definito così:

internal class QuitException : Exception { }

static void Quit()
{
    throw new QuitException();
}

 

L’ultima cosa importante da fare è ritoccare l’evento…

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
    if (e.ExceptionObject is QuitException)
        return;

    if (System.Diagnostics.Debugger.IsAttached)
        System.Diagnostics.Debugger.Break();
}

 

Morale: se viene scatenata una QuitException(), viene eseguito un banale return, che fa chiudere l’applicazione senza messaggi di errore.

In questo modo l’utente vede apparire un messaggio: quando clicca su Ok per chiudere la MessageBox l’applicazione si chiude e deve riavvarla.

Ed il gioco è fatto!

Send to Kindle
.NET World

Una comoda libreria per gestire le trial delle nostre applicazioni WP7

Le applicazioni che noi developers possiamo scrivere per Windows Phone 7 sono fondamentalmente di tre tipi:

  1. gratuite
  2. a pagamento, con supporto della trail
  3. a pagamento, senza trial

Per quelle gratuite basta andare nel Marketplace, trovare l’applicazione e cliccare Installa quando richiesto. Per quelle a pagamento con trail, invece, potete decidere se acquistarle subito, oppure prima provarle. Dal nostro di punta di vista, ci affidiamo alla classe LicenseInformation:

LicenseInformation license = new LicenseInformation();

 

Questa classe espone un metodo IsTrial() che ci dice se la nostra applicazione sta girando in modalità trial oppure no. Qui c’è uno svantaggio: se eseguiamo la nostra applicazione da VS2010, il metodo IsTrial() ci darà sempre false, e quindi facciamo un po’ di fatica a testare l’applicazione con le limitazioni che abbiamo deciso di inserire (pause, banner pubblicitari, chiusura dopo un tot numero di minuti, etc.). Come probabilmente avrete letto da qualche altra parte, si tratta di wrappare la classe LicenseInformation, in modo tale possiamo impostare manualmente tramite un semplice booleano True/False la modalità trial oppure no.

Io ho trovato molto utile la libreria SlickThought.Phone.dll, spiegata a questo indirizzo.

Dopo averla aggiunta nei riferimento, è possibile specificare nello App.xaml:

<Application.ApplicationLifetimeObjects>
    <trial:TrialManager RunAsTrial="True" Expired="TrialManager_Expired" >
        <trial:TrialManager.ApplicationPolicy>
            <trial:UsageExpirationPolicy MaxUsage="5"/>
        </trial:TrialManager.ApplicationPolicy>
    </trial:TrialManager>
    <shell:PhoneApplicationService 
        Launching="Application_Launching" Closing="Application_Closing" 
        Activated="Application_Activated" Deactivated="Application_Deactivated"/>
</Application.ApplicationLifetimeObjects>

 

Il namespace trial è definito come

xmlns:trial="clr-namespace:SlickThought.Phone;assembly=SlickThought.Phone"

 

All’interno del nostro codice possiamo utilizzare la classe TrialManager in questo modo:

if (TrialManager.Current.IsTrial())
{
    // Fai qualcosa che solo la trail dovrebbe fare
}

 

Se il parametro RunAsTrial=”True”, entra in gioco la policy che inietta nella nostra applicazione un particolare comportamento, che fa scadere la trial secondo due diverse modalità: a tempo oppure a numero di avvii dell’applicazione stessa. Le policy sono solamente due: nell’App.xaml definito qui sopra diciamo che l’utente può avviare l’applicazione al massimo per cinque volte. Quando la trial scade, si scatena l’esecuzione dell’evento TrialManager_Expired, all’interno del quale possiamo fare quello che vogliamo (visualizzare un messaggio, chiudere l’app, etc.).

Un app.xaml definito in questo modo:

<trial:TrialManager RunAsTrial="False" Expired="TrialManager_Expired" TimerInterval="00:01:00">
    <trial:TrialManager.ApplicationPolicy>
        <trial:TimeExpirationPolicy TrialDuration="00:01:00" Mode="Lifetime" />
    </trial:TrialManager.ApplicationPolicy>
</trial:TrialManager>

 

scatena l’evento TrialManager_Expired dopo un minuto che l’applicazione è stata avviata, e così via.

Ultima precisazione: se il parametro RunAsTrial=”False”, la chiamata a IsTrial() viene reindirizzata alla classe LicenseInformation del .NET Framework. Ecco quindi trovato il nostro wrapper, che ci permettere di switchare da una modalità all’altra, e quindi di testare la nostra app nella modalità full e nella modalità trial attiva.

Send to Kindle
.NET World

Qualche info su “Silverlight for Windows Phone 7 Toolkit”–November 2010

Il toolkit in oggetto è disponibile su CodePlex a questo indirizzo. E’ un componente molto interessante, oserei dire quasi indispensabile, per chiunque voglia sviluppare applicazioni per Windows Phone 7. Oggi pomeriggio ho dato un’occhiata ai vari controlli aggiuntivi; davvero stupefacente, perchè praticamente a costo zero usate controlli un po’ più in linea con quelle che sono le guidelines, e vi uniformate all’interfaccia utente che WP7 mette già a disposizione.

Controllo ToggleSwitch
In pratica, è una CheckBox standard ri-templizzata, in modo tale da apparire come un interruttore/switch. In pratica, è lo stesso controllo che con WP7 vedete sotto Impostazioni –> toni & suonerie, per attivare le suonerie e la vibrazione. Dal punto di vista developer, è una vera e propria CheckBox, quindi dispone della proprietà IsChecked che può essere messa in binding con una qualsiasi proprietà booleana, etc. etc.

ToggleSwitch

<ctl:ToggleSwitch 
    Header=”Mantieni centrato l’aereo”
    IsChecked=”True” />

Notare che per creare un ToggleSwitch come si deve, bisogna usare la proprietà Header per dare un titolo, che appare in un carattere più piccolo. Non usate la proprietà Content, che invece reimposta quello che appare quando si sposta la levetta. Per default, il ToggleSwitch mostra On/Off. Come tutti i controlli, anche questo può essere personalizzato come si vuole, con colori, template, bordi, font, etc. etc.

Controllo DatePicker
Come dice il nome, è un controllo che visualizza un DateTime nel formato in cui volete voi. Cliccandoci sopra, il controllo naviga in una pagina che permette di impostare una nuova data, scegliendo giorno/mese/anno. E’ esattamente il controllo che WP7 vi fa usare quando reimpostate l’ora di sistema.

DatePicker01

DatePicker02

<ctl:DatePicker
    ValueStringFormat=”{}{0:d}”
    Header=”Data di nascita” />

Ancora una volta, ho utilizzato la proprietà Header per dare un titolo al controllo. Ho usato la proprietà ValueStringFormat per specificare una stringa di formattazione per la data. Specificare “d” significa data in formato breve; specificare “D” significa data in formato esteso. Notare che la Culture con cui viene visualizzata la data dipende dalla lingua del sistema operativo (oppure quella che avete messo in Impostazioni –> paese e lingua). La proprietà PickerPageUri – che non ho avuto tempo di testare – dovrebbe servire ad impostare la pagina XAML per scegliere la data – nel caso in cui quella predefinita non vi andasse bene (ad esempio, notare nell’immagine qui sopra il titolo della pagina “Choose Date”, nonostante l’emulatore stia girando in italiano).

Controllo TimePicker
Tutto come sopra, però per l’ora.

TimePicker01

TimePicker02

<ctl:TimePicker
    ValueStringFormat=”{}{0:t}”
    Header=”Imposta l’ora corrente” />

 

TransitionService – transizioni ed effetti durante la navigazione delle pagine
Tutto molto semplice anche in questo caso. In pratica, voi utilizzate la solita classe NavigationService per spostarvi da una pagina all’altra, ma un engine dietro le quinte le fa apparire con diversi effetti: rotazione, slider, etc. etc. Il tutto è molto semplice e naturale:

<ctl:TransitionService.NavigationInTransition>
    <ctl:NavigationInTransition>
        <ctl:NavigationInTransition.Backward>
            <ctl:RollTransition />
        </ctl:NavigationInTransition.Backward>
        <ctl:NavigationInTransition.Forward>
            <ctl:RollTransition />
        </ctl:NavigationInTransition.Forward>
    </ctl:NavigationInTransition>
</ctl:TransitionService.NavigationInTransition>

 

Il namespace ctl è dichiarato come:

xmlns:ctl="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

 

Il blocco XAML qui sopra va inserito appena dopo il nodo <phone:PhoneApplicationPage>. In pratica, si specifica l’effetto di transizione da applicare quando si raggiunge questa pagina, in ambedue i sensi (cioè…sia attraverso un back che un forward). Ovviamente si può anche specificare l’effetto quando si lascia questa pagina: è sufficiente usare NavigationOutTransition al posto di NavigationInTransition. Gli effetti disponbili attualmente sono:

  • RollTransition
  • RotateTransition
  • SlideTransition
  • SwiveTransition
  • TurnstileTransition

L’ultimo accorgimento per far funzionare le transizioni è andare a toccare il code-behind nel file App.xaml.cs:

private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;
    RootFrame = new TransitionFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;
    phoneApplicationInitialized = true;
}

 

L’oggetto RootFrame, che è dichiarato come PhoneApplicationFrame, deve essere creato con il tipo TransitionFrame, altrimenti nisba.

Ovviamente il tipo TransitionFrame è un tipo dichiarato e definito nell’assembly Microsoft.Phone.Controls.Toolkit, ed eredita da PhoneApplicationFrame.

AutoCompleteBox

A metà strada tra una TextBox ed una ComboBox, controllo imperdibile se volete facilitare la scelta di una voce da un elenco predefinito. Purtroppo il mio tempo per questa sera terminato, lascio approfondire a voi la cosa!

Smile

Send to Kindle