Por muito tempo, a maneira mais fácil de criar um aviso textual foi usando o ShowMessage(‘Hello World’). Claro que o ShowMessage não vem sozinho, com eles uma série de janelas de dialogos que podemos ver no link abaixo:
https://wiki.freepascal.org/Dialog_Examples
Neste artigo vamos ver como substituir o ShowMessage, MessageBox e MessageDlg por uma variação moderna chamada de TaskDialog que funciona no Lazarus e seu comportamento é o mesmo em qualquer sistema operacional. A documentação do TaskDialog pode ser encontrada aqui:
https://wiki.freepascal.org/TTaskDialog
Para usar o TaskDialog, basta incluir a unit Dialogs ao seu uses, na realidade, ela é tão comum que provavelmente você já a possui em todos os seus projetos e apenas desperdiça a possibilidade de usar TaskDialog. Você pode usar o componente TaskDialog, mas perceberá que é mais fácil evitar o componente e ir implementá-lo com código.
Porque usar TaskDialog?
A resposta mais simples de porque usar o TaskDialog é porque ele pode ser usado em todas as situações de interação com o usuário, enquanto as outras formas de Dialogs usam comandos diferentes para cada situação e no caso do MessageBox e MessageDlg, o texto em seus botões podem não estar traduzidos causando o efeito indesejado de pergunta em português e tendo que clicar em Yes, No, Cancel…
Com o TaskDialog é possível acrescentar ícone indicador, titulo de janela, titulo de texto, botões, radiogroups, … deixando-o com cara de Assistente(Wizard). Veja esse exemplo de como o TaskDialog pode ficar se incluir todos os recursos:

ShowMessage, MessageBox e MessageDlg – a queda
O ShowMessage é usado dessa forma:
ShowMessage('Não há quem goste de dor, que a procure e a queira ter, simplesmente porque é dor...');
E exibido assim para o usuário:

