2018.03.10

Indy(IdSMTP)を用いた添付ファイル付きメール送信

SimpleNovelDownloaderに、作成したKindle用mobiファイルを自動的にSend-To-Kindleでxxxxxx@kindle.comにエール送信してしまえば後から手動でいちいち作成したmobiファイルを添付してメール送信する手間が無くなると思い立ち、DelphiでIndyのIdSMTPを用いたメール送信機能を追加することにしました。

ということで早速実装してみたものの、通常のメールアドレスに対して送信する分には添付ファイルも含めて受信出来るものの、xxxx@kindle.comに送信すると「ファイル未添付のお知らせ」メールが帰ってくるという問題が発生し、しばらく悩みました。
その結果、メールソフトを使用してmobiファイルを添付してxxxx@kindle.comに送信する場合には件名と本文がなくても問題ないが、IdSMTPを用いる場合にはネット上で紹介されている一般的な?方法(TIdAttachmentFile.Createを使用して添付ファイルを追加する)を用いた場合、件名と本文がないと「ファイル未添付のお知らせ」となる。また、件名と本文を指定しても成功する場合と失敗する場合があるようだ、ということが判りました。

で、更にネット上で調べたところ、TIdMessageBuilderを使用してメール本文と添付ファイルを追加する方法があることが判り、この方法を用いて件名と本文を追加した状態でxxxx@kindle.comに送信すると安定してmobiファイルをSend-To-Kindle出来ることが確認出来ました。
また、OpenSSLを使用したSSL接続を用いる場合に、ネット上で紹介されているIdSMTP1.UseTL := utUseExplicitTLSではうまくいかないことも判りました。

で、テスト用に作成したものが以下となります。また、一番下にサンプルプロジェクトソースも置いておきます。
Smtptest

// IdSMTPで添付ファイルを含めたメール送信を行うための一般的な方法
// DEKOさんのところのちっぷす(https://ht-deko.com/tech040.html)等で紹介されて
// いる方法
// Send-To-Kindleでhoge@kindle.comに添付ファイルを送信する場合は、Subjectと
// Body.Textが空だと必ず失敗して「ファイル未添付のお知らせ」メールが帰ってくる
procedure TForm1.Button3Click(Sender: TObject);
var
  pt: integer;
begin
	pt									:= StrToInt(SMTPPort.Text);
  IdSMTP1.Host     		:= SMTPServer.Text;
  IdSMTP1.Port     		:= StrToInt(SMTPPort.Text);
  IdSMTP1.Username 		:= SMTPUser.Text;
  IdSMTP1.Password 		:= SMTPPassw.Text;
	// SSL接続時:OpenSSL(https://www.openssl.org/)のインストールが必要
	if UseSSL.Checked then
  begin
    SSL1.Host 				:= IdSMTP1.Host;
    SSL1.Port 				:= IdSMTP1.Port;
    SSL1.Destination  := SSL1.Host + ':' + IntToStr(SSL1.Port);
    IdSMTP1.IOHandler := SSL1;
    // SSLを使用する場合、DEKOさんのちっぷすや他の情報ではUseTLS := utUseExplicitTLS
    // としているが、これでは失敗する
    // 以下の情報を元に、ポートが465の場合にはUseTLS := utUseImplicitTLSとすると
    // うまくいく
		// https://stackoverflow.com/questions/7037929/using-gmails-outgoing-smtp-from-delphiindy-using-tls
    if pt = 465 then
    	IdSMTP1.UseTLS 	:= utUseImplicitTLS
    else
    	IdSMTP1.UseTLS 	:= utUseExplicitTLS;
	end else begin
    IdSMTP1.IOHandler := nil;
    IdSMTP1.UseTLS 	  := utNoTLSSupport;
  end;
 
  try
  	IdMsg.OnInitializeISO           := IdMessage_InitializeISO;
  	IdMsg.ContentType             	:= 'multipart/mixed';
  	IdMsg.CharSet                   := 'UTF-8';
  	IdMsg.ContentTransferEncoding   := 'BASE64';
  	IdMsg.From.Name                 := SenderName.Text;
  	IdMsg.From.Address              := MailAddr.Text;
  	IdMsg.Recipients.EMailAddresses := DestAddr.Text;
  	IdMsg.Subject                 	:= UTF8Encode(MailSubject.Text);
  	IdMsg.Body.Text                 := UTF8Encode(MailBody.Text);
    IdMsg.ConvertPreamble						:= True;
    // 複数のファイルを添付する場合はこれを繰り返す
  	with TIdAttachmentFile.Create(IdMsg.MessageParts, Attached.Text) do
    begin
  		FileName 		:= UTF8Encode(ExtractFileName(Attached.Text));
  		ContentType	:= 'application/octet-stream';
    end;
 
   	IdSMTP1.Connect;

