Skip to content

Commit d57b03a

Browse files
committed
reverted back to @spiegelp animations in XAML version
1 parent 87eb68e commit d57b03a

File tree

2 files changed

+92
-104
lines changed

2 files changed

+92
-104
lines changed

MaterialDesignThemes.Wpf/Snackbar.cs

Lines changed: 57 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Windows.Controls;
88
using System.Windows.Input;
99
using System.Windows.Media;
10-
using System.Windows.Media.Animation;
1110
using System.Windows.Threading;
1211

1312
namespace MaterialDesignThemes.Wpf
@@ -17,17 +16,13 @@ namespace MaterialDesignThemes.Wpf
1716
/// </summary>
1817
public class Snackbar : ContentControl
1918
{
20-
private const string PartActionButtonName = "PART_actionButton";
21-
private const string PartContentGridName = "PART_contentGrid";
22-
private const string PartContentPanelName = "PART_contentPanel";
19+
public const string PartActionButtonName = "PART_actionButton";
2320

2421
/// <summary>
25-
/// The duration of the open and close animations in milliseconds.
22+
/// The duration of the animation in milliseconds.
2623
/// </summary>
2724
public const int AnimationDuration = 300;
2825

29-
private const int OpacityAnimationHintOffset = 50;
30-
3126
/// <summary>
3227
/// The minimum timeout for a visible <see cref="Snackbar" /> in milliseconds.
3328
/// </summary>
@@ -155,45 +150,22 @@ public bool IsOpen
155150
}
156151
}
157152

158-
private static void IsOpenChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs args)
153+
private async static void IsOpenChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs args)
159154
{
160-
// trigger the animations
155+
// the animations are triggered by the value of the IsOpen property
156+
161157
Snackbar snackbar = (Snackbar)sender;
162158

159+
// stop the timer because it may mess up the behaviour of the Snackbar
160+
snackbar._timer?.Stop();
161+
163162
if ((bool)args.NewValue)
164163
{
165-
if (snackbar._openStoryboard != null)
166-
{
167-
// set the duration of the dummy visibility timeout animation as it may has changed since the last call
168-
if (snackbar._dummyVisibilityAnimation != null)
169-
{
170-
int timeout = snackbar.VisibilityTimeout;
171-
172-
if (timeout < MinimumVisibilityTimeout)
173-
{
174-
timeout = MinimumVisibilityTimeout;
175-
}
176-
177-
snackbar._dummyVisibilityAnimation.Duration = TimeSpan.FromMilliseconds(timeout);
178-
}
179-
180-
// start the open animation
181-
snackbar._openStoryboard.Begin(snackbar, true);
182-
}
164+
await snackbar.ShowAsync();
183165
}
184166
else
185167
{
186-
// stop the open animation if the Snackbar should close before the visibility timeout is reached
187-
if (snackbar._openStoryboard != null)
188-
{
189-
snackbar._openStoryboard.Stop(snackbar);
190-
}
191-
192-
// start the close animation
193-
if (snackbar._closeStoryboard != null)
194-
{
195-
snackbar._closeStoryboard.Begin(snackbar, true);
196-
}
168+
await snackbar.HideAsync();
197169
}
198170
}
199171

@@ -237,9 +209,8 @@ public int VisibilityTimeout
237209
}
238210
}
239211

240-
private Storyboard _openStoryboard;
241-
private Storyboard _closeStoryboard;
242-
private DoubleAnimation _dummyVisibilityAnimation;
212+
// used to implement the behaviour of the HalfAutomatic mode defined in the Material Design specs
213+
private DispatcherTimer _timer;
243214

244215
private Button _actionButton;
245216

@@ -248,14 +219,13 @@ static Snackbar()
248219
DefaultStyleKeyProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata(typeof(Snackbar)));
249220

250221
ContentProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata(ContentChangedHandler));
251-
TagProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata(0.0));
222+
TagProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata("0"));
252223
}
253224

254225
public Snackbar() : base() { }
255226

