I have a requirement to skip a page when only a particular component is selected in the component selection page. Please look at the below image
[Components]
Name: DBS\TRACE; Types: DBS TBLWOS; Description: DBS Tracing Bodylife Database;
The requirement is to skip a page when the option "DBS Tracing Bodylife Database" (highlighted in the image) alone is selected and clicked on Next button. If I select only that option, I am successfully able to skip the page using below code.
if PageID = PageToBeSkipped.ID then begin
Result := not (IsComponentSelected('not DBS\TRACE'));
end;
But If I select any other additional component along with the "DBS Tracing Bodylife Database" from this page which is nearly 20, the page should not be skipped. The above code skips the page if any additional component is selected too.
How can I handle this?
Thanks in advance!
To test if any component, except one specific, is selected, you can use WizardSelectedComponents function (which returns a comma-separated list of selected components).
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = PageToBeSkipped.ID then
begin
Result := (CompareText(WizardSelectedComponents(False), 'DBS,DBS\TRACE') = 0);
end;
end;
Note that the WizardSelectedComponents returns even "partially" selected components groups.
Related
How to customize last page (wpFinished) to only have a message, but keep the bottom buttons, like this:
I don't want the image on the left that is there by default.
I was trying to create new page, but don't know how to hide the default finished page or add bottom buttons (Back, Finished, Cancel) on new page.
Hide the WizardBitmapImage2 control and extend remaining controls accordingly.
Something like this:
[Code]
procedure ExtendFinishedPageControl(Control: TControl);
begin
Control.Left := Control.Left - WizardForm.WizardBitmapImage2.Width;
Control.Width := Control.Width + WizardForm.WizardBitmapImage2.Width;
end;
procedure InitializeWizard();
begin
WizardForm.WizardBitmapImage2.Visible := False;
ExtendFinishedPageControl(WizardForm.RunList);
ExtendFinishedPageControl(WizardForm.NoRadio);
ExtendFinishedPageControl(WizardForm.YesRadio);
ExtendFinishedPageControl(WizardForm.FinishedLabel);
ExtendFinishedPageControl(WizardForm.FinishedHeadingLabel);
end;
I want to make a wizard. When the user choose yes, then the page x should follow. When the user choose no, then the page y should follow. At the moment it is only working for me with static pages, so it does not matter, what the user chooses always the same page follows... how can I change this? Please have somebody an idea?
Here is an extract:
ExistPage := CreateInputOptionPage(OldPage.ID,
'', '', 'If you choose "Yes" then the package will be installed automatically with this setup',True, False);
// Add items
ExistPage.Add('Yes');
ExistPage.Add('No');
// Set initial values (optional)
ExistPage.SelectedValueIndex := 0;
TexPage := CreateInputDirPage(ExistPage.ID,
'Root installation directory', 'Please select installation directory',
'',
True, 'New Folder');
TexPage.Add('');
// Set initial value (optional)
TexPage.Values[0] := ExpandConstant('C:\');
When you create your page you can store the page ID into a variable:
// InitializeWizard is called when the wizard is about to begin.
// This is where we add any custom pages into the installation process.
// The relevant XXX_CreatePage methods should be defined BEFORE this method.
procedure InitializeWizard();
begin
// Add the Application Settings page just after the Selected Tasks page
idAppSettingsPage := AppSettings_CreatePage(wpSelectTasks)
end;
If you were to cache the result of your users radio choice into a public variable then you might be able to use this:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
// We don't want to show the select dir, select program group or application settings
// pages if we are upgrading.
if ((PageID = wpSelectDir) or (PageID = idAppSettingsPage) or (PageID = wpSelectProgramGroup)) then
Result := bIsUpgrading
else
Result := False;
end;
This is something I have used in my setup to selectively show pages during the install process. I am just not sure how you check for a custom page ID.
I am just showing the mechanics. If you know you are now about to show page Y, you can decide to show it if variable AA was set to 1. Make sense?
I have a TStringGrid in Lazarus, running on Linux. I have a column which has editor type cbsButton. I want the button to display a certain image, instead of an ellipsis. I have the following code, which causes an error:
procedure TForm1.streams_gridDrawCell(Sender: TObject; aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
var
aCanvas: TCanvas;
aGrid: TStringGrid;
Editor: TWinControl;
image: TImage;
begin
if (aCol <> 1) or (aRow = 0) then begin
Exit;
end;
aGrid := (Sender as TStringGrid);
aCanvas := image.Canvas;
aCanvas.FillRect(aRect);
imagelist1.Draw(aCanvas, aRect.Left+2, aRect.Top+2, 8);
Editor := (aGrid.EditorByStyle(cbsButton) as TButtonCellEditor);
Editor.Brush.Style := TBrushStyle.bsImage;
(Editor.Brush.Image as TImage) := image; // causes the error below
end;
The error is:
mainform.pas(156,23) Error: Class or Object types "TFPCustomImage" and
"TImage" are not related
At this point, I'm sure I'm going about this in entirely the wrong way. Could someone please put me back on the right path?
I doubt that the OnDrawCell event is the correct place to modify a cell editor because probably the correct cell editor does not exist at this moment when the cell is painted.
The correct event to define the cell editor is the OnSelectEditor event of the grid. Please read the wiki (http://wiki.lazarus.freepascal.org/Grids_Reference_Page).
The cbsButton editor which you use inherits from TButton. A TButton does not have a Glyph property - you cannot assign a bitmap to the button. But can you write your own cell editor easily, just follow the standard example in examples/gridexamples/gridcelleditor:
Add a TBitBtn to the form. Delete its Caption, add the requested image to the Glyph property. Set the Visible property to false.
In the OnClick event of this button write how you want to edit the cell. Access the cell specified by the properties Col and Row of the grid. As an example, I assume here that you just want to open an InputBox:
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] :=
InputBox('Input some text', 'Text:', '');
end;
Now write an event handler for the OnSelectEditor event of the grid. It must assign the BitBtn to the Editor parameter of the event and make sure that the button is at the correct position within the selected cell - that's all!
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
var Editor: TWinControl);
var
R: TRect;
begin
if (aCol=2) and (aRow > 0) then begin
R := StringGrid1.CellRect(aCol, ARow);
R.Left := R.Right - (R.Bottom - R.Top);
BitBtn1.BoundsRect := R;
Editor := BitBtn1;
end;
end;
Editor.Brush.Image is a property of type TFPCustomImage. This is a TPersistent descendant. And TImage is a descendant of TCustomImage and thus TGraphicControl and TControl. So these are entirely different classes that are not compatible.
So you are not expected to cast (Editor.Brush.Image as TImage) and assign any TImage instance to it.
An installer I'm working on does most of its work in the PrepareToInstall function, as everything I need to do might fail, and therefore this is the appropriate place to handle these things in case they do fail. Any failure can be automatically reported by passing the error message in the function's result. There are only 3 small files which the installer actually copies over.
The problem is that the wizard seems to freeze (or not respond rather) during this function, just showing a blank page titled "Preparing to install..." while in reality, it's going through my install process.
I would like to show the progress to the user with a simple procedure ShowProgress(const S: String); which shows the user what it's actually doing. How can I do this?
This is how I'm doing my install, where I'd like to wrap each call to Log()...
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
R: Integer;
begin
Result:= '';
try
Log('Doing this...');
R:= DoThis;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing this.';
end;
else begin
Result:= 'Unexpected error doing this: ' + IntToStr(R);
end;
end;
if Result = '' then begin
Log ('Doing that...');
R:= DoThat;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing that.';
end;
else begin
Result:= 'Unexpected error doing that: ' + IntToStr(R);
end;
end;
end;
if Result = '' then begin
Log ('Doing something else...');
R:= DoSomethingElse;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing something else.';
end;
else begin
Result:= 'Unexpected error doing something else: ' + IntToStr(R);
end;
end;
end;
//A few more blocks like above
//Error logging
if Result <> '' then begin
Log('FAILURE: '+Result);
end;
except
Result:= 'EXCEPTION';
Log('EXCEPTION');
end;
end;
I mixed all your answers guys and I found another possible solution to this question.
I know this is not a so called 'elegant solution'.
Anyway it is a quick solution that can avoid you to develop a separated DLL with custom controls managing the communication between the installer and the DLL.
The following approach describe how to create a custom Page that is a copy of the Page Inno Setup shows during PrepareToInstall setup phase.
This copy must have the same look and feel of the original one plus a progress bar and a label that developer can use to indicate the current step of the PrepareToInstall setup phase.
Let's see some code.
First of all, prepare the custom Page:
[Code]
var
PrepareToInstallWithProgressPage : TOutputProgressWizardPage;
Now define PrepareToInstall function to add our custom stuff:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
PrepareToInstallWithProgressPage.SetProgress(0, 0);
PrepareToInstallWithProgressPage.Show;
try
// First preinstallation step: suppose to kill currently app you are going to update
PrepareToInstallWithProgressPage.SetText('Exiting MyApp Running Instances'), '');
ExecuteCmd('taskkill', '/F /IM MyApp');
// Set progress bar to 10%
PrepareToInstallWithProgressPage.SetProgress(1, 10);
// Second preinstallation step
// DoSomething usefull...
// Set progress bar to 20% after done
PrepareToInstallWithProgressPage.SetProgress(2, 10);
// ...do other preinstallation steps till the end
PrepareToInstallWithProgressPage.SetProgress(10, 10);
finally
PrepareToInstallWithProgressPage.Hide;
end;
end;
At this installation phase we completed the steps for prepare to install phase so the default PrepareToInstall Page of Inno Setup is shown for a while.
Now, the user probably cannot understand that the page changes because our PrepareToInstallWithProgressPage has the same look and feel of the original one.
In order to let our page has the same look and feel we can use SetupMessage function to get the 2 strings of the original Inno Setup PrepareToInstall page.
Using SetupMessage we can avoid to duplicate and localize strings for our custom page to copy the originals.
Just like this:
procedure InitializeWizard;
var
A: AnsiString;
S: String;
begin
// The string msgWizardPreparing has the macro '[name]' inside that I have to replace with the name of my app, stored in a define constant of my script.
S := SetupMessage(msgPreparingDesc);
StringChange(S, '[name]', '{#MY_APPNAME}');
A := S;
PrepareToInstallWithProgressPage := CreateOutputProgressPage(SetupMessage(msgWizardPreparing), A);
end;
I hope this help.
Have a look at the CodeDlg.iss example script included with Inno, in particular at the code that uses CreateOutputProgressPage.
This page type allows you to show a status message and/or a progress bar while you are performing other actions. And it automatically pumps messages whenever you change the progress label/value to ensure that the user sees the correct status.
It is intended for precisely this kind of code.
The code written in the PrepareToInstall function blocks the Windows message pump from processing, rendering the wizard form non-responsive. If many controls are needed to be visible in this screen, showing perhaps a progress bar and list of steps to take, this can be done with a form inside of a DLL. Make sure there is a thread in this DLL which does the actual install process, and only update the GUI to display the status to the user. This DLL form may overlay the wizard, or may even be embedded inside of the wizard in a custom wizard page, for example. The idea is to move your install code from the PrepareToInstall function to within this thread, and only then will you be able to achieve a fully responsive GUI during this process. From the PrepareToInstall function, there should be just one DLL call to initiate the install process, which in turn shows the form from the DLL.
The result may look something like this (in the works):
Is it possible to conditionally disable or hide edit boxes from a TInputQueryWizardPage input page (page created by using CreateInputQueryPage function) ?
I have 4 edit boxes and I need to disable/hide the last two of them, based on the input from the previous wizard page. How can I do it ?
You can access them through the TInputQueryWizardPage.Edits indexed property:
[Code]
var
FirstEditIndex: Integer;
SecondEditIndex: Integer;
InputPage: TInputQueryWizardPage;
procedure InitializeWizard;
begin
InputPage := CreateInputQueryPage(wpWelcome, 'Caption', 'Dscription', 'SubCaption');
// the Add method returns the Index of the just added edit box, and
// you need to store those indexes to access the edit boxes later on
FirstEditIndex := InputPage.Add('Name:', False);
SecondEditIndex := InputPage.Add('Surname:', False);
// access the edits through the stored indexes; in your case this will
// happen in the other event, so take this script as a showcase
InputPage.Edits[FirstEditIndex].Enabled := False;
InputPage.Edits[SecondEditIndex].Visible := False;
end;