Migliorare le Xamarin.Forms prestazioni dell'app

Evolvere 2016: ottimizzazione delle prestazioni delle app con

Le prestazioni insoddisfacenti di un'applicazione si manifestano in molti modi. Può sembrare che l'applicazione non risponda, lo scorrimento diventa lento e si riduce la durata della batteria del dispositivo. Tuttavia, l'ottimizzazione delle prestazioni implica più della semplice implementazione di codice efficiente. Deve essere considerata anche l'esperienza dell'utente in termini di prestazioni dell'applicazione. Ad esempio, assicurarsi che le operazioni vengano eseguite senza impedire all'utente di eseguire altre attività può contribuire a migliorare l'esperienza dell'utente.

Esistono molte tecniche per aumentare le prestazioni e le prestazioni percepite delle Xamarin.Forms applicazioni. Nel loro insieme, queste tecniche possono ridurre notevolmente il carico di lavoro di una CPU e la quantità di memoria usata da un'applicazione.

Nota

Prima di leggere questo articolo, è consigliabile vedere Prestazioni multipiattaforma, che illustra le tecniche non specifiche di una piattaforma che consentono di migliorare l'utilizzo della memoria e le prestazioni delle applicazioni compilate con la piattaforma Xamarin.

Abilitare il compilatore XAML

Se necessario, il linguaggio XAML può essere compilato direttamente nel linguaggio intermedio (IL) con il compilatore XAML (XAMLC). XAMLC offre alcuni vantaggi:

  • Esegue il controllo del codice XAML in fase di compilazione, notificando all'utente eventuali errori.
  • Rimuove parte del tempo di carico e di creazione dell'istanza per gli elementi XAML.
  • Consente di ridurre le dimensioni del file dell'assembly finale non includendo più i file XAML.

XAMLC è abilitato per impostazione predefinita nelle nuove Xamarin.Forms soluzioni. ma potrebbe essere necessario abilitarlo nelle soluzioni precedenti. Per altre informazioni, vedere l'articolo sulla compilazione XAML.

Usare binding compilati

Le associazioni compilate migliorano data binding prestazioni nelle applicazioni risolvendo le espressioni di associazione in fase di compilazione, anziché in Xamarin.Forms fase di esecuzione con reflection. La compilazione di un'espressione di binding genera codice compilato che in genere risolve un binding da 8 a 20 volte più velocemente che usando un binding classico. Per altre informazioni, vedere Binding compilati.

Ridurre i binding non necessari

Non usare i binding per il contenuto che può essere impostato facilmente in modo statico. Non vi è alcun vantaggio nell'associare i dati che non richiedono l'associazione poiché i binding non hanno un costo contenuto. Ad esempio, Button.Text = "Accept" l'impostazione ha un sovraccarico inferiore rispetto Button.Text all'associazione a una proprietà viewmodel string con valore "Accept".

Usare renderer veloci

I renderer veloci riducono i costi di aumento e rendering dei controlli Xamarin.Forms in Android appiattindo la gerarchia di controlli nativi risultante. Questo migliora ulteriormente le prestazioni poiché vengono creati meno oggetti e quindi si ottiene una struttura ad albero visuale meno complessa e un minore consumo di memoria.

A Xamarin.Forms partire dalla versione 4.0, tutte le applicazioni di destinazione usano FormsAppCompatActivity renderer veloci per impostazione predefinita. Per altre informazioni, vedere Renderer veloci.

Abilitare la traccia di avvio in Android

La compilazione Ahead Of Time (AOT) in Android riduce al minimo l'utilizzo della memoria e il sovraccarico all'avvio dell'applicazione JIT (Just-In-Time), ma comporta la creazione di un pacchetto dell'applicazione Android molto più grande. In alternativa, è possibile usare la traccia di avvio, che, rispetto alla compilazione AOT convenzionale, offre un compromesso tra le dimensioni del pacchetto dell'applicazione Android e il tempo di avvio.

