Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

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

#c_sharp #wpf


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


Ответы

Ответ 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

Ответ 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), чем я не стал заниматься, а прямо сделал как показано ниже для ускорения разработки примера: Получим что-то следующее: 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 { /// /// Логика взаимодействия для MainWindow.xaml /// /// 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 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, иначе получим пустой снимок. Почему так происходит мне пока неизвестно

Комментариев нет:

Отправить комментарий