Oggi, partendo da una necessità completamente diversa, ho realizzato un template che si comporta come se fosse uno Usercontrol.
Tutto è partito dalla necessità di definire un custom template per una textbox, con Blend, una volta instaurato un buon rapporto, è una operazione "relativamente" semplice e decisamente più veloce che scriversi lo XAML a mano.
In Blend il template appare come in fig.1.
fig.1
La "stranezza" sta nel fatto che nel template della textbox ci sono anche dei pulsanti che voglio utilizzare per copiare e incollare il text della textbox.
Dopo avere collegato le proprietà della textbox al relativo template, lo XAMl risultante è il seguente:
<ControlTemplate x:Key="MyTextboxTemplate" TargetType="{x:Type TextBox}">
<Grid MinHeight="58" MinWidth="175">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.493*"/>
<ColumnDefinition Width="0.507*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="31.097"/>
</Grid.RowDefinitions>
<TextBox x:Name="txt1" Style="{x:Null}" Margin="{TemplateBinding Padding}" Grid.ColumnSpan="2" Text="{TemplateBinding Text}" TextWrapping="Wrap" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}"/>
<Button x:Name="btnCopy" Click="Handle_Click" HorizontalAlignment="Left" Margin="5,5,5,5" VerticalAlignment="Stretch" Width="68.716" Height="Auto" Content="Copy" Grid.Row="1" d:LayoutOverrides="HorizontalAlignment" Grid.ColumnSpan="1"/>
<Button x:Name="btnPaste" Click="Handle_Click" HorizontalAlignment="Stretch" Margin="5,5,5,5" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Content="Paste" Grid.Column="1" Grid.Row="1"/>
</Grid>
</ControlTemplate>
A questo punto, per una migliore organizzazione sempre in Blend ho spostato il template in un proprio ResourceDictionary (MyTemplates.Xaml) e l'ho reso visibile all'applicazione usando MergedDictionaries.
<Application.Resources>
<!-- Resources scoped at the Application level should be defined here. -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Rimane il problema di gestire gli eventi Click dei pulsanti, per far questo ho manualmente creato il codebehind per il ResourceDictionary MyXaml che contiene il template modificando lo xaml di MyTemplates.xaml aggiungendo l'attributo x:Class
ResourceDictionary x:Class="CustomTextbox.MyTemplates"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
e definendo la relativa partial class che nel costruttore chiama InitializeComponent (inizializzando quindi il ResourceDictionary)
partial class MyTemplates:ResourceDictionary
{
public MyTemplates ()
{
InitializeComponent();
}
}
Successivamente ho mappato, come visibile nello XAML che descrive il control template, i due eventi click verso la routine Handle_Click che esegue materialmente l'operazione.
private void Handle_Click (object sender, System.Windows.RoutedEventArgs e)
{
Button btn = sender as Button;
Grid root = btn.Parent as Grid;
TextBox txt = root.FindName("txt1") as TextBox;
if (btn.Name == "btnCopy")
Clipboard.SetData("Text", txt.Text);
else
txt.Text = Clipboard.GetText();
}
Non essendo disponibile un riferimento diretto alla textbox, è stato necessario ricorrere a FindName ma il codice rimane comunque molto semplice.
Come ultima operazione ho definito uno stile implicito associato alla Textbox:
<Style TargetType="{x:Type TextBox}" >
<Setter Property="Template" Value="{StaticResource MyTextboxTemplate}" />
</Style>
Onde evitare ricorsioni infinite ho dovuto evitare che il default style venisse applicato anche alla texbox presente nel template, ecco perchè txt1 ha il proprio stile impostato a null.
Il risultato è quindi quello di avere delle texbox con la funzionalità Copia e Incolla direttamente trascinando un controllo Textbox nella Window.