« 2004年4月 | トップページ | 2004年6月 »

■Delphi 8付属InterBase7.1SP2

ボーランドのサイトで、InterBase7.1SP2の案内がありました。(朝、見たときにはなかった??)Delphi8日本語版に付属のInterBase 7.1(開発ライセンス)は、ライセンス期限切れになるという問題がありましたが、このサービスパックを適用することにより解決するそうです。(私は現在、Firebird1.5を試しているため、確認していません。)
ダウンロードのサイトに行くと以前には無かった次のようなファイルがありましたので、ダウンロードだけしておきました。
InterBase 7.1 SP 2 - Japanese

|

■Delphi7 アップデート1

日本のBorlandのページで、ようやくDelphi7のアップデートの情報が公開されました。前回悩んでいた、バージョン情報が更新されないという問題について、「アップデートに関する既知の問題」の中にありました。読んでみますと「Professional/Personal版ではバージョン情報は更新されません」と書いてあるではないですか。そんないい加減なことでいいのかな?
又、前回アップデートが失敗だと判断したDBExpressでのエラーについては、私がReadme_upd1.txtに書かれていた内容を行っていなかったためです。ちなみに、この情報は、別途ダウンロードできる日本語のReadme_upd1.txtには載っていません。きちんと訳して頂きたいものですね。

Readme_upd1.txtからの引用-----------------------------------------

UPDATING LOCALIZED VERSIONS OF DELPHI 7

(略)

* This update provides new localized versions of the SqlConst
files. After installing the update, backup and delete the
following files from the Delphi 7 installation folder:

Lib\Debug\SqlConst.dcu
Lib\SqlConst.dcu
Source\Vcl\SqlConst.pas

and then remove the appropriate locale extension (.de, .fr, or
.jp) from the new SqlConst files in the same folders. For
example, for the French version of Delphi, rename
SqlConst.dcu.fr to SqlConst.dcu.
----------------------------------------------------------------

|

■5000カウント

今、このページを表示させたら、カウントがちょうど5000でした。今年の4月20日にカウンターを設置したばかりだというのに、多くの方に見て頂き、感謝、感謝です。私のレベルでは高度なことは書けませんが、私が悩んだことを残していくことによって、少しでも皆さんのお役に立てればいいなと思っております。これからもよろしくお願いします。

|

☆イベントの割り当てと解除

[Delphi8 - Windowsフォームアプリケーション]
Delphi7以前やVCLアプリケーションでは、次のようにイベントの割り当てと解除を行いますが、Delphi8では「CLR イベントでは,読み取り/書き込みは許可されません。Include/Exclude 手続きを使用してください」というメッセージが表示されてエラーになります。

 Button1.OnClick := Button_Click; [割り当て]
 Button1.OnClick := nil;        [解除]
このような場合、Windowsフォームアプリケーションでは、次のようにInclude/Excludeを使います。
 1.Windowsフォームアプリケーションを新規作成します。
 2.Button1とCheckBox1を配置します。
 3.下記のプログラムを記述します。

procedure TWinForm.TWinForm_Load(sender: System.Object;
 e: System.EventArgs);
begin
 CheckBox1.Checked := True;
 Include(Self.Button1.Click, Button_Click); //Self.は省略可
end;

procedure Button_Click(sender: System.Object; e: System.EventArgs);
begin
 MessageBox.Show('OK!');
end;

procedure TWinForm.CheckBox1_Click(sender: System.Object;
 e: System.EventArgs);
begin
 if CheckBox1.Checked then
  Include(Self.Button1.Click, Button_Click)    //割り当て
 else
  Exclude(Self.Button1.Click, Button_Click);   //解除
end;

|

☆FormのShowDialog

[Delphi8 - Windowsフォームアプリケーション]
HELPを見てて気づいたんですが、ShowDialogでフォームを表示した場合には、Disposeで解放する必要があるとのことです。その理由は、ShowDialogで表示した場合、DialogResultでフォームを閉じたとしてもCloseが呼ばれないためだということです。×ボタンでフォームを閉じたときにも、DialogResultがDialogResult.Cancelに設定されるということで、Closeが呼ばれません。尚、Showで表示した場合には、Disposeは必要ありません。詳しくは、HELPを参照して下さい。

|

☆多重起動の禁止

