Перейти к содержанию
  • 0
DirtyBorov

[Windows] Как поменять иконку формы без стилизованной рамки?

Вопросы

Подскажите как можно изменить иконку окна в runtime? 

Суть проблемы в том, что мне надо поддерживать приложение для разных заказчиков. Каждый из них хочет иметь собственную иконку в приложении. Очевидное решение - создать несколько проектов и каждому задать требуемую иконку. Однако на практике это весьма утомительное занятие. Хотелось бы сделать одно приложение, а иконки подгружать динамически на основании настроек приложения.

Вспоминая практику VCL и WinAPI, подменить иконку Application оказалось задачей тривиальной:

NewAppIcon := TIcon.Create;
NewAppIcon.LoadFromFile(AIconFile);
SendMessage(ApplicationHWND, WM_SETICON, 1, NewAppIcon.Handle);

А вот дальше начались проблемы. Окна уже прогрузили иконку по умолчанию и добраться до них у меня не получается. 

Добраться через стиль не получается, потому что TForm не является наследником TStyledObject и не имеет StylesData. Через FindStyleResource тоже не получается добраться.

 

Может кто знает как?

 

Отредактировал Brovin Yaroslav

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рекомендуемые сообщения

  • 0

Добрый день,

 

У вас все правильно написано. В вашем коде вы меняете иконку самого приложения в трее. А вот чтобы поменять иконку у формы, нужно отправлять сообщение WM_SETICON окну а не приложению:

uses
  VCL.Graphics, Winapi.Windows, Winapi.Messages, FMX.Platform.Win;


procedure TForm3.Button1Click(Sender: TObject);
var
  NewAppIcon: TIcon;
begin
  NewAppIcon := TIcon.Create;
  NewAppIcon.LoadFromFile('c:\icon.ico');

  SendMessage(ApplicationHWND, WM_SETICON, 1, NewAppIcon.Handle);
  SendMessage(WindowHandleToPlatform(Handle).Wnd, WM_SETICON, 1, NewAppIcon.Handle);
end;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
SendMessage(WindowHandleToPlatform(Handle).Wnd, WM_SETICON, 1, NewAppIcon.Handle);

 

Это первое что попробовал.  Забыл об этом сказать. К сожалению этот трюк не работает.  Именно потому я и обратился за помощь. 

Верней он работает, только если не используется стиль но рамку окна.  

 

Если же используется стиль, то тут уже либо не работает (самодельный стиль на основе стандартных):

 

post-594-0-60220900-1431787253_thumb.png

 

Либо вот такие глюки (стиль jet). Причем если подвигать окно, то окно  восстановит рамку из стиля и... вернет иконку по умолчанию.

 

post-594-0-99522100-1431787972_thumb.png 

 

Очевидно что проблема связана именно с использованием стилей. Я пробовал расширенные стили для XE7 - у всех такая проблема в XE8. У стилей, где нет стилизации окна - проблем нет. Надеюсь что данная проблема связана именно с использованием стилей от ХЕ7 в ХЕ8. Насколько я понял, стили подверглись изменениям в связи с TImageList (картинки не будут отображаться в старых стилях).  К сожалению премиум стилей для ХЕ8 у меня нет и проверить истинность своих догадок я не могу.

В любом случае - спасибо за помощь. По крайней мере я теперь знаю что был на верном пути. :)

Отредактировал DirtyBorov

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вас понял. При стилизованной рамке окна, иконка кэшируется. Первый раз она запрашивается в момент первой отрисовки формы. Поэтому в вашем случае ее нужно задать сразу после создания хендла, а именно в CreateHandle.

  TForm15 = class(TForm)
    StyleBook1: TStyleBook;
  protected
    procedure CreateHandle; override;
  end;

implementation

uses
  VCL.Graphics, Winapi.Windows, Winapi.Messages, FMX.Platform.Win;

{ TForm15 }

procedure TForm15.CreateHandle;
var
  NewAppIcon: TIcon;
begin
  inherited;
  NewAppIcon := TIcon.Create;
  NewAppIcon.LoadFromFile('c:\icon.ico');

  SendMessage(ApplicationHWND, WM_SETICON, 1, NewAppIcon.Handle);
  SendMessage(WindowHandleToPlatform(Handle).Wnd, WM_SETICON, 1, NewAppIcon.Handle);
