Skip to content

Commit 841e00c

Browse files
committed
Snack bar queuing improvments and fixes.
Seprated the concepts of promote and ignore duplicate. To allows these to be controlled independently. Fixes #761 Fixes #614
1 parent 93b3d26 commit 841e00c

File tree

3 files changed

+76
-40
lines changed

3 files changed

+76
-40
lines changed

MaterialDesignThemes.Wpf/ISnackbarMessageQueue.cs

Lines changed: 17 additions & 3 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,21 @@ 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>
55-
void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument, bool promote);
54+
/// <param name="promote">The message will promoted to the front of the queue.</param>
55+
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
56+
void Enqueue<TArgument>(object content, object actionContent, Action<TArgument> actionHandler,
57+
TArgument actionArgument, bool promote, bool neverConsiderToBeDuplicate);
58+
59+
/// <summary>
60+
/// Queues a notificaton message for display in a snackbar.
61+
/// </summary>
62+
/// <param name="content">Message.</param>
63+
/// <param name="actionContent">Content for the action button.</param>
64+
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
65+
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler"/>.</param>
66+
/// <param name="promote">The message will promoted to the front of the queue.</param>
67+
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
68+
void Enqueue(object content, object actionContent, Action<object> actionHandler, object actionArgument,
69+
bool promote, bool neverConsiderToBeDuplicate);
5670
}
5771
}

MaterialDesignThemes.Wpf/SnackbarMessageQueue.cs

Lines changed: 35 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,48 @@ 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, bool neverConsiderToBeDuplicate)
222222
{
223223
if (content == null) throw new ArgumentNullException(nameof(content));
224224

225-
if ((actionContent != null || actionHandler != null || actionArgument != null)
226-
&&
227-
actionContent == null && actionHandler == null && actionArgument == null)
225+
if (actionContent == null ^ actionHandler == null)
228226
{
229227
throw new ArgumentException("All action arguments must be provided if any are provided.",
230-
nameof(actionContent));
228+
actionContent != null ? nameof(actionContent) : nameof(actionHandler));
231229
}
232230

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

235248
var snackbarMessageQueueItem = new SnackbarMessageQueueItem(content, actionContent, actionHandler,
236-
actionArgument, argumentType, promote);
249+
actionArgument, promote, neverConsiderToBeDuplicate);
237250
if (promote)
238251
InsertAsLastNotPromotedNode(snackbarMessageQueueItem);
239252
else
@@ -261,7 +274,7 @@ private async void PumpAsync()
261274
{
262275
while (!_isDisposed)
263276
{
264-
var eventId = WaitHandle.WaitAny(new WaitHandle[] {_disposedEvent, _messageWaitingEvent});
277+
var eventId = WaitHandle.WaitAny(new WaitHandle[] { _disposedEvent, _messageWaitingEvent });
265278
if (eventId == 0) continue;
266279
var exemplar = _pairedSnackbars.FirstOrDefault();
267280
if (exemplar == null)
@@ -281,7 +294,7 @@ private async void PumpAsync()
281294
var message = _snackbarMessages.First.Value;
282295
_snackbarMessages.RemoveFirst();
283296
if (_latestShownItem == null
284-
|| message.IsPromoted
297+
|| message.IgnoreDuplicate
285298
|| !Equals(_latestShownItem.Item1.Content, message.Content)
286299
|| !Equals(_latestShownItem.Item1.ActionContent, message.ActionContent)
287300
|| _latestShownItem.Item2 <= DateTime.Now.Subtract(_messageDuration))
@@ -331,7 +344,7 @@ await Task.Run(async () =>
331344
_pausedEvent, durationPassedWaitHandle, _disposedEvent);
332345

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

336349
//close message on snackbar
337350
await
@@ -399,18 +412,8 @@ private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem)
399412
{
400413
try
401414
{
402-
var action = messageQueueItem.ActionHandler as Action;
403-
if (action != null)
404-
{
405-
action();
406-
return;
407-
}
415+
messageQueueItem.ActionHandler(messageQueueItem.ActionArgument);
408416

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 });
414417
}
415418
catch (Exception exc)
416419
{
@@ -419,7 +422,7 @@ private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem)
419422
Trace.WriteLine(exc.StackTrace);
420423

421424
throw;
422-
}
425+
}
423426
}
424427

425428
private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
@@ -433,10 +436,10 @@ private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
433436

434437
public void Dispose()
435438
{
436-
_isDisposed = true;
439+
_isDisposed = true;
437440
_disposedEvent.Set();
438441
_disposedEvent.Dispose();
439-
_pausedEvent.Dispose();
442+
_pausedEvent.Dispose();
440443
}
441444
}
442445
}

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)