@ITの記事を参考に、アプリケーションの多重起動を禁止するプログラムを書いてみました。(記事を見て頂いたらわかると思いますが、単に移植しているだけですね。)
Win32のプログラムと同様に、ミューテックスを使って処理しています。

↓↓↓参考にさせて頂いた記事はこちらです。↓↓↓
Windowsアプリケーションの多重起動を禁止するには?
多重起動禁止時に実行中のWindowsアプリケーションを最前面に表示するには?

<プログラム>

program Project1;
  (略)
uses
  (略)
 次の3つの名前空間を追加する。
  System.Threading,
  System.Runtime.InteropServices,
  System.Diagnostics,

const
 Mes1 = 'すでに起動しています。2つ同時には起動できません。';
 Mes2 = '多重起動禁止';
 SW_RESTORE = 9; 

var
 mutexObject: Mutex; 
 os: OperatingSystem;
 strAppConstName: String = 'daSampleApp';
 prevProcess: Process;

{$R *.res}

{プログラム/アセンブリ情報}

// Win32 API
[DllImport('user32.dll')]
function SetForegroundWindow(hWnd: IntPtr):
 Boolean; external;

[DllImport('user32.dll')]
function ShowWindowAsync(hWnd: IntPtr; nCmdShow: Integer):
 Boolean; external;

[DllImport('user32.dll')]
function IsIconic(hWnd: IntPtr): Boolean; external;

// 外部プロセスのウィンドウを起動する
procedure WakeupWindow(hWnd: IntPtr);
begin
 if (IsIconic(hWnd)) then
  ShowWindowAsync(hWnd, SW_RESTORE);

 SetForegroundWindow(hWnd);
end;

// 実行中の同じアプリケーションのプロセスを取得する。
// この部分は、少し不安なので、元記事を見て確認して下さい。
function GetPreviousProcess: Process;
var
 curProcess: Process;
 allProcesses: array of Process;
 checkProcess: Process;
 I: Integer;
begin
 curProcess := Process.GetCurrentProcess;
 allProcesses := Process.GetProcessesByName (curProcess.ProcessName);
 for I := Low(allProcesses) to High(allProcesses) do
 begin
  if (allProcesses[I].Id <> curProcess.Id) then
  begin
   checkProcess := allProcesses[I];
   if (System.String.Compare(
    checkProcess.MainModule.FileName,
    curProcess.MainModule.FileName, true) = 0) then
   begin
    Result :=  checkProcess;
    Exit;
   end;
  end;
 end;
 Result := nil;
end;

[STAThread]

begin
 os := Environment.OSVersion;
 if ((os.Platform = PlatformID.Win32NT) and (os.Version.Major >= 5)) then
  strAppConstName := 'Global\' + strAppConstName;
 try
  mutexObject := Mutex.Create(False, strAppConstName);
 except
  raise System.ApplicationException.Create;
  MessageBox.Show(Mes1, Mes2);
  Exit;
 end;

 if (mutexObject.WaitOne(0, false)) then begin
  Application.Run(TWinForm.Create);
  mutexObject.ReleaseMutex();
 end else begin
  prevProcess := GetPreviousProcess;
  if ((prevProcess <> nil) and (prevProcess.MainWindowHandle <> IntPtr.Zero)) then
   WakeupWindow(prevProcess.MainWindowHandle)
  else
  MessageBox.Show(Mes1, Mes2);
 end;
 mutexObject.Close;
end.

|

■Delphi7 Updateは失敗?

Delphi7のアップデートで、バージョンは前のままだけど、インストール後の各ファイル(4つしか調べてないけど)のバージョンが正しいのでアップデートが適用されているみたいだと書きましたが、既存のプロジェクトがコンパイルできないという問題が発生しました。

エラーの内容としては、
[致命的エラー]
○○○○ユニット SqlExpr は異なるバージョン SqlConst.SNOERROR によりコンパイルされています。

ということで、DBExpressを使ったプロジェクトがこのエラーになるので、やはりアップデート失敗なんですよね。

また、再インストールしなきゃ・・・。

|

☆TreeViewのDrag&Drop