end;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Это пожалуй единственное место куда я не догадался проверить. Спасибо. :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Гость
Эта тема закрыта для публикации ответов.

  • Похожий контент

    • От Ihor Matlak
      У меня проблема с модальными формами VCL в Firemonkey проекте. Когда VCL форма вызвана ShowModal, и пользователь параллельно с панели задач вызывает другую программу, по возвращении к моей модалка падает под программу и поднять ее невозможно. Помогите может у кого было такое. Помогите пожалуйста
    • От Алиса Романец
      Доброго времени суток, форумчане!
      Столкнулась с проблемой при стилизации границ формы окна в FireMonkey на Windows. Как только появляется стиль границ - форма начинает вести себя, как прозрачная - то есть, теряет тень. Кто-то из добрых людей уже замечали эту проблему, но в текущих реалиях прикостылить их решения я не смогла. CreateParams в FMX не существует, а вручную добавлять TShadowEffect в стиль не принесло никаких результатов, кроме съехавшего стиля формы и тормозного изменения размеров. Может ли кто пояснить за этот момент, как вообще тень обрабатывается в MS Windows и есть ли какой-нибудь workaround для её ручной активации через WinAPI? Потому что с тенью в macOS всё прекрасно (однако, там и границу окна не стилизуешь, as long as у вас не веб-приложение, хехе).
      Скрины проблемы прикрепила к посту. Стиль полностью векторный.

      Обновление (19.12.2019 11:44 МСК)
      Путём перехвата процедуры CreateHandle и с использованием SetClassLong удалось создать тень, ноо... До тени, что выдаёт Windows 10 ей далеко 😕
      type TForm1 = class(TForm) private {$IFDEF MSWINDOWS}procedure CreateHandle; override;{$ENDIF MSWINDOWS} public { Public declarations } end; begin {$IFDEF MSWINDOWS} procedure TForm1.CreateHandle; begin inherited CreateHandle; var hWnd: HWND := FormToHWND(Form1); if hWnd <> 0 then SetClassLong(hWnd, GCL_STYLE, GetClassLong(hWnd, GCL_STYLE) or CS_DROPSHADOW); end; {$ENDIF MSWINDOWS} end. Результат:

    • От gresaggr
      Всем привет.
      Работаю с DCEF3 последней ветки (https://github.com/hgourvest/dcef3).  Использую Delphi 10.1
      Нужно сделать две одновременно запущенных web версии whatsapp (https://web.whatsapp.com/)
      Динамически создаются два экземпляра Chromium с разными именами, user agent.
      Сначала создается первый и в нем появляется QR-код для авторизации через телефон. 
      После создания второго - в первом QR код исчезает и появляется ТОЛЬКО во втором. 
      Я так понимаю проблема в куках.
      Может кто знает как можно задать отдельную папку/хранить в память для каждого экземпляра?
       
      Сейчас задаю таким образом глобальный куки менеджер:
       CookiesPath := ExtractFilePath(Application.ExeName) + DEFAULT_COOKIES_DIR + currentNumber.ToString;  CM := TCefCookieManagerRef.Global(nil);  CM.SetStoragePath(CookiesPath, true, nil);  
      P.S.  Если запускать копию exe из той же папки где находятся все ресурсы хромиума, то такой проблемы нет.
      P.P.S. Еще заметил, что авторизованная сессия whatsapp не сохраняется после перезапуска программы. А тот же mail.ru сохраняется.
    • От Данил Абдрафиков
      В Color Constants есть константа clActiveCaption, которая возвращает цвет активного окна, но в Windows 10 возвращается совсем не тот цвет, который на данный момент.
      Пробовал так:
      GetSysColor(COLOR_ACTIVECAPTION) Или:
      clActiveCaption В общем-то есть предположение, что это давно уже не работает и несовместимо с Windows 8/10. Есть костыли по вытаскиванию цвета активного окна?
    • От zekelive
      Добрый день, друзья. Подскажите, есть ли возможность в firemonkey открыть форму как представлено на картинке ниже? Если да, то подскажите в какую сторону копать. Спасибо.

    • От Andrew
      Для проведения опытов на Delphi XE8 использовал два планшета Android:
      - Lenovo TAB S8-50LC на базе процессора Intel Atom Z3745: http://www.ixbt.com/portopc/lenovo-tab-s8-50lc.shtml. На нем установлен Android версии 4.4.2. Подключил к USB и, как не странно, тестовая программка "Hello World!" успешно запустилась, хотя думал, что с процессором Intel вообще ничего не получится;

      - Prestigio Multipad PMT5777_3G с процессором ARM MediaTek MT8382. На нем установлен Android 4.2.2.

      Затем на форму красного цвета бросил зеленый TRectangle, присвоил Align значение Client, а также добавил желтую рамку с помощью свойства Stroke (Thinkness=10). Запустил на Prestigio - выглядит нормально, если не считать не дорисованных уголков (см. красные квадраты). Кстати, на Windows уголки рамки отображаются нормально.
       
      MainForm.Top = 25 - видимо, смещение от статус-бара.
       

       
       
      Затем запустил на Lenovo и результат получился не такой красивый. Как видим, верх формы почему-то "уехал" за статус-бар, а ее свойство Top равно 0:
       

       
      ------------------------------------------------------------------------------------------------------------------------------------
       
      Поворачиваем планшеты на 90 градусов. На Prestigio все выглядит нормально:
       

       
       
      На Lenovo произошло какое-то расстройство:
       

       
       
      Как я понял, на Lenovo возникают проблемы с получением высоты статус-бара и размеров экрана при повороте планшета, но с чем это связано - непонятно. Возможно решение где-то в FMX.Platform.Android или глубже. По крайней мере там переменная FStatusBarHeight равна нулю.

      На моем примере ниже можно получить правильные размеры и позицию для формы (в т.ч. с учетом статус-бара):
      NativeWin := SharedActivity.getWindow;
      if NativeWin <> nil then
      begin
      ContentRect := TJRect.Create;
      DecorView := NativeWin.getDecorView;
      DecorView.getWindowVisibleDisplayFrame(ContentRect);
      end;
    • От chaplin.u@gmail.com
      На таблетах форма очень растянута. Как можно задать максимальный размер для главной формы.  Пусть по краям будут полосы.
    • От Pulsarius
      Здравствуйте!
      Может кто-нибудь уже проходил через подобное и знает как это реализовать и подскажет. Мне нужно программно создать виртуальный модем и входящее подключение через него. Ну или хотя бы через командную строку. Пробовал через RAS API, но получается не то, что мне нужно. Вот визуализация того, что мне нужно проделать:
       
    • От notricky
      На Android код, который работает исправно на Win  вызывает ошибку "CalledFromWrongThreadException: Only the original thread that created a view hierarcy can touch its views" 
      Смысл таков, что я пытаюсь показать форму из треда, у которой BorderStyle=none (роли это не играет).
      Решение в Андроиде заключается в том, чтобы пускать через  runOnUiThread  (то есть выполнять интерфейсные штуки в главном потоке). Как я понимаю, в firemonkey эту фичу должен выполнять Synchronize().
      Тем не менее, ошибка возникает.
      А при запуске в режиме дебага на андроид девайсе событие кнопки вообще не срабатывает иногда. А если срабатывает, то возникает описанное выше исключение.
      Я собрал тестовый пример и в нем не сразу видна ошибка, тогда как получил я ее на рабочем проекте.
      Цель: показать бизибокс на время бекграундных действий. Этот бизибокс у меня сначала был просто на каждой форме и я интерфейсно его вызывал, но теперь решил сделать отедльной формой (как и тоаст), но почему так происходит я не понял. Вы что скажете?
       
      unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TWorkThread = class(TThread) public procedure Execute; override; end; var Form1: TForm1; implementation {$R *.fmx} uses unit2; procedure TForm1.Button1Click(Sender: TObject); var t: TWorkThread; begin t := TWorkThread.Create(True); t.FreeOnTerminate := true; t.Start; end; { TWorkThread } procedure TWorkThread.Execute; begin inherited; Self.Synchronize(procedure begin Form2.Show; Form2.Top := Form1.Top; Form2.Left := Form1.Left; Form2.BringToFront; end); Terminate; end; end.  
    • От Error
      Ссылка: https://habrahabr.ru/post/318876/
      Автор: Error
      Описание:
      Delphi и C++Builder разработчики, использующие VCL не по наслышке знают о вездесущей проблеме мерцания контролов.
      Мерцание происходит при перерисовке, вследствие того, что сначала отрисовываеться фон компонента, и только потом сам компонент.
      И если в случае с наследниками от TWinControl частичным решением проблемы является установка свойства DoubleBuffered в True, что заставляет контрол отрисовываться в буфере(однако DoubleBuffered работает тоже не идеально, к прим.: контрол перестает быть прозрачным), то в случае с TGraphicControl решение с DoubleBuffered просто невозможно, из-за отсутствия у TGraphicControl окна, установка же DoubleBuffered в True у родителя не помогает, из-за того что отрисовка вложенных TGraphicControl-ов происходит уже после прорисовки родителя в буфере.
      Обычно остается только одно — смириться с мерцанием, и максимально упростить отрисовку для минимизации эффекта, или использовать по возможности исключительно TWinControl-ы, что не всегда возможно и удобно.
      Однажды намучившись с мерцанием, я не выдержал и решил решить эту проблему, раз и навсегда!
      ...
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...