Skip to content

Commit 789185d

Browse files
committed
basic implementation of a Snackbar
1 parent 688c764 commit 789185d

13 files changed

+687
-168
lines changed

MainDemo.Wpf/MainWindow.xaml

Lines changed: 175 additions & 168 deletions
Large diffs are not rendered by default.

MainDemo.Wpf/MaterialDesignDemo.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@
152152
<Compile Include="Sliders.xaml.cs">
153153
<DependentUpon>Sliders.xaml</DependentUpon>
154154
</Compile>
155+
<Compile Include="Snackbar.xaml.cs">
156+
<DependentUpon>Snackbar.xaml</DependentUpon>
157+
</Compile>
155158
<Compile Include="TextFields.xaml.cs">
156159
<DependentUpon>TextFields.xaml</DependentUpon>
157160
</Compile>
@@ -296,6 +299,10 @@
296299
<SubType>Designer</SubType>
297300
<Generator>MSBuild:Compile</Generator>
298301
</Page>
302+
<Page Include="Snackbar.xaml">
303+
<SubType>Designer</SubType>
304+
<Generator>MSBuild:Compile</Generator>
305+
</Page>
299306
<Page Include="TextFields.xaml">
300307
<SubType>Designer</SubType>
301308
<Generator>MSBuild:Compile</Generator>

MainDemo.Wpf/Snackbar.xaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<UserControl x:Class="MaterialDesignColors.WpfExample.Snackbar"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:local="clr-namespace:MaterialDesignDemo"
7+
mc:Ignorable="d"
8+
d:DesignHeight="300" d:DesignWidth="600" Background="Transparent">
9+
<Grid>
10+
<ScrollViewer HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Auto" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto">
11+
<Grid>
12+
<Grid.ColumnDefinitions>
13+
<ColumnDefinition Width="250" />
14+
<ColumnDefinition Width="250" />
15+
</Grid.ColumnDefinitions>
16+
<Grid.RowDefinitions>
17+
<RowDefinition Height="auto" />
18+
<RowDefinition Height="16" />
19+
<RowDefinition Height="auto" />
20+
</Grid.RowDefinitions>
21+
<TextBlock Margin="8,0" Text="Click the button to show a simple Snackbar." TextWrapping="WrapWithOverflow" />
22+
<Button Grid.Row="2" Content="SHOW" Width="80" Click="ShowSimpleSnackbarButtonClickHandler" />
23+
<TextBlock Grid.Column="1" Margin="8,0" Text="This button shows a Snackbar with an action. The action will show another Snackbar after a short delay." TextWrapping="WrapWithOverflow" />
24+
<Button Grid.Column="1" Grid.Row="2" Content="SHOW" Width="80" Click="ShowSnackbarButtonClickHandler" />
25+
</Grid>
26+
</ScrollViewer>
27+
</Grid>
28+
</UserControl>

MainDemo.Wpf/Snackbar.xaml.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Data;
9+
using System.Windows.Documents;
10+
using System.Windows.Input;
11+
using System.Windows.Media;
12+
using System.Windows.Media.Imaging;
13+
using System.Windows.Navigation;
14+
using System.Windows.Shapes;
15+
16+
using MaterialDesignThemes.Wpf;
17+
18+
namespace MaterialDesignColors.WpfExample
19+
{
20+
public partial class Snackbar : UserControl
21+
{
22+
public Snackbar()
23+
{
24+
InitializeComponent();
25+
}
26+
27+
private async void ShowSimpleSnackbarButtonClickHandler(object sender, RoutedEventArgs args)
28+
{
29+
await SnackbarHost.ShowAsync("RootSnackbarHost", "This is a simple Snackbar.");
30+
}
31+
32+
private async void ShowSnackbarButtonClickHandler(object sender, RoutedEventArgs args)
33+
{
34+
await SnackbarHost.ShowAsync("RootSnackbarHost", "Hello from the Snackbar!", new SnackbarAction("GOT IT", async (object s, RoutedEventArgs a) => {
35+
await Task.Delay(2000);
36+
37+
await SnackbarHost.ShowAsync("RootSnackbarHost", "A second hello from the Snackbar!", new SnackbarAction("BYE"));
38+
}));
39+
}
40+
}
41+
}

MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@
229229
<Generator>MSBuild:Compile</Generator>
230230
<SubType>Designer</SubType>
231231
</Page>
232+
<Page Include="Themes\MaterialDesignTheme.Snackbar.xaml">
233+
<SubType>Designer</SubType>
234+
<Generator>MSBuild:Compile</Generator>
235+
</Page>
232236
</ItemGroup>
233237
<ItemGroup>
234238
<Compile Include="Card.cs" />
@@ -287,6 +291,10 @@
287291
<Compile Include="IconType.cs" />
288292
<Compile Include="ListBoxAssist.cs" />
289293
<Compile Include="Palette.cs" />
294+
<Compile Include="Snackbar.cs" />
295+
<Compile Include="SnackbarAction.cs" />
296+
<Compile Include="SnackbarActionEventHandler.cs" />
297+
<Compile Include="SnackbarHost.cs" />
290298
<Compile Include="Transitions\CircleWipe.cs" />
291299
<Compile Include="IHintProxy.cs" />
292300
<Compile Include="Transitions\IndexedItemOffsetMultiplierExtension.cs" />