256227
public override void OnApplyTemplate()
257228
{
258-
// set the event handler for the action button from the template
259229
if (_actionButton != null)
260230
{
261231
_actionButton.Click -= ActionButtonClickHandler;
@@ -268,80 +238,28 @@ public override void OnApplyTemplate()
268238
_actionButton.Click += ActionButtonClickHandler;
269239
}
270240

271-
// Storyboard for the open animation
272-
_openStoryboard = new Storyboard();
273-
274-
// height
275-
DoubleAnimation contentPanelTagAnimation = new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(AnimationDuration));
276-
contentPanelTagAnimation.BeginTime = TimeSpan.FromMilliseconds(0.0);
277-
contentPanelTagAnimation.EasingFunction = new QuarticEase() { EasingMode = EasingMode.EaseOut };
278-
Storyboard.SetTarget(contentPanelTagAnimation, GetTemplateChild(PartContentPanelName));
279-
Storyboard.SetTargetProperty(contentPanelTagAnimation, new PropertyPath(StackPanel.TagProperty));
280-
_openStoryboard.Children.Add(contentPanelTagAnimation);
281-
282-
// opacity of the content
283-
DoubleAnimation contentGridOpacityAnimation = new DoubleAnimation(0.0, TimeSpan.FromMilliseconds(0.0));
284-
contentGridOpacityAnimation.BeginTime = TimeSpan.FromMilliseconds(0.0);
285-
Storyboard.SetTarget(contentGridOpacityAnimation, GetTemplateChild(PartContentPanelName));
286-
Storyboard.SetTargetProperty(contentGridOpacityAnimation, new PropertyPath(Grid.OpacityProperty));
287-
_openStoryboard.Children.Add(contentGridOpacityAnimation);
288-
289-
contentGridOpacityAnimation = new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(AnimationDuration - OpacityAnimationHintOffset));
290-
contentGridOpacityAnimation.BeginTime = TimeSpan.FromMilliseconds(OpacityAnimationHintOffset);
291-
contentGridOpacityAnimation.EasingFunction = new QuarticEase() { EasingMode = EasingMode.EaseOut };
292-
Storyboard.SetTarget(contentGridOpacityAnimation, GetTemplateChild(PartContentPanelName));
293-
Storyboard.SetTargetProperty(contentGridOpacityAnimation, new PropertyPath(Grid.OpacityProperty));
294-
_openStoryboard.Children.Add(contentGridOpacityAnimation);
295-
296-
// dummy animation to keep the HalfAutomatic mode Snackbar open during the visibility timeout
297-
_dummyVisibilityAnimation = new DoubleAnimation(1.0, 1.0, TimeSpan.FromMilliseconds(VisibilityTimeout));
298-
_dummyVisibilityAnimation.BeginTime = TimeSpan.FromMilliseconds(AnimationDuration);
299-
Storyboard.SetTarget(_dummyVisibilityAnimation, GetTemplateChild(PartContentPanelName));
300-
Storyboard.SetTargetProperty(_dummyVisibilityAnimation, new PropertyPath(StackPanel.TagProperty));
301-
_openStoryboard.Children.Add(_dummyVisibilityAnimation);
302-
303-
_openStoryboard.Completed += (object sender, EventArgs args) =>
304-
{
305-
// close the Snackbar after the animation in the HalfAutomatic mode
306-
if (Mode == SnackbarMode.HalfAutomatic)
307-
{
308-
IsOpen = false;
309-
}
310-
};
311-
312-
// Storyboard for the close animation
313-
_closeStoryboard = new Storyboard();
314-
315-
// height
316-
contentPanelTagAnimation = new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(AnimationDuration));
317-
contentPanelTagAnimation.BeginTime = TimeSpan.FromMilliseconds(0.0);
318-
contentPanelTagAnimation.EasingFunction = new QuarticEase() { EasingMode = EasingMode.EaseOut };
319-
Storyboard.SetTarget(contentPanelTagAnimation, GetTemplateChild(PartContentPanelName));
320-
Storyboard.SetTargetProperty(contentPanelTagAnimation, new PropertyPath(StackPanel.TagProperty));
321-
_closeStoryboard.Children.Add(contentPanelTagAnimation);
322-
323241
base.OnApplyTemplate();
324242
}
325243