Anziché compilare la maggior parte possibile dell'applicazione nel codice non gestito, la traccia di avvio compila solo il set di metodi gestiti che rappresentano le parti più costose dell'avvio dell'applicazione in un'applicazione Xamarin.Forms vuota. Questo approccio, rispetto alla compilazione AOT convenzionale, comporta una riduzione delle dimensioni del pacchetto dell'applicazione Android, offrendo allo stesso tempo miglioramenti simili all'avvio.

Abilitare la compressione dei layout

La compressione dei layout rimuove i layout specificati dalla struttura ad albero visuale, nel tentativo di migliorare le prestazioni del rendering della pagina. Il miglioramento delle prestazioni offerto varia in base alla complessità di una pagina, alla versione del sistema operativo in uso e al dispositivo in cui viene eseguita l'applicazione. Tuttavia, le prestazioni miglioreranno in modo più evidente nei dispositivi meno recenti. Per altre informazioni, vedere Compressione del layout.

Scegliere il layout corretto

Un layout in grado di visualizzare più elementi figlio, ma che ne include solo uno è dispendioso. Ad esempio, il codice riportato di seguito indica un elemento StackLayout con un singolo elemento figlio:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DisplayImage.HomePage">
    <StackLayout>
        <Image Source="waterfront.jpg" />
    </StackLayout>
</ContentPage>

Questo è dispendioso e l'elemento StackLayout deve essere rimosso, come illustra l'esempio di codice seguente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DisplayImage.HomePage">
    <Image Source="waterfront.jpg" />
</ContentPage>

Inoltre, non tentare di riprodurre l'aspetto di un layout specifico usando combinazioni di altri layout, poiché in questo modo vengono eseguiti calcoli di layout non necessari. Ad esempio, non tentare di riprodurre un layout Grid usando una combinazione di istanze StackLayout. Il codice seguente è un esempio di questa prassi non corretta:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Details.HomePage"
             Padding="0,20,0,0">
    <StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Name:" />
            <Entry Placeholder="Enter your name" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Age:" />
            <Entry Placeholder="Enter your age" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Occupation:" />
            <Entry Placeholder="Enter your occupation" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Address:" />
            <Entry Placeholder="Enter your address" />
        </StackLayout>
    </StackLayout>
</ContentPage>

L'operazione risulta dispendiosa poiché vengono eseguiti calcoli di layout non necessari. Il layout può essere invece realizzato in modo ottimale usando un elemento Grid, come illustrato nell'esempio di codice seguente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Details.HomePage"
             Padding="0,20,0,0">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <Label Text="Name:" />
        <Entry Grid.Column="1" Placeholder="Enter your name" />
        <Label Grid.Row="1" Text="Age:" />
        <Entry Grid.Row="1" Grid.Column="1" Placeholder="Enter your age" />
        <Label Grid.Row="2" Text="Occupation:" />
        <Entry Grid.Row="2" Grid.Column="1" Placeholder="Enter your occupation" />
        <Label Grid.Row="3" Text="Address:" />
        <Entry Grid.Row="3" Grid.Column="1" Placeholder="Enter your address" />
    </Grid>
</ContentPage>

Ottimizzare le prestazioni dei layout