IdSMTP1.Send(IdMsg);

except on E:Exception do
MessageDlg('ERROR: ' + E.Message, mtError, [mbOK], 0);
end;
IdSMTP1.Disconnect;
end;

// IdSMTPでTIdMessageBuilderPlain使用を使用した添付ファイルを含めたメール送信
// Send-To-Kindleでhoge@kindle.comに添付ファイルを送信する場合は、この方法の方が
// 安定しているようだ(SubjectとBody.Textが空だと失敗する場合がある)
procedure TForm1.Button1Click(Sender: TObject);
var
Bldr: TIdMessageBuilderPlain;
pt: integer;
begin
pt := StrToInt(SMTPPort.Text);
IdSMTP1.Host := SMTPServer.Text;
IdSMTP1.Port := pt;
IdSMTP1.Username := SMTPUser.Text;
IdSMTP1.Password := SMTPPassw.Text;
// SSL接続時:OpenSSL(https://www.openssl.org/)のインストールが必要
if UseSSL.Checked then
begin
SSL1.Host := IdSMTP1.Host;
SSL1.Port := IdSMTP1.Port;
SSL1.Destination := SSL1.Host + ':' + IntToStr(SSL1.Port);
IdSMTP1.IOHandler := SSL1;
// SSLを使用する場合、DEKOさんのちっぷすや他の情報ではUseTLS := utUseExplicitTLS
// としているが、これでは失敗する
// 以下の情報を元に、ポートが465の場合にはUseTLS := utUseImplicitTLSとすると
// うまくいく
// https://stackoverflow.com/questions/7037929/using-gmails-outgoing-smtp-from-delphiindy-using-tls
if pt = 465 then
IdSMTP1.UseTLS := utUseImplicitTLS
else
IdSMTP1.UseTLS := utUseExplicitTLS;
end else begin
IdSMTP1.IOHandler := nil;
IdSMTP1.UseTLS := utNoTLSSupport;
end;

try
IdMsg.OnInitializeISO := IdMessage_InitializeISO;
// ファイルを添付する場合、multipart/mixedの指定が必要
IdMsg.ContentType := 'multipart/mixed';
IdMsg.CharSet := 'UTF-8';
IdMsg.ContentTransferEncoding := 'BASE64';
IdMsg.From.Name := SenderName.Text;
IdMsg.From.Address := MailAddr.Text;
IdMsg.Recipients.EMailAddresses := DestAddr.Text;
IdMsg.Subject := UTF8Encode(MailSubject.Text);

Bldr := TIdMessageBuilderPlain.Create;
try
Bldr.PlainTextCharSet := 'UTF-8';
// メール本文
Bldr.PlainText.Text := UTF8Encode(MailBody.Text);
// 添付ファイル(複数のファイルを添付する場合はひとつづつAddする)
Bldr.Attachments.Add(Attached.Text);
// まとめてIdMessageにセットする
Bldr.FillMessage(IdMsg);
finally
// IdMessageにセットした後はBldrを開放する
Bldr.Free;
end;

IdSMTP1.Connect;

IdSMTP1.Send(IdMsg);

except on E:Exception do
MessageDlg('ERROR: ' + E.Message, mtError, [mbOK], 0);
end;
IdSMTP1.Disconnect;
end;


「idsmtpsample.zip」をダウンロード


| | コメント (0) | トラックバック (0)

2018.01.31

SaveDialogを拡張してコントロールを追加する(その3:補足)

> 何かもっとやり方があるのではないかと思ったりもするのですが
と言っていましたが、以下のようにComboBoxをひとつだけにしたところ、ラベルの色が黒になったことも含めてWindowsメモ帳の名前をつけて保存ダイアログと同じになりました。
アクセラレータキー(&E)もきちんと反応しますので、この方法がWindowsメモ帳でのファイルダイアログ拡張と同じ方法なのだと思います。

Dialog3