[Windowsフォームアプリケーション]
HELPや[HOW TO] Visual C# .NET アプリケーションにツリー ビューのドラッグ アンド ドロップ機能を追加する方法を参考にTreeViewのDrag&Dropを試してみました。HELPでは、ListBoxでの参考例がありますが、Clickで移動、CTRL+Clickでコピーという動作を実装しています。

フォームにTreeView1を配置して、下記のプログラムを記述します。
procedure TWinForm1.TWinForm1_Load(sender: System.Object;
 e: System.EventArgs);
begin
 TreeView1.AllowDrop := True;

 TreeView1.Nodes.Add('Item1');
 TreeView1.Nodes.Add('Item2');
 TreeView1.Nodes.Add('Item3');
end;

procedure TWinForm1.TreeView1_ItemDrag(sender: System.Object;
 e: System.Windows.Forms.ItemDragEventArgs);
var
 DDE : DragDropEffects;
begin
 //ドラッグ開始
 DDE := TreeView1.DoDragDrop(e.Item, DragDropEffects.All);
 //動作の結果表示
 if ((DDE and DragDropEffects.Move) = DragDropEffects.Move) then
  Self.Text := '移動しました。'
 else if ((DDE and DragDropEffects.Copy) = DragDropEffects.Copy) then
  Self.Text := 'コピーしました。'
 else
  Self.Text := '処理しませんでした。';
end;

procedure TWinForm1.TreeView1_DragOver(sender: System.Object;
 e: System.Windows.Forms.DragEventArgs);

 function IsChild(Item1, Item2: TreeNode): Boolean;
 begin
  if (Item2.Parent = Item1)  then
   Result := True
  else if (Item2.Parent <> nil) then
   Result :=  IsChild(Item1, Item2.Parent)
  else
   Result := False;
 end;

var
 Target, Source: TreeNode;
begin
 //データ形式のチェック
 if not e.Data.GetDataPresent(TypeOf(TreeNode)) then
 begin
  e.Effect := DragDropEffects.None;
  Exit;
 end;

 //ドロップできるかどうか
 Target := TreeView1.GetNodeAt(
       TreeView1.PointToClient(Point.Create(e.X, e.Y)));
 Source := TreeNode(e.Data.GetData(TypeOf(TreeNode)));

 if ((Target <> nil) and (Target <> Source) and 
   (not IsChild(Source, Target))) then begin
  TreeView1.SelectedNode := Target;
  if ((e.KeyState and 8) = 8) and 
   ((e.AllowedEffect and DragDropEffects.Copy) = DragDropEffects.Copy) then
   e.Effect := DragDropEffects.Copy //CTRL + Left Click
  else if ((e.AllowedEffect and DragDropEffects.Move) = DragDropEffects.Move) then
   e.Effect := DragDropEffects.Move
  else
   e.Effect := DragDropEffects.None;
 end else
  e.Effect := DragDropEffects.None;
end;

procedure TWinForm1.TreeView1_DragDrop(sender: System.Object;
 e: System.Windows.Forms.DragEventArgs);
var
 Target, Source, NewNode: TreeNode;
begin
 if (e.Effect = DragDropEffects.None) then Exit;

 Source := TreeNode(e.Data.GetData(TypeOf(TreeNode)));
 Target := TreeView1.GetNodeAt(TreeView1.PointToClient(Point.Create(e.X, e.Y)));

 NewNode := TreeNode(Source.Clone);
 Target.Nodes.Add(NewNode);
 TreeView1.SelectedNode :=NewNode;

 if (e.Effect = DragDropEffects.Move) then
  Source.Remove;
end;

|

■Delphi7のアップデート

Delphi-MLでGeneral Update7.1が公開されたことを知りました。
早速、d7_ja_pro_upd1.exeというファイルをダウンロードしてインストールしました。

Readmeファイルのアップデートの完了の確認によるとアップデートを適用する前のバージョンは 7.0(ビルド 4.453)で、アップデート適用後は、バージョン 7.0(ビルド 8.1)になるということです。Delphiを起動して確認したところ、なぜか 7.0(ビルド 4.453)のままでした。
又、Readmeファイルには、このアップデートによってインストールされるファイルというものが記載されていましたので、その中で下記の4ファイルのバージョンを確認してみましたが、すべて更新されていました。ということは、アップデート適用済みなのかな?

dbexpInt.dll
dcc32.exe
dcc70.dll
delphi32.exe

