尝试创建Class Toast通知,无法多次实例化 [英] Trying to create a Class Toast Notification, cannot instance it multiple times

查看:45
本文介绍了尝试创建Class Toast通知,无法多次实例化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在WPF中创建Toast通知,我的问题是,在这种情况下,如果我从数据库中删除某些内容的速度过快,则当前通知仅会使用新文本进行升级,而不是创建以下内容的新实例:上课,所以我可以看到两个通知.

I am trying to create a Toast Notification in WPF, my problem is that if I, in this case, delete something from the database too quickly, the current notification just gets upgraded with the new text instead of creating a new instance of the class, so I can see two notifications.

该通知从MainWindow变为StackPanel的子级,这样它始终具有相同的位置.这就是为什么我一开始就清除孩子们的原因.

The notification becomes a child of a StackPanelfrom the MainWindow, that way it always has the same position. That is why I clear the children at the beginning.

该类正常工作,只是我一次不能收到多个通知.

The class works, it's just that I cannot manage to have more than one notification at a time.

我想念什么?抱歉,我是一个初学者,我尝试自己做.

What am I missing? Sorry, I am quite a beginner and I tried to do this by myself.

这是我的班级代码

{
   public class NotificationToast
    {
        public Border ToastBorder { get; set; }
        public Grid ToastGrid { get; set; }
        public TextBlock BodyBlock { get; set; }
        public String NotificationString { get; set; }
        public DoubleAnimation Animation1 { get; set; }
        public DoubleAnimation Animation2 { get; set; }
        public TranslateTransform Transformation1 { get; set; }
        public TranslateTransform Transformation2 { get; set; }

        public NotificationToast(MainWindow window, string notificationString)
        {
            InitializeWindow(window, notificationString);   
        }

        private void InitializeWindow(MainWindow window, string notificationString)
        {
            NotificationString = notificationString;
            ToastBorder = new Border();
            ToastBorder.Width = 250;
            ToastBorder.Height = 70;
            ToastBorder.BorderThickness = new Thickness(2);
            ToastBorder.BorderBrush = Brushes.IndianRed;
            ToastBorder.Background = new SolidColorBrush(Color.FromRgb(240, 143, 116));
            TextBlock BodyBlock = new TextBlock();
            BodyBlock.Width = 248;
            BodyBlock.Height = 68;
            BodyBlock.TextWrapping = TextWrapping.Wrap;
            BodyBlock.FontSize = 16;
            BodyBlock.Text = NotificationString;
            BodyBlock.Margin = new Thickness(5);

            ToastBorder.Child = BodyBlock;
            window.stackNotification.Children.Clear();
            window.stackNotification.Children.Add(ToastBorder);

            MovingIn(window.stackNotification);
            MovingOut(window.stackNotification);

        }

        private void MovingIn(StackPanel movingBorder)
        {
            TranslateTransform trans = new TranslateTransform();
            movingBorder.RenderTransform = trans;
            Animation1 = new DoubleAnimation(80, 0, TimeSpan.FromSeconds(1));
            trans.BeginAnimation(TranslateTransform.YProperty, Animation1);
        }
        private async void MovingOut(StackPanel movingBorder)
        {
            await Task.Delay(2500);

            TranslateTransform trans = new TranslateTransform();
            movingBorder.RenderTransform = trans;
            Animation2 = new DoubleAnimation(0, 80, TimeSpan.FromSeconds(1));
            trans.BeginAnimation(TranslateTransform.YProperty, Animation2);
        }
    }
}

然后我这样叫课

WindowToast = new NotificationToast(ParentWindow, Convert.ToString("The Player " + PersonDetails.FirstName + " " + PersonDetails.LastName + " details has been updated."));

推荐答案

以下是使用 Disclamer::我是从头开始做的,而且我不是专业的程序员.WPF是我的业余爱好.因此,该解决方案经过了测试,但可能包含错误或不正确的实现.不要相信我.

Disclamer: I did it by myself from the scratch and I'm not a professional programmer. WPF is my hobby not work. Thus, the solution tested but may contain bugs or inaccurate implementations. Don't trust me.

由于采用了模式方法,我们需要两个帮助器类

Due to the pattern approach, we need two helper classes

// INPC interface implementation, used for notifying UI if some Property was changed.
public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

// Needed for easy Commands use
public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
}

该解决方案的功能:

  • 在右下角显示Toast消息
  • 支持3种严重程度:信息,警告,错误.消息的颜色取决于它.
  • 支持调整邮件的最大数量以一次显示
  • 排队收到超出限制的消息,稍后再显示
  • 用户可以立即关闭任何消息
  • 每条消息在10秒钟后消失
  • 添加了一些外观和消失动画

注意:

  • 不支持并发,例如推送来自不同线程/任务的消息.
  • 不是UserControl或独立解决方案.

...但是您可以改善它.:)

...but you may improve it. :)

数据类:

public enum ToastMessageSeverity
{
    Info = 0,
    Warning = 1,
    Error = 2
}

public class ToastMessage
{
    public string Message { get; set; }
    public ToastMessageSeverity Severity { get; set; }
}

ToastViewModel