// IFileDialogCustomizeをサポートするかどうか問い合わせる
if Supports(FileSaveDialog1.Dialog, IFileDialogCustomize, Fdc) then
begin
// 返されたFileSaveDialog1のIFileDialogCustomizeインターフェイスに対して
// コントロールを追加する
//
// AddTextでラベルを追加すると文字が青色になることとComboBoxとのレイアウト
// が思うようにならないため、StartVisualGroupでラベルと追加する(色はグレーだけど)
Fdc.StartVisualGroup(dwIDCtl, '文字コードセット(&E):');
Fdc.AddComboBox(dwIDCtl + 1);
Fdc.AddControlItem(dwIDCtl + 1, 0, 'Shift-JIS'); // ComboBoxにアイテムを追加する
Fdc.AddControlItem(dwIDCtl + 1, 1, 'JIS');
Fdc.AddControlItem(dwIDCtl + 1, 2, 'EUC-JP');
Fdc.AddControlItem(dwIDCtl + 1, 3, 'Unicode(Little Endian)');
Fdc.AddControlItem(dwIDCtl + 1, 4, 'Unicode(Big Endian)');
Fdc.AddControlItem(dwIDCtl + 1, 5, 'UTF-8');
Fdc.AddControlItem(dwIDCtl + 1, 6, 'UTF-8N');
Fdc.SetSelectedControlItem(dwIDCtl + 1, 0); // 0番目を選択
Fdc.EndVisualGroup;

Fdc.MakeProminent(dwIDCtl + 0); // 上記で登録したコントロールをメイン指定する
Fdc.MakeProminent(dwIDCtl + 1); // この指定を行ったコントロールは「保存(開く)ボタン」の左側に配置されるが、配置出来るのはひとつだけ

// イベントを登録(ボタンが押された等のイベントを処理しない場合は以下の登録は
// しなくても良いようだ)
fdevt := TMyFileDialogEvents.Create;
if Succeeded(FileSaveDialog1.Dialog.Advise(fdevt, cookie)) then
begin
FileDialog := FileSaveDialog1.Dialog;
MyEvents := fdevt;
MyEventsCookie := cookie;
end;
end;

追加検索キーワード
ファイルダイアログのカスタマイズ
コモンダイアログのカスタマイズ

| | コメント (0) | トラックバック (0)

2018.01.29

SaveDialogを拡張してコントロールを追加する(その3:完結編)

Delphiで出来ないとあんまり意味がないんですけどね。と言っていましたが、ついに情報を見つけました。
情報元はMSDNのIFileDialogCustomize::GetSelectedControlItem method、stackoverflowのAdd a IFileDialogCustomize PushButton Eventです。

結論から言いますと、今までノーマークだったコンポーネントパレット[Vista Dialog]のTFielOpenDialogとTFileSaveDialogが答えでした。これらのコンポーネントはWindows Vistaで変更されたファイルダイアログをサポートするためのもののようですが、追加するラベルの文字色も青色になる等、前の記事で書いたC#でCommonOpenFileDialogを使用する場合と同じような感じになるようです。
それにしても、Windows7以降ではコンポーネントパレット[Dialogs]にあるTOpenDialogとTSaveDialogを使用しても同じダイアログボックスが表示されるため、通常はVista DIalogを使用する機会はないものと思われます(私も今まで使ったことがありませんでした)。

で、コントロールの追加の仕方ですが、Delphi標準のファイルダイアログコンポーネントにはないOnExecuteイベントにコントロールを追加する処理を書いて、Executeした後にそれらのコントロールのステータスを確認する処理を追加すれは良いようです。尚、ボタン等の動作イベントを発生するコントロールを追加して、ファイルダイアログが開いたままの状態でそれらのイベントを処理する場合には、OnExecuteでイベント処理を追加して処理させることが出来るようです。一応stackoverflowのQ&Aに記載されているTMyFileDialogEventsユニットを追加していますが、サンプルでは使用していませんのできちんと動作するのかどうかは不明です。

以下がサンプルソースコードになります。

unit DialogTestUnit;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, ShlObj;

type
TForm1 = class(TForm)
Button1: TButton;
FileSaveDialog1: TFileSaveDialog;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FileSaveDialog1Execute(Sender: TObject);
private
{ Private 宣言 }
Fdc: IFileDialogCustomize;
public
{ Public 宣言 }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

uses
MyFileDialogEvents;

var
FileDialog: IFileDialog = nil;
MyEvents: IFileDialogEvents = nil;
MyEventsCookie: DWORD = 0;

