private async Task ShowAsync(Banner banner, BannerMessageQueueItem messageQueueItem, ManualResetEvent actionClickWaitHandle)
        {
            //create and show the message, setting up all the handles we need to wait on
            var tuple         = CreateAndShowMessage(banner, messageQueueItem, actionClickWaitHandle);
            var bannerMessage = tuple.Item1;
            var mouseNotOverManagedWaitHandle = tuple.Item2;

            var durationPassedWaitHandle = new ManualResetEvent(false);

            StartDuration(messageQueueItem.Duration.Add(banner.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 banner
            banner.SetCurrentValue(Banner.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(banner.DeactivateStoryboardDuration);

            //this prevents missing resource warnings after the message is removed from the Banner
            //see https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/2040
            bannerMessage.Resources = BannerMessage.defaultResources;

            //remove message on banner
            banner.SetCurrentValue(Banner.MessageProperty, null);

            mouseNotOverManagedWaitHandle.Dispose();
            durationPassedWaitHandle.Dispose();
        }
        private void InsertItem(BannerMessageQueueItem item)
        {
            lock (_bannerMessagesLock)
            {
                var added = false;
                var node  = _bannerMessages.First;
                while (node != null)
                {
                    if (!IgnoreDuplicate && item.IsDuplicate(node.Value))
                    {
                        return;
                    }

                    if (item.IsPromoted && !node.Value.IsPromoted)
                    {
                        _bannerMessages.AddBefore(node, item);
                        added = true;
                        break;
                    }
                    node = node.Next;
                }
                if (!added)
                {
                    _bannerMessages.AddLast(item);
                }
            }

            _dispatcher.InvokeAsync(ShowNextAsync);
        }
        /// <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(BannerMessageQueueItem item)
        {
            if (item is null)
            {
                throw new ArgumentNullException(nameof(item));
            }

            return(!IgnoreDuplicate &&
                   Equals(item.Content, Content) &&
                   Equals(item.ActionContent, ActionContent));
        }
        private static void DoActionCallback(BannerMessageQueueItem messageQueueItem)
        {
            try
            {
                messageQueueItem.ActionHandler?.Invoke(messageQueueItem.ActionArgument);
            }
            catch (Exception exc)
            {
                Trace.WriteLine("Error during BannerMessageQueue message action callback, exception will be rethrown.");
                Trace.WriteLine($"{exc.Message} ({exc.GetType().FullName})");
                Trace.WriteLine(exc.StackTrace);

                throw;
            }
        }
        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 bannerMessageQueueItem = new BannerMessageQueueItem(content, durationOverride ?? _messageDuration,
                                                                    actionContent, actionHandler, actionArgument, promote, neverConsiderToBeDuplicate);

            InsertItem(bannerMessageQueueItem);
        }
        private static Tuple <BannerMessage, MouseNotOverManagedWaitHandle> CreateAndShowMessage(UIElement banner,
                                                                                                 BannerMessageQueueItem messageQueueItem, EventWaitHandle actionClickWaitHandle)
        {
            var clickCount    = 0;
            var bannerMessage = new BannerMessage
            {
                Content       = messageQueueItem.Content,
                ActionContent = messageQueueItem.ActionContent
            };

            bannerMessage.ActionClick += (sender, args) =>
            {
                if (++clickCount == 1)
                {
                    DoActionCallback(messageQueueItem);
                }
                actionClickWaitHandle.Set();
            };
            banner.SetCurrentValue(Banner.MessageProperty, bannerMessage);
            banner.SetCurrentValue(Banner.IsActiveProperty, true);
            return(Tuple.Create(bannerMessage, new MouseNotOverManagedWaitHandle(banner)));
        }