Как отменить регистрацию стиля с помощью TStyleManager :: UnRegisterStyle () - PullRequest
0 голосов
/ 24 октября 2018

Я хочу отменить регистрацию определенного стиля, используя этот код:

void __fastcall TfrmMain::btnUnregStyleClick(TObject *Sender)
{
    TCustomStyleServices *MyStyle;

    // Get wanted style
    MyStyle = TStyleManager::Style["Emerald"];   // this will return a TCustomStyleServices obj

    if (MyStyle != NULL)
    {
        // Remove it
        TStyleManager::UnRegisterStyle(MyStyle);   // This will set default Windows style (no style)
    }
}

Это работает.Кажется, что стиль не зарегистрирован, и графический интерфейс пользователя автоматически переключается на стиль Windows по умолчанию.

Но когда программа закрывается, я получаю эту ошибку:

Project Project.exe вызывает исключениекласс $ C0000005 с сообщением 'нарушение доступа в 0x5005fd50: чтение адреса 0xffffffd0'.

Вот стек вызовов:

:5005fd50 rtl250.@System@TObject@InheritsFrom$qqrp17System@TMetaClass + 0x8
:50d12a8d vcl250.@Vcl@Styles@TStyleEngine@Notification$qqr54Vcl@Themes@TCustomStyleEngine@TStyleEngineNotificationpv + 0x1d
:00e5a612 vclwinx250.@Vcl@Winxctrls@TSearchBox@$bcdtr$qqrv + 0x1e
:0041fa0f __cleanup + 0x1F
:0041fb92 ; __wstartup

[Обновление: этот сбой исправлен, если яудалить TeeChart из моей формы.Но UnRegisterStyle() все равно не будет работать]


Если после UnRegisterStyle() я позвоню:

TStyleManager::LoadFromFile(usStylePath);
TStyleManager::SetStyle("Emerald");

, он скажет мне, что «изумрудный стиль уже зарегистрирован».

Итак, очевидно, UnRegisterStyle() терпит неудачу.

Получение списка "стилей" с помощью TStyleManager::StyleNames() показывает, что список остается неизменным после UnRegisterStyle().

Embarcadero не помогает по этой функции.Должен ли я назвать что-то еще, кроме UnRegisterStyle()?

1 Ответ

0 голосов
/ 24 октября 2018

Ответ обновлен!Снова.

Ну, я не очень опытный пользователь стилей VCL, но я постараюсь объяснить, что нужно сделать, чтобы избежать вашей проблемы.Прежде чем читать дальше, я должен сказать вам: нет способа отменить регистрацию любого стиля .

Стиль или нет Стиль

Во-первых, вы должны знать, что стили VCL используют внутренний словарь FRegisteredStyles для хранения всех зарегистрированных стилей.Стиль стал зарегистрирован после того, как TStyleManager.LoadFromFile(YourStyleName) был вызван.К сожалению, стиль никогда не удаляется из словаря.

Не используйте UnregisterStyle процедуру.Это не отменяет стиль вообще.Просто удаляет указанный стиль из списка доступных стилей.Таким образом, после вызова UnregisterStyle(YourStyle) вы просто удаляете YourStyle из внутреннего списка, а не из словаря, упомянутого ранее, и не можете установить этот стиль.

После успешного вызова UnregisterStyle() вы можете вызвать LoadFromFile() метод и спросить, почему приложение говорит:

Стиль «Изумруд» уже зарегистрирован.

Это происходит потому, что метод LoadFromFile() загружает указанный стиль и проверяет его наличие во внутреннем словаре зарегистрированных стилей.Эта проверка всегда возвращает true, поскольку UnregisterStyle() процедура не удаляет указанный стиль из словаря.

То же самое относится и к свойству StyleNames.Это свойство возвращает имя стиля, использует внутренний словарь FRegisteredStyles для получения имени стиля с указанным индексом.

