Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement notifyicon in MVVM project

I am trying to implement a notifyicon (http://www.hardcodet.net/projects/wpf-notifyicon) in MVVM project. I understand this control is meant to be used in a regular WPF project.

I am wondering how to implement the ballon feature (Balloon feature). As specified in this tutorial the method "ShowBallonTip" needs to be called

//show balloon with built-in icon
  MyNotifyIcon.ShowBalloonTip(title, text, BalloonIcon.Error);

The only place I could call this method, I can think of, is in the code behind. I do not have a problem with having a little code in a view code behind (even if I would prefer not having any) but I can not figure out how I can have the view model to talk to the view and asks it to call this method.

Even if I place this method in an event how can I raise this event programatically from the viewmodel?

Any idea how I could achieve this?

like image 598
Regis Avatar asked Sep 19 '12 19:09

Regis


2 Answers

I have been able to display a balloon tip by initialising the icon in the viewmodel instead of the XAML.

Just calling the ShowBalloonTip method in my command do the trick.

I created a wrapper for the notify Icon: NotifyService:

public class NotifyService : INotifyService
{
    private TaskbarIcon icon = new TaskbarIcon
        {
            Name = "NotifyIcon",
            Icon =
                new System.Drawing.Icon(
                    Application.GetResourceStream(Utils.FileUtils.MakeUri("/Icons/email.ico")).Stream),
        };


    public void Notify(string message)
    {

        icon.ShowBalloonTip("title", message, BalloonIcon.None);
    }

    public void ChangeIconSource(string path)
    {
        icon.Icon = new System.Drawing.Icon(
                    Application.GetResourceStream(Utils.FileUtils.MakeUri(path)).Stream);
    }
}

And I used it in my view model: viewmodel

public class MainWindowViewModel : WindowViewModelBase
{
    private readonly INotifyService notifyService = new NotifyService();

    #region Fields
    private static HomeWindowViewModel homeViewModel = new HomeWindowViewModel();
    #endregion
    /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
    /// </summary>
    public MainWindowViewModel()
        : base()
    {
        CurrentViewModel = homeViewModel;
    }

    #region Methods

    protected override void OnViewModelPropertyChanged(IViewModel viewModel, string propertyName)
    {
        int t = 2;
    }

    protected override void OnViewModelCommandExecuted(IViewModel viewModel, ICatelCommand command, object commandParameter)
    {
        int t = 2;
        notifyService.ChangeIconSource(@"/Icons/new_email.ico");
        notifyService.Notify("test");
    }
    #endregion
}
like image 78
Regis Avatar answered Sep 28 '22 08:09

Regis


I found a less obvious solution, but one that arguably fit's better with the MVVM pattern. It allows the TaskbarIcon to be created in XAML and fed new balloon data by the ViewModel.

The first thing to do is add some sort of ability to notify the TaskbarIcon we want it to show a bubble I chose to use Microsofts Rx Extensions (NuGet Package Rx-Main) for this purpouse, but any suitable infrastructure will work. This is the new TaskbarIcon class, and a class for holding the data we would like to pass when the ShowBubbleTip method is called.

using Hardcodet.Wpf.TaskbarNotification;
using System;
using System.Windows;
namespace Phi.Utility
{
    public class TaskbarIconRxBallonNotification
    {
        public Hardcodet.Wpf.TaskbarNotification.BalloonIcon Icon
        {
            get;
            private set;
        }
        public string BallonTitle
        {
            get;
            private set;
        }
        public string Message
        {
            get;
            private set;
        }
        public TaskbarIconRxBallonNotification(Hardcodet.Wpf.TaskbarNotification.BalloonIcon icon, string balloonTitle, string message)
        {
            Icon = icon;
            BallonTitle = balloonTitle;
            Message = message;
        }
    }
    public class TaskbarIconRx : TaskbarIcon
    {
        public IObservable<TaskbarIconRxBallonNotification> BalloonTipNotifier
        {
            get { return (IObservable<TaskbarIconRxBallonNotification>)GetValue(BallonTipNotifierProperty); }
            set { SetValue(BallonTipNotifierProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BalloonSubject.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BallonTipNotifierProperty =
            DependencyProperty.Register("BalloonTipNotifier", typeof(IObservable<TaskbarIconRxBallonNotification>), typeof(TaskbarIconRx), new PropertyMetadata(null, BallonTipNotifierChanged));

        //What to do when we get a new ballonIcon request
        protected void OnNextNotification(TaskbarIconRxBallonNotification notification)
        {
            ShowBalloonTip("", notification.Message, BalloonIcon.Info);
        }
        private IDisposable _subscription;

        //Make sure swapping out bindings doesn't break our program.
        public static void BallonTipNotifierChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TaskbarIconRx currentNotifier = d as TaskbarIconRx;
            if (currentNotifier != null)
            {
                IObservable<TaskbarIconRxBallonNotification> prev = e.OldValue as IObservable<TaskbarIconRxBallonNotification>;
                IObservable<TaskbarIconRxBallonNotification> next = e.NewValue as IObservable<TaskbarIconRxBallonNotification>;

                if (currentNotifier._subscription != null)
                {
                    currentNotifier._subscription.Dispose();
                    currentNotifier._subscription = null;
                }
                if ((next != null))
                {

                    currentNotifier._subscription = next.Subscribe(currentNotifier.OnNextNotification);
                }
            }
        }

    }
}

In our Model, we provide an ISubject to bind to

using System.Reactive.Subjects;
namespace Phi.Models {
    public class SomeModel:ModelBase {
        public ISubject<Utility.TaskbarIconRxBallonNotification> NotifierInterface
        {
            get;
            private set;
        }
        public SomeModel() {
            NotifierInterface = new Subject<Utility.TaskbarIconRxBallonNotification>();

        }
    }
}

In our ViewModel, we can now push notifications through the models subject, like so:

namespace Phi.ViewModels{
    public class SomeViewModel:ViewModelBase
    {
        public SomeModel Model{
            get;
            private set;
        }
        public void PushNotification(string message)
        {
            //Pushes a new notification to the TaskbarIcon.
            Model.NotifierInterface.OnNext(new Utility.TaskbarIconRxBallonNotification(Hardcodet.Wpf.TaskbarNotification.BalloonIcon.Info, "Title", message));
        }
    }
}

and in the XAML, we bind to our models ISubject

<Utility:TaskbarIconRx Visibility= IconSource="/Resources/TinyLogo.ico" BalloonTipNotifier="{Binding Model.NotifierInterface}" >
like image 33
Benjamin Avatar answered Sep 28 '22 09:09

Benjamin