Não tem beleza alguma e basta então clicar em “OK” e a janela será finalizada. Uma forma diferente de exibir essa mesma mensagem usando o TaskDialog é:
with TTaskDialog.Create(self) do
try
Caption := 'Titulo da janela';
Title := 'Titulo do texto';
Text := 'Não há quem goste de dor, que a procure e a queira ter, simplesmente porque é dor...';
CommonButtons := [tcbOk]; // (tcbOk, tcbYes, tcbNo, tcbCancel, tcbRetry, tcbClose);
MainIcon := tdiInformation; // (tdiNone, tdiWarning, tdiError, tdiInformation, tdiShield, tdiQuestion);
Execute;
finally
Free;
end 
Agora temos um titulo na janela, temos um titulo para o texto e o texto propriamente dito. Sei o que está pensando: “mas é muito mais código para só mostrar uma mensagem”. O que você não está pensando é:
- Posso refatorar o TaskDialog para substituir ShowMessage
- Totalmente personalizável
Como assim refatorar? Simplesmente Crie um procedimento chamado ShowMessage2 que use o TaskDialog:
procedure ShowMessage2(
ATexto: String;
ATitle: String='Informação:';
ACaption: String='');
begin
if ACaption=emptyStr
then ACaption:=ExtractFileName(Application.ExeName);
if ATitle=emptyStr
then ATitle:='Informação:';
with TTaskDialog.Create(self) do
try
Caption := ACaption;
Title := ATitle;
Text := ATexto;
CommonButtons := [tcbOk]; // (tcbOk, tcbYes, tcbNo, tcbCancel, tcbRetry, tcbClose);
MainIcon := tdiInformation; // (tdiNone, tdiWarning, tdiError, tdiInformation, tdiShield, tdiQuestion);
Execute;
finally
Free;
end
end;E pronto, agora posso substituir todos os ShowMessages por ShowMessages2:
ShowMessage2('Não há quem goste de dor, que a procure e a queira ter, simplesmente porque é dor...');
E em termos de código será igual. Basta agora fazer o search/replace em todo o seu projeto.
Mas e a personalização que falei agora a pouco? Muito bem, o TaskDialog é totalmente personalizável, você pode acrescentar botões, texto expandido, checkbox, radiogroup,…tudo numa única chamada e a parte legal é que obtêm o resultado selecionado sem complicação. Já demonstrei em como substituir o ShowMessage, agora falta os outros dois: MessageBox e MessageDlg que são basicamente duas funções diferentes com o mesmo proposito.
A base para você entender como o TaskDialog funciona com múltiplas opções é a seguinte:
with TTaskDialog.Create(self) do
try
Caption := 'Confirmação:';
Title := 'Posso remover o item?';
Text := 'Posso remover o item selecionado?';
// CommonButtons é para mostrar os botões padrões que podem ser exibidos:
// tcbOk, tcbYes, tcbNo, tcbCancel, tcbRetry, tcbClose e retornam ModalResult:
// mrOK, mrYes, mrNo, mrCancel, mrRetry, mrClose</strong>
CommonButtons := [tcbYes, tcbNo];
// MainIcon é para exibir um ícone padrão que pode ser:
// tdiNone, tdiWarning, tdiError, tdiInformation, tdiShield, tdiQuestion
MainIcon := tdiQuestion;
if Execute then
begin
// aqui analisamos a opção selecionada com o ModalResult
if ModalResult = mrYes then
begin
// Clicou em Sim
ShowMessage2('Sim!');
end;
if ModalResult = mrNo then
begin
// Clicou em Não
ShowMessage2('Não!');
end
end;
finally
Free;
end;Vamos a explicação do código acima:
- A propriedade CommonButtons indica quais botões deverão aparecer, são eles: tcbOk, tcbYes, tcbNo, tcbCancel, tcbRetry, tcbClose que retornarão respectivamente o ModalResult mrOK, mrYes, mrNo, mrCancel, mrRetry, mrClose. Os nomes são autoexplicativos indicando o texto que irá aparecer dentro desses botões e seu ícone, o texto são constantes e não pode ser modificado, se usar tcbYes e tcbNo será respectivamente dois botões “Sim” e “Não”.
- A propriedade MainIcon pode ser definida com o valor: tdiNone, tdiWarning, tdiError, tdiInformation, tdiShield, tdiQuestion. O nome de cada um deles é autoexplicativo e indicará o tipo de ícone que será exibido ao lado da mensagem.
- O método Execute (obrigatório) submete a exibição da janela de dialogo e retornará quase sempre verdadeiro para indicar que a mesma foi exibida para o usuário e uma das opções escolhida, mas retorna falso se o usuário optar por abandonar sem selecionar nenhuma das opções.
- A propriedade ModalResult só é obtida depois que o método Execute for executado, recebendo um desses valores: mrOK, mrYes, mrNo, mrCancel, mrRetry, mrClose em conformidade com a propriedade CommonButtons.
Tendo em mente o que acabei de dizer, terá o suficiente para substituir MessageBox e MessageDlg pelo TaskDialog. Novamente, repita a técnica de refatoração para criar MessageBox2 e/ou MessageDlg2.
Como eu havia dito usar CommonButtons faz com que o texto dentro dos botões sejam inalteráveis, mas é possivel personalizar esses textos criando nossos próprios botões. Para isso funcionar, terá de atribuir a cada um desses botões um ModalResult diferente, por isso a quantidade de botões é limitada. Por exemplo, se eu acrescentar dois botões “Remover” e “Manter” eu terei de dizer que o primeiro terá um ModalResult=mrYes enquanto o segundo escolherei ModalResult=mrNo e somente assim saberei qual foi o botão que o usuário escolheu após o Execute. Importante saber também que caso você opte por usar botões com texto personalizado não poderá usar a propriedade CommonButtons, veja este exemplo simples e fácil de entender:
with TTaskDialog.Create(self) do
try
Caption := 'Confirmação:';
Title := 'Posso remover o item?';
Text := 'Posso remover o item selecionado?';
// CommonButtons deve estar vazio porque irei constituir meus próprios botões
CommonButtons := [];
// Note que cada botão acrescentado, devo ter um ModalResult atribuído para ele
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := 'Remover';
ModalResult := mrYes;
end;
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := 'Manter';
ModalResult := mrNo;
end;
MainIcon := tdiQuestion;
if Execute then
begin
// como cada botão tem seu próprio ModalResult então fica fácil
// detectar o botão que foi clicado.
if ModalResult = mrYes then
begin
ShowMessage2('Item removido!');
end;
if ModalResult = mrNo then
begin
ShowMessage2('Item mantido!');
end;
end;
finally
Free;
end;Novamente, se você estiver desligado, deve se perguntar, porque vou usar um código gigante acima para fazer uma pergunta de sim ou não? Daí novamente vou responder, você não aprendeu a fatorar? Quando tiver que repetir um mesmo código mais de uma vez, crie uma função ou procedimento que torne mais fácil repetir código, veja esse outro exemplo:
function MessageDlg2(
ACaption:String;
ATitle:String;
AText:String;
AButtonText1:String;
AButtonText2:String):TModalResult;
begin
Result:=mrNone;
if ACaption=emptyStr
then ACaption:=ExtractFileName(Application.ExeName);
if ATitle=emptyStr
then ATitle:='Questão:';
with TTaskDialog.Create(self) do
try
Caption := ACaption;
Title := ATitle;
Text := AText;
// CommonButtons deve estar vazio porque irei constituir meus próprios botões
CommonButtons := [];
// Note que cada botão acrescentado, devo ter um ModalResult atribuído para ele
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := AButtonText1;
ModalResult := mrYes;
end;
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := AButtonText2;
ModalResult := mrNo;
end;
MainIcon := tdiQuestion;
if Execute then
begin
Result:=ModalResult;
end;
finally
Free;
end;
end; Daí temos uma simpática função para perguntar algo ao usuário:
var
mrResposta:TModalResult;
begin
mrResposta:=MessageDlg2(
'Você tem certeza?',
'Devo realmente apagar todos os dados?',
'Se apagar todos os usuários não será mais possível recuperá-los.',
'Sim, apague tudo',
'Não, retorne');
if mrResposta=mrYes then
begin
// respondeu "Sim, apague tudo"
end;
if mrResposta=mrNo then
begin
// respondeu "Não, retorne"
end;
if mrResposta=mrNone then
begin
// não respondeu nada, abandonou a questão
end;
end;Você pode fatorar ou criar funções para obter uma resposta onde há duas opções disponíveis como fiz acima e obterá algo semelhante a isso:

