Ответ 1
UPDATE: Эта проблема была настолько интересной для меня, что я ее реализовал. Вы можете найти его на моей странице Github: https://github.com/Domysee/WpfCustomControls. Существует несколько настраиваемых элементов управления, которые вы ищете, это RippleEffectDecorator.
Теперь я объясню, что я сделал:
Я создал настраиваемый элемент управления, который наследуется от ContentControl, RippleEffectDecorator. Он определяет дополнительное свойство зависимостей HighlightBackground, которое используется для фона после щелчка по элементу.
Контрольная таблица RippleEffectDecorator состоит из Grid, Ellipse и 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>
Я использовал Grid вместо Border, чтобы добавить несколько дочерних элементов (необходимо, чтобы Ellipse и ContentPresenter могли перекрываться). Эллипс связывает его свойство Height с его собственной шириной, так что это всегда круг.
Теперь к важной части: анимация.
Grid определяет в своих ресурсах Storyboard, который воспроизводится в каждом событии 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>
Раскадровка оживляет свойство ширины эллипса, чтобы оно полностью заполняло область. Он также должен анимировать маржу, потому что эллипс позиционирует себя относительно верхней левой точки (не вокруг ее центра).
Начальное положение эллипса, его ширина цели и его положение в контейнере во всем эффекте должны быть установлены программно. Я перезаписываю метод OnApplyTemplate(), чтобы добавить обработчик события в событие мыши, которое запускает раскадровку и устанавливает все необходимые значения.
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);
}
Примечание. последний параметр AddHandler() определяет, хотите ли вы получать обработанные события. Важно установить значение true, потому что некоторые UiElements обрабатывают события мыши (например, Button). В противном случае MouseDownEvent не будет запускаться, и поэтому анимация не будет выполнена.
Чтобы использовать его, просто добавьте элемент, на который вы хотите получить этот эффект, в качестве дочернего элемента RippleEffectDecorator и Background to Transparent:
<cc:RippleEffectDecorator Background="Green" HighlightBackground="LightGreen">
<Button FontSize="60" Background="Transparent">stuff</Button>
</cc:RippleEffectDecorator>
Примечание2: некоторые элементы включают триггеры, которые устанавливают шаблон на MouseOver (например, Button) и, следовательно, скрывают эффект. Если вы не хотите, чтобы вы установили шаблон кнопки и удалили эти триггеры. Самый простой способ - использовать Blend, получить шаблон кнопки из него, удалить все триггеры и добавить его в качестве шаблона вашей кнопки.