1
1
using System ;
2
+ using System . Collections . Concurrent ;
2
3
using System . Collections . Generic ;
3
4
using System . Linq ;
5
+ using System . Runtime . InteropServices ;
4
6
using System . Text ;
5
7
using System . Threading ;
6
8
using System . Threading . Tasks ;
@@ -44,7 +46,7 @@ ..maybe the message queue is the controller...i dunno right now...
44
46
45
47
Multiple windows...having the option for one shared queue is nice...if a notification comes in, we can route to the foreground window...
46
48
47
-
49
+ * need to pause timers if a dialog is shown
48
50
49
51
<Snackbar MessageQueue="{Binding MessageQueue}" />
50
52
@@ -57,52 +59,137 @@ public interface ISnackbarMessageQueue
57
59
void Post ( object content , object actionContent , Action actionHandler ) ;
58
60
59
61
void Post < TArgument > ( object content , object actionContent , Action < TArgument > actionHandler , TArgument actionArgument ) ;
60
- }
62
+ }
61
63
62
- internal class SnackbarMessageQueueRegistration
64
+ internal class SnackbarMessage
63
65
{
64
- public SnackbarMessageQueueRegistration ( Snackbar2 snackbar2 )
66
+ public SnackbarMessage ( object content , object actionContent = null , object actionHandler = null , object actionArgument = null , Type argumentType = null )
65
67
{
66
- Snackbar2 = snackbar2 ;
68
+ Content = content ;
69
+ ActionContent = actionContent ;
70
+ ActionHandler = actionHandler ;
71
+ ActionArgument = actionArgument ;
72
+ ArgumentType = argumentType ;
67
73
}
68
74
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 ; }
70
84
}
71
85
72
- public class SnackbarMessageQueue : ISnackbarMessageQueue
86
+ public class SnackbarMessageQueue : ISnackbarMessageQueue , IDisposable
73
87
{
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
+ }
75
97
76
98
//oh if only I had Disposable.Create in this lib :) tempted to copy it in like dragabalz,
77
99
//but this is an internal method so no one will know my direty Action disposer...
78
100
internal Action Pair ( Snackbar2 snackbar )
79
101
{
80
- //assume this internal method is on Dispatcher
81
-
82
102
if ( snackbar == null ) throw new ArgumentNullException ( nameof ( snackbar ) ) ;
83
103
84
- // if (_pairedSnackbars)
85
- // _pairedSnackbars.Add(snackbar);
86
-
87
- // return () => _pairedSnackbars.Remove(snackbar);
88
- return null ;
104
+ _pairedSnackbars . Add ( snackbar ) ;
89
105
90
- //TODO worry about loading unloading...could cause a little leaky if u not carefull...
106
+ return ( ) => _pairedSnackbars . Remove ( snackbar ) ;
91
107
}
92
108
93
109
public void Post ( object content )
94
110
{
95
- throw new NotImplementedException ( ) ;
111
+ if ( content == null ) throw new ArgumentNullException ( nameof ( content ) ) ;
112
+
113
+ _snackbarMessages . Enqueue ( new SnackbarMessage ( content ) ) ;
114
+ _messageWaitingEvent . Set ( ) ;
96
115
}
97
116
98
117
public void Post ( object content , object actionContent , Action actionHandler )
99
118
{
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 ( ) ;
101
123
}
102
124
103
125
public void Post < TArgument > ( object content , object actionContent , Action < TArgument > actionHandler , TArgument actionArgument )
104
126
{
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 ( ) ;
106
193
}
107
194
}
108
195
0 commit comments