Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Wicked Code

3 suggerimenti importanti per lo sviluppo Silverlight

Jeff Prosise

Download codice disponibile dalla Raccolta di codice di MSDN
Selezionare il codice in linea

Contenuto

Caricamento di assembly su richiesta
Rendering just-in-time
Evitare di dipendenze internazionali
L'attivazione di una nuova pagina

Come è possibile scrivere, 2 Silverlight è attiva disattiva le pressioni e agli sviluppatori vengono visualizzati un'occhiata prima di ciò che molti ritengono rappresenta il futuro di programmazione Web.Un proponent Silverlight o individuare ulteriori allure in concorrenti tecnologie quali Adobe flessibile, è interessante vedere alternative in formato HTML, JavaScript, e AJAX emersi e ottenere mindshare per la creazione di applicazioni Web.E con Microsoft già disco rigido in ufficio in Silverlight 3, il futuro è non sembrava più luminosa.

Come è vero qualsiasi piattaforma, la strada a diventare uno sviluppatore di Silverlight non è senza potholes pochi.Non tutti sanno, ad esempio, che molte chiamate a XamlReader.Load che test correttamente sui PC negli Stati Uniti avranno esito negativo in PC in altri paesiCi ha si rende conto che motore di rendering del Silverlight è vincolato intervistati thread dell'interfaccia utente e che questo fatto profoundly è possibile a impatto la struttura del codice?Non tutti sanno che è possibile ridurre le dimensioni dei file XAP in modo dinamico il caricamento degli assembly, ma che in questo modo senza perdere il vantaggio della tipizzazione forte richiede conoscenza di elementi interni CLRSe questo intrigues è, leggere.Ho suggerimenti e indicazioni per condividere che verranno apportare un po'meno bumpy vita con Silverlight, apportare una migliore e più informata troppo programmatore di Silverlight.

Per ulteriori informazioni sul contenuto di Silverlight per il recapito più veloce di imballaggio, vedere ilMese di gennaio 2009 puntata di Cutting Edge.

Caricamento di assembly su richiesta

Uno dei hallmarks di un'applicazione ben progettata di Silverlight è un piccolo file XAP noto formalmente come un pacchetto applicazioni.XAP file dimensioni tutti troppo spesso swell per unmanageable come risultato di risorse incorporate (soprattutto immagini e i riferimenti ad assembly.Maggiore sarà il file XAP, il più necessario per scaricare e se aumenta troppo grande, Silverlight può potranno caricarlo.

Anche grandi applicazioni inserite nei file di piccole dimensioni XAP se si combinano le risorse e gli assembly che l'applicazione utilizza attenzione a e mantenere quelli che può essere caricato in ritardo o scaricare su richiesta nel server Web.È possibile utilizzare WebClient o altre classi nello stack di rete di Silverlight per scaricare ulteriori risorse e gli assembly dopo che il pacchetto di applicazioni è stato scaricato.In genere preferibile per ottenere l'interfaccia utente dell'applicazione fino e in esecuzione rapidamente e quindi le avvio asincrona richieste di rete per i cespiti aggiuntive che è necessario per registrare un 100MB XAP più file e imporre utente dedicare cinque minuti in attesa di un indicatore di stato raggiungere il 100 %.

Risorse su richiesta durante il caricamento in Silverlight tende a essere semplice e semplice.Il frammento di codice nella Figura 1 , ad esempio, download di un'immagine JPEG distribuita nel sito di origine e lo visualizza assegnando i bit scaricati un'immagine XAML denominata MyImage.

Figura 1 download un'immagine dal sito di origine

WebClient wc = new WebClient();
wc.OpenReadCompleted +=
    new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri("JetCat.jpg", UriKind.Relative));
  ...
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        BitmapImage bi = new BitmapImage();
        bi.SetSource(e.Result);
        MyImage.Source = bi;
    }
}

