简体   繁体   English

如何将WPF NotifyIcon与Caliburn.Micro集成

[英]How to integrate WPF NotifyIcon with Caliburn.Micro

I was wondering how to integrate NotifyIcon with Caliburn.Micro. 我想知道如何将NotifyIcon与Caliburn.Micro集成。

I'm trying to integrate with Caliburn using low level Caliburn APIs. 我正在尝试使用低级Caliburn API与Caliburn集成。 Here are the classes: 以下是课程:

ITrayIconManager ITrayIconManager

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

ITrayIcon (wrapper around TaskbarIcon from WPF NotifyIcon ) ITrayIcon(TaskbarIcon包装从WPF的NotifyIcon)

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

ISetTrayIconInstance ISetTrayIconInstance

public interface ISetTrayIconInstance
{
    ITrayIcon Icon { set; }
}

TrayIconWrapper TrayIconWrapper

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);
    }
}

TrayIconManager TrayIconManager

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;
    }
}

This is the code, now how do I use it? 这是代码,现在我该如何使用它? This code relies on Caliburn View - ViewModel binding , that is, I need to create a ViewModel for TasbarkIcon and a View (which must be inherited from TaskbarIcon control): 此代码依赖于Caliburn View - ViewModel绑定 ,也就是说,我需要为TasbarkIcon和View创建一个ViewModel( 必须TaskbarIcon控件继承 ):

TrayIconViewModel TrayIconViewModel

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 is the wrapper for TaskbarIcon control. ITrayIcon是TaskbarIcon控件的包装器。 Now I can call methods on it from my ViewModel, which is great. 现在我可以从我的ViewModel调用它的方法,这很棒。

TrayIconView ( cal:Message:Attach doesn't work - the ShowWindow never gets hit) TrayIconViewcal: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()]">

On my ShellViewModel ( trayIcon is the wrapper around TaskbarIcon): 在我的ShellViewModel上trayIconTaskbarIcon的包装器):

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() is working, however trayIcon.ShowBallonTip(...) doesn't do anything, no errors, no nothing. trayIcon.Show()正在工作,但是trayIcon.ShowBallonTip(...)没有做任何事情,没有错误,没有任何东西。

Issues summary : 问题摘要

  1. Binding Message.Attach is not working, although Caliburn output logging messages for this as it is working. 绑定Message.Attach不起作用,虽然Caliburn输出日志消息,因为它正在工作。
  2. Calling ShowBallonTip on the wrapper seems not to work, although it is calling the actual TaskbarIcon method. 虽然它调用实际的TaskbarIcon方法,但在包装器上调用ShowBallonTip似乎不起作用。 (it works without debugger attached) (无需附带调试器即可使用)

You could use the Event Aggregator to do what you want. 您可以使用Event Aggregator执行您想要的操作。

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

Add a field to your TaskbarViewModel for the Event Aggregator and add a constructor to accomodate the injection: TaskbarViewModel为Event Aggregator添加一个字段,并添加一个构造函数来容纳注入:

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
}

Implement the IHandle interface where you can access the TaskBarIcon and call the ShowBalloonTip method. 实现IHandle接口,您可以在其中访问TaskBarIcon并调用ShowBalloonTip方法。

The complete answer: 完整答案:

ITrayIcon.cs ITrayIcon.cs

Wraps TaskbarIcon control, so you could call methods on TaskbarIcon without having an actual reference to it in your view models. 包装TaskbarIcon控件,因此您可以调用TaskbarIcon上的方法,而无需在视图模型中实际引用它。

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 ISetTrayIconInstance.cs

public interface ISetTrayIconInstance
{
    ITrayIcon Icon { set; }
}

ITrayIconManager.cs ITrayIconManager.cs

Manages TasbarIcon instances. 管理TasbarIcon实例。 You can have as many TasbarIcon instances as you like. 您可以拥有任意数量的TasbarIcon实例。

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

TrayIconWrapper.cs TrayIconWrapper.cs

The implementation leverages from Caliburn ViewModelBinder. 该实现利用Caliburn ViewModelBinder。 ShowBallonTip works alike IWindowManager.ShowWindow(object rootModel...) does. ShowBallonTip的工作方式与IWindowManager.ShowWindow(object rootModel...) It instanciates the view through ViewLocator , binds your rootModel to it and then passes to TaskbarIcon.ShowCustomBallon(UIElement element... . 它通过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();
    }
}

TrayIconManager.cs TrayIconManager.cs

We need to keep track of the instanciated TaskbarIcon's. 我们需要跟踪instanciated TaskbarIcon。 This class is the glue that binds all together. 这个类是粘合在一起的粘合剂。 Need an instance of some TaskbarIcon? 需要一些TaskbarIcon的实例吗? Ask to TrayIconManager, and it will create one (if it isn't already created and alive) and return it to you. 询问TrayIconManager,它将创建一个(如果它尚未创建并存活)并将其返回给您。 The T generic type is the type of your view model that manages an instance of TaskbarIcon. T泛型类型是管理TaskbarIcon实例的视图模型的类型。

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;
    }
}

HOW TO USE : 如何使用

  1. Create a View that inherits from TaskbarIcon: 创建一个继承自TaskbarIcon的视图:

TrayIconView.xaml 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. Create a view model for the view: 为视图创建视图模型:

TrayIconViewModel.cs TrayIconViewModel.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. Instanciate it through ITrayIconManager in any place. 通过ITrayIconManager在任何地方实现它。 For example, in the OnActivat method of your ShellViewModel : 例如,在ShellViewModelOnActivat方法中:

     protected override void OnActivate() { trayIcon = trayIconManager.GetOrCreateFor<TrayIconViewModel>(); } 
  2. Use whenever you like. 随时使用。 For example, in my ChatManager : 例如,在我的ChatManager中

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM