Inno Setup 在选择特定组件时禁用组件选择
Inno Setup disable component selection when a specific component is selected
我希望能够根据所选的特定组件来禁用对组件的选择。我不能通过嵌套组件来做到这一点,因为组件需要自己选择,但如果选择了另一个特定组件则不行。目前我使用显示消息的 NextButtonClick
事件处理此问题:
if IsComponentSelected('Client') and IsComponentSelected('Sync') then
begin
MsgBox('A Client installation cannot have the Synchronisation component selected.',
mbError, MB_OK);
Result := False;
end;
这会阻止用户继续,直到他们取消选择不兼容的组合。但是,如果我可以简单地禁用组件的选择而不是显示消息,那就更优雅了:
if CurPageID = wpSelectComponents then
begin
if IsComponentSelected('Client') then
begin
WizardForm.ComponentsList.Checked[15] := False;
WizardForm.ComponentsList.ItemEnabled[15] := False;
end
else
begin
WizardForm.ComponentsList.ItemEnabled[15] := True;
end;
end;
问题是似乎没有允许它在用户选择或取消选择组件时动态更改的事件。是否有我可以将此代码放入的事件或其他方式来执行此操作?
你有 TLama 的解决方案,它可能比下面的更好,但这段代码仍然是实现目标的一种方法(尽管你需要 innocallback.dll):
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: ".\InnoCallback.dll"; DestDir: "{tmp}"; Flags: dontcopy nocompression
[Components]
Name: "Client"; Description: "Client"; Types: custom
Name: "Sync"; Description: "Sync"; Types: custom
[Code]
var
TimerID: Integer;
type
TTimerProc = procedure(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
external 'wrapcallback@files:InnoCallback.dll stdcall';
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: UINT): UINT; external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: HWND; uIDEvent: UINT): BOOL;
external 'KillTimer@user32.dll stdcall';
procedure KillComponentsTimer;
begin
if TimerID <> 0 then
begin
if KillTimer(0, TimerID) then
TimerID := 1;
end;
end;
procedure OnComponentsCheck(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
begin
if IsComponentSelected('Client') then begin
WizardForm.ComponentsList.Checked[1] := False;
WizardForm.ComponentsList.ItemEnabled[1] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[1] := True;
end;
if IsComponentSelected('Sync') then begin
WizardForm.ComponentsList.Checked[0] := False;
WizardForm.ComponentsList.ItemEnabled[0] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[0] := True;
end;
end;
procedure ComponentsCheck();
var
TimerCallback: LongWord;
begin
TimerCallback := WrapTimerProc(@OnComponentsCheck, 4);
TimerID := SetTimer(0, 0, 100, TimerCallback);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
ComponentsCheck;
end;
if CurPageID = not wpSelectComponents then
begin
KillComponentsTimer;
end;
end;
我最后使用的代码完全基于@TLama他之前提供的代码,因为这不需要使用外部DLL。
[Code]
const
//Define global constants
CompIndexSync = 15;
CompIndexSyncClient = 16;
CompIndexSyncServer = 17;
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');
Invalidate; //required for text state to update correctly
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;
虽然这个和@RobeN 的解决方案都有效,但它们都存在图形更新问题,即当将组件的状态从活动更改为禁用时,反之亦然,文本不会自动变暗或取消变暗,直到滚动条被拖了。
请注意,上述刷新问题已通过添加“无效;”得到解决线。上面更新的代码带有评论以反映这一点。有关详细信息,请参阅 。谢谢@TLama。
我希望能够根据所选的特定组件来禁用对组件的选择。我不能通过嵌套组件来做到这一点,因为组件需要自己选择,但如果选择了另一个特定组件则不行。目前我使用显示消息的 NextButtonClick
事件处理此问题:
if IsComponentSelected('Client') and IsComponentSelected('Sync') then
begin
MsgBox('A Client installation cannot have the Synchronisation component selected.',
mbError, MB_OK);
Result := False;
end;
这会阻止用户继续,直到他们取消选择不兼容的组合。但是,如果我可以简单地禁用组件的选择而不是显示消息,那就更优雅了:
if CurPageID = wpSelectComponents then
begin
if IsComponentSelected('Client') then
begin
WizardForm.ComponentsList.Checked[15] := False;
WizardForm.ComponentsList.ItemEnabled[15] := False;
end
else
begin
WizardForm.ComponentsList.ItemEnabled[15] := True;
end;
end;
问题是似乎没有允许它在用户选择或取消选择组件时动态更改的事件。是否有我可以将此代码放入的事件或其他方式来执行此操作?
你有 TLama 的解决方案,它可能比下面的更好,但这段代码仍然是实现目标的一种方法(尽管你需要 innocallback.dll):
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: ".\InnoCallback.dll"; DestDir: "{tmp}"; Flags: dontcopy nocompression
[Components]
Name: "Client"; Description: "Client"; Types: custom
Name: "Sync"; Description: "Sync"; Types: custom
[Code]
var
TimerID: Integer;
type
TTimerProc = procedure(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
external 'wrapcallback@files:InnoCallback.dll stdcall';
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: UINT): UINT; external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: HWND; uIDEvent: UINT): BOOL;
external 'KillTimer@user32.dll stdcall';
procedure KillComponentsTimer;
begin
if TimerID <> 0 then
begin
if KillTimer(0, TimerID) then
TimerID := 1;
end;
end;
procedure OnComponentsCheck(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
begin
if IsComponentSelected('Client') then begin
WizardForm.ComponentsList.Checked[1] := False;
WizardForm.ComponentsList.ItemEnabled[1] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[1] := True;
end;
if IsComponentSelected('Sync') then begin
WizardForm.ComponentsList.Checked[0] := False;
WizardForm.ComponentsList.ItemEnabled[0] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[0] := True;
end;
end;
procedure ComponentsCheck();
var
TimerCallback: LongWord;
begin
TimerCallback := WrapTimerProc(@OnComponentsCheck, 4);
TimerID := SetTimer(0, 0, 100, TimerCallback);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
ComponentsCheck;
end;
if CurPageID = not wpSelectComponents then
begin
KillComponentsTimer;
end;
end;
我最后使用的代码完全基于@TLama他之前提供的代码,因为这不需要使用外部DLL。
[Code]
const
//Define global constants
CompIndexSync = 15;
CompIndexSyncClient = 16;
CompIndexSyncServer = 17;
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');
Invalidate; //required for text state to update correctly
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;
虽然这个和@RobeN 的解决方案都有效,但它们都存在图形更新问题,即当将组件的状态从活动更改为禁用时,反之亦然,文本不会自动变暗或取消变暗,直到滚动条被拖了。
请注意,上述刷新问题已通过添加“无效;”得到解决线。上面更新的代码带有评论以反映这一点。有关详细信息,请参阅