Su richiesta caricamento degli assembly tende a essere più difficile, tuttavia. A prima vista sembra facile: Utilizzare WebClient per scaricare l'assembly e AssemblyPart.Load per caricarlo nel dominio applicazione. Il problema è che il compilatore JIT di Silverlight può ottenere in modo iniziali molti sviluppatori per ritenere che è possibile scaricare assembly su richiesta e riprodurre i vantaggi della tipizzazione forte, troppo. In realtà, tuttavia, è possibile eseguire entrambe. Ma è necessario sapere cosa si sta operazione e avere una conoscenza di base del funzionamento del caricamento degli assembly di Common Language RUNTIME.

Per dimostrare, si prenda in considerazione il seguente codice:

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    Calendar cal = new Calendar();
    cal.Width = 300.0;
    cal.Height = 200.0;
    cal.SelectedDatesChanged += new
        EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
    LayoutRoot.Children.RemoveAt(0);
    LayoutRoot.Children.Add(cal);
}

fig02.gif

Nella figura 2, mantenere un assembly compreso il XAP

È un pulsante fare clic su gestore, che crea un controllo calendario e lo aggiunge alla scena XAML in modo dinamico. (Eliminato anche il pulsante che ha generato l'evento, si presuppone che sia l'elemento 0th nell'insieme di elementi figlio del LayoutRoot.) Perché calendario è implementato in System.Windows.Controls.dll, che non è incorporata nel Silver­light plug-in invece parte BCL esteso, questo codice funziona bene purché aggiungere un riferimento a System.Windows.Controls.dll al progetto. Il riferimento causa System.Windows.Controls.dll da includere nel file XAP e automaticamente caricato al dominio applicazione.

A questo punto si supponga di essere intelligenti e solo caricamento System.Windows.Controls.dll la se è necessario, ovvero se l'utente fa clic sul pulsante. In modo che è possibile aggiungere un riferimento a System.Windows.Controls.dll al progetto per soddisfare il compilatore (in caso contrario, il compilatore non compila un riferimento al calendario perché il compilatore non è alcuna idea che il tipo di calendario è) e, nella finestra proprietà di Visual Studio impostare copia locale proprietà della System.Windows.Controls.dll false per evitare di essere incorporata nel file XAP (come nella Figura 2 ).

Successivamente, è possibile distribuire una copia di System.Windows.Controls.dll con il file XAP nella cartella di ClientBin dell'applicazione sul server. Infine, ristrutturare il codice come illustrato nella Figura 3 . Fare clic sul pulsante su gestore ora System.Windows.Controls.dll download dal server Web, si carica nel dominio applicazione con Assembly­Part.Load e crea un'istanza di un controllo calendario.

Nella figura 3 su richiesta assembly caricamento che non attività

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    wc.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
    wc.OpenReadAsync(new Uri("System.Windows.Controls.dll",
        UriKind.Relative));
}

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null) 
    {
        // Load the downloaded assembly
        AssemblyPart part = new AssemblyPart();
        part.Load(e.Result);

        // Create a Calendar control
        Calendar cal = new Calendar();
        cal.Width = 300.0;
        cal.Height = 200.0;
        cal.SelectedDatesChanged += new
           EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
        LayoutRoot.Children.RemoveAt(0);
        LayoutRoot.Children.Add(cal);
    }
}        

Sembra ragionevole e il codice viene compilato solo fine, ma in fase di esecuzione, il gestore di fare clic su genera un'eccezione come quello nella Figura 4 . Il codice compilato è valido. Codice che genera eccezioni non. In modo che cosa offre? Il messaggio di errore sembra per indicare che Common Language RUNTIME sta tentando di caricare System.Windows.Controls.dll, ma non necessariamente poiché si sta caricare a livello di programmazione.

Questo è un ottimo esempio di un caso in cui conoscenza di elementi interni CLR può apportare è un migliore programmatore di Silverlight. Il problema è che quando il compilatore JIT compila il metodo wc_OpenReadCompleted, analizza il metodo, rileva che faccia riferimento a un tipo denominato calendario e tenta di caricare System.Windows.Controls.dll in modo che il riferimento può essere risolto.

fig04.gif

Nella figura 4 È!