const
dwIDCtl = 19000;

procedure TForm1.FileSaveDialog1Execute(Sender: TObject);
var
fdevt: IFileDialogEvents;
cookie: DWORD;
begin
inherited;

// IFileDialogCustomizeをサポートするかどうか問い合わせる
if Supports(FileSaveDialog1.Dialog, IFileDialogCustomize, Fdc) then
begin
// 返されたFileSaveDialog1のIFileDialogCustomizeインターフェイスに対して
// コントロールを追加する
//
// AddTextでラベルを追加すると文字が青色になることとComboBoxとのレイアウト
// が思うようにならないため、StartVisualGroupでラベルと追加する(色はグレーだけど)
Fdc.StartVisualGroup(dwIDCtl, PWideChar('文字コードセット:'));
Fdc.AddComboBox(dwIDCtl + 1);
Fdc.AddControlItem(dwIDCtl + 1, 0, 'Shift-JIS'); // ComboBoxにアイテムを追加する
Fdc.AddControlItem(dwIDCtl + 1, 1, 'JIS');
Fdc.AddControlItem(dwIDCtl + 1, 2, 'EUC-JP');
Fdc.AddControlItem(dwIDCtl + 1, 3, 'Unicode(Little Endian)');
Fdc.AddControlItem(dwIDCtl + 1, 4, 'Unicode(Big Endian)');
Fdc.AddControlItem(dwIDCtl + 1, 5, 'UTF-8');
Fdc.AddControlItem(dwIDCtl + 1, 6, 'UTF-8N');
Fdc.SetSelectedControlItem(dwIDCtl + 1, 0); // 0番目を選択
Fdc.EndVisualGroup;

Fdc.StartVisualGroup(dwIDCtl + 2, PWideChar('改行コード:'));
Fdc.AddComboBox(dwIDCtl + 3);
Fdc.AddControlItem(dwIDCtl + 3, 0, 'CR+LF'); // ComboBoxにアイテムを追加する
Fdc.AddControlItem(dwIDCtl + 3, 1, 'CR');
Fdc.AddControlItem(dwIDCtl + 3, 2, 'LF');
Fdc.SetSelectedControlItem(dwIDCtl + 3, 0); // 0番目を選択
Fdc.EndVisualGroup;

// イベントを登録(ボタンが押された等のイベントを処理しない場合は以下の登録は
// しなくても良いようだ)
fdevt := TMyFileDialogEvents.Create;
if Succeeded(FileSaveDialog1.Dialog.Advise(fdevt, cookie)) then
begin
FileDialog := FileSaveDialog1.Dialog;
MyEvents := fdevt;
MyEventsCookie := cookie;
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
id1, id2: DWORD;
s1, s2: string;
begin
FileDialog := nil;
MyEvents := nil;
MyEventsCookie := 0;

if FileSaveDialog1.Execute then
begin
// dwIDCtl + 1のComboBoxのItemIndexを取得する
Fdc.GetSelectedControlItem(dwIDCtl + 1, id1);
// dwIDCtl + 3のComboBoxのItemIndexを取得する
Fdc.GetSelectedControlItem(dwIDCtl + 3, id2);
case id1 of
0: s1 := 'Shift-JIS';
1: s1 := 'JIS';
2: s1 := 'EUC-JP';
3: s1 := 'Unicode(Little Endian)';
4: s1 := 'Unicode(Big Endian)';
5: s1 := 'UTF-8';
6: s1 := 'UTF-8N';
end;
case id2 of
0: s2 := 'CR+LF';
1: s2 := 'CR';
2: s2 := 'LF';
end;
Label1.Caption := s1 + ' (' + s2 + ')';
if (FileDialog <> nil) and (MyEventsCookie <> 0) then
FileDialog.Unadvise(MyEventsCookie);
FileDialog := nil;
MyEvents := nil;
MyEventsCookie := 0;
end;
end;

end.



「SaveDialogを拡張してコントロールを追加する」の最初の記事ではテンプレートを用いた拡張を行うため、TSaveDialogを継承したコンポーネントにしましたが、今回の例ではTFileSaveDialogの標準イベントOnExecuteにコントロールを追加するための処理を追加するだけですので、わざわざコンポーネント化する必要もないのかと思いました。
それにしても、C#ではNuGetでパッケージライブラリを追加する必要があったのですが、Delphiでは標準の状態でファイルダイアログを拡張することが出来ちゃいましたと言う事でした。
まぁ、ラベルテキストの文字色が青色だったり、追加するコントロールのレイアウトをうまく設定出来なかったりと、何かもっとやり方があるのではないかと思ったりもするのですが、これで(私としては)機能的には充分なので取り敢えずは完結で良いかなと思った次第です。

