Skip to content

Commit f39eac7

Browse files
authored
Merge pull request #762 from Keboo/fix761
Snack bar queuing improvements and fixes.
2 parents 56fee88 + 1d1a6bd commit f39eac7

File tree

3 files changed

+89
-39
lines changed

3 files changed

+89
-39
lines changed

MaterialDesignThemes.Wpf/ISnackbarMessageQueue.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public interface ISnackbarMessageQueue
4141
/// <param name="content">Message.</param>
4242
/// <param name="actionContent">Content for the action button.</param>
4343
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
44-
/// <param name="promote">The message will promoted to the front ot the queue and never considered to be a duplicate.</param>
44+
/// <param name="promote">The message will promoted to the front of the queue.</param>
4545
void Enqueue(object content, object actionContent, Action actionHandler, bool promote);
4646

4747
/// <summary>
@@ -51,7 +51,31 @@ public interface ISnackbarMessageQueue
5151
/// <param name="actionContent">Content for the action button.</param>
5252
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
5353
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler"/>.</param>
54-
/// <param name="promote">The message will promoted to the front ot the queue and never considered to be a duplicate.</param>
54+
/// <param name="promote">The message will be promoted to the front of the queue and never considered to be a duplicate.</param>
5555
void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument, bool promote);
56+
57+
/// <summary>
58+
/// Queues a notificaton message for display in a snackbar.
59+
/// </summary>
60+
/// <param name="content">Message.</param>
61+
/// <param name="actionContent">Content for the action button.</param>
62+
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
63+
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler"/>.</param>
64+
/// <param name="promote">The message will be promoted to the front of the queue.</param>
65+
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
66+
void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler,
67+
TArgument actionArgument, bool promote, bool neverConsiderToBeDuplicate);
68+
69+
/// <summary>
70+
/// Queues a notificaton message for display in a snackbar.
71+
/// </summary>
72+
/// <param name="content">Message.</param>
73+
/// <param name="actionContent">Content for the action button.</param>
74+
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
75+
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler"/>.</param>
76+
/// <param name="promote">The message will promoted to the front of the queue.</param>
77+
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
78+
void Enqueue(object content, object actionContent, Action<object> actionHandler, object actionArgument,
79+
bool promote, bool neverConsiderToBeDuplicate);
5680
}
5781
}

MaterialDesignThemes.Wpf/SnackbarMessageQueue.cs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private class MouseNotOverManagedWaitHandle : IDisposable
3030
private readonly ManualResetEvent _disposedWaitHandle = new ManualResetEvent(false);
3131
private Action _cleanUp;
3232
private bool _isWaitHandleDisposed;
33-
private readonly object _waitHandleGate = new object();
33+
private readonly object _waitHandleGate = new object();
3434

