1

I would like to change taskbar icon to notify the user when a new message is received like outlook when you receive a new mail.

I already search solution on web and it's all about changing the window icon like this:

Uri iconUri = new Uri("Resources/envelop.ico", UriKind.Relative);
this.Icon = BitmapFrame.Create(iconUri);

It's working well on Visual Studio but I found out that it's not changing my task bar on deployed app because it's a read-only variable. And worse it's change only the top-left attached icon that I didn't want to change anyways.

So is there a way to do it? Outlook did it and Chrome too so there must be a way.

UPDATE

To force the icon refresh on my deployed app I have to pin/unpin my icon taskbar, unfortunately this is a user command only so I can't do this in wpf programmatically without exploiting some dirty manners which would be too unstable.

Actually I'm trying to find a way to refresh Icon cache without doing it for each Windows OS or version.

Safe
  • 313
  • 3
  • 13
  • Have you tried adding your 2 Icons to your `Properties.Resources` of your project and then setting `MainWindow.Icon` to that ? - For me that is working .. – Felix D. Apr 03 '17 at 07:46
  • @FelixD. What do you mean by `Properties.Resources` ? I set it on my project property panel in Application (Tab)->Resources (Section)->Icon maybe this it which block the change? – Safe Apr 03 '17 at 07:52
  • In your project expand the `Properties` and doubleclick `Resources.resx` then you can hit `Add Resource -> Add Existing File` and add your Icons. This makes them available in Code via `YourProjectName.Properties.Resources.NameOfIcon`. They are automatically added to a Resourcefolder in your project. Make sure to change the `BuildOption` to `Resource`. – Felix D. Apr 03 '17 at 07:57
  • I don't really understand what this will do to do this. My test are already working on Visual studio. And on deployed app I saw that the access to my resources (icons) are ok. – Safe Apr 03 '17 at 08:06
  • But you cannot change it on a deployed app ? The code I posted below is working for me .. – Felix D. Apr 03 '17 at 08:10
  • @FelixD. Still the same working on VS but it's not working after a deploy (only change menu icon app and not taskbar) – Safe Apr 03 '17 at 08:42
  • On a deploy is no icon displayed or just the change not working ? – Felix D. Apr 03 '17 at 08:51
  • What happens if you set `CopyAlways` & `BuildOptions` to `EmbeddedResource` of your Resources ? – Felix D. Apr 03 '17 at 08:51
  • @FelixD. the change is working on the top left icon menu but not on the taskbar menu. – Safe Apr 03 '17 at 12:08
  • Is this issue still open ? - Just to be sure... You aren't talking about `Tray` just the `TaskBar` right ? – Felix D. Jul 07 '17 at 08:43
  • Did you find a way to fix this ? The same issue happens to me – SilentRage47 Jun 17 '20 at 15:28
  • There are 2 two issues is see. If the app starts minimized the default icon is shown and it wont change to/or with the main forms icon changing unless you pin/unpin as Safe mentioned. This also happens when the app is pinned to the taskbar. The default application icon will always be used. There is an api added to Windows10 that almost works. It would allow programmatic taskbar access but it requires the user to approve the pinning. See https://learn.microsoft.com/en-us/windows/apps/design/shell/pin-to-taskbar – C J May 06 '22 at 11:30

3 Answers3

2

Here is a small working demo doing the job !

Add Icons:

enter image description here enter image description here

Chose 2 Icons (for example Outlook Standard & Outlook MailReceived):

Add this to the code behind of your MainWindow

public partial class MainWindow : Window
{
    private int _imgId;

    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        //Start timer to periodically change the App Icon:
        new System.Threading.Thread(() =>
        {
            System.Timers.Timer timer = new System.Timers.Timer();
            timer.Interval = 100;
            timer.AutoReset = true;
            timer.Elapsed += Timer_Elapsed;
            timer.Start();
        }).Start();
    }

    private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        try
        {      
            //Change AppIcon on UI-Thread         
            Dispatcher.Invoke(() =>
            {
                /* CHANGE YOUR ICONS HERE !!! */
                BitmapSource ms_icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(Properties.Resources.Microsoft_logo.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                BitmapSource so_icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(Properties.Resources.images.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                if (_imgId == 0)
                {
                    this.Icon = so_icon;
                    _imgId = 1;
                }
                else
                {
                    this.Icon = ms_icon;
                    _imgId = 0;
                }
            });
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.WriteLine(ex.Message);               
        }            
    }
}

