Skip to content

Commit d8a34cd

Browse files
committed
Merge branch 'Snackbar' of https://github.com/spiegelp/MaterialDesignInXamlToolkit into snackbar
2 parents b5723ed + 6f3e840 commit d8a34cd

13 files changed

+810
-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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
<ColumnDefinition Width="250" />
16+
</Grid.ColumnDefinitions>
17+
<Grid.RowDefinitions>
18+
<RowDefinition Height="auto" />
19+
<RowDefinition Height="16" />
20+
<RowDefinition Height="auto" />
21+
</Grid.RowDefinitions>
22+
<TextBlock Margin="8,0" Text="Click the button to show a simple Snackbar." TextWrapping="WrapWithOverflow" />
23+
<Button Grid.Row="2" Content="SHOW" Width="80" Click="ShowSimpleSnackbarButtonClickHandler" />
24+
<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" />
25+
<Button Grid.Column="1" Grid.Row="2" Content="SHOW" Width="80" Click="ShowSnackbarButtonClickHandler" />
26+
<TextBlock Grid.Column="2" Margin="8,0" Text="A click on the button shows a multiline Snackbar." TextWrapping="WrapWithOverflow" />
27+
<Button Grid.Column="2" Grid.Row="2" Content="SHOW" Width="80" Click="ShowMultilineSnackbarButtonClickHandler" />
28+
</Grid>
29+
</ScrollViewer>
30+
</Grid>
31+
</UserControl>