環境 WindowsXP Pro SP1 + Delphi7Pro(Delphi8に同梱されていたもの)

|

☆PropertyGridを使ってみる。

[Windowsフォームアプリケーション]
PropertyGridを使って、アプリケーションでのオプション設定へ応用してみます。
応用などど大げさに書いていますが、MSDNの.NET Framework の PropertyGrid コントロールの高度な活用を参考にしています。
下記のプログラムで、きちんと動作しているようですが、C#と同じような属性部分の記述が今までのDelphiではなかっただけに、本当にこれでよいのか少し不安・・・。

次のようなオプション設定を前提にします。

<設定一覧>

[ウィンドウ]
Size - ウィンドウのサイズを設定します。
Color - ウィンドウ ウィンドウの色を設定します。
[エディタ]
Font - エディタのフォントを設定します。
[バージョン]
Version - MyAppのバージョンを表示します。
[一般設定]
RecentFiles - 最近使ったファイルの表示数
DefaultFileName - デフォルトファイル名
[計算オプション]
CalcOptions
AutoCalc - 自動計算機能
UserFunc - ユーザー関数機能

<プログラム>

フォームにPropertyGridを一つ配置しただけです。

unit WinForm;

interface

uses
 System.Drawing, System.Collections, System.ComponentModel,
 System.Windows.Forms, System.Data, System.Globalization;
 //System.Globalizationは自前で記述した。

type
 //ドロップダウン プロパティのサポートを提供します。
 //DefaultFileName プロパティは文字列型であるため、StringConverter から継承します。
 TFileNameConverter = class(StringConverter)
 public
  function GetStandardValuesSupported
   (Context: ITypeDescriptorContext): Boolean; override;
  function GetStandardValues(Context: ITypeDescriptorContext):    
   TypeConverter.StandardValuesCollection; override;
  function GetStandardValuesExclusive
   (Context: ITypeDescriptorContext): Boolean; override;
 end;

 TCalcOptionsConverter = class(ExpandableObjectConverter)
 public
  function CanConvertTo(Context: ITypeDescriptorContext;
    DestinationType: System.Type): Boolean; override;
  function ConvertTo(Context: ITypeDescriptorContext;
   Culture: CultureInfo; Value:
   TObject; DestinationType: System.Type): TObject; override;
  function CanConvertFrom(Context: ITypeDescriptorContext;
   SourceType: System.Type): Boolean; override;
  function ConvertFrom(Context: ITypeDescriptorContext;
   Culture: CultureInfo; Value: TObject): TObject; override;
 end;

 TCalcOptions = class
 private
  FAutoCalc: Boolean;
  FUserFunc: Boolean;
 public
  constructor Create;
 published
  [DefaultValueAttribute(True)]
  property AutoCalc: Boolean read FAutoCalc write FAutoCalc;
  [DefaultValueAttribute(False)]
  property UserFunc: Boolean read FUserFunc write FUserFunc;
 end;

 [DefaultPropertyAttribute('RecentFiles')]
 TMyAppSettings = class
 private
  FWindowSize: Size;
  FWindowColor: Color;
  FEditorFont: Font;
  FVersion: String;
  FRecentFiles: Integer;
  FDefaultFileName: String;
  FCalcOptions: TCalcOptions;
 public
  constructor Create;
 published
  [CategoryAttribute('ウィンドウ'),
  DescriptionAttribute('アプリケーションのウィンドウサイズを設定します。')]
  property Size: Size read FWindowSize write FWindowSize;
  [CategoryAttribute('ウィンドウ'),
  DescriptionAttribute('アプリケーションのウィンドウカラーを設定します。')]
  property Color: Color read FWindowColor write FWindowColor;
  [CategoryAttribute('エディタ'),
  DescriptionAttribute('エディタのフォントを設定します。')]
  property Font: Font read FEditorFont write FEditorFont;
  //[BrowsableAttribute(False), DefaultValueAttribute(False)]
  [CategoryAttribute('バージョン'), DefaultValueAttribute('1.0'),
  ReadOnlyAttribute(True)]
  property Version: String read FVersion write FVersion;
  [CategoryAttribute('一般設定'), DefaultValueAttribute(5),
  DescriptionAttribute('最近使ったファイルの表示数を設定します。')]
  property RecentFiles: Integer read FRecentFiles write FRecentFiles;
  [TypeConverter(TypeOf(TFileNameConverter)), CategoryAttribute('一般設定'),
  DescriptionAttribute('新規作成時のファイル名を設定します。')]
  property DefaultFileName: String read FDefaultFileName write FDefaultFileName;
  [TypeConverterAttribute(TypeOf(TCalcOptionsConverter)),
  DescriptionAttribute('計算オプションを展開して表示します。')]
  [CategoryAttribute('計算オプション')]
  property CalcOptions: TCalcOptions read FCalcOptions write FCalcOptions;
 end;

 TWinForm = class(System.Windows.Forms.Form)

  (略)

 private
  MyAppSettings: TMyAppSettings;

  (略)
 end;

  (略)