Lembre-se que este foi apenas um exemplo e você pode criar outras funções para a quantidade de opções tantas quanto forem necessárias dum jeito elegante e produtivo.
Botões com RadioButton
Agora vamos incrementar ainda mais nossas opções juntando Botões como foram apresentados nos paragrafos anteriores com RadioButtons que são opções mais flexiveis quando temos uma variade maior de opções:
function QueroEscolher(
ACaption:String;
ATitle:String;
AText:String;
AOptions:Array of String;
AIcon:TTaskDialogIcon=tdiNone;
AProsseguirCaption:String='Prosseguir';
ADesistirCaption:String='Desistir'):SmallInt;
var
i:Integer;
iCount:Cardinal;
sBotaoCap:String;
bDefault:Boolean;
mrResposta:TModalResult;
begin
Result:=-1;
iCount:=0;
if Length(AOptions)=0 then
Exit;
bDefault:=false;
if ACaption=emptyStr
then ACaption:=ExtractFileName(Application.ExeName);
if ATitle=emptyStr
then ATitle:='Escolha uma das opções abaixo:';
with TTaskDialog.Create(nil) do
try
Caption := ACaption;
Title := ATitle;
Text := AText;
// // tdiNone = 0; tdiWarning = 1; tdiError = 2; tdiInformation = 3; tdiShield = 4; tdiQuestion=5
MainIcon := AIcon;
// CommonButtons deve estar vazio porque irei constituir meus próprios botões
CommonButtons := [];
// Caso inclua mais botoões, as opcoes validas na sequencia são:
// mrYes(0), mrNo(1), mrOk(2), mrCancel(3), mrAbort(4), mrRetry(5),
// mrIgnore(6), mrAll(7), mrNoToAll(8), mrYesToAll(9), mrClose(10);
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := AProsseguirCaption;
ModalResult := mrYes;
end;
with TTaskDialogButtonItem(Buttons.Add) do
begin
Caption := ADesistirCaption;
ModalResult := mrNo;
end;
// Note que cada botão acrescentado, devo ter um ModalResult atribuído para ele
for i := Low(AOptions) to High(AOptions) do
begin
sBotaoCap:=Trim(AOptions[i]);
bDefault:=false;
if (RightStr(sBotaoCap,1)='*') then
begin
bDefault:=true;
sBotaoCap:=LeftStr(sBotaoCap, Length(sBotaoCap)-1);
end;
if (sBotaoCap<>emptystr) then
begin
with RadioButtons.Add do
begin
Caption := sBotaoCap;
Default:=bDefault;
Inc(iCount);
end;
end;
end;
// só executo o TaskDialog se houverem opções disponíveis
if iCount>0 then
begin
if Execute then
begin
mrResposta:=ModalResult;
if mrResposta = mrYes then
Result:=RadioButton.Index;
end;
end;
finally
Free;
end;
end;Se você notar o código acima, ao invés de usar “with TTaskDialogButtonItem(Buttons.Add)” para acrescentar novos botões usamos “with RadioButtons.Add” e temos ao invés de novos botões, apenas novas opções de radiobutton.
Adicionalmente, fizemos com que nossas escolhas sejam um array de strings então o numero de opções é limitada apenas pela quantidade de elementos que couberem na tela. E fiz um pequeno mimo a mim mesmo, se uma das opções terminar com “*” no texto da opção então a mesma será considerada um radiobutton default.
Veja a forma de usar a função acima:
var
iResposta:Smallint;
begin
iResposta:=QueroEscolher(
'Gerar planilha contendo os dados', // ACaption:String;
'Gerar planilha contendo os dados sigilosos?', // ATitle:String;
'Ao prosseguir você concorda com os termos LGPD e manterá os dados dessa planilha '+
'sob controle absoluto e restringindo o seu acesso apenas a si mesmo ou para uso '+
'apenas de uma das opções abaixo.', // AText:String;
[ 'Planilha básica sem dados que possam ligar os dados à pessoa',
'Planilha básica sem dados que possam ligar os dados à pessoa, apenas enviar por email',
'Planilha escondendo apenas o CPF e CNPJ*',
'Planilha com todos os detalhes restritos',
'Planilha contendo todos os colaboradores que solicitaram esta planilha'
]);
if iResposta<0 then
Exit;
// prosseguindo com a planilha...E o resultado será algo assim num ambiente Windows:

