继我的问题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;
答案 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;