テキストをAddTextで追加した場合・・・文字が青色になり、ComboBoxがその下に配置される。
Dialog2

テキストをStartVisualGroupで追加し、ComboBox追加後にEndVisualGroupした場合・・・文字色は灰色となり、ComboBoxはその右側に配置される。
Dialog1


まぁ、お好みなのでしょうが、個人的には後の例の方がイメージに合うかなという感じです。

#追加情報や違ったやり方があるよという方は、コメントに書き込んでいただけると嬉しいです。
#て言いますか、Mr. X-RAYさんの所のダイアログ関連でまとめてくれないかな(他力本願)・・・


サンプルプロジェク「FileDialogTest.zip」をダウンロード

| | コメント (0) | トラックバック (0)

2018.01.27

SaveDialogを拡張してコントロールを追加する(その2)

先日の記事では、ダイアログボックスのデザインがXPスタイルになっていました。どうもテンプレートを用いた場合には、XPスタイルになってしまうようです。

一方で、Windows7/10標準のメモ帳では
Win7memo_2


このようにWindows7スタイルのダイアログボックスのままでコントロールが追加されています。そこでネット上を検索したところ、C#での拡張の仕方を見つけました。見つけたのはtinqさんの夕暮れログです。
尚、この記事ではコモンダイアログ拡張に必要なWindowsApiCodePackを直接ダウンロードして手動で参照を追加していますが、VisualStudio2015/2017ではNugetを使用してプロジェクトに追加する方法をおすすめします。

で、テスト用コードがこちら。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack;
using Microsoft.WindowsAPICodePack.Dialogs;
using Microsoft.WindowsAPICodePack.Dialogs.Controls;

namespace CustomFileSaveDlg
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
CommonFileDialogLabel cdlb = new CommonFileDialogLabel("文字コードセット");
CommonFileDialogComboBox comboBox = new CommonFileDialogComboBox();
comboBox.Items.Add(new CommonFileDialogComboBoxItem("Shift-JIS"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("JIS"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("EUC-JP"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("Unicode(Little Endian)"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("Unicode(Big Endian)"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("UTF-8"));
comboBox.Items.Add(new CommonFileDialogComboBoxItem("UTF-8N"));
comboBox.SelectedIndex = 0;
dialog.Controls.Add(cdlb);
dialog.Controls.Add(comboBox);

if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
label1.Text = comboBox.Items[comboBox.SelectedIndex].Text;
}
}
}
}

実際のダイアログボックスがこちら。
Csfilesavedlg

うーーーん。一応Windows7スタイルのダイアログボックスにラベルとコンボボックスが追加されていますが、何かちょっと違う感じがします。
追加するコントロールの位置や、ラベルのテキスト色を指定することが出来ないようですし、ファイルの種類を選択するコンボボックスもなくなっていますし微妙です・・・

メモ帳のダイアログボックスを見る限り何らかの方法があると思いますので調査継続でしょうか。
#て言うか、C#(.NET Framework)で実現出来ても、Delphiで実現出来ないとあんまり意味がないんですけどね。
 
 

| | コメント (0) | トラックバック (0)

2018.01.20

文字コードの自動判定

以前私のホームページにてIKEDA Takahiroさん作のJConvert.pasを基にしてUTF-8/UTF-8Nも判定できるコードを公開していましたが(現在も公開中)、Delphiも進化しており文字コードの変換に関しては自前のコードが不要になったことから、あらためて文字コード判定に機能を絞って整理してみました
一応、文字コードを自動判定してのテキストファイル読み込みや、文字コードと改行コードを指定してのテキストファイル保存処理も追加しています。
一応、MiGrep2を作成するに当たって改訂版を作成して使用していたのですが、ぐちゃぐちゃした部分があったため今後の再利用を考慮して整理したもので、今後のMiGrep2バージョンアップの際にはこのルーチンに切り替える予定です。
今回新たに整理し直した文字コード処理ルーチン「CheckCCode.pas」は、後日私のホームページ上で公開する予定ですが、取り敢えずここでサンプルプログラムと合わせて公開します。

「checkccode.zip」をダウンロード

公開ファイルの構成
checkccode.zip
 CheckCCode.pas    文字コード判定処理ルーチン
 TextLoad.dpr     テスト用プロジェクトファイル
  TxtLoadUnit.pas
  TxtLoadUnit.dfm
TextLoad.zip    テスト用プロジェクトの実行ファイル

注記)プロジェクトのコンパイルには一つ前の記事で公開しているSaveDialogExコンポーネントが必要です。