MaterialDesignThemes.Wpf/Snackbar.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Media;
9+
using System.Windows.Threading;
10+
11+
namespace MaterialDesignThemes.Wpf
12+
{
13+
[TemplateVisualState(GroupName = VisibilityStatesGroupName, Name = HiddenStateName)]
14+
[TemplateVisualState(GroupName = VisibilityStatesGroupName, Name = VisibleStateName)]
15+
public class Snackbar : Control
16+
{
17+
public const string PartActionButtonName = "PART_actionButton";
18+
19+
public const string VisibilityStatesGroupName = "VisibilityStates";
20+
public const string HiddenStateName = "Hidden";
21+
public const string VisibleStateName = "Visible";
22+
23+
public static readonly DependencyProperty ActionLabelProperty = DependencyProperty.Register(nameof(ActionLabel), typeof(object), typeof(Snackbar), new PropertyMetadata(null));
24+
25+
public object ActionLabel
26+
{
27+
get
28+
{
29+
return GetValue(ActionLabelProperty);
30+
}
31+
32+
set
33+
{
34+
SetValue(ActionLabelProperty, value);
35+
}
36+
}
37+
38+
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(object), typeof(Snackbar), new PropertyMetadata(null));
39+
40+
public object Message
41+
{
42+
get
43+
{
44+
return GetValue(MessageProperty);
45+
}
46+
47+
set
48+
{
49+
SetValue(MessageProperty, value);
50+
}
51+
}
52+
53+
public SnackbarActionEventHandler ActionHandler { get; internal set; }
54+
55+
private DispatcherTimer _timer;
56+
57+
static Snackbar()
58+
{
59+
DefaultStyleKeyProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata(typeof(Snackbar)));
60+
}
61+
62+
public Snackbar() { }
63+
64+
public override void OnApplyTemplate()
65+
{
66+
Button actionButton = (Button)GetTemplateChild(PartActionButtonName);
67+
actionButton.Click += ActionButtonClickHandler;
68+
69+
VisualStateManager.GoToState(this, HiddenStateName, false);
70+
71+
base.OnApplyTemplate();
72+
}
73+
74+
public async Task Show()
75+
{
76+
// trigger animation and wait for it
77+
VisualStateManager.GoToState(this, VisibleStateName, true);
78+
79+
await Task.Delay(300);
80+
81+
// start timer which will hide the Snackbar
82+
_timer = new DispatcherTimer();
83+
_timer.Tick += async (object sender, EventArgs args) =>
84+
{
85+
await Hide();
86+
};
87+
_timer.Interval = new TimeSpan(0, 0, 0, 3, 0);
88+
_timer.Start();
89+
}
90+
91+
public async Task Hide()
92+
{
93+
// stop the timer
94+
_timer.Stop();
95+
96+
// trigger animation and wait for it
97+
VisualStateManager.GoToState(this, HiddenStateName, true);
98+
99+
await Task.Delay(300);
100+
101+
// finally remove the Snackbar from the UI
102+
FindSnackbarHost()?.RemoveSnackbar(this);
103+
}
104+
105+
private async void ActionButtonClickHandler(object sender, RoutedEventArgs args)
106+
{
107+
await Hide();
108+
109+
// call the optional action handler
110+
ActionHandler?.Invoke(this, new RoutedEventArgs(args.RoutedEvent, this));
111+
}
112+
113+
private SnackbarHost FindSnackbarHost()
114+
{
115+
DependencyObject parent = VisualTreeHelper.GetParent(this);
116+
117+
while (parent != null)
118+
{
119+
if (parent is SnackbarHost)
120+
{
121+
return (SnackbarHost)parent;
122+
}
123+
124+
parent = VisualTreeHelper.GetParent(parent);
125+
}
126+
127+
return null;
128+
}
129+
}
130+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace MaterialDesignThemes.Wpf
8+
{
9+
public class SnackbarAction
10+
{
11+
private string _actionLabel;
12+
private SnackbarActionEventHandler _actionHandler;
13+
14+
public SnackbarActionEventHandler ActionHandler
15+
{
16+
get
17+
{
18+
return _actionHandler;
19+
}
20+
}
21+
22+
public string ActionLabel
23+
{
24+
get
25+
{
26+
return _actionLabel;
27+
}
28+
}
29+
30+
public SnackbarAction(string actionLabel) : this(actionLabel, null) { }
31+
32+
public SnackbarAction(string actionLabel, SnackbarActionEventHandler actionHandler)
33+
{
34+
_actionLabel = actionLabel;
35+
_actionHandler = actionHandler;
36+
}
37+
}
38+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
using System.Windows;
2+
3+
namespace MaterialDesignThemes.Wpf
4+
{
5+
public delegate void SnackbarActionEventHandler(object sender, RoutedEventArgs args);
6+
}

0 commit comments

Comments
 (0)