Technology Experience
.NET World

Usare in un DataTemplateSelector un ResourceDictionary definito in un file XAML

Ok, il titolo di questo post è più confuso che altro. Amen.

Nei giorni scorsi ho creato una class library per .NET Framework 3.5, all’interno del quale ci sono Windows in XAML, ResourceDictionary, immagini e video. C’è anche una bella classe TagDataTemplateSelector, che eredita da DataTemplateSelector.

Come ben sa chi sviluppa in WPF, una classe figlia di DataTemplateSelector ha un metodo pubblico SelectTemplate, che restituisce un DataTemplate piuttosto che un altro, in base alla logica che implementiamo nel metodo stesso. Questo serve ad esempio se avete una ListBox e volete applicare un template differente per ogni elemento: supponiamo di avere una classe Person che espone una proprietà Sex, che può valere Sex.Male o Sex.Female usando un’enum. Se un’istanza ha Sex == Sex.Male, vogliamo uno sfondo azzurro, mentre se Sex == Sex.Female, vogliamo uno sfondo rosa. I DataTemplateSelector servono proprio a questo.

Manco a farlo apposta, il mio esempio è un po’ più complesso di così.

All’interno di questa class library ho creato un ResourceDictionary in un file TagStyle.xaml. In questo ResourceDictionary ho definito due stili: InternetTag e LocalTag. Questi sono due stili praticamente identici, se non per un’immagine PNG che cambia. Ecco lo XAML che definisce lo stile InternetTag.

<Style x:Key="InternetTag" TargetType="{x:Type ListBox}"> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Margin="4" Source="ImagesInternetTagLogo.png" /> <Label VerticalContentAlignment="Center" Content="{Binding Path=Identifier}" FontSize="10pt" Padding="4" Width="110"> </Label> </StackPanel> </DataTemplate> </Setter.Value> </Setter> </Style>

Il mio problema è che non riuscivo a restituire i due stili dalla classe TagDataTemplateSelector. Di solito, infatti, riesco a raggiungere l’istanza dello stile tramite l’applicazione corrente. In questo caso, invece, il codice gira dentro una class library, e quindi non ho un’applicazione istanziata. L’unico modo che ho trovato è quello di caricare il ResourceDictionary via codice, reperire gli stili definiti.

Il costruttore del DataTemplateSelector è diventato quindi il seguente:

1 public TagDataTemplateSelector() 2 { 3 ResourceDictionary dict = new ResourceDictionary(); 4 dict.Source = 5 new Uri("pack://application:,,,/VivendoByte.WindowsLiveWriterPlugins.TechnoratiTagsWPF;component/TagStyle.xaml"); 6 7 Style s = (Style)dict["InternetTag"]; 8 Setter st = (Setter)s.Setters[0]; 9 internetDataTemplate = (DataTemplate)st.Value; 10 11 s = (Style)dict["LocalTag"]; 12 st = (Setter)s.Setters[0]; 13 localDiskDataTemplate = (DataTemplate)st.Value; 14 }

Alla riga 1 istanzio un ResourceDictionary, che viene riempito alla riga 2 specificando un Uri adeguato. In pratica, specifico l’assembly nel quale è contenuto il ResourceDictionary, e specifico anche il nome del file XAML. Le righe 7-9 e 11-14 hanno la stessa logica: nel primo blocco recupero lo style InternetTag, poi il suo setter (riga 8) e salvo in un membro privato della classe il Value di quel setter. Tale membro privato è internetDataTemplate. La stessa cosa accade per l’altro stile LocalTag, che viene salvato in localDiskDataTemplate.

Tutto questo accade nel costruttore. Il metodo SelectTemplate diventa a questo punto un semplice switch:

1 public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) 2 { 3 if (item != null && item is TechnoratiTag) 4 { 5 TechnoratiTag tag = item as TechnoratiTag; 6 7 switch (tag.Source) 8 { 9 case TagSource.Internet: 10 { 11 return internetDataTemplate; 12 } 13 case TagSource.LocalDisk: 14 { 15 return localDiskDataTemplate; 16 } 17 } 18 } 19 20 return null; 21 }

Il DataTemplate da restituire dipende come dicevamo da un valore della proprietà dell’oggetto item, che viene quindi castato a TechnoratiTag. In base alla sua proprietà Source restituisco alla ListBox un DataTemplate piuttosto che un altro.

La particolarità di questa soluzione sta nel fatto che non passiamo da un’istanza di Application.Current, perchè quest’ultima non esiste e di conseguenza vale null. Se potessimo accederci, invece, potremmo usare il metodo FindResource per recuperare le risorse definite dentro App.xaml. Oppure potremmo usare la collection Windows[], ma neppure questo è fattibile. L’unica tecnica possibile, come mostrato dal codice, è quello di caricare a run-time un ResourceDictionary e cercare al suo interno i DataTemplate voluti.

Technorati Tags:   

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.