Wpf, per certi versi, è decisamente una brutta bestia che se non conosci bene tende a mordere più del dovuto… :-)
Uno dei concetti più importanti, e forse meno trattati, probabilmente è la differenza tra: LogicalTree e VisualTree.
Ma peccchèèè!?!?! :-)
Se arrivate dal mondo Windows Forms avete due sane abitudini, vero che le avete? :-):
  1. Avendo a che fare con componenti che implementano IDisposable ove li maneggiate direttamente chiamate sempre Dispose() oppure usate il costrutto using;
  2. Utilizzate gli eventi che segnano il lifecycle di un controllo per fare operazioni di cleanup come ad esempio sganciare gli event handler;
Attached Property: portatrice di verità
Siccome, come me, il manuale delle istruzioni lo avete usato per qualche porta che non ne vuole sapere di stare aperta la prima volta che scrivete una Attached Property vi comportate da bravi sviluppatori diligenti e scoprite che:
  • IDisposable è un concetto che per Wpf non è buono… :-), e questo probabilmente lo sapevamo già;
  • l’evento FrameworkElement.Unloaded, per citarne uno significativo, viene (in moltissime circostanze) invocato una sbrodolata di volte, ma proprio tante, anche quando non vi aspettate che debba accadere;
Il primo problema è abbastanza comprensbile, in realtà non è un problema ma una scelta ben ponderata e derivante dal fatto che, ad esempio, non abbiamo più di mezzo risorse preziose come gli handle gdi; resta però necessario e importante, affinchè il Garbage Collector faccia il suo lavoro come si deve, che il nostro codice non tenga in vita reference a oggetti che non servono più e in questo caso WeakReference is the word :-)
Il secondo punto è un po’ più contorto, vediamo a cosa porta e come ci sono cascato:
  • immaginiamo di avere 2 attached property che definiscono un comportamento su un controllo TextBox;
  • siccome le proprietà in questione hanno una dipendenza logica “vicendevole” avete bisogno di sapere quando entrambe sono settate e Wpf non vi da nessun sistema per dare precedenze in questo senso: allora tipicamente possiamo fare:
    • definiamo una terza attached property (private): IsLoadEventAttached;
    • all’atto del cambiamento di una delle due:
      • agganciamo un handler all’evento “Loaded” del controllo;
      • segnamo sulla attached property privata che l’evento Loaded è “attached” in questo modo la seconda proprietà che viene settata semplicemente non fa nulla;
    • al momento dell’evento “Loaded” siamo certi che entrambe le proprietà hanno avuto la possibilità di essere settate e quindi possiamo prendere decisioni in merito;
    • tipicamente agganciamo l’evento “Unloaded” per fare cleanup…e ad esempio sganciare l’handler da Loaded;
n.d.r: per evitare questa manfrina un Behavior è sicuramente la manna :-)
Altro che cleanup: tabba che ti passa :-)
Funziona tutto a meraviglia finchè non provate a mettere il controllo con le attached property dentro un TabControl, ad esempio… e scoprite la differenza fondamentale tra Logical Tree e Visual Tree:
Logical Tree
Questo è un esempio di logical tree:
<Grid>
    <ContentControl>
        <s:String>Hello World!s:String>
    <ContentControl>
>
Visual Tree
Il Logical Tree per ottimi, e in parte ovvi, motivi a runtime diventa qualcosa di più complesso:
image
come si vede quello che abbiamo “disegnato” con lo xaml è stato esploso in qualcosa di più corposo, orbene… se piazzate quella “roba” in un TabControl succede che il VisualTree vien distrutto e ricostruito spesso e volentieri, in particolare, ad esempio, quando la TabPage che ci ospita viene nascosta.
Se ci pensiamo anche questo ha molto senso ma fa si che la gestione degli eventi di un controllo visuale non è detto che sia il modo migliore per fare cleanup, anzi nel caso della Attached Property è proprio l’ultima delle soluzioni.
Developer avvisato developer mezzo salvato :-)
.m