Un effetto semplice, ma che appaga l'utente nell'utilizzo di un'applicazione è la comparsa delle immagini di una lista durante lo scorrimento della stessa. Realizzare questo effetto richiede qualche riga di codice e in ottica di riutilizzo del codice con questo script si vuole mostrare come realizzare un attached behavior che permetta facilmente di ottenere l'effetto desiderato.
Innanzitutto si crea una classe, ad esempio FadeImage, dove vengono definite due attached property.
public class FadeImage { public static readonly DependencyProperty UriSourceProperty = DependencyProperty.RegisterAttached("UriSource", typeof(Uri), typeof(FadeImage), new PropertyMetadata(null, OnUriSourceChanged)); private static readonly DependencyProperty StoryboardProperty = DependencyProperty.RegisterAttached("Storyboard", typeof(Storyboard), typeof(FadeImage), new PropertyMetadata(null)); public static void SetUriSource(DependencyObject image, Uri uri) { image.SetValue(UriSourceProperty, uri); } public static Uri GetUriSource(DependencyObject image) { return image.GetValue(UriSourceProperty) as Uri; }
La prima rappresenta l'URI dell'immagine da caricare, mentre la seconda serve per mantenere il riferimento alla storyboard che, quando necessario, si utilizza per animare l'opacity dell'immagine. Il callback OnUriSourceChanged viene invocato quando l'URI dell'immagine viene impostata sulla proprietà. Grazie alla virtualizzazione degli elementi, l'oggetto Image viene riutilizzato più volte quando questo scompare per via dell'attività di scrolling dell'utente. Per questo motivo nel callback si crea l'oggetto BitmapImage solo la prima volta, così come per la Storyboard di animazione, per agire solo sull'immagine da caricare nelle volte successive. In virtù di questo si procede sempre ad impostare l'opacity a zero, per nascondere l'immagine.
private static void OnUriSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Image image = (Image)d; // Creo la prima volta l'oggetto per caricare le immagini // da un Uri BitmapImage bitmapImage = image.Source as BitmapImage; if (bitmapImage == null) { bitmapImage = new BitmapImage(); // Opzione per migliorare le prestazioni bitmapImage.CreateOptions = BitmapCreateOptions.DelayCreation | BitmapCreateOptions.BackgroundCreation; image.Source = bitmapImage; // Intercetto il caricamento dell'immagine image.ImageOpened += Image_ImageOpened; // Preparo l'animazione sull'opacity DoubleAnimation animation = new DoubleAnimation(); animation.To = 1; animation.Duration = TimeSpan.FromSeconds(1); Storyboard.SetTarget(animation, image); Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); // Avvio l'animazione Storyboard storyboard = new Storyboard(); storyboard.Children.Add(animation); // Mantengo un riferimento image.SetValue(StoryboardProperty, storyboard); } // Fermo la storyboard già in corso, per evitare strani effetti Storyboard oldStoryboard = (Storyboard)image.GetValue(StoryboardProperty); if (oldStoryboard != null) oldStoryboard.Stop(); // Rimetto sempre a zero l'opacity image.Opacity = 0d; // Imposto il nuovo Uri bitmapImage.UriSource = (Uri)e.NewValue; }
Poiché l'utente può eseguire scroll veloci, una stessa immagine può subire più avvii di storyboard, e per questo motivo si effettua sempre lo Stop. L'evento ImageOpened intercettato esegue infine l'attività di comparsa dell'immagine, avviando la storyboard già pronta all'uso.
static void Image_ImageOpened(object sender, RoutedEventArgs e) { Image image = (Image)sender; Storyboard storyboard = (Storyboard)image.GetValue(StoryboardProperty); storyboard.Begin(); }
Grazie all'uso dell'opacity, l'animazione creata sfrutta la GPU e quindi garantisce ottime prestazioni. Non resta a questo punto che utilizzare l'attached behavior.
<Image Width="100" Height="100" Local:FadeImage.UriSource="{Binding Image}"> </Image>
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Inference di dati strutturati da testo con Semantic Kernel e ASP.NET Core Web API
Fissare una versione dell'agent nelle pipeline di Azure DevOps
Rinnovare il token di una GitHub App durante l'esecuzione di un workflow
Rendere i propri workflow e le GitHub Action utilizzate più sicure
Recuperare l'ultima versione di una release di GitHub
Fornire parametri ad un Web component HTML
Creare una libreria CSS universale - Rotazione degli elementi
Selettore CSS :has() e i suoi casi d'uso avanzati
Migliorare l'organizzazione delle risorse con Azure Policy
Conoscere il rendering Server o WebAssembly a runtime in Blazor
Utilizzare una qualunque lista per i parametri di tipo params in C#
Applicare un filtro per recuperare alcune issue di GitHub
I più letti di oggi
- .NET Conference Italia 2025 - Milano
- Usare i settings di serializzazione/deserializzazione di System.Text.Json di ASP.NET all'interno di un'applicazione non web
- The Agentic Day - Milano
- Gestione ciclo di vita in .NET Aspire
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- Gestione CSS in Blazor con .NET 9
- Gestione file Javascript in Blazor con .NET 9
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!