Will produce this output (switch every 100ms):

enter image description here

Felix D.
  • 4,811
  • 8
  • 38
  • 72
  • 1) Does your code also work if you have installed your application (exe) under c:\program files..\? 2) ms_icon or so_icon is a Sytem.Windows.Media.Imaging.BitmapSource and this.Icon is a System.Drawing.Icon. How does your program automatically convert BitmapSource to System.Drawing.Icon? – creg Apr 14 '21 at 14:34
0

Two Steps you need to consider:

  1. You need to pay attention if your software is also installed in Control Panel. If it does, then try to uninstall it from there but keep your runtime folder of your software somewhere to keep them for a test.

  2. Delete from %AppData% your software data files and try to run software again without installing it (Where you copied your runtime folder) and you should be able to see how Taskbar is refreshing it.

One thing you need to sort out is why having software installed in Control panel makes such glitch that it doesn't refresh your icon in taskbar.

XtaXCraft
  • 27
  • 4
  • it also might be the case that your Program Files (x86) doesn't have permissions to change icons. As soon as you change your location it will work. – XtaXCraft Jul 02 '20 at 15:21
  • I just realised that this problem occurs only if you create desktop shortcut of your app... – XtaXCraft Jul 02 '20 at 15:58
-1

For me this solution was working.

Created this with code from Write text on bitmap in C#

    private Icon DrawIcon(Brush brush)
    {
        //https://stackoverflow.com/questions/6311545/c-sharp-write-text-on-bitmap     
        Bitmap bmp = new Bitmap(MyNameSpace.Properties.Resources.desktop_windows_48px);

        // Create a rectangle for the entire bitmap
        RectangleF rectf = new RectangleF(0, 0, bmp.Width, bmp.Height);

        // Create graphic object that will draw onto the bitmap
        Graphics g = Graphics.FromImage(bmp);

        // The smoothing mode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing). One exception is that path gradient brushes do not obey the smoothing mode. Areas filled using a PathGradientBrush are rendered the same way (aliased) regardless of the SmoothingMode property.
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

        // The interpolation mode determines how intermediate values between two endpoints are calculated.
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

        // Use this property to specify either higher quality, slower rendering, or lower quality, faster rendering of the contents of this Graphics object.
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

        // This one is important
        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;

        // Draw the text onto the image
        g.FillRectangle(brush, new Rectangle(2, 4, 20, 12));

        // Flush all graphics changes to the bitmap
        g.Flush();

        Bitmap temp = bmp;

        // Get an Hicon for myBitmap.
        IntPtr Hicon = temp.GetHicon();

        // Create a new icon from the handle.
        Icon newIcon = Icon.FromHandle(Hicon);

        return newIcon;            
    }

I am also using a NotifyIcon but that does not change alot for you.

_notifyIcon.Icon = DrawIcon(Brushes.Blue);
MainWindow.Icon =  Imaging.CreateBitmapSourceFromHIcon(_notifyIcon.Icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

Lateron I change it like this (might be similar when just changing the icon source.

    private void App_StateChanged(object sender, StateChangedEventArgs e)
    {
        if(e.State == ApplicationState.Defective)
        {
            //Show window on error !
            ShowMainWindow();
            //(sender as Window).Activate();
        }
        _notifyIcon.Icon = DrawIcon(e.StateBrush);
        MainWindow.Icon = Imaging.CreateBitmapSourceFromHIcon(_notifyIcon.Icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
        _notifyIcon.Visible = true;
    }

It looks similar to this:

Default:

enter image description here

On Error:

enter image description here

Hope this is helpful !

Community
  • 1
  • 1
Felix D.
  • 4,811
  • 8
  • 38
  • 72