private async Task ShowAsync(Snackbar snackbar, SnackbarMessageQueueItem messageQueueItem, ManualResetEvent actionClickWaitHandle) { snackbar.Visibility = Visibility.Visible; snackbar.SetCurrentValue(Snackbar.IsActiveProperty, false); //create and show the message, setting up all the handles we need to wait on var mouseNotOverManagedWaitHandle = CreateAndShowMessage(snackbar, messageQueueItem, actionClickWaitHandle); var durationPassedWaitHandle = new ManualResetEvent(false); StartDuration(messageQueueItem.Duration.Add(snackbar.ActivateStoryboardDuration), durationPassedWaitHandle); //wait until time span completed (including pauses and mouse overs), or the action is clicked await WaitForCompletionAsync(mouseNotOverManagedWaitHandle, durationPassedWaitHandle, actionClickWaitHandle); //close message on snackbar snackbar.SetCurrentValue(Snackbar.IsActiveProperty, false); //we could wait for the animation event, but just doing //this for now...at least it is prevent extra call back hell await Task.Delay(snackbar.DeactivateStoryboardDuration); //remove message on snackbar snackbar.SetCurrentValue(Snackbar.MessageProperty, null); mouseNotOverManagedWaitHandle.Dispose(); durationPassedWaitHandle.Dispose(); }
private static MouseNotOverManagedWaitHandle CreateAndShowMessage(UIElement snackbar, SnackbarMessageQueueItem messageQueueItem, EventWaitHandle actionClickWaitHandle) { var clickCount = 0; var snackbarMessage = new SnackbarMessage { Content = messageQueueItem.Content, ActionContent = messageQueueItem.ActionContent }; snackbarMessage.MouseDown += (sender, args) => { //snackbar.Visibility = Visibility.Collapsed; actionClickWaitHandle.Set(); }; snackbarMessage.ActionClick += (sender, args) => { if (++clickCount == 1) { DoActionCallback(messageQueueItem); } actionClickWaitHandle.Set(); }; snackbar.SetCurrentValue(Snackbar.MessageProperty, snackbarMessage); snackbar.SetCurrentValue(Snackbar.IsActiveProperty, true); return(new MouseNotOverManagedWaitHandle(snackbar)); }
public void Enqueue(object content, object actionContent, Action <object> actionHandler, object actionArgument, bool promote, bool neverConsiderToBeDuplicate) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if (actionContent == null ^ actionHandler == null) { throw new ArgumentException("All action arguments must be provided if any are provided.", actionContent != null ? nameof(actionContent) : nameof(actionHandler)); } var snackbarMessageQueueItem = new SnackbarMessageQueueItem(content, actionContent, actionHandler, actionArgument, promote, neverConsiderToBeDuplicate); if (promote) { InsertAsLastNotPromotedNode(snackbarMessageQueueItem); } else { _snackbarMessages.AddLast(snackbarMessageQueueItem); } _messageWaitingEvent.Set(); }
private void InsertItem(SnackbarMessageQueueItem item) { lock (_snackbarMessagesLock) { var added = false; var node = _snackbarMessages.First; while (node != null) { if (!IgnoreDuplicate && item.IsDuplicate(node.Value)) { return; } if (item.IsPromoted && !node.Value.IsPromoted) { _snackbarMessages.AddBefore(node, item); added = true; break; } node = node.Next; } if (!added) { _snackbarMessages.AddLast(item); } } _dispatcher.InvokeAsync(ShowNextAsync); }
private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem) { try { var action = messageQueueItem.ActionHandler as Action; if (action != null) { action(); return; } if (messageQueueItem.ArgumentType == null) { return; } var genericType = typeof(Action <>).MakeGenericType(messageQueueItem.ArgumentType); var method = genericType.GetMethod("Invoke"); method.Invoke(messageQueueItem.ActionHandler, new[] { messageQueueItem.ActionArgument }); } catch (Exception exc) { Trace.WriteLine("Error during SnackbarMessageQueue message action callback, exception will be rethrown."); Trace.WriteLine($"{exc.Message} ({exc.GetType().FullName})"); Trace.WriteLine(exc.StackTrace); throw; } }
private async Task ShowAsync(Snackbar snackbar, SnackbarMessageQueueItem messageQueueItem, ManualResetEvent actionClickWaitHandle) { //create and show the message, setting up all the handles we need to wait on var tuple = CreateAndShowMessage(snackbar, messageQueueItem, actionClickWaitHandle); var snackbarMessage = tuple.Item1; var mouseNotOverManagedWaitHandle = tuple.Item2; var durationPassedWaitHandle = new ManualResetEvent(false); StartDuration(messageQueueItem.Duration.Add(snackbar.ActivateStoryboardDuration), durationPassedWaitHandle); //wait until time span completed (including pauses and mouse overs), or the action is clicked await WaitForCompletionAsync(mouseNotOverManagedWaitHandle, durationPassedWaitHandle, actionClickWaitHandle); //close message on snackbar snackbar.SetCurrentValue(Snackbar.IsActiveProperty, false); //we could wait for the animation event, but just doing //this for now...at least it is prevent extra call back hell await Task.Delay(snackbar.DeactivateStoryboardDuration); //this prevents missing resource warnings after the message is removed from the Snackbar //see https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/2040 snackbarMessage.Resources = SnackbarMessage.defaultResources; //remove message on snackbar snackbar.SetCurrentValue(Snackbar.MessageProperty, null); mouseNotOverManagedWaitHandle.Dispose(); durationPassedWaitHandle.Dispose(); }
public void Enqueue <TArgument>(object content, object actionContent, Action <TArgument> actionHandler, TArgument actionArgument, bool promote) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if ((actionContent != null || actionHandler != null || actionArgument != null) && actionContent == null && actionHandler == null && actionArgument == null) { throw new ArgumentException("All action arguments must be provided if any are provided.", nameof(actionContent)); } var argumentType = actionArgument != null ? typeof(TArgument) : null; var snackbarMessageQueueItem = new SnackbarMessageQueueItem(content, actionContent, actionHandler, actionArgument, argumentType, promote); if (promote) { InsertAsLastNotPromotedNode(snackbarMessageQueueItem); } else { _snackbarMessages.AddLast(snackbarMessageQueueItem); } _messageWaitingEvent.Set(); }
private static SnackbarMessage Create(SnackbarMessageQueueItem messageQueueItem) { return(new SnackbarMessage { Content = messageQueueItem.Content, ActionContent = messageQueueItem.ActionContent }); }
private async void PumpAsync() { while (!_isDisposed) { var eventId = WaitHandle.WaitAny(new WaitHandle[] { _disposedEvent, _messageWaitingEvent }); if (eventId == 0) { continue; } Snackbar exemplar = _pairedSnackbars.FirstOrDefault(); if (exemplar == null) { Trace.TraceWarning( "A snackbar message as waiting, but no Snackbar instances are assigned to the message queue."); _disposedEvent.WaitOne(TimeSpan.FromSeconds(1)); continue; } //find a target var snackbar = await FindSnackbar(exemplar.Dispatcher); //show message if (snackbar != null) { SnackbarMessageQueueItem message = _snackbarMessages.First.Value; _snackbarMessages.RemoveFirst(); if (_latestShownItem == null || IgnoreDuplicate || message.IgnoreDuplicate || !Equals(_latestShownItem.Item1.Content, message.Content) || !Equals(_latestShownItem.Item1.ActionContent, message.ActionContent) || _latestShownItem.Item2 <= DateTime.Now.Subtract(_messageDuration)) { await ShowAsync(snackbar, message); _latestShownItem = new Tuple <SnackbarMessageQueueItem, DateTime>(message, DateTime.Now); } } else { //no snackbar could be found, take a break _disposedEvent.WaitOne(TimeSpan.FromSeconds(1)); } if (_snackbarMessages.Count > 0) { _messageWaitingEvent.Set(); } else { _messageWaitingEvent.Reset(); } } }
/// <summary> /// Checks if given item is a duplicate to this /// </summary> /// <param name="item">Item to check for duplicate</param> /// <returns><c>true</c> if given item is a duplicate to this, <c>false</c> otherwise</returns> public bool IsDuplicate(SnackbarMessageQueueItem item) { if (item is null) { throw new ArgumentNullException(nameof(item)); } return(!IgnoreDuplicate && Equals(item.Content, Content) && Equals(item.ActionContent, ActionContent)); }
public bool IsDuplicate(SnackbarMessageQueueItem value) { if (value is null) { throw new ArgumentNullException(nameof(value)); } if (AlwaysShow) { return(false); } return(Equals(value)); }
private static void DoActionCallback(SnackbarMessageQueueItem messageQueueItem) { try { messageQueueItem.ActionHandler(messageQueueItem.ActionArgument); } catch (Exception exc) { Trace.WriteLine("Error during SnackbarMessageQueue message action callback, exception will be rethrown."); Trace.WriteLine($"{exc.Message} ({exc.GetType().FullName})"); Trace.WriteLine(exc.StackTrace); throw; } }
private void InsertAsLastNotPromotedNode(SnackbarMessageQueueItem snackbarMessageQueueItem) { var node = _snackbarMessages.First; while (node != null) { if (!node.Value.IsPromoted) { _snackbarMessages.AddBefore(node, snackbarMessageQueueItem); return; } node = node.Next; } _snackbarMessages.AddLast(snackbarMessageQueueItem); }
private async Task ShowAsync(Snackbar snackbar, SnackbarMessageQueueItem messageQueueItem) { await Task.Run(async() => { //create and show the message, setting up all the handles we need to wait on var actionClickWaitHandle = new ManualResetEvent(false); var mouseNotOverManagedWaitHandle = await snackbar.Dispatcher.InvokeAsync( () => CreateAndShowMessage(snackbar, messageQueueItem, actionClickWaitHandle)); var durationPassedWaitHandle = new ManualResetEvent(false); DurationMonitor.Start(messageQueueItem.Duration.Add(snackbar.ActivateStoryboardDuration), _pausedEvent, durationPassedWaitHandle, _disposedEvent); //wait until time span completed (including pauses and mouse overs), or the action is clicked await WaitForCompletionAsync(mouseNotOverManagedWaitHandle, durationPassedWaitHandle, actionClickWaitHandle); //close message on snackbar await snackbar.Dispatcher.InvokeAsync( () => snackbar.SetCurrentValue(Snackbar.IsActiveProperty, false)); //we could wait for the animation event, but just doing //this for now...at least it is prevent extra call back hell _disposedEvent.WaitOne(snackbar.DeactivateStoryboardDuration); //remove message on snackbar await snackbar.Dispatcher.InvokeAsync( () => snackbar.SetCurrentValue(Snackbar.MessageProperty, null)); mouseNotOverManagedWaitHandle.Dispose(); durationPassedWaitHandle.Dispose(); }) .ContinueWith(t => { if (t.Exception == null) { return; } var exc = t.Exception.InnerExceptions.FirstOrDefault() ?? t.Exception; Trace.WriteLine("Error occured whilst showing Snackbar, exception will be rethrown."); Trace.WriteLine($"{exc.Message} ({exc.GetType().FullName})"); Trace.WriteLine(exc.StackTrace); throw t.Exception; }); }
private static MouseNotOverManagedWaitHandle CreateAndShowMessage(UIElement snackbar, SnackbarMessageQueueItem messageQueueItem, EventWaitHandle actionClickWaitHandle) { var clickCount = 0; SnackbarMessage snackbarMessage = Create(messageQueueItem); snackbarMessage.ActionClick += (sender, args) => { if (++clickCount == 1) { DoActionCallback(messageQueueItem); } actionClickWaitHandle.Set(); }; snackbar.SetCurrentValue(Snackbar.MessageProperty, snackbarMessage); snackbar.SetCurrentValue(Snackbar.IsActiveProperty, true); return(new MouseNotOverManagedWaitHandle(snackbar)); }
public void Enqueue(object content, object actionContent, Action <object> actionHandler, object actionArgument, bool promote, bool neverConsiderToBeDuplicate, TimeSpan?durationOverride = null) { if (content is null) { throw new ArgumentNullException(nameof(content)); } if (actionContent is null ^ actionHandler is null) { throw new ArgumentException("All action arguments must be provided if any are provided.", actionContent != null ? nameof(actionContent) : nameof(actionHandler)); } var snackbarMessageQueueItem = new SnackbarMessageQueueItem(content, durationOverride ?? _messageDuration, actionContent, actionHandler, actionArgument, promote, neverConsiderToBeDuplicate); InsertItem(snackbarMessageQueueItem); }
private void InsertItem(SnackbarMessageQueueItem item) { var node = _snackbarMessages.First; while (node != null) { if (!IgnoreDuplicate && item.IsDuplicate(node.Value)) { return; } if (item.IsPromoted && !node.Value.IsPromoted) { _snackbarMessages.AddBefore(node, item); return; } node = node.Next; } _snackbarMessages.AddLast(item); }