using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using HandyControl.Data; using HandyControl.Interactivity; using HandyControl.Tools; using HandyControl.Tools.Extension; namespace HandyControl.Controls { /// /// 消息提醒 /// [TemplatePart(Name = ElementPanelMore, Type = typeof(Panel))] [TemplatePart(Name = ElementGridMain, Type = typeof(Grid))] [TemplatePart(Name = ElementButtonClose, Type = typeof(Button))] public class Growl : Control { #region Constants private const string ElementPanelMore = "PART_PanelMore"; private const string ElementGridMain = "PART_GridMain"; private const string ElementButtonClose = "PART_ButtonClose"; #endregion Constants #region Data private Panel _panelMore; private Grid _gridMain; private Button _buttonClose; private bool _showCloseButton; private bool _staysOpen; private int _waitTime = 6; /// /// 计数 /// private int _tickCount; /// /// 关闭计时器 /// private DispatcherTimer _timerClose; private static readonly Dictionary PanelDic = new Dictionary(); #endregion Data public Growl() { CommandBindings.Add(new CommandBinding(ControlCommands.Close, ButtonClose_OnClick)); CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, ButtonCancel_OnClick)); CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, ButtonOk_OnClick)); } public static void Register(string token, Panel panel) { if (string.IsNullOrEmpty(token) || panel == null) return; PanelDic[token] = panel; InitGrowlPanel(panel); } public static void Unregister(string token, Panel panel) { if (string.IsNullOrEmpty(token) || panel == null) return; if (PanelDic.ContainsKey(token)) { if (ReferenceEquals(PanelDic[token], panel)) { PanelDic.Remove(token); panel.ContextMenu = null; panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue); } } } public static void Unregister(Panel panel) { if (panel == null) return; var first = PanelDic.FirstOrDefault(item => ReferenceEquals(panel, item.Value)); if (!string.IsNullOrEmpty(first.Key)) { PanelDic.Remove(first.Key); panel.ContextMenu = null; panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue); } } public static void Unregister(string token) { if (string.IsNullOrEmpty(token)) return; if (PanelDic.ContainsKey(token)) { var panel = PanelDic[token]; PanelDic.Remove(token); panel.ContextMenu = null; panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue); } } protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); _buttonClose.Show(_showCloseButton); } protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); _buttonClose.Collapse(); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _panelMore = GetTemplateChild(ElementPanelMore) as Panel; _gridMain = GetTemplateChild(ElementGridMain) as Grid; _buttonClose = GetTemplateChild(ElementButtonClose) as Button; CheckNull(); Update(); } private void CheckNull() { if (_panelMore == null || _gridMain == null || _buttonClose == null) throw new Exception(); } private Func ActionBeforeClose { get; set; } /// /// 消息容器 /// public static Panel GrowlPanel { get; set; } internal static readonly DependencyProperty CancelStrProperty = DependencyProperty.Register( "CancelStr", typeof(string), typeof(Growl), new PropertyMetadata(default(string))); internal static readonly DependencyProperty ConfirmStrProperty = DependencyProperty.Register( "ConfirmStr", typeof(string), typeof(Growl), new PropertyMetadata(default(string))); public static readonly DependencyProperty ShowDateTimeProperty = DependencyProperty.Register( "ShowDateTime", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.TrueBox)); public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof(string), typeof(Growl), new PropertyMetadata(default(string))); public static readonly DependencyProperty TimeProperty = DependencyProperty.Register( "Time", typeof(DateTime), typeof(Growl), new PropertyMetadata(default(DateTime))); public static readonly DependencyProperty IconProperty = DependencyProperty.Register( "Icon", typeof(Geometry), typeof(Growl), new PropertyMetadata(default(Geometry))); public static readonly DependencyProperty IconBrushProperty = DependencyProperty.Register( "IconBrush", typeof(Brush), typeof(Growl), new PropertyMetadata(default(Brush))); public static readonly DependencyProperty TypeProperty = DependencyProperty.Register( "Type", typeof(InfoType), typeof(Growl), new PropertyMetadata(default(InfoType))); public static readonly DependencyProperty GrowlParentProperty = DependencyProperty.RegisterAttached( "GrowlParent", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) => { if ((bool)args.NewValue && o is Panel panel) { SetGrowlPanel(panel); } })); public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached( "Token", typeof(string), typeof(Growl), new PropertyMetadata(default(string), OnTokenChanged)); private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Panel panel) { if (e.NewValue == null) { Unregister(panel); } else { Register(e.NewValue.ToString(), panel); } } } public static void SetToken(DependencyObject element, string value) => element.SetValue(TokenProperty, value); public static string GetToken(DependencyObject element) => (string) element.GetValue(TokenProperty); public static void SetGrowlParent(DependencyObject element, bool value) => element.SetValue(GrowlParentProperty, value); public static bool GetGrowlParent(DependencyObject element) => (bool)element.GetValue(GrowlParentProperty); public InfoType Type { get => (InfoType)GetValue(TypeProperty); set => SetValue(TypeProperty, value); } internal string CancelStr { get => (string)GetValue(CancelStrProperty); set => SetValue(CancelStrProperty, value); } internal string ConfirmStr { get => (string)GetValue(ConfirmStrProperty); set => SetValue(ConfirmStrProperty, value); } public bool ShowDateTime { get => (bool)GetValue(ShowDateTimeProperty); set => SetValue(ShowDateTimeProperty, value); } public string Message { get => (string)GetValue(MessageProperty); set => SetValue(MessageProperty, value); } public DateTime Time { get => (DateTime)GetValue(TimeProperty); set => SetValue(TimeProperty, value); } public Geometry Icon { get => (Geometry)GetValue(IconProperty); set => SetValue(IconProperty, value); } public Brush IconBrush { get => (Brush)GetValue(IconBrushProperty); set => SetValue(IconBrushProperty, value); } /// /// 开始计时器 /// private void StartTimer() { _timerClose = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _timerClose.Tick += delegate { if (IsMouseOver) { _tickCount = 0; return; } _tickCount++; if (_tickCount >= _waitTime) Close(); }; _timerClose.Start(); } /// /// 消息容器 /// /// private static void SetGrowlPanel(Panel panel) { GrowlPanel = panel; InitGrowlPanel(panel); } private static void InitGrowlPanel(Panel panel) { var menuItem = new MenuItem { Header = Properties.Langs.Lang.Clear }; menuItem.Click += (s, e) => { foreach (var item in panel.Children.OfType()) { item.Close(); } }; panel.ContextMenu = new ContextMenu { Items = { menuItem } }; PanelElement.SetFluidMoveBehavior(panel, ResourceHelper.GetResource(ResourceToken.BehaviorXY400)); } private void Update() { if (ActionBeforeClose != null) { _staysOpen = true; _showCloseButton = false; _panelMore.IsEnabled = true; _panelMore.Show(); } var transform = new TranslateTransform { X = MaxWidth }; _gridMain.RenderTransform = transform; transform.BeginAnimation(TranslateTransform.XProperty, AnimationHelper.CreateAnimation(0)); if (!_staysOpen) StartTimer(); } /// /// 显示信息 /// /// private static void Show(GrowlInfo growlInfo) { #if netle40 Application.Current.Dispatcher.Invoke(new Action(() => { var ctl = new Growl { Message = growlInfo.Message, Time = DateTime.Now, Icon = ResourceHelper.GetResource(growlInfo.IconKey), IconBrush = ResourceHelper.GetResource(growlInfo.IconBrushKey), _showCloseButton = growlInfo.ShowCloseButton, ActionBeforeClose = growlInfo.ActionBeforeClose, _staysOpen = growlInfo.StaysOpen, ShowDateTime = growlInfo.ShowDateTime, ConfirmStr = growlInfo.ConfirmStr, CancelStr = growlInfo.CancelStr, Type = growlInfo.Type, _waitTime = Math.Max(growlInfo.WaitTime, 2) }; if (!string.IsNullOrEmpty(growlInfo.Token)) { if (PanelDic.TryGetValue(growlInfo.Token, out var panel)) { panel?.Children.Insert(0, ctl); } } else { GrowlPanel.Children.Insert(0, ctl); } })); #else Application.Current.Dispatcher.Invoke(() => { var ctl = new Growl { Message = growlInfo.Message, Time = DateTime.Now, Icon = ResourceHelper.GetResource(growlInfo.IconKey), IconBrush = ResourceHelper.GetResource(growlInfo.IconBrushKey), _showCloseButton = growlInfo.ShowCloseButton, ActionBeforeClose = growlInfo.ActionBeforeClose, _staysOpen = growlInfo.StaysOpen, ShowDateTime = growlInfo.ShowDateTime, ConfirmStr = growlInfo.ConfirmStr, CancelStr = growlInfo.CancelStr, Type = growlInfo.Type, _waitTime = Math.Max(growlInfo.WaitTime, 2) }; if (!string.IsNullOrEmpty(growlInfo.Token)) { if (PanelDic.TryGetValue(growlInfo.Token, out var panel)) { panel?.Children.Insert(0, ctl); } } else { GrowlPanel.Children.Insert(0, ctl); } }); #endif } /// /// 成功 /// /// /// public static void Success(string message, string token = "") => Success(new GrowlInfo { Message = message, Token = token }); /// /// 成功 /// /// public static void Success(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.SuccessGeometry; growlInfo.IconBrushKey = ResourceToken.SuccessBrush; growlInfo.Type = InfoType.Success; } Show(growlInfo); } /// /// 消息 /// /// /// public static void Info(string message, string token = "") => Info(new GrowlInfo { Message = message, Token = token }); /// /// 消息 /// /// public static void Info(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.InfoGeometry; growlInfo.IconBrushKey = ResourceToken.InfoBrush; growlInfo.Type = InfoType.Info; } Show(growlInfo); } /// /// 警告 /// /// /// public static void Warning(string message, string token = "") => Warning(new GrowlInfo { Message = message, Token = token }); /// /// 警告 /// /// public static void Warning(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.WarningGeometry; growlInfo.IconBrushKey = ResourceToken.WarningBrush; growlInfo.Type = InfoType.Warning; } Show(growlInfo); } /// /// 错误 /// /// /// public static void Error(string message, string token = "") => Error(new GrowlInfo { Message = message, Token = token }); /// /// 错误 /// /// public static void Error(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.ErrorGeometry; growlInfo.IconBrushKey = ResourceToken.DangerBrush; growlInfo.StaysOpen = true; growlInfo.Type = InfoType.Error; } Show(growlInfo); } /// /// 严重 /// /// /// public static void Fatal(string message, string token = "") => Fatal(new GrowlInfo { Message = message, Token = token }); /// /// 严重 /// /// public static void Fatal(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.FatalGeometry; growlInfo.IconBrushKey = ResourceToken.PrimaryTextBrush; growlInfo.StaysOpen = true; growlInfo.ShowCloseButton = false; growlInfo.Type = InfoType.Fatal; if (GrowlPanel.ContextMenu != null) GrowlPanel.ContextMenu.Opacity = 0; } Show(growlInfo); } /// /// 询问 /// /// /// /// public static void Ask(string message, Func actionBeforeClose, string token = "") => Ask(new GrowlInfo { Message = message, ActionBeforeClose = actionBeforeClose, Token = token }); /// /// 询问 /// /// public static void Ask(GrowlInfo growlInfo) { if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo)); if (!growlInfo.IsCustom) { growlInfo.IconKey = ResourceToken.AskGeometry; growlInfo.IconBrushKey = ResourceToken.AccentBrush; growlInfo.StaysOpen = true; growlInfo.ShowCloseButton = false; growlInfo.Type = InfoType.Ask; } Show(growlInfo); } private void ButtonClose_OnClick(object sender, RoutedEventArgs e) => Close(); /// /// 关闭 /// private void Close() { _timerClose?.Stop(); var transform = new TranslateTransform(); _gridMain.RenderTransform = transform; var animation = AnimationHelper.CreateAnimation(MaxWidth); animation.Completed += (s, e) => { if (Parent is Panel panel) { panel.Children.Remove(this); } }; transform.BeginAnimation(TranslateTransform.XProperty, animation); } /// /// 清除 /// /// public static void Clear(string token = "") { if (!string.IsNullOrEmpty(token)) { if (PanelDic.TryGetValue(token, out var panel)) { Clear(panel); } } else { Clear(GrowlPanel); } } /// /// 清除 /// /// private static void Clear(Panel panel) { panel.Children.Clear(); if (panel.ContextMenu != null) { panel.ContextMenu.IsOpen = false; panel.ContextMenu.Opacity = 1; } } private void ButtonCancel_OnClick(object sender, RoutedEventArgs e) { if (ActionBeforeClose?.Invoke(false) == true) { Close(); } } private void ButtonOk_OnClick(object sender, RoutedEventArgs e) { if (ActionBeforeClose?.Invoke(true) == true) { Close(); } } } }