Sfortunatamente, ciò si verifica prima il metodo ancora in esecuzione, pertanto non si ottiene la possibilità di chiamare AssemblyPart.Load. Si tratta di un problema chicken ed egg classico. È necessario chiamare AssemblyPart.Load per caricare l'assembly, ma prima di chiamare il compilatore JIT intervenes e tenta di caricare per l'utente. Il tentativo non riesce poiché non è System.Windows.Controls.dll nel pacchetto di applicazioni.

Questo è il punto in cui i programmatori molti generare i le mani e uno concludere che su richiesta caricamento degli assembly non in Silverlight o ricorrere alla reflection per creare un'istanza il tipo di calendario:

AssemblyPart part = new AssemblyPart();
Assembly a = part.Load(e.Result);
Object cal = (Object)a.CreateInstance("Calendar");

Questo approccio funziona, ma è clumsy. È impossibile eseguire il cast del riferimento restituito dalla Assembly.Create­Instance a un calendario, poiché tale così causerebbe il compilatore JIT tentare di caricare l'assembly prima del metodo eseguito. E se è impossibile eseguire il cast al calendario, quindi metodi del controllo, proprietà ed eventi necessario accedervi tramite reflection, troppo. Il codice aumenta rapidamente così difficoltoso che è tentato solo assegnare in incorporare System.Windows.Controls.dll nel pacchetto di applicazioni e live con la maggiore dimensione XAP.

La buona notizia è che è possibile combinare il caricamento di assembly dinamico e la tipizzazione forte. Ristrutturare semplicemente il codice lungo le linee di figura 5 . Osservare che wc_OpenReadCompleted fa riferimento non al tipo di calendario, un metodo separato denominato CreateCalendar sono stati spostati tutti i riferimenti. Inoltre, CreateCalendar viene attribuito in modo che il compilatore JIT non tenterà in linea il metodo. (Se inline dovesse verificarsi, sarebbe sarà nuovamente a destra in cui è stata avviata poiché wc_OpenReadCompleted potrebbe contenere un riferimento implicito nel tipo di calendario.) A questo punto il compilatore JIT non verificare se è stato caricato System.Windows.Controls.dll finché non viene chiamato CreateCalendar, quel momento, è stata già caricata nel dominio applicazione.

Nella figura 5 su richiesta assembly caricamento che Works

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    wc.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
    wc.OpenReadAsync(new Uri("System.Windows.Controls.dll",
        UriKind.Relative));
}

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        // Load the downloaded assembly
        AssemblyPart part = new AssemblyPart();
        part.Load(e.Result);

        // Create a Calendar control
        CreateCalendar();
    }
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void CreateCalendar()
{
    Calendar cal = new Calendar();
    cal.Width = 300.0;
    cal.Height = 200.0;
    cal.SelectedDatesChanged += new
        EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
    LayoutRoot.Children.RemoveAt(0);
    LayoutRoot.Children.Add(cal);
}

Approfondimenti: il rendering e il thread dell'interfaccia utente

Si noti che Jeff detto che un aspetto di Silverlight non ottenere numerose l'attenzione sono il fatto che completa il rendering in Silverlight viene eseguito sul thread dell'interfaccia utente dell'applicazione e se si hog thread dell'interfaccia utente, è impedire qualsiasi rendering verifichi. Poiché WPF fornisce un thread di rendering, è probabile che surprising che Silverlight non. Può accadere che si desidera conoscere il motivo.

La decisione fornita verso il basso per un compromesso tra overhead di sistema e decoupling framerates. Con Silverlight, è possibile causa di un peso più chiaro sul thread approccio e non è stata isolare il codice dell'applicazione dal sistema di rendering. Ciò significa che è possibile eseguire più l'animazione (ad esempio essere basato layout animazione o codice personalizzato eseguito) ed esiste latenza minima e generali di ottenere il sistema di rendering. Il lato basso è il che caso troppo, possono interferire con operazioni quali la riproduzione di video.

Ciò detto, Silverlight il rendering verrà sfruttare l'elaborazione multicore e utilizzare più thread per velocizzare il rendering per esso. Pertanto, il rendering è raramente "sul thread", ma viene sincronizzata con l'applicazione per evitare la sincronizzazione e copie dei dati.