implementation

  (略)

procedure TWinForm.TWinForm_Load(sender: System.Object; e: System.EventArgs);
begin
 MyAppSettings := TMyAppSettings.Create;
 PropertyGrid1.SelectedObject := MyAppSettings;
 PropertyGrid1.ToolbarVisible := False;
end;

{ TFileNameConverter }

function TFileNameConverter.GetStandardValuesSupported(
 Context: ITypeDescriptorContext): Boolean;
begin
 Result := True;
end;

function TFileNameConverter.GetStandardValues(
 Context: ITypeDescriptorContext): TypeConverter.StandardValuesCollection;
var
 S: ArrayList;
begin
 S := ArrayList.Create;
 S.Add('新しいファイル');
 S.Add('報告書');
 S.Add('計算書');
 Result := StandardValuesCollection.Create(S);
end;

function TFileNameConverter.GetStandardValuesExclusive(
 Context: ITypeDescriptorContext): Boolean;
begin
 Result := False;
end;

{ TCalcOptionsConverter }

function TCalcOptionsConverter.CanConvertTo(
 Context: ITypeDescriptorContext; DestinationType: System.Type): Boolean;
begin
 if (DestinationType= TypeOf(TCalcOptions)) then
  Result := True
 else
  Result := inherited CanConvertTo(Context, DestinationType);
end;

function TCalcOptionsConverter.ConvertTo(
 Context: ITypeDescriptorContext; Culture: CultureInfo; Value: TObject;
 DestinationType: System.Type): TObject;
var
 CO: TCalcOptions;
begin
 if ((DestinationType = TypeOf(System.String)) and
   (Value is TCalcOptions)) then begin
   CO := TCalcOptions(Value);
   Result := '自動計算機能:'  + Convert.ToString(CO.AutoCalc) +
        '、ユーザー関数機能: ' + Convert.ToString(CO.UserFunc);
 end else
  Result := inherited ConvertTo(Context, Culture, Value, DestinationType);
end;

function TCalcOptionsConverter.CanConvertFrom(
 Context: ITypeDescriptorContext; SourceType: System.Type): Boolean;
begin
 if (SourceType = TypeOf(String)) then
  Result := True
 else
  Result := inherited CanConvertFrom(Context, SourceType);
end;

function TCalcOptionsConverter.ConvertFrom(
 Context: ITypeDescriptorContext; Culture: CultureInfo;
 Value: TObject): TObject;
var
 AutoCalc, UserFunc: String;
 S: String;
 Colon, Comma: Integer;
 CO: TCalcOptions;
begin
 if (Value = TypeOf(String)) then begin
  try
   S := Convert.ToString(Value);
   Colon := S.IndexOf(':');
   Comma := S.IndexOf(',');

   if (Colon <> -1) and (Comma <> -1) then
   begin
    AutoCalc := S.Substring(Colon + 1 , (Comma - Colon - 1));
    Colon := S.IndexOf(':', Comma + 1);
    Comma := S.IndexOf(',', Comma + 1);

    UserFunc := S.Substring(Colon + 1 , (Comma - Colon -1));

    CO := TCalcOptions.Create;

    CO.AutoCalc :=System.Boolean.Parse(AutoCalc);
    CO.UserFunc := System.Boolean.Parse(UserFunc);

    Result := CO;
    Exit;
   end;
  except
  end;
 end;
 Result := inherited ConvertFrom(Context, Culture, Value);
end;

{ TCalcOptions }

