Inno Setup Components图形刷新问题

时间:2015-07-01 16:35:43

标签: inno-setup

继我的问题Inno Setup disable component selection when a specific component is selected之后,两个解决方案都存在一个问题,即组件列表无法正常刷新,直到用户与其进行进一步的交互。即,当将组件的状态从活动状态更改为禁用状态,反之亦然时,在拖动滚动条之前,文本不会自动变暗或不变。任何人都可以提供一种方法来正确刷新它而无需拖动滚动条吗?

这是一个可编译的示例脚本,只有九个组件条目具有相同的行为。复选框的状态正确更新,但文本的状态不正确。滚动或单击项目似乎是使状态(变暗或不变)刷新的唯一方法。

#define MyAppName "My Program"
#define MyAppVersion "1.5"
#define MyAppPublisher "My Company, Inc."
#define MyAppURL "http://www.example.com/"
#define MyAppExeName "MyProg.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{1A3F520A-40DD-4E79-A711-201077215049}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
OutputDir=Output
OutputBaseFilename=ComponentTest
Compression=lzma
SolidCompression=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Components]
;Define which installation components are preselected for each installation type
Name: "Client"; Description: "Client"; Flags: disablenouninstallwarning
Name: "Dummy1"; Description: "Dummy Entry"; Flags: disablenouninstallwarning
Name: "Dummy2"; Description: "Dummy Entry"; Flags: disablenouninstallwarning
Name: "Dummy3"; Description: "Dummy Entry"; Flags: disablenouninstallwarning
Name: "Dummy4"; Description: "Dummy Entry"; Flags: disablenouninstallwarning
Name: "Dummy5"; Description: "Dummy Entry"; Flags: disablenouninstallwarning
Name: "Sync"; Description: "Synchronisation"; Flags: disablenouninstallwarning
Name: "Sync\Client"; Description: "Sync Client"; Flags: exclusive disablenouninstallwarning
Name: "Sync\Server"; Description: "Sync Server"; Flags: exclusive disablenouninstallwarning

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "C:\Program Files (x86)\Inno Setup 5\Examples\MyProg.exe"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

[Code]
const
//Define global constants
  CompIndexSync = 6;
  CompIndexSyncClient = 7;
  CompIndexSyncServer = 8;

var
//Define global variables
  CompPageVisited: Boolean;
  DefaultCompClickCheck: TNotifyEvent;
  DefaultCompTypeChange: TNotifyEvent;

//Uncheck and set the enabled state of the Sync components based on whether the Client component is selected
procedure UpdateComponents;
begin
  with WizardForm.ComponentsList do
    begin
      if IsComponentSelected('Client') then
        begin
          CheckItem(CompIndexSync, coUncheck);
        end; 
      ItemEnabled[CompIndexSync] := not IsComponentSelected('Client');
      ItemEnabled[CompIndexSyncClient] := not IsComponentSelected('Client');
      ItemEnabled[CompIndexSyncServer] := not IsComponentSelected('Client');
    end;
end;

//Update the component states if the component states change and restore the original event handler procedures
procedure ComponentsClickCheck(Sender: TObject);
begin
  DefaultCompClickCheck(Sender);
  UpdateComponents;
end;

procedure ComponentsTypesComboChange(Sender: TObject);
begin
  DefaultCompTypeChange(Sender);
  UpdateComponents;
end;

procedure InitializeWizard();
begin
//Store the original Components Page OnClickCheck and Types Combo Box OnChange event procedures and assign custom procedures
  DefaultCompClickCheck := WizardForm.ComponentsList.OnClickCheck;
  WizardForm.ComponentsList.OnClickCheck := @ComponentsClickCheck;
  DefaultCompTypeChange := WizardForm.TypesCombo.OnChange;
  WizardForm.TypesCombo.OnChange := @ComponentsTypesComboChange;
end;

procedure CurPageChanged(CurPageID: Integer);
begin
//Update the Components Page if entered for the first time
  if (CurPageID = wpSelectComponents) and not CompPageVisited then
    begin
      CompPageVisited := True;
      UpdateComponents;
    end;
end;

1 个答案:

答案 0 :(得分:2)

问题在于ItemEnabled setter使项目无效的方式。它不是使整个项目矩形无效,而是调用InvalidateCheck方法,该方法仅使项目的复选框部分无效。请参阅此代码(由我评论):

procedure TNewCheckListBox.InvalidateCheck(Index: Integer);
var
  IRect: TRect;
begin
  // store the full item rectangle into the IRect variable
  IRect := ItemRect(Index);
  // offset this stored rectangle's left position by the item level
  Inc(IRect.Left, (FCheckWidth + 2 * Offset) * (ItemLevel[Index]));
  // set its right position to the left edge + width of the check image + margin offsets,
  // but with no item text consideration, because this method was intended to invalidate
  // just the check box portion of the item which is not enough for enabled state change
  IRect.Right := IRect.Left + (FCheckWidth + 2 * Offset);
  // flip the rectangle for RTL reading and ask the control to invalidate that rectangle
  FlipRect(IRect, ClientRect, FUseRightToLeft);
  InvalidateRect(Handle, @IRect, FThemeData <> 0);
end;

启用状态setter需要具有类似这样的内容(伪代码):

procedure TNewCheckListBox.SetItemEnabled(Index: Integer; const AEnabled: Boolean);
begin
  if ItemStates[Index].Enabled <> AEnabled then
  begin
    ItemStates[Index].Enabled := AEnabled;
    // invalidate the check portion of the item
    InvalidateCheck(Index);
    // and invalidate also the text portion of the item
    InvalidateText(Index);
  end;
end;

所以这是TNewCheckListBox控件中的错误。因为无法访问项目矩形,所以当您完成更新项目的启用状态时,可以通过调用Invalidate来要求整个控件无效:

procedure UpdateComponents;
begin
  with WizardForm.ComponentsList do
  begin
    ...
    ItemEnabled[CompIndexSync] := not IsComponentSelected('Client');
    ItemEnabled[CompIndexSyncClient] := not IsComponentSelected('Client');
    ItemEnabled[CompIndexSyncServer] := not IsComponentSelected('Client');
    ...
    Invalidate;
  end;
end;