Per ottenere le migliori prestazioni possibili del layout, attenersi alle seguenti indicazioni:

  • Ridurre la profondità delle gerarchie di layout specificando i valori delle proprietà Margin consentendo la creazione di layout con meno visualizzazioni con wrapping. Per altre informazioni, vedere l'articolo sulle proprietà Margin e Padding.
  • Quando si usa un elemento Grid, provare a verificare che sia impostato il minor numero possibile di righe e colonne sulla dimensione Auto. Ogni riga o colonna ridimensionata automaticamente causerà l'esecuzione di ulteriori calcoli di layout da parte del motore di layout. Usare invece righe e colonne con dimensioni fisse, se possibile. In alternativa, impostare righe e colonne in modo che occupino una quantità proporzionale di spazio con il valore di enumerazione GridUnitType.Star, purché l'albero padre segua queste linee guida relative al layout.
  • Non impostare le proprietà VerticalOptions e HorizontalOptions di un layout a meno che non sia richiesto. I valori predefiniti di LayoutOptions.Fill e LayoutOptions.FillAndExpand consentono la migliore ottimizzazione del layout. La modifica di queste proprietà ha un costo e consuma memoria, anche quando vengono impostate sui valori predefiniti.
  • Evitare di usare un elemento RelativeLayout quando possibile. Causa un notevole aumento del lavoro per la CPU.
  • Quando si usa un elemento AbsoluteLayout, evitare di usare la proprietà AbsoluteLayout.AutoSize ovunque sia possibile.
  • Se si usa un elemento StackLayout, verificare che sia impostato un solo elemento figlio su LayoutOptions.Expands. Questa proprietà garantisce che l'elemento figlio specificato occupi lo spazio più ampio che StackLayout è in grado di assegnare ed è dispendioso eseguire questi calcoli più di una volta.
  • Evitare di chiamare uno dei metodi della classe Layout, perché causano l'esecuzione di calcoli dispendiosi. È invece probabile che si possa ottenere un determinato comportamento del layout impostando le proprietà TranslationX e TranslationY. In alternativa, sottoclassare la classe Layout<View> per ottenere tale comportamento del layout.
  • Non aggiornare le istanze di Label più spesso di quanto necessario, poiché modificando le dimensioni dell'etichetta è possibile che l'intero layout della schermata venga ricalcolato.
  • Non impostare la proprietà Label.VerticalTextAlignment se non viene richiesto.
  • Impostare l'elemento LineBreakMode di qualsiasi istanza di Label su NoWrap quando possibile.

Usare la programmazione asincrona

La velocità di risposta complessiva dell'applicazione può essere migliorata e spesso si evitano colli di bottiglia delle prestazioni usando la programmazione asincrona. In .NET il modello asincrono basato su attività è lo schema progettuale consigliato per le operazioni asincrone. Tuttavia, l'uso errato del TAP può causare applicazioni non prestazioni. Pertanto, quando si usa il TAP, è necessario seguire le linee guida seguenti.

Fundamentals

  • Comprendere il ciclo di vita dell'attività, rappresentato TaskStatus dall'enumerazione . Per altre informazioni, vedere Significato di TaskStatus e Stato dell'attività.

  • Usare il metodo per attendere in modo asincrono il completamento di più operazioni asincrone, anziché Task.WhenAll singolarmente await una serie di operazioni asincrone. Per altre informazioni, vedere Task.WhenAll.

  • Usare il Task.WhenAny metodo per attendere in modo asincrono il completamento di una di più operazioni asincrone. Per altre informazioni, vedere Task.WhenAny.

  • Usare il Task.Delay metodo per produrre un oggetto che termina dopo Task l'ora specificata. Ciò è utile per scenari come il polling dei dati e il ritardo nella gestione dell'input utente per un periodo di tempo predeterminato. Per altre informazioni, vedere Task.Delay.

  • Eseguire operazioni della CPU sincrone intensive nel pool di thread con il Task.Run metodo . Questo metodo è un collegamento per il TaskFactory.StartNew metodo , con gli argomenti più ottimali impostati. Per altre informazioni, vedere Task.Run.

  • Evitare di provare a creare costruttori asincroni. Usare invece gli eventi del ciclo di vita o una logica di inizializzazione separata per eseguire correttamente await qualsiasi inizializzazione. Per altre informazioni, vedere Costruttori asincroni blog.stephencleary.com.

  • Usare il modello di attività lazy per evitare di attendere il completamento delle operazioni asincrone durante l'avvio dell'applicazione. Per altre informazioni, vedere AsyncLazy.

  • Creare un wrapper di attività per le operazioni asincrone esistenti, che non usano tap, creando TaskCompletionSource<T> oggetti . Questi oggetti ottengono i vantaggi della programmabilità e consentono di controllare la durata e il Task completamento dell'oggetto Task associato. Per altre informazioni, vedere Natura di TaskCompletionSource.

  • Restituire un oggetto , anziché restituire un oggetto atteso, quando non è necessario elaborare TaskTask il risultato di un'operazione asincrona. Si tratta di un'operazione più performante a causa di una minore commutazione di contesto eseguita.

  • Usare la libreria di flussi di dati Task Parallel Library (TPL) in scenari come l'elaborazione dei dati non appena diventano disponibili o quando sono presenti più operazioni che devono comunicare tra loro in modo asincrono. Per altre informazioni, vedere Flusso di dati (Task Parallel Library).

