UPDATE: Questo problema è stato così interessante per me che l'ho implementato. Puoi trovarlo sulla mia pagina Github: https://github.com/Domysee/WpfCustomControls. Esistono più controlli personalizzati, quello che stai cercando è RippleEffectDecorator.
Ora spiego quello che ho fatto:
ho creato un controllo personalizzato che eredita da ContentControl, RippleEffectDecorator. Definisce una proprietà di dipendenza aggiuntiva HighlightBackground, che viene utilizzata per lo sfondo dopo aver fatto clic sull'elemento.
Il ControlTemplate di RippleEffectDecorator è costituito da una griglia, un'ellisse e un ContentPresenter.
<ControlTemplate TargetType="{x:Type l:RippleEffectDecorator}">
<Grid x:Name="PART_grid" ClipToBounds="True" Background="{TemplateBinding Background}"
Width="{Binding ElementName=PART_contentpresenter, Path=ActualWidth}"
Height="{Binding ElementName=PART_contentpresenter, Path=ActualHeight}">
<Ellipse x:Name="PART_ellipse"
Fill="{Binding Path=HighlightBackground, RelativeSource={RelativeSource TemplatedParent}}"
Width="0" Height="{Binding Path=Width, RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ContentPresenter x:Name="PART_contentpresenter" />
</Grid>
</ControlTemplate>
ho usato una griglia al posto di un bordo in modo che io possa aggiungere più elementi figlio (necessari che Ellisse e ContentPresenter possono sovrapporsi). L'ellisse lega la sua proprietà Height alla sua larghezza, in modo che sia sempre un cerchio.
Ora per la parte importante: l'animazione.
La griglia definisce nelle sue risorse uno storyboard, che viene riprodotto su ogni evento MouseDown.
<Storyboard x:Key="PART_animation" Storyboard.TargetName="PART_ellipse">
<DoubleAnimation Storyboard.TargetProperty="Width" From="0" />
<ThicknessAnimation Storyboard.TargetProperty="Margin" />
<DoubleAnimation BeginTime="0:0:1" Duration="0:0:0.25" Storyboard.TargetProperty="Opacity"
From="1" To="0" />
<DoubleAnimation Storyboard.TargetProperty="Width" To="0" BeginTime="0:0:1.25" Duration="0:0:0" />
<DoubleAnimation BeginTime="0:0:1.25" Duration="0:0:0" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
Lo storyboard anima la proprietà di larghezza dell'ellisse in modo che riempia completamente l'area. Inoltre deve animare il margine, poiché l'ellisse si posiziona rispetto al punto in alto a sinistra (non attorno al centro).
La posizione iniziale dell'ellisse, la sua larghezza di destinazione e la sua posizione nel contenitore per tutto l'effetto devono essere impostate a livello di programmazione. Sovrascrivo il metodo OnApplyTemplate() per aggiungere un gestore eventi all'evento mouse down, che avvia lo storyboard e imposta tutti i valori necessari.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ellipse = GetTemplateChild("PART_ellipse") as Ellipse;
grid = GetTemplateChild("PART_grid") as Grid;
animation = grid.FindResource("PART_animation") as Storyboard;
this.AddHandler(MouseDownEvent, new RoutedEventHandler((sender, e) =>
{
var targetWidth = Math.Max(ActualWidth, ActualHeight) * 2;
var mousePosition = (e as MouseButtonEventArgs).GetPosition(this);
var startMargin = new Thickness(mousePosition.X, mousePosition.Y, 0, 0);
//set initial margin to mouse position
ellipse.Margin = startMargin;
//set the to value of the animation that animates the width to the target width
(animation.Children[0] as DoubleAnimation).To = targetWidth;
//set the to and from values of the animation that animates the distance relative to the container (grid)
(animation.Children[1] as ThicknessAnimation).From = startMargin;
(animation.Children[1] as ThicknessAnimation).To = new Thickness(mousePosition.X - targetWidth/2, mousePosition.Y - targetWidth/2, 0, 0);
ellipse.BeginStoryboard(animation);
}), true);
}
Nota: l'ultimo parametro di AddHandler() determina se si desidera o meno ricevere eventi gestiti. È importante impostarlo su true, perché alcuni UiElements gestiscono gli eventi del mouse (ad esempio Button). In caso contrario, MouseDownEvent non si attiva e pertanto l'animazione non viene eseguita.
Per utilizzarlo è sufficiente aggiungere l'elemento sul quale si desidera avere questo effetto come figlio di RippleEffectDecorator, e lo sfondo trasparente:
<cc:RippleEffectDecorator Background="Green" HighlightBackground="LightGreen">
<Button FontSize="60" Background="Transparent">stuff</Button>
</cc:RippleEffectDecorator>
Nota 2: alcuni elementi includono trigger che definiscono il modello su MouseOver (ad es. Pulsante) e quindi nascondere l'effetto. Se non vuoi che devi impostare il modello del pulsante e rimuovere questi trigger. Il modo più semplice è utilizzare Blend, ottenere il modello del pulsante da esso, rimuovere tutti i trigger e aggiungerlo come modello del pulsante.
Il modo in cui di solito funziona è che si presenti il codice che avete tentato, ma hanno problemi con e vi diciamo dove hai bisogno di aiuto. In questo caso la domanda è così genericamente scritta da essere impossibile da aiutarti. Prova qualcosa, quando ricevi un errore, posta una domanda. – paqogomez
@paqogomez OK. Mi dispiace e aggiornerò questa domanda. – Tokfrans
Questo ha avviato una discussione [sulla meta] (http://meta.stackoverflow.com/questions/291399/is-there-really-a-universal-code-requirement). –