chore: add ControlTokenManager.

This commit is contained in:
NaBian committed 2025-08-03 21:38:52 +08:00
1 parent f9982820a9
commit 2b35e4bdaf
8 files changed
+287 -310

No files matched your search

@@ -11,7 +11,7 @@
Background="{DynamicResource SecondaryRegionBrush}"
ResizeMode="CanResizeWithGrip"
WindowStartupLocation="CenterScreen"
d:DesignHeight="800"
d:DesignHeight="800"
d:DesignWidth="1400"
hc:Empty.ShowEmpty="True"
Icon="/HandyControlDemo;component/Resources/Img/icon.ico">
@@ -4,7 +4,7 @@
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
xmlns:ex="clr-namespace:HandyControlDemo.Tools.Extension"
Background="{DynamicResource RegionBrush}"
Background="{DynamicResource RegionBrush}"
MaxWidth="550"
Height="600">
<hc:TransitioningContentControl>
@@ -45,7 +45,7 @@
</ScrollViewer>
</Border>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,16,0,0">
<ScrollViewer>
<ScrollViewer hc:ScrollViewerAttach.IsHoverResizingEnabled="False" hc:ScrollViewerAttach.AutoHide="False">
<WrapPanel Orientation="Horizontal">
<Border Style="{StaticResource BorderRegion}" Width="200" Height="200" Margin="10,10,32,10">
<Border Background="{DynamicResource PrimaryBrush}">
@@ -13,8 +13,11 @@ namespace HandyControl.Controls;
public class ConfettiCannon
{
private static readonly ControlTokenManager<FrameworkElement> TokenManager =
new(unregisterCallback: OnTokenUnregistered);
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(ConfettiCannon), new PropertyMetadata(null, OnTokenChanged));
"Token", typeof(string), typeof(ConfettiCannon), new PropertyMetadata(null, TokenManager.OnTokenChanged));
public static void SetToken(DependencyObject element, string value)
=> element.SetValue(TokenProperty, value);
@@ -23,7 +26,6 @@ public class ConfettiCannon
=> (string) element.GetValue(TokenProperty);
private static readonly ConcurrentDictionary<string, ConfettiCannon> ConfettiCannons = new();
private static readonly Dictionary<string, FrameworkElement> ContainerDict = new();
private static readonly Lazy<ConfettiCannon> DefaultConfettiCannon = new(() => new ConfettiCannon(), true);
private static readonly Random Random = new();
@@ -50,35 +52,8 @@ public class ConfettiCannon
confettiCannon.StartAnimation();
}
public static void Register(string token, FrameworkElement element)
private static void OnTokenUnregistered(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element is null)
{
return;
}
ContainerDict[token] = element;
}
public static void Unregister(FrameworkElement element)
{
if (element is null)
{
return;
}
var first = ContainerDict.FirstOrDefault(item => ReferenceEquals(element, item.Value));
Unregister(first.Key);
}
public static void Unregister(string token)
{
if (string.IsNullOrEmpty(token))
{
return;
}
ContainerDict.Remove(token);
ConfettiCannons.TryRemove(token, out _);
}
@@ -98,23 +73,6 @@ public class ConfettiCannon
return ConfettiCannons[token];
}
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not FrameworkElement element)
{
return;
}
if (e.NewValue is null)
{
Unregister(element);
}
else
{
Register(e.NewValue.ToString(), element);
}
}
private static AdornerLayer GetAdornerLayer(string token)
{
AdornerDecorator decorator;
@@ -125,7 +83,7 @@ public class ConfettiCannon
}
else
{
ContainerDict.TryGetValue(token, out FrameworkElement element);
TokenManager.TryGetControl(token, out FrameworkElement element);
decorator = element is System.Windows.Window
? VisualHelper.GetChild<AdornerDecorator>(element)
: VisualHelper.GetChild<ConfettiCannonContainer>(element);
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
@@ -20,7 +19,7 @@ public class Dialog : ContentControl
private Border _backElement;
private AdornerContainer _container;
private static readonly Dictionary<string, FrameworkElement> ContainerDict = new();
private static readonly ControlTokenManager<FrameworkElement> TokenManager = new();
private static readonly Dictionary<string, Dialog> DialogDict = new();
public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register(
@@ -33,7 +32,8 @@ public class Dialog : ContentControl
}
public static readonly DependencyProperty MaskCanCloseProperty = DependencyProperty.RegisterAttached(
"MaskCanClose", typeof(bool), typeof(Dialog), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
"MaskCanClose", typeof(bool), typeof(Dialog),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetMaskCanClose(DependencyObject element, bool value)
=> element.SetValue(MaskCanCloseProperty, ValueBoxes.BooleanBox(value));
@@ -51,22 +51,7 @@ public class Dialog : ContentControl
}
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(Dialog), new PropertyMetadata(default(string), OnTokenChanged));
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement element)
{
if (e.NewValue == null)
{
Unregister(element);
}
else
{
Register(e.NewValue.ToString(), element);
}
}
}
"Token", typeof(string), typeof(Dialog), new PropertyMetadata(null, TokenManager.OnTokenChanged));
public static void SetToken(DependencyObject element, string value)
=> element.SetValue(TokenProperty, value);
@@ -79,45 +64,6 @@ public class Dialog : ContentControl
CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
}
public static void Register(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
ContainerDict[token] = element;
}
public static void Unregister(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
if (ContainerDict.ContainsKey(token))
{
if (ReferenceEquals(ContainerDict[token], element))
{
ContainerDict.Remove(token);
}
}
}
public static void Unregister(FrameworkElement element)
{
if (element == null) return;
var first = ContainerDict.FirstOrDefault(item => ReferenceEquals(element, item.Value));
if (!string.IsNullOrEmpty(first.Key))
{
ContainerDict.Remove(first.Key);
}
}
public static void Unregister(string token)
{
if (string.IsNullOrEmpty(token)) return;
if (ContainerDict.ContainsKey(token))
{
ContainerDict.Remove(token);
}
}
public static Dialog Show<T>(string token = "") where T : new() => Show(new T(), token);
public static Dialog Show(object content, string token = "")
@@ -140,10 +86,10 @@ public class Dialog : ContentControl
{
Close(token);
DialogDict[token] = dialog;
ContainerDict.TryGetValue(token, out element);
decorator = element is System.Windows.Window ?
VisualHelper.GetChild<AdornerDecorator>(element) :
VisualHelper.GetChild<DialogContainer>(element);
TokenManager.TryGetControl(token, out element);
decorator = element is System.Windows.Window
? VisualHelper.GetChild<AdornerDecorator>(element)
: VisualHelper.GetChild<DialogContainer>(element);
}
if (decorator != null)
@@ -183,7 +129,7 @@ public class Dialog : ContentControl
{
Close(WindowHelper.GetActiveWindow());
}
else if (ContainerDict.TryGetValue(_token, out var element))
else if (TokenManager.TryGetControl(_token, out var element))
{
Close(element);
DialogDict.Remove(_token);
@@ -216,6 +162,7 @@ public class Dialog : ContentControl
{
decorator.Child.IsEnabled = true;
}
var layer = decorator.AdornerLayer;
layer?.Remove(_container);
IsClosed = true;
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -28,6 +27,11 @@ public class Growl : Control
private const string ElementButtonClose = "PART_ButtonClose";
private const int MinWaitTime = 2;
private static GrowlWindow GrowlWindow;
private static readonly ControlTokenManager<Panel> TokenManager =
new(registerCallback: OnTokenRegistered, unregisterCallback: OnTokenUnregistered);
public static readonly DependencyProperty GrowlParentProperty = DependencyProperty.RegisterAttached(
"GrowlParent", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
@@ -36,31 +40,40 @@ public class Growl : Control
SetGrowlPanel(panel);
}
}));
public static readonly DependencyProperty ShowModeProperty = DependencyProperty.RegisterAttached(
"ShowMode", typeof(GrowlShowMode), typeof(Growl),
new FrameworkPropertyMetadata(default(GrowlShowMode), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty ShowDateTimeProperty = DependencyProperty.Register(
nameof(ShowDateTime), typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
nameof(Message), typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register(
nameof(Time), typeof(DateTime), typeof(Growl), new PropertyMetadata(default(DateTime)));
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
nameof(Icon), typeof(Geometry), typeof(Growl), new PropertyMetadata(default(Geometry)));
public static readonly DependencyProperty IconBrushProperty = DependencyProperty.Register(
nameof(IconBrush), typeof(Brush), typeof(Growl), new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
nameof(Type), typeof(InfoType), typeof(Growl), new PropertyMetadata(default(InfoType)));
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(Growl), new PropertyMetadata(default(string), OnTokenChanged));
"Token", typeof(string), typeof(Growl), new PropertyMetadata(null, TokenManager.OnTokenChanged));
internal static readonly DependencyProperty CancelStrProperty = DependencyProperty.Register(
nameof(CancelStr), typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
internal static readonly DependencyProperty ConfirmStrProperty = DependencyProperty.Register(
nameof(ConfirmStr), typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
private static readonly DependencyProperty IsCreatedAutomaticallyProperty = DependencyProperty.RegisterAttached(
"IsCreatedAutomatically", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.FalseBox));
private static GrowlWindow GrowlWindow;
private static readonly Dictionary<string, Panel> PanelDic = new();
private Panel _panelMore;
private Grid _gridMain;
@@ -141,51 +154,15 @@ public class Growl : Control
CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, ButtonOk_OnClick));
}
public static void Register(string token, Panel panel)
private static void OnTokenRegistered(string token, Panel panel)
{
if (string.IsNullOrEmpty(token) || panel == null) return;
PanelDic[token] = panel;
InitGrowlPanel(panel);
}
public static void Unregister(string token, Panel panel)
private static void OnTokenUnregistered(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);
}
panel.ContextMenu = null;
panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
}
protected override void OnMouseEnter(MouseEventArgs e)
@@ -219,36 +196,26 @@ public class Growl : Control
if (_panelMore == null || _gridMain == null || _buttonClose == null) throw new Exception();
}
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 SetShowMode(DependencyObject element, GrowlShowMode value) => element.SetValue(ShowModeProperty, value);
public static void SetShowMode(DependencyObject element, GrowlShowMode value) =>
element.SetValue(ShowModeProperty, value);
public static GrowlShowMode GetShowMode(DependencyObject element) => (GrowlShowMode) element.GetValue(ShowModeProperty);
public static GrowlShowMode GetShowMode(DependencyObject element) =>
(GrowlShowMode) element.GetValue(ShowModeProperty);
public static void SetGrowlParent(DependencyObject element, bool value) => element.SetValue(GrowlParentProperty, ValueBoxes.BooleanBox(value));
public static void SetGrowlParent(DependencyObject element, bool value) =>
element.SetValue(GrowlParentProperty, ValueBoxes.BooleanBox(value));
public static bool GetGrowlParent(DependencyObject element) => (bool) element.GetValue(GrowlParentProperty);
private static void SetIsCreatedAutomatically(DependencyObject element, bool value) => element.SetValue(IsCreatedAutomaticallyProperty, ValueBoxes.BooleanBox(value));
private static void SetIsCreatedAutomatically(DependencyObject element, bool value) =>
element.SetValue(IsCreatedAutomaticallyProperty, ValueBoxes.BooleanBox(value));
private static bool GetIsCreatedAutomatically(DependencyObject element) => (bool) element.GetValue(IsCreatedAutomaticallyProperty);
private static bool GetIsCreatedAutomatically(DependencyObject element) =>
(bool) element.GetValue(IsCreatedAutomaticallyProperty);
/// <summary>
/// 开始计时器
@@ -308,7 +275,8 @@ public class Growl : Control
}
};
PanelElement.SetFluidMoveBehavior(panel, ResourceHelper.GetResourceInternal<FluidMoveBehavior>(ResourceToken.BehaviorXY400));
PanelElement.SetFluidMoveBehavior(panel,
ResourceHelper.GetResourceInternal<FluidMoveBehavior>(ResourceToken.BehaviorXY400));
}
private void Update()
@@ -353,37 +321,37 @@ public class Growl : Control
#if NET40
new Action(
#endif
() =>
() =>
{
if (GrowlWindow == null)
{
if (GrowlWindow == null)
{
GrowlWindow = new GrowlWindow();
GrowlWindow.Show();
InitGrowlPanel(GrowlWindow.GrowlPanel);
GrowlWindow.Init();
}
GrowlWindow.Show(true);
var ctl = new Growl
{
Message = growlInfo.Message,
Time = DateTime.Now,
Icon = ResourceHelper.GetResource<Geometry>(growlInfo.IconKey) ?? growlInfo.Icon,
IconBrush = ResourceHelper.GetResource<Brush>(growlInfo.IconBrushKey) ?? growlInfo.IconBrush,
_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, MinWaitTime),
FlowDirection = growlInfo.FlowDirection
};
ShowInternal(GrowlWindow.GrowlPanel, ctl);
GrowlWindow = new GrowlWindow();
GrowlWindow.Show();
InitGrowlPanel(GrowlWindow.GrowlPanel);
GrowlWindow.Init();
}
GrowlWindow.Show(true);
var ctl = new Growl
{
Message = growlInfo.Message,
Time = DateTime.Now,
Icon = ResourceHelper.GetResource<Geometry>(growlInfo.IconKey) ?? growlInfo.Icon,
IconBrush = ResourceHelper.GetResource<Brush>(growlInfo.IconBrushKey) ?? growlInfo.IconBrush,
_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, MinWaitTime),
FlowDirection = growlInfo.FlowDirection
};
ShowInternal(GrowlWindow.GrowlPanel, ctl);
}
#if NET40
)
#endif
@@ -400,38 +368,38 @@ public class Growl : Control
#if NET40
new Action(
#endif
() =>
() =>
{
var ctl = new Growl
{
var ctl = new Growl
{
Message = growlInfo.Message,
Time = DateTime.Now,
Icon = ResourceHelper.GetResource<Geometry>(growlInfo.IconKey) ?? growlInfo.Icon,
IconBrush = ResourceHelper.GetResource<Brush>(growlInfo.IconBrushKey) ?? growlInfo.IconBrush,
_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, MinWaitTime)
};
Message = growlInfo.Message,
Time = DateTime.Now,
Icon = ResourceHelper.GetResource<Geometry>(growlInfo.IconKey) ?? growlInfo.Icon,
IconBrush = ResourceHelper.GetResource<Brush>(growlInfo.IconBrushKey) ?? growlInfo.IconBrush,
_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, MinWaitTime)
};
if (!string.IsNullOrEmpty(growlInfo.Token))
if (!string.IsNullOrEmpty(growlInfo.Token))
{
if (TokenManager.TryGetControl(growlInfo.Token, out var panel))
{
if (PanelDic.TryGetValue(growlInfo.Token, out var panel))
{
ShowInternal(panel, ctl);
}
}
else
{
// GrowlPanel is null, we create it automatically
GrowlPanel ??= CreateDefaultPanel();
ShowInternal(GrowlPanel, ctl);
ShowInternal(panel, ctl);
}
}
else
{
// GrowlPanel is null, we create it automatically
GrowlPanel ??= CreateDefaultPanel();
ShowInternal(GrowlPanel, ctl);
}
}
#if NET40
)
#endif
@@ -514,6 +482,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.SuccessGeometry;
growlInfo.IconBrushKey ??= ResourceToken.SuccessBrush;
}
break;
case InfoType.Info:
if (!growlInfo.IsCustom)
@@ -526,6 +495,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.InfoGeometry;
growlInfo.IconBrushKey ??= ResourceToken.InfoBrush;
}
break;
case InfoType.Warning:
if (!growlInfo.IsCustom)
@@ -538,6 +508,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.WarningGeometry;
growlInfo.IconBrushKey ??= ResourceToken.WarningBrush;
}
break;
case InfoType.Error:
if (!growlInfo.IsCustom)
@@ -551,6 +522,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.ErrorGeometry;
growlInfo.IconBrushKey ??= ResourceToken.DangerBrush;
}
break;
case InfoType.Fatal:
if (!growlInfo.IsCustom)
@@ -565,6 +537,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.FatalGeometry;
growlInfo.IconBrushKey ??= ResourceToken.PrimaryTextBrush;
}
break;
case InfoType.Ask:
growlInfo.StaysOpen = true;
@@ -579,6 +552,7 @@ public class Growl : Control
growlInfo.IconKey ??= ResourceToken.AskGeometry;
growlInfo.IconBrushKey ??= ResourceToken.AccentBrush;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(infoType), infoType, null);
@@ -844,7 +818,8 @@ public class Growl : Control
_timerClose?.Stop();
var transform = new TranslateTransform();
_gridMain.RenderTransform = transform;
var animation = AnimationHelper.CreateAnimation(FlowDirection == FlowDirection.LeftToRight ? ActualWidth : -ActualWidth);
var animation =
AnimationHelper.CreateAnimation(FlowDirection == FlowDirection.LeftToRight ? ActualWidth : -ActualWidth);
animation.Completed += (s, e) =>
{
if (Parent is Panel panel)
@@ -853,7 +828,7 @@ public class Growl : Control
if (GrowlWindow != null)
{
if (GrowlWindow.GrowlPanel != null && GrowlWindow.GrowlPanel.Children.Count == 0)
if (GrowlWindow.GrowlPanel is { Children.Count: 0 })
{
GrowlWindow.Close();
GrowlWindow = null;
@@ -861,9 +836,9 @@ public class Growl : Control
}
else
{
if (GrowlPanel != null && GrowlPanel.Children.Count == 0 && GetIsCreatedAutomatically(GrowlPanel))
if (GrowlPanel is { Children.Count: 0 } && GetIsCreatedAutomatically(GrowlPanel))
{
// If the count of children is zero, we need to remove the panel, provided that the panel was created automatically
// If the count of children is zero, we need to remove the panel, provided that the panel was created automatically
RemoveDefaultPanel(GrowlPanel);
GrowlPanel = null;
}
@@ -881,7 +856,7 @@ public class Growl : Control
{
if (!string.IsNullOrEmpty(token))
{
if (PanelDic.TryGetValue(token, out var panel))
if (TokenManager.TryGetControl(token, out var panel))
{
Clear(panel);
}
@@ -58,13 +58,16 @@ public class NotifyIcon : FrameworkElement, IDisposable
private static int NextId;
private static readonly Dictionary<string, NotifyIcon> NotifyIconDic = new();
private static readonly ControlTokenManager<NotifyIcon> TokenManager = new();
static NotifyIcon()
{
VisibilityProperty.OverrideMetadata(typeof(NotifyIcon), new PropertyMetadata(ValueBoxes.VisibleBox, OnVisibilityChanged));
DataContextProperty.OverrideMetadata(typeof(NotifyIcon), new FrameworkPropertyMetadata(DataContextPropertyChanged));
ContextMenuProperty.OverrideMetadata(typeof(NotifyIcon), new FrameworkPropertyMetadata(ContextMenuPropertyChanged));
VisibilityProperty.OverrideMetadata(typeof(NotifyIcon),
new PropertyMetadata(ValueBoxes.VisibleBox, OnVisibilityChanged));
DataContextProperty.OverrideMetadata(typeof(NotifyIcon),
new FrameworkPropertyMetadata(DataContextPropertyChanged));
ContextMenuProperty.OverrideMetadata(typeof(NotifyIcon),
new FrameworkPropertyMetadata(ContextMenuPropertyChanged));
}
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -78,6 +81,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
{
ctl.OnIconChanged();
}
ctl.UpdateIcon(true);
}
else if (ctl._iconCurrentHandle != IntPtr.Zero)
@@ -132,48 +136,9 @@ public class NotifyIcon : FrameworkElement, IDisposable
_dispatcherTimerPos.Tick += DispatcherTimerPos_Tick;
}
public static void Register(string token, NotifyIcon notifyIcon)
{
if (string.IsNullOrEmpty(token) || notifyIcon == null) return;
NotifyIconDic[token] = notifyIcon;
}
public static void Unregister(string token, NotifyIcon notifyIcon)
{
if (string.IsNullOrEmpty(token) || notifyIcon == null) return;
if (NotifyIconDic.ContainsKey(token))
{
if (ReferenceEquals(NotifyIconDic[token], notifyIcon))
{
NotifyIconDic.Remove(token);
}
}
}
public static void Unregister(NotifyIcon notifyIcon)
{
if (notifyIcon == null) return;
var first = NotifyIconDic.FirstOrDefault(item => ReferenceEquals(notifyIcon, item.Value));
if (!string.IsNullOrEmpty(first.Key))
{
NotifyIconDic.Remove(first.Key);
}
}
public static void Unregister(string token)
{
if (string.IsNullOrEmpty(token)) return;
if (NotifyIconDic.ContainsKey(token))
{
NotifyIconDic.Remove(token);
}
}
public static void ShowBalloonTip(string title, string content, NotifyIconInfoType infoType, string token)
{
if (NotifyIconDic.TryGetValue(token, out var notifyIcon))
if (TokenManager.TryGetControl(token, out var notifyIcon))
{
notifyIcon.ShowBalloonTip(title, content, infoType);
}
@@ -223,22 +188,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
}
public static readonly DependencyProperty TokenProperty = DependencyProperty.Register(
nameof(Token), typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string), OnTokenChanged));
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NotifyIcon notifyIcon)
{
if (e.NewValue == null)
{
Unregister(notifyIcon);
}
else
{
Register(e.NewValue.ToString(), notifyIcon);
}
}
}
nameof(Token), typeof(string), typeof(NotifyIcon), new PropertyMetadata(null, TokenManager.OnTokenChanged));
public string Token
{
@@ -247,7 +197,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text), typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string), OnTextChanged));
nameof(Text), typeof(string), typeof(NotifyIcon), new PropertyMetadata(null, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
@@ -268,7 +218,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
nameof(Icon), typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(default(ImageSource), OnIconChanged));
nameof(Icon), typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(null, OnIconChanged));
private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
@@ -298,7 +248,8 @@ public class NotifyIcon : FrameworkElement, IDisposable
}
public static readonly DependencyProperty BlinkIntervalProperty = DependencyProperty.Register(
nameof(BlinkInterval), typeof(TimeSpan), typeof(NotifyIcon), new PropertyMetadata(TimeSpan.FromMilliseconds(500), OnBlinkIntervalChanged));
nameof(BlinkInterval), typeof(TimeSpan), typeof(NotifyIcon),
new PropertyMetadata(TimeSpan.FromMilliseconds(500), OnBlinkIntervalChanged));
private static void OnBlinkIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
@@ -331,6 +282,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
};
ctl._dispatcherTimerBlink.Tick += ctl.DispatcherTimerBlinkTick;
}
ctl._dispatcherTimerBlink.Start();
}
else
@@ -419,6 +371,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
{
hWnd = InteropMethods.FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
}
return hWnd;
}
@@ -432,6 +385,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
hTrayWnd = FindTrayToolbarOverFlowWindow();
isTrue = FindNotifyIcon(hTrayWnd, ref rectNotifyList);
}
rectList = rectNotifyList;
return isTrue;
}
@@ -446,8 +400,11 @@ public class NotifyIcon : FrameworkElement, IDisposable
if (count > 0)
{
InteropMethods.GetWindowThreadProcessId(hTrayWnd, out var trayPid);
var hProcess = InteropMethods.OpenProcess(InteropValues.ProcessAccess.VMOperation | InteropValues.ProcessAccess.VMRead | InteropValues.ProcessAccess.VMWrite, false, trayPid);
var address = InteropMethods.VirtualAllocEx(hProcess, IntPtr.Zero, 1024, InteropValues.AllocationType.Commit, InteropValues.MemoryProtection.ReadWrite);
var hProcess = InteropMethods.OpenProcess(
InteropValues.ProcessAccess.VMOperation | InteropValues.ProcessAccess.VMRead |
InteropValues.ProcessAccess.VMWrite, false, trayPid);
var address = InteropMethods.VirtualAllocEx(hProcess, IntPtr.Zero, 1024,
InteropValues.AllocationType.Commit, InteropValues.MemoryProtection.ReadWrite);
var btnData = new InteropValues.TBBUTTON();
var trayData = new InteropValues.TRAYDATA();
@@ -456,7 +413,8 @@ public class NotifyIcon : FrameworkElement, IDisposable
for (uint i = 0; i < count; i++)
{
InteropMethods.SendMessage(hTrayWnd, InteropValues.TB_GETBUTTON, i, address);
var isTrue = InteropMethods.ReadProcessMemory(hProcess, address, out btnData, Marshal.SizeOf(btnData), out _);
var isTrue =
InteropMethods.ReadProcessMemory(hProcess, address, out btnData, Marshal.SizeOf(btnData), out _);
if (!isTrue) continue;
if (btnData.dwData == IntPtr.Zero)
@@ -464,18 +422,21 @@ public class NotifyIcon : FrameworkElement, IDisposable
btnData.dwData = btnData.iString;
}
InteropMethods.ReadProcessMemory(hProcess, btnData.dwData, out trayData, Marshal.SizeOf(trayData), out _);
InteropMethods.ReadProcessMemory(hProcess, btnData.dwData, out trayData, Marshal.SizeOf(trayData),
out _);
InteropMethods.GetWindowThreadProcessId(trayData.hwnd, out var dwProcessId);
if (dwProcessId == (uint) handle)
{
var rect = new InteropValues.RECT();
var lngRect = InteropMethods.VirtualAllocEx(hProcess, IntPtr.Zero, Marshal.SizeOf(typeof(Rect)), InteropValues.AllocationType.Commit, InteropValues.MemoryProtection.ReadWrite);
var lngRect = InteropMethods.VirtualAllocEx(hProcess, IntPtr.Zero, Marshal.SizeOf(typeof(Rect)),
InteropValues.AllocationType.Commit, InteropValues.MemoryProtection.ReadWrite);
InteropMethods.SendMessage(hTrayWnd, InteropValues.TB_GETITEMRECT, i, lngRect);
InteropMethods.ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out _);
InteropMethods.VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect), InteropValues.FreeType.Decommit);
InteropMethods.VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect),
InteropValues.FreeType.Decommit);
InteropMethods.VirtualFreeEx(hProcess, lngRect, 0, InteropValues.FreeType.Release);
var left = rectTray.Left + rect.Left;
@@ -492,10 +453,12 @@ public class NotifyIcon : FrameworkElement, IDisposable
isFind = true;
}
}
InteropMethods.VirtualFreeEx(hProcess, address, 0x4096, InteropValues.FreeType.Decommit);
InteropMethods.VirtualFreeEx(hProcess, address, 0, InteropValues.FreeType.Release);
InteropMethods.CloseHandle(hProcess);
}
return isFind;
}
@@ -515,6 +478,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
IconHelper.GetDefaultIconHandles(out _, out _iconHandle);
_iconDefaultHandle = _iconHandle.CriticalGetHandle();
}
_iconCurrentHandle = _iconDefaultHandle;
}
}
@@ -610,6 +574,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
_dispatcherTimerPos.Interval = TimeSpan.FromMilliseconds(200);
_dispatcherTimerPos.Start();
}
break;
}
}
@@ -638,12 +603,12 @@ public class NotifyIcon : FrameworkElement, IDisposable
RoutedEvent = ClickEvent
});
}
_doubleClick = false;
}
private void ShowContextMenu()
{
if (ContextContent != null)
{
_contextContent ??= new Popup
@@ -725,6 +690,7 @@ public class NotifyIcon : FrameworkElement, IDisposable
{
_dispatcherTimerBlink.Stop();
}
UpdateIcon(false);
}
@@ -104,6 +104,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Data\Enum\GrowlShowMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Data\Enum\LinearLayout.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Data\Enum\RunningDirection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\ControlTokenManager`1.cs" />
<Compile Include="..\..\Shared\HandyControl_Shared\Input\SimpleMouseBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interactivity\EventToCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interactivity\IEventArgsConverter.cs" />
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Windows;
namespace HandyControl.Tools;
internal class ControlTokenManager<T>(
Action<string, T> registerCallback = null,
Action<string, T> unregisterCallback = null
)
where T : FrameworkElement
{
private static readonly Dictionary<string, WeakReference<T>> ControlDict = new();
public void Register(string token, T control)
{
if (string.IsNullOrEmpty(token) || control == null)
{
return;
}
ControlDict[token] = new WeakReference<T>(control);
registerCallback?.Invoke(token, control);
}
public void Unregister(string token, T control)
{
if (string.IsNullOrEmpty(token) || control == null)
{
return;
}
if (!ControlDict.TryGetValue(token, out var reference))
{
return;
}
if (!reference.TryGetTarget(out var target))
{
return;
}
if (!ReferenceEquals(target, control))
{
return;
}
ControlDict.Remove(token);
unregisterCallback?.Invoke(token, control);
}
public void Unregister(T control)
{
if (control == null)
{
return;
}
string unregisteredToken = null;
foreach (var item in ControlDict)
{
if (!item.Value.TryGetTarget(out var target) || !ReferenceEquals(control, target))
{
continue;
}
unregisteredToken = item.Key;
break;
}
if (unregisteredToken == null)
{
return;
}
ControlDict.Remove(unregisteredToken);
unregisterCallback?.Invoke(unregisteredToken, control);
}
public void Unregister(string token)
{
if (string.IsNullOrEmpty(token))
{
return;
}
if (!ControlDict.TryGetValue(token, out var reference))
{
return;
}
ControlDict.Remove(token);
if (!reference.TryGetTarget(out var target))
{
return;
}
unregisterCallback?.Invoke(token, target);
}
public bool TryGetControl(string token, out T control)
{
control = null;
if (string.IsNullOrEmpty(token) || !ControlDict.TryGetValue(token, out var reference))
{
return false;
}
return reference.TryGetTarget(out control);
}
public void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not T control)
{
return;
}
if (e.NewValue == null)
{
Unregister(control);
}
else
{
Register(e.NewValue.ToString(), control);
}
}
}