Interfaccia utente

  • Chiamare una versione asincrona di un'API, se disponibile. Questo consente di mantenere sbloccato il thread dell'interfaccia utente, contribuendo a migliorare l'esperienza utente dell'applicazione.

  • Aggiornare gli elementi dell'interfaccia utente con i dati delle operazioni asincrone nel thread dell'interfaccia utente, per evitare la generazione di eccezioni. Tuttavia, verrà effettuato automaticamente il marshalling degli aggiornamenti alla proprietà ListView.ItemsSource nel thread dell'interfaccia utente. Per informazioni su come determinare se il codice è in esecuzione nel thread dell'interfaccia utente, vedere Xamarin.Essentials: MainThread .

    Importante

    Qualsiasi proprietà del controllo aggiornata tramite data binding verrà automaticamente sottoposto a marshalling al thread dell'interfaccia utente.

Gestione degli errori

  • Informazioni sulla gestione asincrona delle eccezioni. Le eccezioni non gestite generate dal codice in esecuzione in modo asincrono vengono propagate al thread chiamante, ad eccezione di determinati scenari. Per altre informazioni, vedere Gestione delle eccezioni (Task Parallel Library).
  • Evitare di async void creare metodi e invece di creare async Task metodi. Questi strumenti consentono una gestione degli errori più semplice, la componibilità e la testabilità. L'eccezione a questa linea guida sono i gestori eventi asincroni, che devono restituire void . Per altre informazioni, vedere Evitare Async Void.
  • Non combinare codice di blocco e asincrono chiamando i metodi , o , perché possono causare Task.WaitTask.Result un GetAwaiter().GetResult deadlock. Tuttavia, se questa linea guida deve essere violata, l'approccio preferito consiste nel chiamare il metodo perché GetAwaiter().GetResult mantiene le eccezioni dell'attività. Per altre informazioni, vedere Async All the Way and Task Exception Handling in .NET 4.5.
  • Usare il ConfigureAwait metodo quando possibile per creare codice privo di contesto. Il codice privo di contesto ha prestazioni migliori per le applicazioni per dispositivi mobili ed è una tecnica utile per evitare un deadlock quando si lavora con una codebase parzialmente asincrona. Per altre informazioni, vedere Configurare il contesto.
  • Usare attività di continuazione per funzionalità quali la gestione delle eccezioni generate dall'operazione asincrona precedente e l'annullamento di una continuazione prima dell'avvio o durante l'esecuzione. Per altre informazioni, vedere Concatenamento di attività tramite attività continue.
  • Usare un'implementazione ICommand asincrona quando le operazioni asincrone vengono richiamate da ICommand . In questo modo è possibile gestire eventuali eccezioni nella logica di comando asincrona. Per altre informazioni, vedere Programmazione asincrona: modelli per applicazioni MVVM asincrone: comandi.

Scegliere con attenzione un contenitore di inserimento delle dipendenze

I contenitori di inserimento delle dipendenze introducono nelle applicazioni per dispositivi mobili ulteriori vincoli relativi alle prestazioni. La registrazione e la risoluzione dei tipi con un contenitore comportano un costo in termini di prestazioni perché il contenitore usa la reflection per la creazione di ogni tipo, soprattutto se le dipendenze vengono ricostruite per la navigazione di ogni pagina nell'app. Se le dipendenze presenti sono numerose o complete, il costo della creazione può aumentare in modo significativo. Inoltre, la registrazione dei tipi, che in genere viene eseguita durante l'avvio dell'applicazione, può avere un notevole effetto sul tempo di avvio, a seconda del contenitore usato.

In alternativa, l'inserimento delle dipendenze può essere reso più efficiente implementandolo manualmente tramite factory.

Creare applicazioni shell

Xamarin.Forms Le applicazioni shell offrono un'esperienza di navigazione basata su riquadri a comparsa e schede. È utile implementare con la shell l'esperienza utente dell'applicazione, se possibile. Le applicazioni shell consentono di evitare un'esperienza di avvio insoddisfacente, perché le pagine vengono create on demand in risposta alla navigazione invece che all'avvio dell'applicazione, come avviene per le applicazioni che usano una classe TabbedPage. Per altre informazioni, vedere Xamarin.Forms Shell.

Usare CollectionView invece di ListView

CollectionView è una vista per presentare elenchi di dati usando specifiche di layout diverse. Offre un'alternativa a ListView più flessibile ed efficiente. Per altre informazioni, vedere Xamarin.Forms CollectionView.

Ottimizzare le prestazioni di ListView

Se si usa ListView esistono alcune esperienze utente che devono essere ottimizzate:

  • Inizializzazione: l'intervallo di tempo che inizia quando si crea il controllo e termina quando gli elementi sono visualizzati sullo schermo.
  • Scorrimento: la possibilità di scorrere l'elenco e verificare che l'interfaccia utente non resti indietro rispetto ai movimenti tocco.
  • Interazione per l'aggiunta, l'eliminazione e la selezione di elementi.

Il controllo ListView richiede un'applicazione per specificare i dati e i modelli di cella. Il modo in cui viene eseguita questa operazione ha un notevole impatto sulle prestazioni del controllo. Per altre informazioni, vedere Prestazioni di ListView.

Ottimizzare le risorse immagine

Visualizzare le risorse immagine può aumentare notevolmente il footprint della memoria di un'applicazione. Di conseguenza, tali risorse devono essere create solo se necessario e rilasciate non appena l'applicazione non le richiede più. Ad esempio, se un'applicazione visualizza un'immagine leggendone i dati da un flusso, verificare che tale flusso venga creato solo quando è necessario e venga rilasciato quando non è più necessario. Questo risultato può essere ottenuto creando il flusso quando viene creata la pagina oppure quando viene generato l'evento Page.Appearing e successivamente eliminando il flusso quando viene generato l'evento Page.Disappearing.

Quando si scarica un'immagine da visualizzare con il metodo ImageSource.FromUri, per memorizzare nella cache l'immagine scaricata, verificare che la proprietà UriImageSource.CachingEnabled sia impostata su true. Per altre informazioni, vedere l'articolo sulla gestione delle immagini.

Per altre informazioni, vedere Optimize Image Resources (Ottimizzare le risorse immagine).

Ridurre le dimensioni della struttura ad albero visuale

La riduzione del numero di elementi in una pagina consente di velocizzare il rendering della pagina. Esistono due tecniche principali per ottenere questo risultato. La prima consiste nel nascondere gli elementi che non sono visibili. La proprietà IsVisible di ogni elemento determina se l'elemento deve essere parte della struttura ad albero visuale o meno. Di conseguenza, se un elemento non è visibile perché è nascosto dietro altri elementi, rimuovere l'elemento o impostarne la proprietà IsVisible su false.

La seconda tecnica è rimuovere gli elementi non necessari. Ad esempio, il codice seguente illustra un layout di pagina contenente più oggetti Label:

<StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Hello" />
    </StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Welcome to the App!" />
    </StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Downloading Data..." />
    </StackLayout>
</StackLayout>

Si può mantenere lo stesso layout di pagina con un numero ridotto di elementi, come illustra l'esempio di codice seguente:

<StackLayout Padding="20,35,20,20" Spacing="25">
  <Label Text="Hello" />
  <Label Text="Welcome to the App!" />
  <Label Text="Downloading Data..." />
</StackLayout>

Ridurre le dimensioni del dizionario risorse dell'applicazione