—Ashraf Michail, architetto software principale, Silverlight

Per inciso, se si trattasse Windows Presentation Foundation (WPF), anziché di Silverlight, è impossibile risolvere il problema in modo più elegante registrando un gestore eventi AppDomain.AssemblyResolve e caricamento System.Windows.Controls.dll presenti. AppDomain.AssemblyResolve esiste in Silverlight, ma di attribuire SecurityCritical, ovvero il codice utente non può registrare gestori per tale.

Nella figura 5 presuppone che compreso nel progetto un riferimento a System.Windows.Controls.dll, ma impostare copia locale su false (vedere la Figura 2 ) e distribuito l'assembly in cartella ClientBin. Per dimostrare il funzionamento, scaricare l'applicazione OnDemandAssemblyDemo che accompagna questo articolo e fare clic sul pulsante denominata Crea controllo Calendar. Un controllo di calendario viene visualizzato del pulsante. Significativamente, OnDemandAssemblyDemo.xap non contiene una copia di System.Windows.Controls.dll, è possibile verificare facilmente aprendo il file XAP con WinZip. Chiave. Breaker tempeste ottimale per la festa di Silverlight successiva verrà eseguita.

Rendering just-in-time

Un aspetto di Silverlight non ottenere numerose premere è il fatto che completa il rendering in Silverlight viene eseguito sul thread dell'interfaccia utente dell'applicazione e se si hog thread dell'interfaccia utente, è impedire qualsiasi rendering da venga eseguito. Ciò significa che si desidera evitare cicli a esecuzione prolungata sul thread UI se si sta modificando una scena XAML in tale ciclo o se le animazioni sono hanno luogo nello stesso momento.

Sembra semplice, evitare cicli a esecuzione prolungata sul thread UI, ma in pratica, può disporre di un impatto efficaci sul codice che si scrive. Considerare l'applicazione denominata OpenFileDialogDemo, pictured nella Figura 6 . Viene illustrato come utilizzare classe OpenFileDialog di Silverlight per consentire all'utente il proprio disco rigido per i file di immagine e quindi carica le immagini in oggetti immagine XAML. Eseguire l'applicazione, fare clic sul pulsante Apri nella parte superiore della pagina, selezionare più file di immagine (la più grande la migliore), quindi pulsante Apri il OpenFileDialog.

Nella figura 6 OpenFileDialogDemo in azione

Si noterà che uno alla volta, le immagini selezionata pop in scena utilizzando gli oggetti creati dinamicamente con XamlReader.Load e assumere posizioni casuali nella pagina. Dopo le immagini vengono visualizzate, è possibile fare clic su per renderli di tornare in primo piano e anche utilizzare il mouse per trascinare le intorno alla pagina.

Nonostante la semplicità apparente OpenFileDialogDemo offre una pratica lezione del corso judicious con il thread dell'interfaccia utente. Quando HO scritto originariamente il codice per visualizzare il OpenFileDialog e caricare i file di immagine, È necessario strutturato simile il frammento di codice illustrato nella Figura 7 è. Una volta che l'utente ha chiuso la finestra di dialogo, un ciclo foreach semplice scorre i file selezionati e li carica uno alla volta.

Nella figura 7 approccio semplice per il caricamento dei file di immagine

OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
    "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
ofd.FilterIndex = 1;
ofd.Multiselect = true;

if ((bool)ofd.ShowDialog())
{
    foreach (FileInfo fi in ofd.Files)
    {
        using (Stream stream = fi.OpenRead())
        {
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            GetNextImage().Source = bi;
        }
    }
}

Sfortunatamente, nessuna delle immagini visualizzati sullo schermo fino a quando non sono stati caricati tutti i. Il ritardo non era una quantità di grandi se l'utente selezionato uno o due file di immagine, ma era intolerable se sono stati selezionati file 40 o 50. In breve, l'applicazione è in non di soddisfare i requisiti minimi che è possibile impostare, perché volevo "POP" sullo schermo come fossero caricare le immagini. Ottenerlo? POP. POP. POP.

