Dettagli tecnici su un ContextMenu personalizzato
Da quando Roberto tartassò il mio ContextMenu alla cena degli ultimi Community Days, ho dedicato la mia vita a creare e a sviluppare un ContextMenu degno di tal nome. Non che ci voglia tanto con WPF, sono io che sono imbastito con gli strumenti grafici. E comunque Roberto un pochino aveva ragione: il ContextMenu al quale faceva riferimento era veramente orribile, a partire dall’impaginazione dei vari controlli. Dicevo: ho cercato di impegnarmi. Facciamo un passo alla volta.
Ho una normalissima Window che contiene una Grid con una normalissima TextBox.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top"> <TextBox Margin="6" VerticalAlignment="Stretch" Height="22" Name="txtUsername" Text="SERVERUsername" Background="Salmon"> </TextBox> </Grid>
Non ho associato alcun ContextMenu alla TextBox: ciò significa che se lanciassi il progetto e cliccassi con il destro all’interno della TextBox mi apparirebbe il solito menù con le voci Taglia/Copia/Incolla. Adesso inseriamo nelle risorse della Window il ContextMenu con le voci che ci interessa creare.
<Window.Resources> <ContextMenu x:Key="menuDX"> <MenuItem Header="Apri Browser" /> <MenuItem Header="Apri Microsoft Word" /> <MenuItem Header="Apri Microsoft Excel" /> <Separator /> <MenuItem Header="Taglia" /> <MenuItem Header="Copia" /> <MenuItem Header="Incolla" /> </ContextMenu> </Window.Resources>
A questo possiamo impostare la proprietà ContextMenu della TextBox alla risorsa statica con Key = “menuDX“.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top"> <TextBox ContextMenu="{StaticResource menuDX}" Margin="6" VerticalAlignment="Stretch" Height="22" Name="txtUsername" Text="MARTEIgor" Background="Salmon"> </TextBox> </Grid>
Fino a questo punto il ContextMenu non ha un effetto grafico particolare, perchè non abbiamo dato stili o template particolare e quindi WPF lo disegna nel modo che conosce. Per fare cose più succulente occorre settare la proprietà Header a qualcosa di diverso dal solito banale e triste testo. Andiamo con calma: potremmo scrivere il ContextMenu di prima in questo modo:
<Window.Resources> <ContextMenu x:Key="menuDX"> <MenuItem> <MenuItem.Header> <StackPanel Orientation="Horizontal"> <Ellipse Margin="4" Fill="Red" Width="10" Height="10" Name="Cerchio" /> <TextBlock Margin="4" Text="Apri Browser" Name="txtTextBlock" /> </StackPanel> </MenuItem.Header> </MenuItem> ... ... </Window.Resources>
Ho riportato solo il primo MenuItem per brevità. Ho settato Header ad uno StackPanel, che contiene un Ellipse ed una TextBlock. La stessa cosa andrebbe fatta anche per gli altri MenuItem, variando opportunamente il colore dell’ellisse (proprietà Fill) ed il testo della TextBlock (proprietà Text). Lungo ed un po’ prolisso. Ho migliorato le cose creando uno UserControl chiamato con molta fantasia CustomMenuItem il cui XAML è:
<StackPanel x:Class="WindowsApplication1.CustomMenuItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Orientation="Horizontal"> <Ellipse Margin="4" Fill="Red" Width="10" Height="10" Name="Cerchio" /> <TextBlock Margin="4" Text="Apri Browser" Name="txtTextBlock" /> </StackPanel>
Nel code-behind di questo UserControl ho fatto in modo di estrarre le dependency-property del colore dell’ellisse ed il testo, in modo tale che siano visibili anche all’esterno della classe ed impostabili dallo XAML di chi fa uso dello UserControl stesso.
public string Testo { get { return txtTextBlock.Text; } set { txtTextBlock.Text = value; } } public Brush ColoreCerchio { get { return Cerchio.Fill; } set { Cerchio.Fill = value; } }
Ho messo i nomi delle proprietà in italiano perchè stavo sviluppando e ragionavo in lingua madre. In pratica, ho estratto quello che prima avevo inserito nell’Header del menù per inserirlo in un controllo a sè stante. A questo punto la definizione del ContextMenu può far uso di questo UserControl in questo modo:
<Window.Resources> <ContextMenu x:Key="menuDX"> <MenuItem> <MenuItem.Header> <ctl:CustomMenuItem Testo="Apri blocco note" ColoreCerchio="Red" /> </MenuItem.Header> </MenuItem> <MenuItem> <MenuItem.Header> <ctl:CustomMenuItem Testo="Apri Microsoft Word" ColoreCerchio="Blue" /> </MenuItem.Header> </MenuItem> <MenuItem> <MenuItem.Header> <ctl:CustomMenuItem Testo="Apri Microsoft Excel" ColoreCerchio="Green" /> </MenuItem.Header> </MenuItem> </ContextMenu> </Window.Resources>
In questo modo l’aspetto di ogni MenuItem è inserito all’interno dello UserControl, per cui se volessimo cambiarlo non dovremmo toccare le risorse della Window, ma modificare lo UserControl. Non che fosse una novità, ma l’ho detto lo stesso. Ho provato qualche elaborazione diversa usando template e TemplateBinding ed ho raggiunto praticamente gli stessi risultati, ma con qualche spunto in più su cui vorrei ragionare. Se ce la faccio, con questo caldo…
Ne parlerò fra un po’!
Technorati Tags: programming WPF