Tutte le risorse usate nell'applicazione devono essere archiviate nel dizionario risorse dell'applicazione per evitare duplicati. Ciò è utile per ridurre la quantità di codice XAML da analizzare in tutta l'applicazione. L'esempio di codice riportato di seguito illustra la risorsa HeadingLabelStyle, che è usata a livello di applicazione e quindi viene definita nel dizionario risorse dell'applicazione:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Resources.App">
     <Application.Resources>
         <ResourceDictionary>
            <Style x:Key="HeadingLabelStyle" TargetType="Label">
                <Setter Property="HorizontalOptions" Value="Center" />
                <Setter Property="FontSize" Value="Large" />
                <Setter Property="TextColor" Value="Red" />
            </Style>
         </ResourceDictionary>
     </Application.Resources>
</Application>

Tuttavia, il codice XAML specifico di una pagina non deve essere incluso nel dizionario risorse dell'applicazione per evitare che le risorse vengano analizzate all'avvio dell'applicazione invece che quando richiesto da una pagina. Se una risorsa viene usata da una pagina che non è la pagina di avvio, deve essere inserita nel dizionario risorse per quella pagina e in questo modo si riduce il codice XAML analizzato all'avvio dell'applicazione. L'esempio di codice riportato di seguito illustra la risorsa HeadingLabelStyle, che è usata solo per una pagina e viene quindi definita nel dizionario risorse della pagina:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Test.HomePage"
             Padding="0,20,0,0">
    <ContentPage.Resources>
        <ResourceDictionary>
          <Style x:Key="HeadingLabelStyle" TargetType="Label">
              <Setter Property="HorizontalOptions" Value="Center" />
              <Setter Property="FontSize" Value="Large" />
              <Setter Property="TextColor" Value="Red" />
          </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

Per altre informazioni sulle risorse dell'applicazione, vedere Stili XAML.

Usare il modello di renderer personalizzato

La maggior parte delle classi renderer espone il metodo , che viene chiamato quando viene creato un controllo personalizzato Xamarin.Forms per eseguire il rendering del controllo nativo OnElementChangedXamarin.Forms corrispondente. Personalizzare le classi renderer per ogni progetto della piattaforma, quindi eseguire l'override di questo metodo per creare un'istanza del controllo nativo e personalizzare il controllo. Il metodo SetNativeControl viene usato per creare un'istanza del controllo nativo e assegna anche il riferimento a controllo alla proprietà Control.

Tuttavia, in alcune circostanze il metodo OnElementChanged può essere chiamato più volte. Di conseguenza, per evitare perdite di memoria, che possono avere un impatto sulle prestazioni, prestare attenzione quando si crea un'istanza di un nuovo controllo nativo. L'approccio da usare quando si crea un'istanza di un nuovo controllo nativo in un renderer personalizzato è illustrato nell'esempio di codice seguente:

protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
  base.OnElementChanged (e);

  if (e.OldElement != null)
  {
    // Unsubscribe from event handlers and cleanup any resources
  }

  if (e.NewElement != null)
  {
    if (Control == null)
    {
      // Instantiate the native control with the SetNativeControl method
    }
    // Configure the control and subscribe to event handlers
  }
}

Per un nuovo controllo nativo l'istanza deve essere creata solo una volta, quando la proprietà Control è null. Inoltre, il controllo deve essere creato, configurato e i gestori eventi devono essere sottoscritti solo quando il renderer personalizzato è associato a un nuovo Xamarin.Forms elemento. Analogamente, per i gestori degli eventi sottoscritti l'iscrizione deve essere annullata solo in caso di modifica dell'elemento allegato al renderer. L'adozione di questo approccio consente di creare un renderer personalizzato dalle prestazioni ottimali che non subisce perdite di memoria.

Importante

Il metodo SetNativeControl deve essere richiamato solo se la proprietà e.NewElement non è null e la proprietà Control è null.

Per altre informazioni sui renderer personalizzati, vedere l'articolo relativo alla personalizzazione dei controlli su ogni piattaforma.