Il problema è stato ovviamente, che il ciclo foreach viene eseguito sul thread UI e durante il ciclo di esecuzione, Silverlight Impossibile rendere le immagini come sono state aggiunte per la scena, che deve era volta nuovamente il passaggio, eseguire un attendere e ristrutturare il codice per semplificare in grado di eseguire il rendering delle immagini in modo tempestivo.

Nella figura 8 viene illustrata una soluzione al problema. Foreach modificato ciclo non esegue alcuna operazione più di aggiungere oggetti FileInfo un System.Collections.Generic.Queue. In questo modo il ciclo eseguire rapidamente e mano nuovo controllo a Silverlight in modo da può ottenere verso il basso per l'azienda del rendering. Forse l'aspetto più interessante del codice restructured è come dequeues e si elabora gli oggetti FileInfo in risposta a eventi CompositionTarget.Rendering.

Figura il modo migliore 8 A caricamento i file di immagine

private Queue<FileInfo> _files = new Queue<FileInfo>();
 ...
public Page()
{
    InitializeComponent();

    // Register a handler for Rendering events
    CompositionTarget.Rendering +=
        new EventHandler(CompositionTarget_Rendering);
}
 ...
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
    "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
ofd.FilterIndex = 1;
ofd.Multiselect = true;

if ((bool)ofd.ShowDialog())
{
    // Reset the queue
    _files.Clear();

    // Place each FileInfo in a queue
    foreach (FileInfo fi in ofd.Files)
    {
        _files.Enqueue(fi);
    }
}
 ...
private void CompositionTarget_Rendering(Object sender, EventArgs e)
{
    if (_files.Count != 0)
    {
        FileInfo fi = _files.Dequeue();
        using (Stream stream = fi.OpenRead())
        {
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            GetNextImage().Source = bi;
        }
    }
}

CompositionTarget.Rendering è un per frame rendering callback tradizionalmente utilizzato per implementare cicli giochi. È stato prestito di WPF e mostrato i tardi nel ciclo di sviluppo 2 Silverlight. L'evento viene generato ogni volta che Silverlight è pronto a re-render la scena.

OpenFileDialogDemo registra un gestore eventi Composition­Target.Rendering (CompositionTarget_Rendering) e dequeues un oggetto FileInfo, convertirlo in un'immagine XAML, ogni volta che il gestore viene chiamato. Il risultato? Le immagini estrae sullo schermo come siano caricati poiché Silverlight è ora la possibilità di aggiornare la scena dopo l'aggiunta di ogni nuova immagine. Si tratta di come è strutturato il ciclo foreach nella versione finale di OpenFileDialogDemo ed è uno dei motivi per cui quando è stata eseguita, si è visto le immagini visualizzate sullo schermo uno alla volta anziché tutti contemporaneamente.

Si desidera fare attenzione a non overuse CompositionTarget.Rendering. Se OpenFileDialogDemo animazioni in esecuzione come il aggiunto le immagini la scena, le animazioni sarebbero probabilmente stutter poiché ciascun frame potrebbe essere ritardata dalla quantità di tempo richiesto per caricare i bit di immagine e assegnarli a un oggetto immagine. Ma quando è necessario, è necessario e OpenFileDialogDemo è un buon esempio di un utilizzo consentito di CompositionTarget.Rendering—indeed, qui l'obiettivo sarebbe difficile da eseguire in caso contrario.

Evitare di dipendenze internazionali

Un suggerimento finale considerata Utilizzo XamlReader.Load per creare oggetti XAML in modo dinamico. Si potranno individuare la novità errato con questo codice?

Rectangle rect = (Rectangle)XamlReader.Load(
    String.Format(
        "<Rectangle xmlns=\"https://schemas.microsoft.com/client/2007\" " +
        "Width=\"{0}\" Height=\"{1}\" Stroke=\"Black\" Fill=\"Yellow\" />",
        100.5, 100.0
    )
); 

