Skip to content

Commit e191253

Browse files
committed
fleshing out snackbar message queue
1 parent a2c1071 commit e191253

File tree

1 file changed

+106
-19
lines changed

1 file changed

+106
-19
lines changed

MaterialDesignThemes.Wpf/Snackbar.cs

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
5+
using System.Runtime.InteropServices;
46
using System.Text;
57
using System.Threading;
68
using System.Threading.Tasks;
@@ -44,7 +46,7 @@ ..maybe the message queue is the controller...i dunno right now...
4446
4547
Multiple windows...having the option for one shared queue is nice...if a notification comes in, we can route to the foreground window...
4648
47-
49+
* need to pause timers if a dialog is shown
4850
4951
<Snackbar MessageQueue="{Binding MessageQueue}" />
5052
@@ -57,52 +59,137 @@ public interface ISnackbarMessageQueue
5759
void Post(object content, object actionContent, Action actionHandler);
5860

5961
void Post<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument);
60-
}
62+
}
6163

62-
internal class SnackbarMessageQueueRegistration
64+
internal class SnackbarMessage
6365
{
64-
public SnackbarMessageQueueRegistration(Snackbar2 snackbar2)
66+
public SnackbarMessage(object content, object actionContent = null, object actionHandler = null, object actionArgument = null, Type argumentType = null)
6567
{
66-
Snackbar2 = snackbar2;
68+
Content = content;
69+
ActionContent = actionContent;
70+
ActionHandler = actionHandler;
71+
ActionArgument = actionArgument;
72+
ArgumentType = argumentType;
6773
}
6874

69-
public Snackbar2 Snackbar2 { get; }
75+
public object Content { get; }
76+
77+
public object ActionContent { get; }
78+
79+
public object ActionHandler { get; }
80+
81+
public object ActionArgument { get; }
82+
83+
public Type ArgumentType { get; }
7084
}
7185

72-
public class SnackbarMessageQueue : ISnackbarMessageQueue
86+
public class SnackbarMessageQueue : ISnackbarMessageQueue, IDisposable
7387
{
74-
private readonly IList<SnackbarMessageQueueRegistration> _pairedSnackbars = new List<SnackbarMessageQueueRegistration>();
88+
private readonly HashSet<Snackbar2> _pairedSnackbars = new HashSet<Snackbar2>();
89+
private readonly Queue<SnackbarMessage> _snackbarMessages = new Queue<SnackbarMessage>();
90+
private readonly ManualResetEvent _disposedEvent = new ManualResetEvent(false);
91+
private bool _isDisposed;
92+
93+
public SnackbarMessageQueue()
94+
{
95+
Task.Factory.StartNew(Pump);
96+
}
7597

7698
//oh if only I had Disposable.Create in this lib :) tempted to copy it in like dragabalz,
7799
//but this is an internal method so no one will know my direty Action disposer...
78100
internal Action Pair(Snackbar2 snackbar)
79101
{
80-
//assume this internal method is on Dispatcher
81-
82102
if (snackbar == null) throw new ArgumentNullException(nameof(snackbar));
83103

84-
// if (_pairedSnackbars)
85-
// _pairedSnackbars.Add(snackbar);
86-
87-
// return () => _pairedSnackbars.Remove(snackbar);
88-
return null;
104+
_pairedSnackbars.Add(snackbar);
89105

90-
//TODO worry about loading unloading...could cause a little leaky if u not carefull...
106+
return () => _pairedSnackbars.Remove(snackbar);
91107
}
92108

93109
public void Post(object content)
94110
{
95-
throw new NotImplementedException();
111+
if (content == null) throw new ArgumentNullException(nameof(content));
112+
113+
_snackbarMessages.Enqueue(new SnackbarMessage(content));
114+
_messageWaitingEvent.Set();
96115
}
97116

98117
public void Post(object content, object actionContent, Action actionHandler)
99118
{
100-
throw new NotImplementedException();
119+
if (content == null) throw new ArgumentNullException(nameof(content));
120+
121+
_snackbarMessages.Enqueue(new SnackbarMessage(content, actionContent, actionHandler));
122+
_messageWaitingEvent.Set();
101123
}
102124

103125
public void Post<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument)
104126
{
105-
throw new NotImplementedException();
127+
if (content == null) throw new ArgumentNullException(nameof(content));
128+
129+
if (actionContent != null ^ actionHandler != null ^ actionArgument != null)
130+
{
131+
throw new ArgumentException("All action arguments must be provided if any are provided.", nameof(actionContent));
132+
}
133+
134+
var argumentType = actionArgument != null ? typeof(TArgument) : null;
135+
136+
_snackbarMessages.Enqueue(new SnackbarMessage(content, actionContent, actionHandler, actionArgument, argumentType));
137+
_messageWaitingEvent.Set();
138+
}
139+
140+
private readonly ManualResetEvent _messageWaitingEvent = new ManualResetEvent(false);
141+
142+
private async void Pump()
143+
{
144+
while (!_isDisposed)
145+
{
146+
var eventId = WaitHandle.WaitAny(new WaitHandle[] {_disposedEvent, _messageWaitingEvent});
147+
if (eventId == 0) continue;
148+
149+
//find a target
150+
var snackbar = _pairedSnackbars.FirstOrDefault(sb =>
151+
{
152+
if (!sb.IsLoaded || sb.Visibility != Visibility.Visible) return false;
153+
var window = Window.GetWindow(sb);
154+
return window != null && window.WindowState != WindowState.Minimized;
155+
});
156+
157+
if (snackbar != null)
158+
{
159+
var message = _snackbarMessages.Dequeue();
160+
//TODO check duplicates
161+
//TODO manage awaiting of animations
162+
//TODO action callbacks
163+
await Show(snackbar, message);
164+
165+
//THOUGHT: I think we need a complete "SnackbarMessage" control, within the Snackbar...to ensure the callbacks are safely wired to the correct Post message
166+
}
167+
168+
if (_snackbarMessages.Count > 0)
169+
_messageWaitingEvent.Set();
170+
}
171+
172+
}
173+
174+
private async Task Show(Snackbar2 snackbar, SnackbarMessage message)
175+
{
176+
await Task.Run(() =>
177+
{
178+
//TODO set message on snackbar
179+
snackbar.Dispatcher.BeginInvoke(new Action(() => { }));
180+
181+
//wait
182+
_disposedEvent.WaitOne(3000);
183+
184+
//remove message on snackbar
185+
snackbar.Dispatcher.BeginInvoke(new Action(() => { }));
186+
});
187+
}
188+
189+
public void Dispose()
190+
{
191+
_isDisposed = true;
192+
_disposedEvent.Set();
106193
}
107194
}
108195

0 commit comments

Comments
 (0)