Skip to content

Commit 1ec5a66

Browse files
committed
building up concept for pausing mechanism
1 parent 207d198 commit 1ec5a66

File tree

3 files changed

+114
-49
lines changed

3 files changed

+114
-49
lines changed

MainDemo.Wpf/ProvingGround.xaml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,17 @@
4343
<ColumnDefinition />
4444
</Grid.ColumnDefinitions>
4545

46+
<!-- example 1 -->
47+
<!-- simplest form -->
4648
<materialDesign:Snackbar2 Message="hello 1"
4749
IsActive="False"
4850
x:Name="SnackbarOne" />
4951

5052
<ToggleButton IsChecked="{Binding ElementName=SnackbarOne, Path=IsActive, Mode=TwoWay}"
5153
Grid.Row="1" Grid.Column="0" />
5254

55+
<!-- example 2 -->
56+
<!-- long hand form for setting the message -->
5357
<materialDesign:Snackbar2 IsActive="True"
5458
Grid.Row="0"
5559
Grid.Column="1"
@@ -61,16 +65,24 @@
6165
Grid.Row="1" Grid.Column="1" />
6266

6367

68+
<!-- example 3 -->
69+
<!--
70+
* using a message queue to manage queuing of messages, and getting onto correct thread
71+
* notice the shorthand syntax {materialDesign:MessageQueue} for creating a new message
72+
queue without expanded XAML. useful in code-behind scenarios if you dont want to bind
73+
a message queue in to the snackbar.
74+
-->
6475
<materialDesign:Snackbar2 MessageQueue="{materialDesign:MessageQueue}"
6576
Grid.Row="0"
6677
Grid.Column="2"
6778
x:Name="SnackbarThree" />
79+
6880
<StackPanel Orientation="Horizontal"
6981
Grid.Row="1"
70-
Grid.Column="2" >
71-
<TextBlock>Message: </TextBlock>
72-
<TextBlock x:Name="MessageTextBox">Hello World</TextBlock>
73-
<Button Click="SnackBar3_OnClick">Send</Button>
82+
Grid.Column="2">
83+
<TextBlock HorizontalAlignment="Center">Message: </TextBlock>
84+
<TextBox x:Name="MessageTextBox" HorizontalAlignment="Center">Hello World</TextBox>
85+
<Button Click="SnackBar3_OnClick" HorizontalAlignment="Center">Send</Button>
7486
</StackPanel>
7587

7688
</Grid>

MainDemo.Wpf/ProvingGround.xaml.cs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,16 @@ public ProvingGround()
3232
{
3333
SelectedTime = new DateTime(2000, 1, 1, 3, 15, 0)
3434
};
35-
}
36-
37-
private void Button_Click(object sender, RoutedEventArgs e)
38-
{
39-
System.Diagnostics.Process.Start("https://twitter.com/James_Willock");
40-
41-
}
42-
43-
private void ButtonsDemoChip_OnClick(object sender, RoutedEventArgs e)
44-
{
45-
Console.WriteLine("Chip clicked.");
46-
}
47-
48-
private void ButtonsDemoChip_OnDeleteClick(object sender, RoutedEventArgs e)
49-
{
50-
Console.WriteLine("Chip delete clicked.");
51-
}
52-
53-
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
54-
{
55-
new PaletteHelper().QueryPalette();
56-
}
35+
}
5736

5837
private void SnackBar3_OnClick(object sender, RoutedEventArgs e)
5938
{
60-
SnackbarThree.MessageQueue.Enqueue(MessageTextBox.Text);
39+
//use the message queue to send a message.
40+
var messageQueue = SnackbarThree.MessageQueue;
41+
var message = MessageTextBox.Text;
42+
43+
//the message queue can be called from any thread
44+
Task.Factory.StartNew(() => messageQueue.Enqueue(message));
6145
}
6246
}
6347

MaterialDesignThemes.Wpf/SnackbarMessageQueue.cs

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66
using System.Windows;
7+
using System.Windows.Input;
78