Если вы хотите узнать мое мнение, такое поведение стиля странно.Метод UnregisterStyle() также должен удалить указанный стиль из словаря.Может быть, я ошибаюсь, но это настоящая ошибка.С другой стороны, ошибки нет - вы пытаетесь использовать недокументированный метод.Истина где-то рядом.

Styled Result

В заключение я бы порекомендовал вам использовать прямой доступ к стилям, чтобы решить, можете ли вы использовать выбранный стиль или нет.См. Код ниже (при условии, что у вас включен стиль приложения):

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(TStyleManager.Style['Emerald']) and TStyleManager.Style['Emerald'].Available then
    // Do your code
  else
    ShowMessage('Specified style is not available!');
end;  

Если у вас есть источники VCL.Themes, вы можете легко проверить достоверность того, что я написал здесь.

Высокие технологии

Я создал class-helper для TStyleManager, который позволяет проверить, зарегистрирован ли файл стиля, и отменить его регистрацию при необходимости.Чтобы заполнить TStyleInfo запись реальными значениями из файла стилей, нам нужно сохранить указанный стиль в TMemoryStream и затем передать поток в функцию IsValidStyle.Также мы могли бы использовать физический путь для стиля (например, C:\Embarcadero\Delphi\Styles\Emerald.vsf) вместо варианта с потоком, но последний выглядит более элегантно.

К сожалению, согласно комментарию Реми (я не знаю, как сделать ссылку на него), C++ Builder (который используется OP) не поддерживает class-helperособенность.Единственное решение - создать простой модуль, содержащий весь код, необходимый для class-helper.Для работы достаточно добавить такой модуль в предложение uses и вызвать соответствующие публичные процедуры \ функции.

Это устройство имеет следующую структуру:

unit StyleManager_CH;

interface

uses
  VCL.Themes;


  function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
  procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);


implementation

uses
  System.SysUtils, System.Classes, System.Generics.Collections;


type
  TStyleManagerHelper = class helper for TStyleManager
  public
    class function IsStyleRegistered(var AStyle: TCustomStyleServices): Boolean;
    class procedure UnregisterStyleEx(var AStyle: TCustomStyleServices);
  end;


class function TStyleManagerHelper.IsStyleRegistered(var
  AStyle: TCustomStyleServices): Boolean;
begin
  Result := Assigned(AStyle) and
            TStyleManager.FRegisteredStyles.ContainsKey(AStyle.Name);
end;

class procedure TStyleManagerHelper.UnregisterStyleEx(var
  AStyle: TCustomStyleServices);
var
  MS: TMemoryStream;
  StyleInfo: TStyleInfo;
  SourceInfo: VCL.Themes.TStyleManager.TSourceInfo;
begin
  if Assigned(AStyle) then
    begin
      MS := TMemoryStream.Create;
      try
        AStyle.SaveToStream(MS);
        MS.Position := 0;
        if AStyle.IsValidStyle(MS, StyleInfo) then
          begin
            if TStyleManager.FRegisteredStyles.ContainsKey(StyleInfo.Name) then
              begin
                SourceInfo := TStyleManager.FRegisteredStyles.Items[StyleInfo.Name];
                if Assigned(SourceInfo.Data) then
                  FreeAndNil(SourceInfo.Data);

                TStyleManager.FStyles.Remove(AStyle);
                TStyleManager.FRegisteredStyles.Remove(StyleInfo.Name);
                FreeAndNil(AStyle);
              end;
          end;
      finally
        MS.Free;
      end;
    end;
end;

function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
begin
  Result := TStyleManager.IsStyleRegistered(AStyle);
end;

procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
begin
  TStyleManager.UnregisterStyleEx(AStyle);
end;


end.

Приложение _CH обозначает class-helper сокращение.Также были исправлены некоторые утечки памяти.

Я не уверен, есть ли другой способ перезагрузить файл стиля "на лету".

...