MainDemo.Wpf/Snackbar.xaml.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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("HELLO", 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+
private async void ShowMultilineSnackbarButtonClickHandler(object sender, RoutedEventArgs args)
42+
{
43+
await SnackbarHost.ShowAsync(
44+
"RootSnackbarHost",
45+
"The specs says that the maximum with should be 568dp. However there sould be at most only two lines of text.",
46+
new SnackbarAction("GOT IT"));
47+
}
48+
}
49+
}

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: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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+
/// <summary>
14+
/// Implements a <see cref="Snackbar"/> according to the Material Design specs. Instances are considered for a single-use Snackbar.
15+
/// </summary>
16+
[TemplateVisualState(GroupName = VisibilityStatesGroupName, Name = HiddenStateName)]
17+
[TemplateVisualState(GroupName = VisibilityStatesGroupName, Name = VisibleStateName)]
18+
public class Snackbar : Control
19+
{
20+
public const string PartActionButtonName = "PART_actionButton";
21+
22+
public const string VisibilityStatesGroupName = "VisibilityStates";
23+
public const string HiddenStateName = "Hidden";
24+
public const string VisibleStateName = "Visible";
25+
26+
public static readonly DependencyProperty ActionLabelProperty = DependencyProperty.Register(nameof(ActionLabel), typeof(object), typeof(Snackbar), new PropertyMetadata(null));
27+
28+
public object ActionLabel
29+
{
30+
get
31+
{
32+
return GetValue(ActionLabelProperty);
33+
}
34+
35+
set
36+
{
37+
SetValue(ActionLabelProperty, value);
38+
}
39+
}
40+
41+
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(object), typeof(Snackbar), new PropertyMetadata(null));
42+
43+
public object Message
44+
{
45+
get
46+
{
47+
return GetValue(MessageProperty);
48+
}
49+
50+
set
51+
{
52+
SetValue(MessageProperty, value);
53+
}
54+
}
55+
56+
public SnackbarActionEventHandler ActionHandler { get; internal set; }
57+
58+
private SnackbarState _state;
59+
60+
private SnackbarState State
61+
{
62+
get
63+
{
64+
return _state;
65+
}
66+
67+
set
68+
{
69+
_state = value;
70+
71+
// set the according visual state to trigger the animations
72+
if (_state == SnackbarState.Initialized)
73+
{
74+
VisualStateManager.GoToState(this, HiddenStateName, false);
75+
}
76+
else if (_state == SnackbarState.Visible)
77+
{
78+
VisualStateManager.GoToState(this, VisibleStateName, true);
79+
}
80+
else if (_state == SnackbarState.Hidden)
81+
{
82+
VisualStateManager.GoToState(this, HiddenStateName, true);
83+
}
84+
}
85+
}
86+
87+
private DispatcherTimer _timer;
88+
89+
static Snackbar()
90+
{
91+
DefaultStyleKeyProperty.OverrideMetadata(typeof(Snackbar), new FrameworkPropertyMetadata(typeof(Snackbar)));
92+
}
93+
94+
public Snackbar() { }
95+
96+
public override void OnApplyTemplate()
97+
{
98+
Button actionButton = (Button)GetTemplateChild(PartActionButtonName);
99+
actionButton.Click += ActionButtonClickHandler;
100+
101+
State = SnackbarState.Initialized;
102+
103+
base.OnApplyTemplate();
104+
}
105+
106+
/// <summary>
107+
/// Shows this <see cref="Snackbar"/> inside its parent <see cref="SnackbarHost"/>.
108+
/// </summary>
109+
/// <returns></returns>
110+
public async Task Show()
111+
{
112+
if (State != SnackbarState.Initialized)
113+
{
114+
// only a fresh initialized Snackbar can be shown
115+
return;
116+
}
117+
118+
// set the state, trigger the animation and wait for it
119+
State = SnackbarState.Visible;
120+
121+
await Task.Delay(300);
122+
123+
// start timer which will hide the Snackbar
124+
_timer = new DispatcherTimer();
125+
_timer.Tick += async (object sender, EventArgs args) =>
126+
{
127+
await Hide();
128+
};
129+
_timer.Interval = new TimeSpan(0, 0, 0, 3, 0);
130+
_timer.Start();
131+
}
132+
133+
/// <summary>
134+
/// Hides this <see cref="Snackbar"/>.
135+
/// </summary>
136+
/// <returns></returns>
137+
public async Task Hide()
138+
{
139+
if (State != SnackbarState.Visible)
140+
{
141+
// only a visible Snackbar can be hidden
142+
return;
143+
}
144+
145+
// stop the timer
146+
_timer.Stop();
147+
148+
// set the state, trigger the animation and wait for it
149+
State = SnackbarState.Hidden;
150+
151+
await Task.Delay(300);
152+
153+
// finally remove the Snackbar from the UI
154+
FindSnackbarHost()?.RemoveSnackbar(this);
155+
}
156+
157+
private async void ActionButtonClickHandler(object sender, RoutedEventArgs args)
158+
{
159+
await Hide();
160+
161+
// call the optional action handler
162+
ActionHandler?.Invoke(this, new RoutedEventArgs(args.RoutedEvent, this));
163+
}
164+
165+
private SnackbarHost FindSnackbarHost()
166+
{
167+
DependencyObject parent = VisualTreeHelper.GetParent(this);
168+
169+
while (parent != null)
170+
{
171+
if (parent is SnackbarHost)
172+
{
173+
return (SnackbarHost)parent;
174+
}
175+
176+
parent = VisualTreeHelper.GetParent(parent);
177+
}
178+
179+
return null;
180+
}
181+
182+
private enum SnackbarState : byte
183+
{
184+
Initialized,
185+
Visible,
186+
Hidden
187+
}
188+
}
189+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
/// <summary>
10+
/// An optional parameter for the <see cref="SnackbarHost.ShowAsync(object, string, SnackbarAction)"/> method to provide an action for the <see cref="Snackbar"/>.
11+
/// A <see cref="ActionLabel"/> is mandatory to label the action button.
12+
/// The <see cref="ActionHandler"/> is an optional event handler, which will be called by clicking the action button.
13+
/// </summary>
14+
public class SnackbarAction
15+
{
16+
private string _actionLabel;
17+
private SnackbarActionEventHandler _actionHandler;
18+
19+
/// <summary>
20+
/// Optional event handler, which will be called by clicking the action button.
21+
/// </summary>
22+
public SnackbarActionEventHandler ActionHandler
23+
{
24+
get
25+
{
26+
return _actionHandler;
27+
}
28+
}
29+
30+
/// <summary>
31+
/// Mandatory label for the action button.
32+
/// </summary>
33+
public string ActionLabel
34+
{
35+
get
36+
{
37+
return _actionLabel;
38+
}
39+
}
40+
41+
/// <summary>
42+
/// Creates a new instance.
43+
/// </summary>
44+
/// <param name="actionLabel">Mandatory label for the action button.</param>
45+
public SnackbarAction(string actionLabel) : this(actionLabel, null) { }
46+
47+
/// <summary>
48+
/// Creates a new instance.
49+
/// </summary>
50+
/// <param name="actionLabel">Mandatory label for the action button.</param>
51+
/// <param name="actionHandler">Optional event handler, which will be called by clicking the action button.</param>
52+
public SnackbarAction(string actionLabel, SnackbarActionEventHandler actionHandler)
53+
{
54+
_actionLabel = actionLabel;
55+
_actionHandler = actionHandler;
56+
}
57+
}
58+
}
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)