89
namespace MaterialDesignThemes.Wpf
910
{
@@ -13,14 +14,15 @@ public class SnackbarMessageQueue : ISnackbarMessageQueue, IDisposable
1314
private readonly HashSet<Snackbar2> _pairedSnackbars = new HashSet<Snackbar2>();
1415
private readonly Queue<SnackbarMessageQueueItem> _snackbarMessages = new Queue<SnackbarMessageQueueItem>();
1516
private readonly ManualResetEvent _disposedEvent = new ManualResetEvent(false);
17+
private readonly ManualResetEvent _notPausedEvent = new ManualResetEvent(true);
1618
private int _pauseCounter = 0;
1719
private bool _isDisposed;
1820

1921
public SnackbarMessageQueue() : this(TimeSpan.FromSeconds(3))
2022
{ }
2123

2224
public SnackbarMessageQueue(TimeSpan messageDuration)
23-
{
25+
{
2426
_messageDuration = messageDuration;
2527
Task.Factory.StartNew(Pump);
2628
}
@@ -38,18 +40,15 @@ internal Action Pair(Snackbar2 snackbar)
3840

3941
internal Action Pause()
4042
{
41-
//TODO...dialogs, for example, can pause the "countdown" of an active message as they are hiding it
42-
43+
if (_isDisposed) return () => { };
4344

4445
if (Interlocked.Increment(ref _pauseCounter) == 1)
45-
//TODO cease counting down
46-
Console.WriteLine("Paused");
46+
_notPausedEvent.Set();
4747

4848
return () =>
4949
{
5050
if (Interlocked.Decrement(ref _pauseCounter) == 0)
51-
//TODO resume counting down
52-
Console.WriteLine("Paus realesed");
51+
_notPausedEvent.Reset();
5352
};
5453
}
5554

@@ -79,12 +78,13 @@ public void Enqueue<TArgument>(object content, object actionContent, Action<TArg
7978
}
8079

8180
var argumentType = actionArgument != null ? typeof(TArgument) : null;
82-
83-
_snackbarMessages.Enqueue(new SnackbarMessageQueueItem(content, actionContent, actionHandler, actionArgument, argumentType));
81+
_snackbarMessages.Enqueue(new SnackbarMessageQueueItem(content, actionContent, actionHandler,
82+
actionArgument, argumentType));
8483
_messageWaitingEvent.Set();
8584
}
8685

8786
private readonly ManualResetEvent _messageWaitingEvent = new ManualResetEvent(false);
87+
private Tuple<SnackbarMessageQueueItem, DateTime> _latestShownItem;
8888

8989
private async void Pump()
9090
{
@@ -114,43 +114,110 @@ private async void Pump()
114114
if (snackbar != null)
115115
{
116116
var message = _snackbarMessages.Dequeue();
117-
await Show(snackbar, message);
117+
if (_latestShownItem == null || !Equals(_latestShownItem.Item1.Content, message.Content) ||
118+
!Equals(_latestShownItem.Item1.ActionContent, message.ActionContent) ||
119+
_latestShownItem.Item2 <= DateTime.Now.Subtract(_messageDuration))
120+
{
121+
await Show(snackbar, message);
122+
_latestShownItem = new Tuple<SnackbarMessageQueueItem, DateTime>(message, DateTime.Now);
123+
}
118124
}
119125

120126
if (_snackbarMessages.Count > 0)
121127
_messageWaitingEvent.Set();
122128
else
123129
_messageWaitingEvent.Reset();
124-
}
125-
130+
}
131+
}
132+
133+
private class MouseNotOverManagedWaitHandle : IDisposable
134+
{
135+
private readonly ManualResetEvent _waitHandle;
136+
private Action _cleanUp;
137+
138+
public MouseNotOverManagedWaitHandle(UIElement uiElement)
139+
{
140+
if (uiElement == null) throw new ArgumentNullException(nameof(uiElement));
141+
142+
uiElement.MouseEnter += UiElementOnMouseEnter;
143+
uiElement.MouseLeave += UiElementOnMouseLeave;
144+
_waitHandle = new ManualResetEvent(!uiElement.IsMouseOver);
145+
146+
_cleanUp = () =>
147+
{
148+
uiElement.MouseEnter -= UiElementOnMouseEnter;
149+
uiElement.MouseLeave -= UiElementOnMouseLeave;
150+
_waitHandle.Dispose();
151+
_cleanUp = () => { };
152+
};
153+
}
154+
155+
public WaitHandle WaitHandle => _waitHandle;
156+
157+
private void UiElementOnMouseLeave(object sender, MouseEventArgs mouseEventArgs)
158+
{
159+
_waitHandle.Set();
160+
}
161+
162+
private void UiElementOnMouseEnter(object sender, MouseEventArgs mouseEventArgs)
163+
{
164+
_waitHandle.Reset();
165+
}
166+
167+
public void Dispose()
168+
{
169+
_cleanUp();
170+
}
171+
}
172+
173+
private class TimeSpanSpinManagedHandle : IDisposable
174+
{
175+
public TimeSpanSpinManagedHandle(WaitHandle pausedWaitHandle)
176+
{
177+
//new System.Threading.Timer()
178+
179+
180+
181+
}
182+
183+
184+
185+
public void Dispose()
186+
{
187+
throw new NotImplementedException();
188+
}
126189
}
127190

128191
private async Task Show(Snackbar2 snackbar, SnackbarMessageQueueItem messageQueueItem)
129192
{
130-
await Task.Run(() =>
193+
await Task.Run(async () =>
131194
{
132195
var completedHandle = new ManualResetEvent(false);
133196
//TODO set message on snackbar
134-
snackbar.Dispatcher.InvokeAsync(() =>
135-
{
197+
198+
var mouseNotOverManagedWaitHandle = await snackbar.Dispatcher.InvokeAsync(() =>
199+
{
136200
snackbar.SetCurrentValue(Snackbar2.MessageProperty, Create(messageQueueItem));
137-
snackbar.SetCurrentValue(Snackbar2.IsActiveProperty, true);
201+
snackbar.SetCurrentValue(Snackbar2.IsActiveProperty, true);
202+
return new MouseNotOverManagedWaitHandle(snackbar);
138203
});
139204

140-
141205

206+
WaitHandle.WaitAll(new WaitHandle[]
207+
{
208+
mouseNotOverManagedWaitHandle.WaitHandle,
209+
});
142210

143211
//TODO wait until 3 things:
144-
// (
145-
// * NOT PAUSED
212+
// (
146213
// * NOT MOUSE OVER
147214
// * TIME SPAN COMPLETED -- PAUSE MUST KEEP UPPING TIMESPAN
148215
// )
149216
// OR
150217
// * ACTION CLICK
151218

152219
//quick hack implementation
153-
_disposedEvent.WaitOne(snackbar.ActivateStoryboardDuration.Add(_messageDuration));
220+
_disposedEvent.WaitOne(_messageDuration);
154221

155222

156223
//remove message on snackbar
@@ -166,7 +233,7 @@ await Task.Run(() =>
166233

167234

168235
});
169-
}
236+
}
170237

171238
private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
172239
{
@@ -179,8 +246,10 @@ private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem)
179246

180247
public void Dispose()
181248
{
182-
_isDisposed = true;
249+
_isDisposed = true;
183250
_disposedEvent.Set();
251+
_disposedEvent.Dispose();
252+
_notPausedEvent.Dispose();
184253
}
185254
}
186255
}

0 commit comments

Comments
 (0)