| | コメント (0) | トラックバック (0)

SaveDialogを拡張してコントロールを追加する

昔、自作のテキストエディタ用にSaveDialogに文字コードと改行コードを選択するComboBoxを追加した拡張コンポーネントを使用していたのを思い出して、Delphi 10.2.2にインストールしたもののそのままではうまく動作しなかったため、あらためて焼き直し版を作成しました。
今回あらためてWeb上を検索したところ、SaveDialogを拡張する方法やコンポーネント等を見つけることが出来なかったため、参考になればということで公開します。

Windows7上
Savedialogex

Windows10上
W10savedlgex
このSaveDialogでは、保存する形式:で文字コードを、改行コード:で改行コードを選択することが出来、選択結果はプロパティCodeTypeとBreakTypeで取得・設定出来ます。

unit SaveDialogEx;
 
interface
 
uses
  System.SysUtils, System.Classes, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  System.Types, Vcl.Controls, Windows;
 
type
  TSaveDialogEx = class(TSaveDialog)
  private
    { Private 宣言 }
	FExtPanel: TPanel;
	FCodeLabel: TLabel;
	FBreakLabel: TLabel;
	FCodeCombo: TComboBox;
	FBreakCombo: TComboBox;
	FCodeType,
	FBreakType: integer;
	procedure ChangeItem(Sender: TObject);
  protected
    { Protected 宣言 }
	procedure DoShow; override;
  public
    { Public 宣言 }
    constructor Create(AOwner: TComponent); override;
	destructor Destroy; override;
	function Execute: Boolean; override;
  published
    { Published 宣言 }
	property CodeType: integer read FCodeType write FCodeType;
	property BreakType: integer read FBreakType write FBreakType;
  end;
 
procedure Register;
 
implementation
 
// Dialogリソースにリソース名DLGEXのパネル(高さ22X幅600、Style=Child)を準備する
// ことで、このパネルサイズでダイアログボックスの下側が拡張される
// 詳しくは、XNResourceEditor等でSAVEDLG.RESを開いて見て下さい
{$R SAVEDLGEX.RES}
 
uses Vcl.Consts, Vcl.Forms, WinApi.CommDlg, WinApi.Dlgs;
 
 
constructor TSaveDialogEx.Create(AOwner: TComponent);
begin
	inherited Create(AOwner);
 
	FExtPanel := TPanel.Create(Self);
	with FExtPanel do
	begin
  	// リソースDLGEXで拡張されたダイアログボックスの位置に合わせてPanelを
    // 生成する
		SetBounds(0, 348, 600, 30);
		Name 				:= 'ExtPanel';
		Caption 		:= '';
		// ダイアログボックスのサイズを変更してもパネルの位置がズレないように
    // パネルの配置をAlign=alBottomにする
    Align 			:= alBottom;
		BevelOuter 	:= bvNone;
		BorderWidth := 6;
		TabOrder 		:= 1;
	end;
  // 生成したパネルにコントロールを生成して貼り付けていく(PanelをParentにする)
	FCodeLabel 		:= TLabel.Create(Self);
	with FCodeLabel do
	begin
		SetBounds(113, 8, 100, 23);
		Name 				:= 'CodeLabel';
		Caption 		:= '保存する形式:';
		Parent 			:= FExtPanel;
	end;
	FCodeCombo 		:= TComboBox.Create(Self);
	with FCodeCombo do
	begin
		SetBounds(225, 4, 130, 20);
		Name 				:= 'CharCodeType';
		Style 			:= csDropDownList;
		TabOrder 		:= 0;
		Parent 			:= FExtPanel;
		OnChange 		:= ChangeItem;
	end;
	FBreakCombo		:= TComboBox.Create(Self);
	with FBreakCombo do
	begin
		SetBounds(440, 4, 72, 20);
		Name 				:= 'LineBreakType';
		Style 			:= csDropDownList;
		TabOrder 		:= 1;
		Parent 			:= FExtPanel;
		OnChange 		:= ChangeItem;
	end;
	FBreakLabel 			:= TLabel.Create(Self);
	with FBreakLabel do
	begin
		SetBounds(380, 8, 100, 23);
		Name 				:= 'CrLabel';
		Caption 		:= '改行コード:';
		Parent 			:= FExtPanel;
	end;
  FCodeType  := 0;
  FBreakType := 0;