326244
private async Task ShowContentAsync(object content)
327245
{
328246
if (Mode == SnackbarMode.HalfAutomatic)
329247
{
330-
// first close the Snackbar if it is already visible
248+
// first hide the Snackbar if its already visible
331249
if (IsOpen)
332250
{
333251
IsOpen = false;
334252

335-
// wait for the animation, otherwise the new content will already be shown in the close animation
253+
// wait for the animation, otherwise the new content will already be shown in the hide animation
336254
await Task.Delay(AnimationDuration);
337255
}
338256

339-
// now set the new content and open the Snackbar
257+
// now set the new content and show the Snackbar
340258
InternalContent = content;
341259

342260
if (content != null)
343261
{
344-
// only open it with content
262+
// only show it with content
345263
IsOpen = true;
346264

347265
await Task.Delay(AnimationDuration);
@@ -354,6 +272,44 @@ private async Task ShowContentAsync(object content)
354272
}
355273
}
356274

275+
private async Task ShowAsync()
276+
{
277+
// wait for the animation
278+
await Task.Delay(AnimationDuration);
279+
280+
// start the timeout in HalfAutomatic mode
281+
if (Mode == SnackbarMode.HalfAutomatic)
282+
{
283+
// start the timer which will hide the Snackbar
284+
int timeout = VisibilityTimeout;
285+
286+
if (timeout < MinimumVisibilityTimeout)
287+
{
288+
timeout = MinimumVisibilityTimeout;
289+
}
290+
291+
if (_timer == null)
292+
{
293+
_timer = new DispatcherTimer();
294+
}
295+
296+
_timer.Tick += async (object sender, EventArgs args) =>
297+
{
298+
IsOpen = false;
299+
300+
await Task.Delay(AnimationDuration);
301+
};
302+
_timer.Interval = new TimeSpan(0, 0, 0, 0, timeout);
303+
_timer.Start();
304+
}
305+
}
306+
307+
private async Task HideAsync()
308+
{
309+
// wait for the animation
310+
await Task.Delay(AnimationDuration);
311+
}
312+
357313
private async void ActionButtonClickHandler(object sender, RoutedEventArgs args)
358314
{
359315
// do not you raise the event if the Snackbar is not fully visible
@@ -364,7 +320,7 @@ private async void ActionButtonClickHandler(object sender, RoutedEventArgs args)
364320

365321
Task task = null;
366322

367-
// close the Snackbar in HalfAutomatic mode
323+
// hide the Snackbar in HalfAutomatic mode
368324
if (Mode == SnackbarMode.HalfAutomatic)
369325
{
370326
IsOpen = false;
@@ -408,4 +364,4 @@ public enum SnackbarMode : byte
408364
Manual
409365
}
410366
}
411-
}
367+
}

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Snackbar.xaml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<ControlTemplate TargetType="{x:Type wpf:Snackbar}">
2121
<Grid HorizontalAlignment="Center" VerticalAlignment="Bottom">
2222
<Border x:Name="PART_border" Background="#FF323232" CornerRadius="2" MaxWidth="568" MinWidth="288" SnapsToDevicePixels="True">
23-
<StackPanel x:Name="PART_contentPanel" HorizontalAlignment="Stretch" Orientation="Vertical" SnapsToDevicePixels="True">
23+
<StackPanel x:Name="PART_contentPanel" HorizontalAlignment="Stretch" Orientation="Vertical">
2424
<StackPanel.Tag>
2525
<system:Double>0.0</system:Double>
2626
</StackPanel.Tag>
@@ -52,7 +52,7 @@
5252
</DataTemplate>
5353
</ContentControl.Resources>
5454
</ContentControl>
55-
<Button x:Name="PART_actionButton" Grid.Column="1" Content="{Binding Path=ActionLabel, RelativeSource={RelativeSource TemplatedParent}}" SnapsToDevicePixels="True">
55+
<Button x:Name="PART_actionButton" Grid.Column="1" Content="{Binding Path=ActionLabel, RelativeSource={RelativeSource TemplatedParent}}">
5656
<Button.Style>
5757
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatAccentButton}">
5858
<Setter Property="Margin" Value="8,0,8,0" />
@@ -66,9 +66,41 @@
6666
</StackPanel>
6767
</Border>
6868
</Grid>
69+
<ControlTemplate.Triggers>
70+
<Trigger Property="IsOpen" Value="True">
71+
<Trigger.EnterActions>
72+
<BeginStoryboard>
73+
<Storyboard>
74+
<DoubleAnimation Storyboard.TargetName="PART_contentPanel" Storyboard.TargetProperty="Tag" From="0" To="1" Duration="0:0:0.3">
75+
<DoubleAnimation.EasingFunction>
76+
<QuarticEase EasingMode="EaseOut" />
77+
</DoubleAnimation.EasingFunction>
78+
</DoubleAnimation>
79+
<DoubleAnimation Storyboard.TargetName="PART_contentGrid" Storyboard.TargetProperty="Opacity" To="0" BeginTime="0" Duration="0" />
80+
<DoubleAnimation Storyboard.TargetName="PART_contentGrid" Storyboard.TargetProperty="Opacity" From="0" To="1" BeginTime="0:0:0.05" Duration="0:0:0.25">
81+
<DoubleAnimation.EasingFunction>
82+
<QuarticEase EasingMode="EaseOut" />
83+
</DoubleAnimation.EasingFunction>
84+
</DoubleAnimation>
85+
</Storyboard>
86+
</BeginStoryboard>
87+
</Trigger.EnterActions>
88+
<Trigger.ExitActions>
89+
<BeginStoryboard>
90+
<Storyboard>
91+
<DoubleAnimation Storyboard.TargetName="PART_contentPanel" Storyboard.TargetProperty="Tag" From="1" To="0" Duration="0:0:0.3">
92+
<DoubleAnimation.EasingFunction>
93+
<QuarticEase EasingMode="EaseOut" />
94+
</DoubleAnimation.EasingFunction>
95+
</DoubleAnimation>
96+
</Storyboard>
97+
</BeginStoryboard>
98+
</Trigger.ExitActions>
99+
</Trigger>
100+
</ControlTemplate.Triggers>
69101
</ControlTemplate>
70102
</Setter.Value>
71103
</Setter>
72104
</Style>
73-
105+
74106
</ResourceDictionary>

0 commit comments

Comments
 (0)