Se è riconosciuto che questo codice funziona sulla maggior parte dei PC negli Stati Uniti ma avrà esito negativo sulla maggior parte dei PC in Europa e in altre parti del mondo, assegnare l'automatica un pat sul retro. Per dimostrare, configurare innanzitutto il sistema operativo per visualizzare i numeri, valute, date e ore in stati uniti formattare se non è configurato già in questo modo. (In Vista, selezionare il formato scheda della finestra Opzioni internazionali e della lingua accessibile tramite il Pannello di controllo.) Eseguire la chiamata a XamlReader.Load e verificare che la chiamata verrà eseguita correttamente. A questo punto modificare il formato internazionali per Francese ed eseguire nuovamente la chiamata. Questa volta XamlReader.Load genera un'eccezione: "valore attributo non valido 100,5 per la proprietà width" ( Figura 9 ). Il problema è che i numeri decimali, ad esempio 100.5 vengono scritti 100,5 si noti che il punto e virgola della virgola decimale in molti paesi. E poiché String.Format invece di si basa sulle impostazioni internazionali in host PC, diventa "100,5 100.5 decimale. Sfortunatamente, XamlReader.Load non sa come rendere di 100,5, in modo che viene generata un'eccezione.

Nella Figura 9 eccezione generata da XamlReader.Load

Il codice seguente mostra il modo corretto per chiamare XamlReader.Load, in modo che funziona su qualsiasi PC che esegue Silverlight:

Rectangle rect = (Rectangle)XamlReader.Load(
    String.Format(
        CultureInfo.InvariantCulture,
        "<Rectangle xmlns=\"https://schemas.microsoft.com/client/2007\" " +
        "Width=\"{0}\" Height=\"{1}\" Stroke=\"Black\" Fill=\"Yellow\" />",
        100.5, 100.0
    )
);

Il primo parametro passato a String.Format è un oggetto CultureInfo che fa riferimento la lingua invariabile. XamlReader.Load prevede stringhe indipendente dalla lingua, in modo che l'utilizzo CultureInfo.InvariantCulture assicura String.Format genera formattati correttamente i valori decimali (, nonché formattate correttamente le date e ore) se si sta utilizzando le. L'applicazione OpenFileDialogDemo in della sezione precedente non quasi utilizza questa tecnica per garantire che i proprio chiamate a XamlReader.Load funzionano indipendentemente dalle impostazioni internazionali.

Se le stringhe si passa a XamlReader.Load includono valori decimali generati da String.Format, sempre utilizzare CultureInfo.InvariantCulture per ottenere la corretta formattazione. Dopo l'applicazione è in esecuzione nella circolazione, è dovuta l'imposta eseguire in numerose impostazioni internazionali.

L'attivazione di una nuova pagina

Se si sta iniziando a utilizzare Silverlight e si è un esperto sviluppatore .NET, si conosce già fino al 90 % di ciò che si desidera conoscere. Ma Silverlight porta nuances .NET che è necessario comprendere.

In termini di pagine, molti lettori hanno richiesto quando desidera aggiornare il Framework di pagina, turno di Silverlight 1.0presentati in maggio 2008 per Silverlight 2. Anche il porting è completata. È possibile visualizzare un'applicazione di esempio utilizza il framework aggiornato al Wintellect.com/Silverlight/pageturndemo/, ed è possibile scaricare il codice sorgente da Wintellect.com/Downloads/PageTurnDemo2.zip.

Il framework di attivazione pagina vive in PageTurn.cs e il codice in Page.xaml.cs illustra tutti funzionamento. L'API è simile alla versione Silverlight 1.0 in C# anziché JavaScript) e dopo aver apportato modifiche per rendere la migliore di pagina. È incluso un evento PageTurned è generato dal framework ogni volta che un turno di pagina è stata completata in modo è possibile aggiornare l'interfaccia utente, pronunciare, per visualizzare il numero di pagina corrente.

Inviare domande e commenti per Luca a Wicked@Microsoft.com.

Jeff Prosise è consulente redattore di MSDN Magazine e l'autore di molti libri, tra cui Programmare Microsoft .NET (Microsoft Press, 2002). Egli è anche cofondatore di Wintellect ( Wintellect.com), confermato che una società di consulenza software e formazione specializzata in Microsoft. NET. Contattare Jeff in Wicked@Microsoft.com.