С# WPF Иконка в трее

334
24 декабря 2017, 06:39

Кто может подсказать решение такой задачи? есть программа которая сворачивается в трей, в программе есть 3 выпадающих списка с выбором цвета, надо сделать в трее иконку которая будет видом в 3 колонки и каждая колонка должна закрашиваться в тот цвет который выбран в списках программы. Не знаю как это сделать думал как то использовать UserControl вместо иконки (графического элемента) но не знаю как это воплотить в жизнь

Answer 1

Дам вам пищу для размышления (возьму пример из своего приложения, по желанию можно и переделать). В главном окне приложения объявим вот такую переменную:

private System.Windows.Forms.NotifyIcon _notifyIcon;

В ресурсы приложения я добавил картинку (иконку) AppIcon. И напишем вот такой метод создания иконки в трее и ее отображение:

private void SetIconToMainApplication()
{
    _notifyIcon = new System.Windows.Forms.NotifyIcon();
    _notifyIcon.Icon = Properties.Resources.ResourceManager.GetObject("AppIcon") as Icon;
    _notifyIcon.Visible = true;
}

Который поместим внутрь конструктора окна сразу после InitializeComponent:

public MainWindow()
{
    InitializeComponent();
    SetIconToMainApplication();
}

Да, когда приложение закрываете, нужно вызвать Dispose() у NotifyIcon, вот так:

if (_notifyIcon != null)
{
    _notifyIcon.Dispose();
}

Думаю, что вы догадываетесь о том, что вам нужно создавать иконку, во время выбора цвета и выводить ее в трей. Касаемо этого вопроса - тут не подскажу...хотя, возможно стоит сделать заготовку нескольких иконок с разными цветами, но это будет достаточно сложно если много цветов, а если их не много, то будет проще. Возможно стоит покапать в строну динамического создания иконок, точнее не создания, а изменения.

  • Dynamically Generating Icons (safely)
  • Dynamically change application icon at compile time? (.NET)

Класс NotifyIcon - указывает компонент, который создает значок в области уведомлений. Этот класс не наследуется.

Возможно стоит инициализировать новый экземпляр класса NotifyIcon с указанным контейнером через конструктор NotifyIcon (IContainer), а уже контейнер будет предоставлять функции. Контейнеры являются объектами, которые логически содержат нуль или более компонентов. Может быть можно с помощью контейнеров это сделать, тут нужно пробовать, увы, задача не из простых.

А также будет полезно:

  • WPF Application that only has a tray icon
Answer 2

Так как ТС не хочет пользоваться в WPF тем что предложили ранее, или не хочет мешать WinForm с WPF.

Приступим

Для начала переопределим метод OnStartup в App.xaml.cs таким образом, что бы мы могли инициализировать TaskbarIcon из библиотеки Hardcodet.NotifyIcon.Wpf.

protected override void OnStartup(StartupEventArgs e)
{
    TbIcon = new TaskbarIcon();
    base.OnStartup(e);
}

Далее определим статическое поле для того что бы могли обращаться к нему из любого места приложения.

public static TaskbarIcon TbIcon { get; protected set; }

Так же переопределим метод OnExit и в нем будем вызывать Dispose() для иконки.

protected override void OnExit(ExitEventArgs e)
{
    TbIcon?.Dispose();
    base.OnExit(e);
}

Прежде чем продолжить предлагаю ознакомиться с тем, как в WPF делается скриншот элементов. Думаю данный расширяющий класс достаточно локаничен к каждому кто его посмотрит:

public static partial class Extension
{
    // расширение для FrameworkElement
    public static ImageSource TakeScreenShoot(this FrameworkElement element)
    {
        var renderTargetBitmap = new RenderTargetBitmap((int) element.ActualWidth, (int) element.ActualHeight, 96,
            96, PixelFormats.Default);
        renderTargetBitmap.Render(element);
        return renderTargetBitmap;
    }
    // расширение для RenderTargetBitmap преобразование в Bitmap
    public static Bitmap ToBitmap(this RenderTargetBitmap source)
    {
        using (var memoryStream = new MemoryStream())
        {
            BitmapEncoder enc = new BmpBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(source));
            enc.Save(memoryStream);
            var bitmap = new Bitmap(memoryStream);
            return new Bitmap(bitmap);
        }
    }
    // расширение для ImageSource преобразование в Bitmap
    public static Bitmap ToBitmap(this ImageSource source)
    {
        if (!(source is RenderTargetBitmap temp))
            throw new NotSupportedException(nameof(source));
        using (var memoryStream = new MemoryStream())
        {
            BitmapEncoder enc = new BmpBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(temp));
            enc.Save(memoryStream);
            var bitmap = new Bitmap(memoryStream);
            return new Bitmap(bitmap);
        }
    }
}