ToastViewModel

public class ToastViewModel : ReadOnlyObservableCollection<ToastMessage>
{
    private readonly ObservableCollection<ToastMessage> _items;
    private readonly int _maxCount;
    private readonly Queue<ToastMessage> _messagesQueue;
    private ICommand _removeItem;

    private void RemoveMessage(ToastMessage message)
    {
        if (_items.Contains(message))
        {
            _items.Remove(message);
            if (_messagesQueue.Count > 0) Push(_messagesQueue.Dequeue());
        }
    }

    public void Push(ToastMessage message)
    {
        if (_items.Count >= _maxCount)
            _messagesQueue.Enqueue(message);
        else
        {
            _items.Add(message);
            Task.Run(async () => await Task.Delay(10500)).ContinueWith(_ => RemoveMessage(message), TaskScheduler.FromCurrentSynchronizationContext());
        }
    }

    public ICommand RemoveItem => _removeItem ?? (_removeItem = new RelayCommand(parameter => 
    {
        if (parameter is ToastMessage message) RemoveMessage(message);
    }));

    public ToastViewModel() : this(6) { }
    public ToastViewModel(int maxCount) : this(new ObservableCollection<ToastMessage>(), maxCount) { }
    private ToastViewModel(ObservableCollection<ToastMessage> items, int maxCount) : base(items)
    {
        _items = items;
        _maxCount = maxCount;
        _messagesQueue = new Queue<ToastMessage>();
    }
}

MainViewModel

MainViewModel

public class MainViewModel : NotifyPropertyChanged
{
    private ToastViewModel _toastMessages;
    private ICommand _pushToastCommand;
    public ToastViewModel ToastMessages
    {
        get => _toastMessages;
        set
        {
            _toastMessages = value;
            OnPropertyChanged();
        }
    }
    private int counter = 0;
    public ICommand PushToastCommand => _pushToastCommand ?? (_pushToastCommand = new RelayCommand(parameter =>
    {
        ToastMessageSeverity severity = ToastMessageSeverity.Info;
        if (parameter is string severityString)
        {
            foreach (ToastMessageSeverity tms in Enum.GetValues(typeof(ToastMessageSeverity)))
            {
                if (severityString == tms.ToString())
                {
                    severity = tms;
                    break;
                }
            }
        }
        ToastMessages.Push(new ToastMessage { Message = severity + " message " + counter++, Severity = severity });
    }));
    public MainViewModel()
    {
        ToastMessages = new ToastViewModel();
    }
}

完整的标记(将允许复制整个应用程序)

And full markup (will allow to reproduce the entire app)

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        Title="MainWindow" Height="600" Width="1000" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid>
            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Button Content="Info message" Command="{Binding PushToastCommand}" Padding="10,5" Margin="10" />
                <Button Content="Warning message" Command="{Binding PushToastCommand}" CommandParameter="Warning" Padding="10,5" Margin="10" />
                <Button Content="Error message" Command="{Binding PushToastCommand}" CommandParameter="Error" Padding="10,5" Margin="10" />
            </StackPanel>
        </Grid>
        <Grid>
            <ItemsControl ItemsSource="{Binding ToastMessages}" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border HorizontalAlignment="Right" >
                            <Border.Style>
                                <Style TargetType="Border">
                                    <Setter Property="BorderThickness" Value="2"/>
                                    <Setter Property="CornerRadius" Value="5"/>
                                    <Setter Property="Margin" Value="10,5"/>
                                    <Setter Property="Padding" Value="15,10"/>
                                    <Setter Property="MaxWidth" Value="300"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Severity}" Value="Info">
                                            <Setter Property="BorderBrush" Value="CadetBlue"/>
                                            <Setter Property="Background" Value="LightCyan"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Severity}" Value="Warning">
                                            <Setter Property="BorderBrush" Value="Orange"/>
                                            <Setter Property="Background" Value="LightYellow"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Severity}" Value="Error">
                                            <Setter Property="BorderBrush" Value="Red"/>
                                            <Setter Property="Background" Value="LightPink"/>
                                        </DataTrigger>
                                        <EventTrigger RoutedEvent="Border.Loaded">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.5" />
                                                    <ThicknessAnimation Storyboard.TargetProperty="Margin" From="10,15,10,-5" To="10,5" Duration="0:0:0.5" />
                                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" BeginTime="0:0:10" Duration="0:0:0.2" />
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Border.Style>
                            <Grid>
                                <TextBlock Text="{Binding Message}" FontSize="16" TextWrapping="Wrap" />
                                <Button HorizontalAlignment="Right" VerticalAlignment="Top" Margin="-13" Command="{Binding ItemsSource.RemoveItem, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}">
                                    <Button.Template>
                                        <ControlTemplate>
                                            <TextBlock Text="×" FontSize="16" Foreground="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=Border}}" Cursor="Hand"/>
                                        </ControlTemplate>
                                    </Button.Template>
                                </Button>
                            </Grid>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </Grid>
</Window>

[

传统上, MVVM 新用户:代码隐藏类

And traditionally for MVVM newcomers: code-behind class

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

这篇关于尝试创建Class Toast通知,无法多次实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