end;
 
destructor TSaveDialogEx.Destroy;
begin
	FCodeCombo.Free;
	FBreakCombo.Free;
  FBreakLabel.Free;
	FCodeLabel.Free;
	FExtPanel.Free;
	inherited Destroy;
end;
 
procedure TSaveDialogEx.DoShow;
var
	OptionRect: TRect;
begin
	GetClientRect(Handle, OptionRect);
 
	FExtPanel.ParentWindow := Handle;
	FExtPanel.Top := OptionRect.Height - 40; // ダイアログボックスの高さを元に拡張パネルの高さを決める
	FExtPanel.Visible := True; // Windows98/XPでは不要だったがWindows7では指示が必要
	with FCodeCombo do
	begin
	// アイテムリストを準備する
  	if Items.Count > -1 then
    begin
			Items.Clear;
			Items.Add('Shift-JISコード');
			Items.Add('JISコード');
			Items.Add('EUC-JPコード');
			Items.Add('Unicode(Little Endian)');
			Items.Add('Unicode(Big Endian');
			Items.Add('UTF-8コード');
			Items.Add('UTF-8Nコード');
    end;
    ItemIndex := FCodeType;
	end;
	with FBreakCombo do
	begin
  	// アイテムリストを準備する
  	if Items.Count > -1 then
    begin
			Items.Clear;
			Items.Add('CR+LF');
			Items.Add('CR');
			Items.Add('LF');
    end;
    ItemIndex := FBreakType;
	end;
 
	inherited DoShow;
	DoTypeChange;
end;
 
function TSaveDialogEx.Execute: Boolean;
begin
  if NewStyleControls and not (ofOldStyleDialog in Options) then
		Template := 'DLGEX'	//SAVEDLGEX.RES内のDialogリソースDLGEXを指定する
  else
    Template := nil;    //ofOldStyleDialog=Trueの場合は拡張パネルを表示しない
	Result := DoExecute(@GetSaveFileName);
end;
 
procedure TSaveDialogEx.ChangeItem(Sender: TObject);
begin
	FCodeType 	:= FCodeCombo.ItemIndex;
	FBreakType 	:= FBreakCombo.ItemIndex;
end;
 
procedure Register;
begin
  RegisterComponents('Dialogs', [TSaveDialogEx]);
end;
 
end.

「SaveDialogEx.zip」をダウンロード

SaveDialogEx.pasとsavedlg.resをライブラリパスが通ったフォルダにコピーして、SaveDialogEx.pasをコンポーネントのインストールで登録してください。

| | コメント (0) | トラックバック (0)

2018.01.07

Delphi 10.2 Release2

開発環境をDelphi 10.2 Release2に変更しました。
それにしても10.2からリビジョンが一つ上がっただけということになっている割には、10.2をアンインストールしてからRelease2をインストールしなければならないのは非常に面倒です。
デスクトップ環境は「移行ツール」で反映出来るのですが、跡から追加しているコンポーネントやパッケージは改めてインストールし直さなければなりません。まぁ、コンポーネント関連は10.2でインストールした際のプロジェクトファイルを開いてインストールするだけでOKなので、ものすごく手間がかかるというわけではないのですが・・・
まぁ、実際にはコンポーネントのアイコンデザインが変わったりデスクトップテーマを変更出来たりと、見た目は結構変わっていますので、実際にはマイナーバージョンアップくらいにはなっているのでしょうが。
#デスクトップテーマの切り替えはまぁ良いとしてエディタ環境の方は、独自設定している場合に一度切り替えると元々の独自設定環境に戻れないという落とし穴があるようです。

ところで今回、コンポーネントアイコンのデザインを変えたのは良いとして、個人的には未だにImagesフォルダにインストールされるアイコンのデザインを何とかして欲しいなぁと思うのですが・・・
Delphi102

これって、Delphi3の頃から変わっていないんですよねぇ・・・

| | コメント (0) | トラックバック (0)

その他のカテゴリー

Delphi | Inno Setup | Lazarus | Nightly Build | Raspberry Pi | VisualStudio/C#