3535
public MouseNotOverManagedWaitHandle(UIElement uiElement)
3636
{
@@ -124,7 +124,7 @@ private DurationMonitor(
124124

125125
Task.Factory.StartNew(() =>
126126
{
127-
//keep upping the completion time in case it's paused...
127+
//keep upping the completion time in case it's paused...
128128
while (DateTime.Now < _completionTime && !ceaseWaitHandle.WaitOne(granularity))
129129
{
130130
if (pausedWaitHandle.WaitOne(TimeSpan.Zero))
@@ -194,8 +194,7 @@ public void Enqueue(object content, bool neverConsiderToBeDuplicate)
194194
{
195195
if (content == null) throw new ArgumentNullException(nameof(content));
196196

197-
_snackbarMessages.AddLast(new SnackbarMessageQueueItem(content));
198-
_messageWaitingEvent.Set();
197+
Enqueue(content, null, null, null, false, neverConsiderToBeDuplicate);
199198
}
200199

201200
public void Enqueue(object content, object actionContent, Action actionHandler)
@@ -206,34 +205,52 @@ public void Enqueue(object content, object actionContent, Action actionHandler)
206205
public void Enqueue(object content, object actionContent, Action actionHandler, bool promote)
207206
{
208207
if (content == null) throw new ArgumentNullException(nameof(content));
209-
210-
_snackbarMessages.AddLast(new SnackbarMessageQueueItem(content, actionContent, actionHandler));
211-
_messageWaitingEvent.Set();
208+
if (actionContent == null) throw new ArgumentNullException(nameof(actionContent));
209+
if (actionHandler == null) throw new ArgumentNullException(nameof(actionHandler));
210+
211+
Enqueue(content, actionContent, _ => actionHandler(), promote, false, false);
212212
}
213213

214214
public void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler,
215215
TArgument actionArgument)
216216
{
217-
Enqueue<TArgument>(content, actionContent, actionHandler, actionArgument, false);
217+
Enqueue(content, actionContent, actionHandler, actionArgument, false, false);
218218
}
219219

220220
public void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler,
221-
TArgument actionArgument, bool promote)
221+
TArgument actionArgument, bool promote) =>
222+
Enqueue(content, actionContent, actionHandler, actionArgument, promote, promote);
223+
224+
public void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler,
225+
TArgument actionArgument, bool promote, bool neverConsiderToBeDuplicate)
222226
{
223227
if (content == null) throw new ArgumentNullException(nameof(content));
224228

225-
if ((actionContent != null || actionHandler != null || actionArgument != null)
226-
&&
227-
actionContent == null && actionHandler == null && actionArgument == null)
229+
if (actionContent == null ^ actionHandler == null)
228230
{
229231
throw new ArgumentException("All action arguments must be provided if any are provided.",
230-
nameof(actionContent));
232+
actionContent != null ? nameof(actionContent) : nameof(actionHandler));
231233
}
232234

233-
var argumentType = actionArgument != null ? typeof(TArgument) : null;
235+
Action<object> handler = actionHandler != null
236+
? new Action<object>(argument => actionHandler((TArgument)argument))
237+
: null;
238+
Enqueue(content, actionContent, handler, actionArgument, promote, neverConsiderToBeDuplicate);
239+
}
240+
241+
public void Enqueue(object content, object actionContent, Action<object> actionHandler,
242+
object actionArgument, bool promote, bool neverConsiderToBeDuplicate)
243+
{
244+
if (content == null) throw new ArgumentNullException(nameof(content));
245+
246+
if (actionContent == null ^ actionHandler == null)
247+
{
248+
throw new ArgumentException("All action arguments must be provided if any are provided.",
249+
actionContent != null ? nameof(actionContent) : nameof(actionHandler));
250+
}
234251

235252
var snackbarMessageQueueItem = new SnackbarMessageQueueItem(content, actionContent, actionHandler,
236-
actionArgument, argumentType, promote);
253+
actionArgument, promote, neverConsiderToBeDuplicate);
237254
if (promote)
238255
InsertAsLastNotPromotedNode(snackbarMessageQueueItem);
239256
else
@@ -261,7 +278,7 @@ private async void PumpAsync()
261278
{
262279
while (!_isDisposed)
263280
{
264-
var eventId = WaitHandle.WaitAny(new WaitHandle[] {_disposedEvent, _messageWaitingEvent});
281+
var eventId = WaitHandle.WaitAny(new WaitHandle[] { _disposedEvent, _messageWaitingEvent });
265282
if (eventId == 0) continue;
266283
var exemplar = _pairedSnackbars.FirstOrDefault();
267284
if (exemplar == null)
@@ -281,7 +298,7 @@ private async void PumpAsync()
281298
var message = _snackbarMessages.First.Value;
282299
_snackbarMessages.RemoveFirst();
283300
if (_latestShownItem == null
284-
|| message.IsPromoted
301+
|| message.IgnoreDuplicate
285302
|| !Equals(_latestShownItem.Item1.Content, message.Content)
286303
|| !Equals(_latestShownItem.Item1.ActionContent, message.ActionContent)
287304
|| _latestShownItem.Item2 <= DateTime.Now.Subtract(_messageDuration))
@@ -331,7 +348,7 @@ await Task.Run(async () =>
331348
_pausedEvent, durationPassedWaitHandle, _disposedEvent);
332349

333350
//wait until time span completed (including pauses and mouse overs), or the action is clicked
334-
await WaitForCompletionAsync(mouseNotOverManagedWaitHandle, durationPassedWaitHandle, actionClickWaitHandle);
351+
await WaitForCompletionAsync(mouseNotOverManagedWaitHandle, durationPassedWaitHandle, actionClickWaitHandle);
335352

336353
//close message on snackbar
337354
await
@@ -399,18 +416,8 @@ private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem)
399416
{
400417
try
401418
{
402-
var action = messageQueueItem.ActionHandler as Action;
403-
if (action != null)
404-
{
405-
action();
406-
return;
407-
}
419+
messageQueueItem.ActionHandler(messageQueueItem.ActionArgument);
408420

409-
if (messageQueueItem.ArgumentType == null) return;
410-
411-
var genericType = typeof(Action<>).MakeGenericType(messageQueueItem.ArgumentType);
412-
var method = genericType.GetMethod("Invoke");
413-
method.Invoke(messageQueueItem.ActionHandler, new[] { messageQueueItem.ActionArgument });
414421
}
415422
catch (Exception exc)
416423
{
@@ -419,7 +426,7 @@ private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem)
419426
Trace.WriteLine(exc.StackTrace);
420427

421428
throw;
422-
}
429+
}
423430
}
424431