Ao mostrar como usar botões e radiobuttons numa janela de dialogo do TaskDialog creio que cobrimos 90% do uso prático para ele, mas isso não quer dizer que restam apenas 10% de recursos novos que não detalhei, vou repetir “cobrimos 90% do uso prático” pois ainda há muitas outras formas de uso com inclusão de hyperlinks, checkboxes, rodapés, ícones personalizados… mas que particularmente não considero o feijão com arroz que precisamos para o dia a dia, mas caso queira considerá-los poderá visitar os links que mencionei.
Conclusion
O TaskDialog é uma excelente opção para criação de janelas de dialogo personalizáveis. E neste artigo usamos ele para fatorar janelas de diálogos repetitivos onde as opções de respostas estão previamente definidas como foi feito acima com as funções ShowMessage2 e MessageDlg2.
Você pode criar dentro do seu projeto funções genéricas usando o TaskDialog por baixo do capô como eu demonstrei aqui, assim você evitar trocentas linhas de programação a cada vez que usar o TaskDialog.
Se você está preocupado com o Delphi, o Delphi tem uso bastante similar ao que mencionei aqui, alias creio que o código será o mesmo para Lazarus e Delphi. Minha experiencia com o Lazarus é que há constantes novas no Lazarus que não existem no Delphi, por exemplo o Delphi não possui a constante tdiQuestion para usarmos na propriedade MainIcon.
Escrevam bons códigos e até+