Оригинал класса лежит здесь

Для чего же он нужен? Все очень просто, будем использовать его для получения скриншота контрола, и преобразования в System.Drawing.Icon.

Далее нам понадобится любой удобный ColorPicker, я использовал: WpfToolkit. Так же доступен Nuget пакет.

Сделаем простейший контрол(UserControl), чем я не стал заниматься, а прямо сделал как показано ниже для ускорения разработки примера:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        SnapsToDevicePixels="True"
        xmlns:xwpf="http://schemas.xceed.com/wpf/xaml/toolkit"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="120" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Bottom">
            <xwpf:ColorPicker SelectedColor="Red" Name="ColorPicker1" VerticalAlignment="Center"
                              HorizontalAlignment="Center" />
            <xwpf:ColorPicker Name="ColorPicker2" VerticalAlignment="Center"
                              HorizontalAlignment="Center" SelectedColor="Green" />
            <xwpf:ColorPicker SelectedColor="Blue" Name="ColorPicker3" VerticalAlignment="Center"
                              HorizontalAlignment="Center" />
        </StackPanel>
        <Border Visibility="Visible" x:Name="IconData" HorizontalAlignment="Left" Background="White"
                BorderThickness="1" Width="120"
                Height="120" BorderBrush="Black" Grid.Row="0" Grid.Column="0">
            <Border BorderBrush="Black" BorderThickness="0,1,0,1" VerticalAlignment="Center"
                    HorizontalAlignment="Stretch" Height="40">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="40" />
                        <ColumnDefinition Width="40" />
                        <ColumnDefinition Width="40" />
                    </Grid.ColumnDefinitions>
                    <Border Width="39" HorizontalAlignment="Left" BorderBrush="Black"
                            BorderThickness="0,0,1,0">
                        <Border.Background>
                            <SolidColorBrush
                                Color="{Binding ElementName=ColorPicker1, Path=SelectedColor}" />
                        </Border.Background>
                    </Border>
                    <Border HorizontalAlignment="Left" Width="39" BorderBrush="Black" BorderThickness="0,0,1,0" Grid.ColumnSpan="2" Margin="39,0,0,0" >
                        <Border.Background>
                            <SolidColorBrush
                                Color="{Binding ElementName=ColorPicker2, Path=SelectedColor}" />
                        </Border.Background>
                    </Border>
                    <Border Width="40" HorizontalAlignment="Left" Grid.Column="1" Grid.ColumnSpan="2" Margin="38,0,0,0">
                        <Border.Background>
                            <SolidColorBrush Color="{Binding ElementName=ColorPicker3, Path=SelectedColor}" />
                        </Border.Background>
                    </Border>
                </Grid>
            </Border>
        </Border>
    </Grid>
</Window>

Получим что-то следующее:

CodeBehind окна будет выглядеть примерно так (все сделано просто для примера, над кодом нужна работа):

using System;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Color = System.Windows.Media.Color;
namespace WpfApp1
{
    /// <summary>
    ///     Логика взаимодействия для MainWindow.xaml
    /// </summary>
    /// 
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
            ColorPicker1.SelectedColorChanged += ColorPicker1_SelectedColorChanged;
            ColorPicker2.SelectedColorChanged += ColorPicker1_SelectedColorChanged;
            ColorPicker3.SelectedColorChanged += ColorPicker1_SelectedColorChanged;
        }
        private void ColorPicker1_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<Color?> e)
        {
            UpdateImage();
        }
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateImage();
        }
        private void UpdateImage()
        {
            Icon = IconData.TakeScreenShoot();
            App.TbIcon.Icon = System.Drawing.Icon.FromHandle(Icon.ToBitmap().GetHicon());
        }
    }
}

Ну что ж, теперь запускаем, и любуемся тому что у окна, и иконки в трее одинаковые значки, а так же при изменении цвета в них, меняется и иконка.

Результат:

Предупреждение

У данного подхода есть один минус, контрол с которого делается снимок, должен лежать по X/Y = 0, иначе получим пустой снимок. Почему так происходит мне пока неизвестно

READ ALSO
Закрасить всю строку в Excel

Закрасить всю строку в Excel

Открываю в c# Excel, туда записываю данные в соответствующие строки и столбцы, необходимо по условию выделить строчку каким-то цветом, как это...

384
Incorrect syntax near the keyword &#39;WHERE&#39; - C#

Incorrect syntax near the keyword 'WHERE' - C#

ошибка в этой строке SqlDataReader dr = sqlCommand1ExecuteReader(); остальной код программы

276
C# Как убрать пространство иконки в ToolStripMenuItem

C# Как убрать пространство иконки в ToolStripMenuItem

Возле menuitem всегда квадратная пустая иконкаКак ее убрать?

308