425432
private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
@@ -433,10 +440,10 @@ private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
433440

434441
public void Dispose()
435442
{
436-
_isDisposed = true;
443+
_isDisposed = true;
437444
_disposedEvent.Set();
438445
_disposedEvent.Dispose();
439-
_pausedEvent.Dispose();
446+
_pausedEvent.Dispose();
440447
}
441448
}
442449
}

MaterialDesignThemes.Wpf/SnackbarMessageQueueItem.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,45 @@ namespace MaterialDesignThemes.Wpf
44
{
55
internal class SnackbarMessageQueueItem
66
{
7-
public SnackbarMessageQueueItem(object content, object actionContent = null, object actionHandler = null, object actionArgument = null, Type argumentType = null, bool isPromoted = false)
7+
public SnackbarMessageQueueItem(object content, object actionContent = null, Action<object> actionHandler = null, object actionArgument = null,
8+
bool isPromoted = false, bool ignoreDuplicate = false)
89
{
910
Content = content;
1011
ActionContent = actionContent;
1112
ActionHandler = actionHandler;
1213
ActionArgument = actionArgument;
13-
ArgumentType = argumentType;
1414
IsPromoted = isPromoted;
15+
IgnoreDuplicate = ignoreDuplicate;
1516
}
1617

18+
/// <summary>
19+
/// The content to be displayed
20+
/// </summary>
1721
public object Content { get; }
1822

23+
/// <summary>
24+
/// The content for the action button on the snackbar
25+
/// </summary>
1926
public object ActionContent { get; }
2027

21-
public object ActionHandler { get; }
28+
/// <summary>
29+
/// Handler to be invoked when the action button is clicked
30+
/// </summary>
31+
public Action<object> ActionHandler { get; }
2232

33+
/// <summary>
34+
/// The argument to pass to the <see cref="ActionHandler"/> delegate.
35+
/// </summary>
2336
public object ActionArgument { get; }
2437

25-
public Type ArgumentType { get; }
26-
38+
/// <summary>
39+
/// Promote the message, pushing it in front of any message that is not promoted.
40+
/// </summary>
2741
public bool IsPromoted { get; }
42+
43+
/// <summary>
44+
/// Still display this message even if it is a duplicate.
45+
/// </summary>
46+
public bool IgnoreDuplicate { get; }
2847
}
2948
}

0 commit comments

Comments
 (0)