Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

Il SelectedItem di un WrapPanel: si può?

Leggendo il forum di cui parlavo ieri, ho notato una domanda ed una risposta che non mi hanno convinto. Dopo questo post sul mio blog, probabilmente risponderò anche sul forum, giusto per capire se ho capito bene oppure no.

La domanda fondamentale è: come posso ottenere l'elemento selezionato all'interno di un WrapPanel? La prima risposta, cito testualmente, è: A WrapPanel does not implement the concept of a currently selected item. Mi permetto di non essere completamente d'accordo. Il discorso è questo: è senz'altro vero che ci sono controlli più adatti che implementano internamente il concetto di selected item, ma è altrettanto vero che ci vuole un attimo a sfruttare il WrapPanel per ottenere lo stesso risultato. Rispetto alla ListBox o alla ListView, il WrapPanel è molto comodo, perchè riposiziona automaticamente i propri Children: se serve una feature di questo tipo, perchè star lì a reinventare la ruota?

Mi sono sentito coinvolto con quella domanda, perchè nel mio plug-in faccio uso del WrapPanel per le emoticons e capirete bene che gestisco eccome il concetto di selected item, dal momento che quando cliccate su un Button quello diventa l'emoticon corrente. Non capisco quindi le risposte negative che sono state date al tizio sul forum.

In 5 minuti ho tirato quindi in piedi un piccolo progetto WPF molto banale per dimostrare quello che sto dicendo. Immaginatevi una normalissima Window il cui codice XAML è:

<Window x:Class="TestWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TestWPF" Height="300" Width="300" > <WrapPanel Name="pnlMainPanel"> </WrapPanel> </Window>

Niente di particolare, fino a questo punto. La Window si chiama MainWindow, il titolo è "TestWPF" e le dimensioni sono di 300x300 punti. All'interno della Window vive un WrapPanel chiamato pnlMainWindow.
Nel code-behind di questa Window il costruttore è il seguente:

public MainWindow() { InitializeComponent(); initialize(); populateChildren(); }

Dopo la classica InitializeComponent() chiamo una initialize() ed una populateChildren(). La prima non fa altro che aggiungere un handler all'evento MouseDown al WrapPanel.

void initialize() { pnlMainPanel.MouseDown += new MouseButtonEventHandler(WrapPanelMouseDown); }

La seconda aggiunge una serie di 20 TextBlock al WrapPanel, ciascuna con il suo bravo testo per poterle distinguere.

void populateChildren() { TextBlock text; for (int i = 0; i < 20; i++) { text = new TextBlock(); text.Text = string.Format("Testo n.{0}; ", i.ToString()); text.FontFamily = new FontFamily("Tahoma"); text.FontSize = 12.0; pnlMainPanel.Children.Add(text); } }

Non ci resta che definire l'handler WrapPanelMouseDown agganciato al WrapPanel. Il codice è il seguente:

void WrapPanelMouseDown(object sender, MouseButtonEventArgs e) { TextBlock t = e.Source as TextBlock; if (t != null) Title = t.Text; else Title = e.Source.ToString(); }

Per chi è abituato a lavorare con le tradizionali Windows Forms, il codice qui sopra richiede qualche spiegazione. Siamo abituati a capire qual'è l'oggetto che ha sollevato un determinato evento esaminando il parametro sender. Con WPF non è proprio così: con WPF il sender ritorna e ritornerà sempre l'oggetto a cui è agganciato l'handler. In questo caso il WrapPanel. Il meccanismo di bubbling degli eventi di WPF è innovativo (?), perchè propaga lo scatenarsi di un evento dagli oggetti child fino alla root del visual-tree della Window. Semplificando un po', possiamo dire che il visual-tree della Window qui sopra è Window --> WrapPanel --> TextBlock. Quando una TextBlock solleva un evento MouseDown, quindi, tale evento risale l'albero fino all'oggetto Window passando dal WrapPanel. Se a quest'ultimo è agganciato l'handler, ecco che possiamo gestire il MouseDown su ciascuno degli elementi contenuti. Come dicevo però, non possiamo usare sender, bensì la proprietà Source esposta dalla classe MouseButtonEventArgs, esattamente come ho fatto sopra. L'evento è intercettato comunque dal WrapPanel, ma posso capire chi davvero ha sollevato il MouseDown. Di conseguenza, posso castare e.Source verso un TextBlock. Se il casting ha avuto successo, mostro il .Text del TextBlock nella barra del titolo della Window, altrimenti visualizzo un banale ToString(), perchè a questo punto non so con chi ho a che fare.

Torniamo a noi. Proviamo a compilare il codice e ad eseguire il tutto.
WPF mostra una finestra sullo schermo con 20 TextBlock al suo interno, il cui testo va da "Testo n.0" a "Testo n.19". Se clicchiamo su un TextBlock, nella barra del titolo appare il testo corrispondente al TextBlock cliccato. Questo per me rappresenta il concetto di selected item, che poi era il tema della domanda. Nel mio plug-in faccio bene o male la stessa cosa, solo che lì i children del WrapPanel sono Button e gestisco l'evento Click per "capire" cosa è stato cliccato.

Print | posted on Friday, February 23, 2007 12:38 PM | Filed Under [ Sviluppo .NET ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET