如何将WPF NotifyIcon与Caliburn.Micro集成

本文关键字:Caliburn Micro 集成 NotifyIcon WPF | 更新日期: 2023-09-27 18:34:47

我想知道如何将NotifyIcon与Caliburn.Micro集成。

我正在尝试使用低级 Caliburn API 与 Caliburn 集成。以下是课程:

ITrayIconManager

public interface ITrayIconManager
{
    ITrayIcon GetOrCreateFor<T>();
}

ITrayIcon(来自WPF NotifyIconTaskbarIcon的包装器(

 public interface ITrayIcon : IDisposable
{
    void ShowBalloonTip(string title, string message, BalloonIcon symbol);
    void Show();
    void Hide();
}

ISetTrayIconInstance

public interface ISetTrayIconInstance
{
    ITrayIcon Icon { set; }
}

托盘图标包装器

public class TrayIconWrapper : ITrayIcon
{
    private readonly TaskbarIcon icon;
    public TrayIconWrapper(TaskbarIcon icon)
    {
        this.icon = icon;
    }
    public bool IsDisposed { get; private set; }
    public void Dispose()
    {
        icon.Dispose();
        IsDisposed = true;
    }
    public void Show()
    {
        icon.Visibility = Visibility.Visible;
    }
    public void Hide()
    {
        icon.Visibility = Visibility.Collapsed;
    }
    public void ShowBalloonTip(string title, string message, BalloonIcon symbol)
    {
        icon.ShowBalloonTip(title, message, symbol);
    }
}

托盘图标管理器

public class TrayIconManager : ITrayIconManager
{
    private readonly IDictionary<WeakReference, WeakReference> icons;
    public TrayIconManager()
    {
        icons = new Dictionary<WeakReference, WeakReference>();
    }
    public ITrayIcon GetOrCreateFor<T>()
    {
        if (!icons.Any(i => i.Key.IsAlive && typeof(T).IsAssignableFrom(i.Key.Target.GetType())))
            return Create<T>();
        var reference = icons.First(i => i.Key.IsAlive && typeof(T).IsAssignableFrom(i.Key.Target.GetType())).Value;
        if (!reference.IsAlive)
            return Create<T>();
        var wrapper = (TrayIconWrapper)reference.Target;
        if (wrapper.IsDisposed)
            return Create<T>();
        return wrapper;
    }
    private ITrayIcon Create<T>()
    {
        var rootModel = IoC.Get<T>();
        var view = ViewLocator.LocateForModel(rootModel, null, null);
        var icon = view is TaskbarIcon ? (TaskbarIcon)view : new TaskbarIcon();
        var wrapper = new TrayIconWrapper(icon);
        ViewModelBinder.Bind(rootModel, view, null);
        SetIconInstance(rootModel, wrapper);
        icons.Add(new WeakReference(rootModel), new WeakReference(wrapper));
        return wrapper;
    }
    private void SetIconInstance(object rootModel, ITrayIcon icon)
    {
        var instance = rootModel as ISetTrayIconInstance;
        if (instance != null)
            instance.Icon = icon;
    }
}

这是代码,现在我该如何使用它?这段代码依赖于 Caliburn View - ViewModel 绑定,也就是说,我需要为 TasbarkIcon 创建一个 ViewModel 和一个视图(必须继承自 TaskbarIcon 控件(:

托盘图标视图模型

public class TrayIconViewModel : IMainTrayIcon, ISetTrayIconInstance
{
    public TrayIconViewModel()
    {
    }
    public ITrayIcon Icon { get; set; }
    public void ShowWindow()
    {
        Icon.Hide();
        System.Windows.Application.Current.MainWindow.Show(); //very very bad :(
    }

ITrayIcon 是 TaskbarIcon 控件的包装器。现在我可以从我的 ViewModel 调用它的方法,这很棒。

TrayIconView (cal:Message:Attach 不起作用 - ShowWindow 永远不会被击中(

<tb:TaskbarIcon x:Class="Communicator.Softphone.Views.TrayIconView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:tb="http://www.hardcodet.net/taskbar"
            xmlns:cal="http://www.caliburnproject.org"
            mc:Ignorable="d" 
            d:DesignHeight="300" d:DesignWidth="300"
            IconSource="/Communicator.ControlLibrary;component/Assets/phone_icon.ico"
            ToolTipText="Secretária do Futuro - Comunicador"
            Visibility="Collapsed"
            cal:Message.Attach="[Event TrayLeftMouseDown] = [Action ShowWindow()]">

在我的ShellViewModel上(trayIcon是TaskbarIcon的包装器(:

private ITrayIcon trayIcon;
protected override void OnActivate()
    {
        trayIcon = trayIconManager.GetOrCreateFor<IMainTrayIcon>();
        ActivateItem(containers.FirstOfType<IPhone>());
    }
public override void CanClose(Action<bool> callback)
    {
        trayIcon.Show();
        trayIcon.ShowBalloonTip("Comunicador", "Comunicador foi minimizado", BalloonIcon.Info);
        (GetView() as Window).Hide();
        callback(false);
    }

trayIcon.Show()正在工作,但是trayIcon.ShowBallonTip(...)什么都不做,没有错误,什么都没有。

问题摘要

  1. 绑定消息.附加不起作用,尽管 Caliburn 在工作时为此输出日志记录消息。
  2. 包装器上调用 ShowBallonTip 似乎不起作用,尽管它正在调用实际的 TaskbarIcon 方法。(它无需附加调试器即可工作(

如何将WPF NotifyIcon与Caliburn.Micro集成

您可以使用事件聚合器来执行所需的操作。

文档:http://caliburnmicro.com/documentation/event-aggregator

向事件聚合器的TaskbarViewModel添加一个字段,并添加一个构造函数以适应注入:

public class TaskbarViewModel : PropertyChangedBase, ITaskbar {
    private readonly IEventAggregator _eventAggregator;
    public TaskbarViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
    }
    public void Show() {
        IsVisible = true;
        _eventAggregator.PublishOnUIThread("Your balloontip message");
    }
    /// Rest of the implementation
}

实现可以访问TaskBarIcon并调用ShowBalloonTip方法的IHandle接口。

完整答案:

ITrayIcon.cs

包装任务栏图标

控件,因此您可以在任务栏图标上调用方法,而无需在视图模型中实际引用它。

public interface ITrayIcon : IDisposable
{
    void Show();
    void Hide();
    void ShowBalloonTip(string title, string message);
    void ShowBalloonTip(object rootModel, PopupAnimation animation, TimeSpan? timeout = null);
    void CloseBalloon();
}

ISetTrayIconInstance.cs

public interface ISetTrayIconInstance
{
    ITrayIcon Icon { set; }
}

ITrayIconManager.cs

管理 TasbarIcon 实例。您可以拥有任意数量的 TasbarIcon 实例。

public interface ITrayIconManager
{
    ITrayIcon GetOrCreateFor<T>();
}

托盘图标包装器.cs

该实现利用了Caliburn ViewModelBinder。ShowBallonTip的工作方式与IWindowManager.ShowWindow(object rootModel...)相似。它通过ViewLocator实例化视图,将rootModel绑定到它,然后传递给TaskbarIcon.ShowCustomBallon(UIElement element...

public class TrayIconWrapper : ITrayIcon
{
    private readonly TaskbarIcon icon;
    public TrayIconWrapper(TaskbarIcon icon)
    {
        this.icon = icon;
    }
    public bool IsDisposed { get; private set; }
    public void Dispose()
    {
        icon.Dispose();
        IsDisposed = true;
    }
    public void Show()
    {
        icon.Visibility = Visibility.Visible;
    }
    public void Hide()
    {
        icon.Visibility = Visibility.Collapsed;
    }
    public void ShowBalloonTip(string title, string message)
    {
        icon.ShowBalloonTip(title, message, BalloonIcon.Info);
    }
    public void ShowBalloonTip(object rootModel, PopupAnimation animation, TimeSpan? timeout = null)
    {
        var view = ViewLocator.LocateForModel(rootModel, null, null);
        ViewModelBinder.Bind(rootModel, view, null);
        icon.ShowCustomBalloon(view, animation, timeout.HasValue ? (int)timeout.Value.TotalMilliseconds : (int?)null);
    }
    public void CloseBalloon()
    {
        icon.CloseBalloon();
    }
}

托盘图标管理器.cs

我们需要跟踪实例化的任务栏图标。这个类是将所有东西粘合在一起的粘合剂。需要某个任务栏图标的实例?询问TrayIconManager,它将创建一个(如果它尚未创建并且处于活动状态(并将其返回给您。T 泛型类型是管理任务栏图标实例的视图模型的类型。

public class TrayIconManager : ITrayIconManager
{
    private readonly IDictionary<WeakReference, WeakReference> icons;
    public TrayIconManager()
    {
        icons = new Dictionary<WeakReference, WeakReference>();
    }
    public ITrayIcon GetOrCreateFor<T>()
    {
        if (!icons.Any(i => i.Key.IsAlive && typeof(T).IsAssignableFrom(i.Key.Target.GetType())))
            return Create<T>();
        var reference = icons.First(i => i.Key.IsAlive && typeof(T).IsAssignableFrom(i.Key.Target.GetType())).Value;
        if (!reference.IsAlive)
            return Create<T>();
        var wrapper = (TrayIconWrapper)reference.Target;
        if (wrapper.IsDisposed)
            return Create<T>();
        return wrapper;
    }
    private ITrayIcon Create<T>()
    {
        var rootModel = IoC.Get<T>();
        var view = ViewLocator.LocateForModel(rootModel, null, null);
        var icon = view is TaskbarIcon ? (TaskbarIcon)view : new TaskbarIcon();
        var wrapper = new TrayIconWrapper(icon);
        ViewModelBinder.Bind(rootModel, view, null);
        SetIconInstance(rootModel, wrapper);
        icons.Add(new WeakReference(rootModel), new WeakReference(wrapper));
        return wrapper;
    }
    private void SetIconInstance(object rootModel, ITrayIcon icon)
    {
        var instance = rootModel as ISetTrayIconInstance;
        if (instance != null)
            instance.Icon = icon;
    }
}

如何使用

  1. 创建继承自任务栏图标的视图:

TrayIconView.xaml

<tb:TaskbarIcon x:Class="Communicator.Softphone.Views.TrayIconView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:tb="http://www.hardcodet.net/taskbar"
            xmlns:cal="http://www.caliburnproject.org"
            mc:Ignorable="d" 
            d:DesignHeight="300" d:DesignWidth="300"
            IconSource="/Communicator.ControlLibrary;component/Assets/phone_icon.ico"
            ToolTipText="Secretária do Futuro - Comunicador"
            cal:Message.Attach="[Event TrayLeftMouseDown] = [Action ShowWindow()]"
            TrayLeftMouseDown="TaskbarIcon_TrayLeftMouseDown">

  1. 为视图创建视图模型:

托盘图标视图模型.cs

public class TrayIconViewModel : ISetTrayIconInstance
{
    public TrayIconViewModel()
    {
    }
    public ITrayIcon Icon { get; set; }
    public void ShowWindow()
    {
        System.Windows.Application.Current.MainWindow.Show(); //very very bad :(
    }
}
  1. 在任何地方通过ITrayIconManager实例化它。例如,在 ShellViewModelOnActivat 方法中:

    protected override void OnActivate()
    {
        trayIcon = trayIconManager.GetOrCreateFor<TrayIconViewModel>();
    }
    
  2. 随时使用。例如,在我的聊天管理器中:

    public void NewMessage(IChatMessage message)
    {
        trayIcon = trayIconManager.GetOrCreateFor<TrayIconViewModel>();
        var notification = new ChatNotificationViewModel(message);
        trayIcon.ShowBalloonTip(notification, PopupAnimation.Slide, TimeSpan.FromSeconds(5));
    }