constructor TCalcOptions.Create;
begin
 inherited Create;
 AutoCalc := True;
 UserFunc := False;
end;

{ TMyAppSettings }

constructor TMyAppSettings.Create;
begin
 inherited Create;
 Size := System.Drawing.Size.Create(100,100);
 Color := SystemColors.Control;
 Font := System.Drawing.Font.Create('MS UI Gothic', 9, FontStyle.Regular);
 Version := '1.0';
 RecentFiles := 5;
 DefaultFileName := '';
 CalcOptions := TCalcOptions.Create;
end;

end.
MyAppSettings.gif

|

☆PropertyGridをとりあえず使ってみる。

[Windowsフォームアプリケーション]
とりあえずPropertyGridを使ってみました。
1.Windowsフォームアプリケーションを新規作成して、PropertyGridを配置します。
2.下記のコードを書いて実行します。

procedure TWinForm.TWinForm_Load(sender: System.Object;
 e: System.EventArgs);
begin
 PropertyGrid1.SelectedObject := Self;
end;

これだけで、フォームのプロパティがセットされています。すごいですね。

|

☆クリップボードを使ってみる。

[Windowsフォームアプリケーション] TextBoxを使ってクリップボードを扱うサンプルを書いてみました。
1.フォームにContextMenu1とTextBox1, TextBox2, TextBox3を配置します。
2.ContextMenu1にCopy, Cut, Paste, Deleteというメニューを作成し、
  下記のClickイベントを記述します。
3.ContextMenu1のPopupイベントに下記のPopupイベントを記述します。
4.各TextBoxのContextMenuをContextMenu1に設定します。

procedure TWinForm.ContextMenu1_Popup(sender: System.Object;
 e: System.EventArgs);
begin
 Copy.Enabled := (ActiveControl is TextBox) and
  (TextBox(ActiveControl).SelectionLength > 0);
 Cut.Enabled := Copy.Enabled;
 Delete.Enabled := Copy.Enabled;
 Paste.Enabled := (ActiveControl is TextBox) and
  (Clipboard.GetDataObject.GetDataPresent(TypeOf(String)))
end;

procedure TWinForm.Copy_Click(sender: System.Object;
 e: System.EventArgs);
begin
 ClipBoard.SetDataObject(TextBox(ActiveControl).SelectedText, True);
 //Copyメソッドを使う
 //TextBox(ActiveControl).Copy;
end;

procedure TWinForm.Cut_Click(sender: System.Object;
 e: System.EventArgs);
begin
 ClipBoard.SetDataObject(TextBox(ActiveControl).SelectedText, True);
 TextBox(ActiveControl).SelectedText := '';
 //Cutメソッドを使う
 //TextBox(ActiveControl).Cut;
end;

procedure TWinForm.Paste_Click(sender: System.Object;
 e: System.EventArgs);
begin
 if Clipboard.GetDataObject.GetDataPresent(TypeOf(String)) then
  TextBox(ActiveControl).SelectedText := 
   String(Clipboard.GetDataObject.GetData(TypeOf(String)));
 //Pasteメソッドを使う
 //TextBox(ActiveControl).Paste;
end;

procedure TWinForm.Delete_Click(sender: System.Object;
 e: System.EventArgs);
var
 Data: String;
 Flag: Boolean;
begin
 TextBox(ActiveControl).SelectedText := '';

 //Clearメソッドを使う
 //但し、全て消去してしまうため削除の動作とは違う
 //TextBox(ActiveControl).Clear  ;

 //Clearメソッドでは削除の動作とは違うため
 //Undoメソッドを使えるようにするためにCutを使って
 //コテコテに実装してみる。
 //Flag := Clipboard.GetDataObject.GetDataPresent(TypeOf(String));
 //if Flag then
  //Data := String(Clipboard.GetDataObject.GetData(TypeOf(String)));

 //TextBox(ActiveControl).Cut;

 //if Flag then
  //ClipBoard.SetDataObject(Data, True);
end;
TextBoxでは、クリップボードを扱うメソッドが用意されていますので、それを使うべきだと思いますけど、このような方法もあるということで、参考にして下さい。
ビットマップ等、テキスト以外のデータを扱う場合にも、同様にプログラムすれば使えるみたいです。

|

« 2004年4月 | トップページ | 2004年6月 »