全力わはー RSSフィード

2017-07-30

ユニットが使用しているシンボルの一覧を出力する

12:42 | ユニットが使用しているシンボルの一覧を出力するを含むブックマーク

概要

「--symbol-report」オプションを使うことで、各ユニットが使用しているシンボルの一覧を.symbol_reportファイルとして出力できるようになります。

方法

「プロジェクトオプション」の「コンパイラに渡す追加オプション」に「--symbol-report」を指定します。

f:id:tales:20170730123546p:image

正しく指定されていれば、ビルド後にユニットの出力ディレクトリに対して.symbol_reportファイルが生成されます。

f:id:tales:20170730124117p:image

サンプル

新規フォームを生成し、そのままビルドを行った場合、このようなファイルが生成されます。

<?xml version="1.0" encoding="UTF-8"?>
<unit name="Unit1">
  <uses name="Vcl.Dialogs">
  </uses>
  <uses name="Vcl.Forms">
    <symbol name="TForm"/>
    <symbol name="TCustomForm.AlignControls"/>
    <symbol name="TCustomForm.ClientWndProc"/>
    <symbol name="TCustomForm.CreateParams"/>
    <symbol name="TCustomForm.CreateWindowHandle"/>
    <symbol name="TCustomForm.CreateWnd"/>
    <symbol name="TCustomForm.DefineProperties"/>
    <symbol name="TCustomForm.DestroyHandle"/>
    <symbol name="TCustomForm.DestroyWindowHandle"/>
    <symbol name="TCustomForm.DoCreate"/>
    <symbol name="TCustomForm.DoDestroy"/>
    <symbol name="TCustomForm.DoThumbButtonNotify"/>
    <symbol name="TCustomForm.DoWindowPreviewRequest"/>
    <symbol name="TCustomForm.DoThumbPreviewRequest"/>
    <symbol name="TCustomForm.GetClientRect"/>
    <symbol name="TCustomForm.GetFloating"/>
    <symbol name="TCustomForm.Loaded"/>
    <symbol name="TCustomForm.Notification"/>
    <symbol name="TCustomForm.PaintWindow"/>
    <symbol name="TCustomForm.GetDesignDpi"/>
    <symbol name="TCustomForm.ScaleForCurrentDpi"/>
    <symbol name="TCustomForm.ReadState"/>
    <symbol name="TCustomForm.RequestAlign"/>
    <symbol name="TCustomForm.SetParentBiDiMode"/>
    <symbol name="TCustomForm.SetParent"/>
    <symbol name="TCustomForm.UpdateActions"/>
    <symbol name="TCustomForm.ValidateRename"/>
    <symbol name="TCustomForm.WndProc"/>
    <symbol name="TCustomForm.Resizing"/>
    <symbol name="TCustomForm.QueryInterface"/>
    <symbol name="TCustomForm.Create"/>
    <symbol name="TCustomForm.CreateNew"/>
    <symbol name="TCustomForm.Destroy"/>
    <symbol name="TCustomForm.ScaleForPPI"/>
    <symbol name="TCustomForm.CloseQuery"/>
    <symbol name="TCustomForm.DefaultHandler"/>
    <symbol name="TCustomForm.SetFocus"/>
    <symbol name="TCustomForm.SetFocusedControl"/>
    <symbol name="TCustomForm.ShowModal"/>
    <symbol name="TCustomForm.WantChildKey"/>
    <symbol name="TCustomForm.AfterConstruction"/>
    <symbol name="TCustomForm.BeforeDestruction"/>
    <symbol name="TScrollingWinControl.AdjustClientRect"/>
    <symbol name="TScrollingWinControl.AutoScrollEnabled"/>
    <symbol name="TScrollingWinControl.AutoScrollInView"/>
    <symbol name="TScrollingWinControl.DoGesture"/>
    <symbol name="TScrollingWinControl.DoGetGestureOptions"/>
    <symbol name=".TForm"/>
  </uses>
  <uses name="Vcl.Controls">
    <symbol name="TWinControl.UpdateStyleElements"/>
    <symbol name="TWinControl.AsyncSchedule"/>
    <symbol name="TWinControl.AssignTo"/>
    <symbol name="TWinControl.CanAutoSize"/>
    <symbol name="TWinControl.CanResize"/>
    <symbol name="TWinControl.ConstrainedResize"/>
    <symbol name="TWinControl.CreateHandle"/>
    <symbol name="TWinControl.CustomAlignInsertBefore"/>
    <symbol name="TWinControl.CustomAlignPosition"/>
    <symbol name="TWinControl.DestroyWnd"/>
    <symbol name="TWinControl.DockReplaceDockClient"/>
    <symbol name="TWinControl.GetClientOrigin"/>
    <symbol name="TWinControl.GetControlExtents"/>
    <symbol name="TWinControl.GetDeviceContext"/>
    <symbol name="TWinControl.ResetIme"/>
    <symbol name="TWinControl.ScaleControlsForDpi"/>
    <symbol name="TWinControl.SetIme"/>
    <symbol name="TWinControl.SetParentBackground"/>
    <symbol name="TWinControl.SetParentDoubleBuffered"/>
    <symbol name="TWinControl.ShowControl"/>
    <symbol name="TWinControl.UpdateControlOriginalParentSize"/>
    <symbol name="TWinControl.UpdateTIPStatus"/>
    <symbol name="TWinControl.Invalidate"/>
    <symbol name="TWinControl.Repaint"/>
    <symbol name="TWinControl.SetBounds"/>
    <symbol name="TWinControl.Update"/>
    <symbol name="TControl.GetParentCurrentDpi"/>
    <symbol name="TControl.CreateTouchManager"/>
    <symbol name="TControl.GetAction"/>
    <symbol name="TControl.GetEnabled"/>
    <symbol name="TControl.GetFloatingDockSiteClass"/>
    <symbol name="TControl.DefaultScalingFlags"/>
    <symbol name="TControl.SetAutoSize"/>
    <symbol name="TControl.SetDragMode"/>
    <symbol name="TControl.SetEnabled"/>
    <symbol name="TControl.SetName"/>
    <symbol name="TControl.SetBiDiMode"/>
    <symbol name="TControl.SetStyleElements"/>
    <symbol name="TControl.GetDragImages"/>
    <symbol name="TControl.InitiateAction"/>
  </uses>
  <uses name="Vcl.Graphics">
  </uses>
  <uses name="System.Classes">
    <symbol name="TComponent.CanObserve"/>
    <symbol name="TComponent.ObserverAdded"/>
    <symbol name="TComponent.GetObservers"/>
    <symbol name="TComponent.UpdateRegistry"/>
    <symbol name="TComponent.WriteState"/>
    <symbol name="TComponent.SafeCallException"/>
    <symbol name="TComponent.UpdateAction"/>
    <symbol name="TPersistent.Assign"/>
  </uses>
  <uses name="System.Variants">
  </uses>
  <uses name="System.SysUtils">
  </uses>
  <uses name="Winapi.Messages">
  </uses>
  <uses name="Winapi.Windows">
  </uses>
  <uses name="SysInit">
  </uses>
  <uses name="System">
    <symbol name="TObject.Equals"/>
    <symbol name="TObject.GetHashCode"/>
    <symbol name="TObject.ToString"/>
    <symbol name="TObject.Dispatch"/>
    <symbol name="TObject.NewInstance"/>
    <symbol name="TObject.FreeInstance"/>
  </uses>
</unit>

2017-04-02

TJsonSerializerの実用例

23:01 | TJsonSerializerの実用例を含むブックマーク

TFormなどを継承したクラスの場合

例えば起動時と終了時にフォームの位置を記憶・復元することを考えた場合、TJsonSerializerにTForm1などのインスタンスを渡すことになりますが、デフォルトでは余計なデータまで保存されてしまうため、保存項目を自分で指定するためにまずTForm1にJsonSerialize(TJsonMemberSerialization.In)というカスタム属性を指定し、次に保存したいメンバにJsonIn属性を持たせることで任意の項目のみをシリアライズすることになります。

しかし自分で定義した型と違い、TFormの派生クラスでは親クラスより上の段階でLeftやTopといったプロパティが定義されているため、ぱっと見属性を書く場所がありません。そういった場合はプロパティを再定義し、そこに属性を記述します。

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  published
    [JsonIn]
    property Left; // <==再定義
    [JsonIn]
    property Top; // <==再定義
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  serializer: TJsonSerializer;
begin
  if FileExists('config.json') then
  begin
    serializer := TJsonSerializer.Create;
    try
      serializer.Populate(TFile.ReadAllText('config.json', TEncoding.UTF8), Self);
    finally
      serializer.Free;
    end;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  serializer: TJsonSerializer;
begin
  serializer := TJsonSerializer.Create;
  try
    TFile.WriteAllText('config.json', serializer.Serialize(Self), TEncoding.UTF8);
  finally
    serializer.Free;
  end;
end;

ちなみに属性を何も指定せずにTFormのインスタンスをシリアライズしようとすると、スタックオーバーフローが発生します。これはTFormのプロパティを辿っていくと再び自身(TForm)が出てくる場合があり、無限ループになってしまうせいです。そのため必ず属性による制御が必要です。

定義済みの型の一部のみをシリアライズしたい場合

先の例は継承したクラスがあるため自分で属性付けが可能でしたが、継承せずそのまま使うクラスであったり、そもそも継承が不可能なレコードの場合、直接カスタム属性を付与することはできません。この場合は少し面倒になりますが、TJsonDynamicContractResolverを使用して動的に属性を付与することになります。

例えばTRectをシリアライズしようと考えた場合、そのままシリアライズするとLeft, Top, Right, Bottomだけではなく、可変部分であるTopLeftとBottomRightも一緒に出力されてしまいます。そこでLeft, Top, Right, Bottomのみをシリアライズするような場合を考えます。

var
  rect: TRect;
  serializer: TJsonSerializer;
  resolver: TJsonDynamicContractResolver;
begin
  rect.Left := 100;
  rect.Top := 200;
  rect.Right := 300;
  rect.Bottom := 400;

  serializer := TJsonSerializer.Create;
  try
    resolver := TJsonDynamicContractResolver.Create;
    serializer.ContractResolver := resolver;
    resolver.SetFieldsIgnored(TypeInfo(TRect), ['TopLeft', 'BottomRight']);
    TFile.WriteAllText('rect.json', serializer.Serialize(rect), TEncoding.UTF8);
  finally
    serializer.Free;
  end;
end;

何も指定せずにシリアライズすると{"Left":100,"Top":200,"Right":300,"Bottom":400,"TopLeft":{"X":100,"Y":200},"BottomRight":{"X":300,"Y":400}}となってしまいますが、このコードでシリアライズすると{"Left":100,"Top":200,"Right":300,"Bottom":400}のみが得られます。

なお、ContractResolverプロパティはインターフェース型で、代入した時点で自動管理になるのでresolverを自身で解放する必要はありません。

2017-03-31

TJsonSerializerの使い方。

00:46 | TJsonSerializerの使い方。を含むブックマーク

待っててもdocwikiに空ページすら作られる気配が無いので、使い方を調べてみました。

System.JSON.SerializersとSystem.JSON.Convertersユニット

この2つは10.2 Tokyoで新しく追加されたJSON関連のユニットです。2つありますがメインはSystem.JSON.Serializersで、System.JSON.ConvertersはSerializers内で使用するためのコンバータクラス(詳細は後述)が定義されています。

TJsonSerializer

System.JSON.Serializersは名前の通り、JSONにシリアライズ/デシリアライズするためのユニットで、その機能はTJsonSerializerクラスに集約されています。関連するカスタム属性を使うことで柔軟かつ複雑な処理を行うこともできますが、必要最低限のコードは以下の通り非常にシンプルです。

uses
  System.JSON.Serializers;

type
  TFoo = record
    Field1: Integer;
    Field2: string;
  end;

procedure Sample;
var
  foo: TFoo;
  serializer: TJsonSerializer;
begin
  foo.Field1 := 123;
  foo.Field2 := 'ABC';
  serializer := TJsonSerializer.Create;
  try
    // シリアライズ
    ShowMessage(serializer.Serialize(foo)); // -> '{"Field1":123,"Field2":"ABC"}'

    // デシリアライズ(新規に生成する場合)
    foo := serializer.Deserialize<TFoo>('{"Field1":456,"Field2":"DEF"}');
    ShowMessageFmt('%d, %s', [foo.Field1, foo.Field2]); // -> '456, DEF'

    // デシリアライズ(既存のものに代入する場合)
    serializer.Populate('{"Field2":"XYZ"}', foo);
    ShowMessageFmt('%d, %s', [foo.Field1, foo.Field2]); // -> '456, XYZ'
  finally
    serializer.Free;
  end;
end;

カスタム属性による制御

基本的な使い方が分かったところで、次はカスタム属性を使ったより便利な使い方を紹介します。シリアライズ/デシリアライズの際に使用可能なカスタム属性はこのように複数あります。

カスタム属性用途デフォルト値
JsonConverterAttributeコンバータの指定なし
JsonIgnoreAttribute無視するメンバの指定なし
JsonNameAttributeメンバ名を別名で出力するなし(元の識別子)
JsonInAttributeシリアライズするメンバの指定(TJsonMemberSerializationがInの時のみ有効)なし
JsonObjectHandlingAttributeクラス型メンバをデシリアライザ側で生成するかどうかTJsonObjectHandling.Auto
JsonObjectOwnershipデシリアライザでクラス型メンバを生成した場合の元のインスタンスをどうするかTJsonObjectOwnership.Auto
JsonSerializeAttributeシリアライズ/デシリアライズするメンバの選び方TJsonMemberSerialization.Fields

なお、カスタム属性は例えばFooAttributeという名前の場合、Attribute部分を省いてFooとすることが可能なため、以降では省略して記述します。

JsonSerialize属性

このカスタム属性は型に直接指定するもので、メンバのうちどれをシリアライズ/デシリアイズするかの方法を指定します。指定時は列挙型であるTJsonMemberSerialization型の引数を取り、Fields, Public, Inの3つが指定できます。

例えば以下のようなレコードTFooとクラスTBarを考えます。

type
  [JsonSerialize(TJsonMemberSerialization.Fields)]
  TFoo = record
  private
    FValue1: Integer;
  public
    Value2: Integer;
    property Value3: Integer read FValue1 write FValue1;
  end;
  TBar = class
  private
    FValue1: Integer;
  public
    Value2: Integer;
    property Value3: Integer read FValue1 write FValue1;
  end;

これらの型にTJsonMemberSerialization.Fieldsを指定した場合、あるいは何も指定しなかった場合(=デフォルト)、TJsonSerializerは全てのフィールドを処理対象とします。この場合TFooもTBarも同じでFValue1とFValue2が該当します。フィールドのRTTIは可視性にかかわらず全て生成されるため、privateでもpublicでも処理対象となりますが、$RTTI指令でこれが変更されていた場合、TJsonSerializerの処理対象もそれに追随します。

次にTJsonMemberSerialization.Publicを指定した場合を考えます。この場合可視性がpublicなものを処理対象とするため、TFooもTBarでもValue2とValue3が対象となりそうなものですが、実はレコードのプロパティはRTTIが生成されないため、実際にはTFooはValue2のみ処理対象となります。プロパティを持ったレコードを扱う際は注意が必要です。

最後にTJsonMemberSerialization.Inを指定した場合です。これは前の2つと異なり、指定しただけでは何も起きません。このモードではJsonInというカスタム属性と組み合わせることで、任意のメンバを対象とします。例えば以下のようなクラスがあった場合、JsonInを付けたメンバのみがシリアライズ/デシリアライズの対象となります。この場合FValue1とValue3です。

type
  [JsonSerialize(TJsonMemberSerialization.In)]
  TBar = class
  private
    [JsonIn]
    FValue1: Integer;
    FValue2: string;
  public
    [JsonIn]
    property Value3: Integer read GetValue3 write SetValue3;
  end;
JsonIgnore属性

このカスタム属性はシリアライズ/デシリアライズをしないメンバに対して指定します。TJsonMemberSerializationのFieldsとPublicは細かい指定ができないため、この属性と組み合わせることで対象となるメンバを調整します。仕様上JsonInと組み合わせで使うこともできますが、動作としてはJsonIgnoreの方が優先されるため、両方する指定する意味はあまりありません。

JsonName属性

このカスタム属性はJSONのキー名を実際のメンバ名とは別のものにする際に指定します。例えば以下のようなレコードを考えます。

type
  TFoo = record
    [JsonName('Id')]
    Field1: Integer;
    [JsonName('Value')]
    Field2: string;
  end;

このレコードをシリアライズすると、{"Id": 123, "Value": "abc"}のようなJSONが得られます。デシリアライズ時も同様に機能します。

JsonObjectHandling属性

このカスタム属性は引数に列挙型のTJsonObjectHandlingを取り、デシリアライズ時のクラス型メンバの生成方法を制御しますが、Deserializeメソッドは無条件で全て新規に生成するため、この属性はPopulateメソッド専用です。

動作モードは3種類あり、Reuseは新しくインスタンスを生成することなく渡されたものをそのまま使います。ただし対象のメンバがnilだった場合は新しく生成します。Replaceは対象のメンバの中身にかかわらず新しく生成した上で代入します。その際に元々入っていたインスタンスの扱いは後述のJsonObjectOwnershipで指定します。Autoは単にカスタム属性とは別にTJsonSerializerが持つObjectHandling設定を使うというだけです。コンポーネントでいうParentColorやParentFontプロパティのようなもので、親設定に従う、というものです。ただTJsonSerializerの初期値もAutoのため、特に変更のない場合Reuseとして動作します。

JsonObjectOwnership属性

このカスタム属性はDeserializeやPopulateで新しくインスタンスが生成された時、元の値を解放するかどうかを決定します。JsonObjectHandlingにReplaceが指定された時にだけ意味を持つ属性です。

動作モードは3種類あり、Ownedは自身が所有権を持つということで、新しいインスタンスが代入された時に元からあったインスタンスを解放します。NotOwnedは代入されても何もしません。AutoはJsonObjectHandlingと同じくTJsonSerializerのObjectOwnershipプロパティに従います。TJsonSerializerの初期値もAutoのため、その場合は実質的にOwnedとして動作します。

…のはずなんですが、動作しません。何を指定してもAuto扱いになってしまうようです。ドキュメントがないため使い方が間違っている可能性もありますが、ソースを見る限りではTJsonDefaultContractResolver.SetPropertySettingsFromAttributes内でこの属性だけ受け渡しがされていないのが原因のような気がします。ただしAutoの場合上述のようにTJsonSerializerの設定を使いますが、ここでの指定は問題ないためNotOwnedが指定したい場合はこれしか方法が無さそうです。ただし全体に適用されてしまうため注意が必要です。

JsonConverter属性

このカスタム属性は引数にコンバータクラスを取り、複雑な構造を持つ型をシリアライズ/デシリアライズしたり、デフォルトの処理とは違う方法でシリアライズ/デシリアライズする際の動作を指定します。

コンバータクラスはTJsonConverterクラスを継承して作成しますが、ある程度使用頻度が高そうなものはSystem.JSON.Convertersユニット内にあらかじめ実装されています。例えばTJsonEnumNameConverterは列挙型のシリアライズに、TJsonStackConverter<V>はTList<T>をJSONの配列にシリアライズできます。コンバータを自作する際はこれらが豊富なサンプルになりそうです。

TJsonSerializerのプロパティ

TJsonSerializerにはオプションが複数あり、これを指定することでシリアライズ/デシリアライズの動作を変更することが可能です。全てをテストしたわけではないため、説明はソースからの推測を含みます。

プロパティ名説明取り得る値
DateFormatHandling日付のフォーマット(シリアライズ時のみ)Iso(デフォルト), Unix, FormatSettings
DateParseHandlingTDateTime形式としてパースするか否か(デシリアライズ時のみ)None(デフォルト), DateTime
DateTimeZoneHandlingTDateTimeのタイムゾーン設定Local(デフォルト), Utc
FloatFormatHandlingNaNなどの特殊な小数値の出力設定String(デフォルト), Symbol, DefaultValue
Formattingインデント設定(シリアライズ時のみ)None(デフォルト), Indented
MaxDepth読み取るネストの深さ(デシリアライズ時のみ)Integer(デフォルト=-1(無制限))
ObjectHandlingJsonObjectHandling属性の全体設定Auto(デフォルト), Reuse, Replace
ObjectOwnershipJsonObjectOwnership属性の全体設定Auto(デフォルト), Owned, NotOwned
StringEscapeHandling文字列のエスケープ(シリアライズ時のみ)Default(デフォルト), EscapeNonAscii, EscapeHtml

実行時にカスタム属性を変更

カスタム属性は通常設計時に指定するため、ある時はFieldAとFieldBを、ある時はFieldAとFieldCを…というように、実行時に出力したいメンバを変更することができません。このような時はTJsonDynamicContractResolverを使用することで、型やメンバに紐付いたカスタム属性を実行時に動的に変更が可能なようです。

これについてはまだ実際に使用していないので憶測になってしまいますが、TJsonSerializerにContractResolverというIJsonContractResolver型のプロパティがあり、ここにTJsonDynamicContractResolverのインスタンスを代入して使用するようです。動作原理としてはTJsonSerializerがシリアライズする際、カスタム属性をIJsonContractResolver経由で取得するようなのですが、その際にTJsonDynamicContractResolverであらかじめ上書きされたカスタム属性を本来のカスタム属性より優先的に返すことで、実行時にカスタム属性の付け替えを擬似的に行っているようです。

2017-03-23

Delphi 10.2 Tokyoのdocwikiに書かれてなさそうな変更点。

00:31 | Delphi 10.2 Tokyoのdocwikiに書かれてなさそうな変更点。を含むブックマーク

ざっくりと見ただけなので全体を網羅できてないですし、10.1以前での変更点も含むかもしれない程度に適当ですのであらかじめご了承下さい。

System.SysUtils

IntToHexのoverloadが増えた

具体的にこんなのが増えてます。それぞれ引数の型サイズ×2の桁数で表示されるようです。

function IntToHex(Value: Int8): string; overload; inline;
function IntToHex(Value: UInt8): string; overload; inline;
function IntToHex(Value: Int16): string; overload; inline;
function IntToHex(Value: UInt16): string; overload; inline;
function IntToHex(Value: Int32): string; overload; inline;
function IntToHex(Value: UInt32): string; overload; inline;
function IntToHex(Value: Int64): string; overload; inline;
function IntToHex(Value: UInt64): string; overload; inline;
文字列を32bit符号なし整数に変換できるようになった

以下の3つの関数が追加されてます。今までStrToIntはあったんですが、符号付きの範囲しか変換できなかったため、$80000000以上$FFFFFFFF以下を変換しようと思うとStrToInt64などを使わざるを得ませんでした。

function StrToUInt(const S: string): Cardinal; overload;
function StrToUIntDef(const S: string; Default: Cardinal): Cardinal; overload;
function TryStrToUInt(const S: string; out Value: Cardinal): Boolean; overload;

System.Classes

TStreamが64bitサイズの読み書きに対応

以前まではReadメソッドやWriteメソッドのCount引数の型がIntegerだったため、どうあがいても一度に読み書きできるのは32bit範囲に限定されていましたが、今回Read64とWrite64というCountの型がInt64になったメソッドが新設されたことでその制限が無くなりました。ただし形無し引数版は無く、対応してるのはTBytesの読み書きだけです。

同時に、TStreamのReadBufferやWriteBuffer、それに関連してTReaderやTWriter、TParserなどの中にあるCount引数を持つメソッドで、Countの型がIntegerからNativeIntになりました。これはoverloadされたメソッドが新設されたわけではなく既存メソッドの仕様変更なので、場合によっては注意する必要がありそうです。

Countの件とは関係ないですが、TStreamにはReadData<T>やWriteData<T>というメソッドも追加されてます。

TMemoryStreamのCapacityの仕様が変更

具体的にはアクセサであるSetCapacityメソッドがprivateからprotectedに移動し、virtual化してます。

またTStreamの64bit対応に合わせてか、Capacityの型もLongintからNativeIntになったので、64bitアプリではより大きなサイズを扱えるようになりました。

System.JSON.ConvertersとSystem.JSON.Serializersユニットが追加

こういうボリューミーな新機能こそdocwikiに書いておいて欲しいですが、なんか追加されたみたいです。

Serializersの方はJsonIgnoreAttributeやJsonNameAttributeみたいなカスタム属性があり、シリアライズする際の挙動を細かく指定できるようでなかなか便利そうです。だからちゃんと説明して下さい>エンバカデロ

Vcl.ExtCtrls

TImage.OnFindGraphicClassイベント

TImage.Pictureに画像を読み込む際、今までは拡張子やクリップボードの内容をVCLが自動で判定して適切なTGraphic派生クラスを使用していましたが、このイベントを使うことでそれを制御できるようになったようです。さらにTPictureにLoadFromStreamが新設され、その読み込み判定にも使用されます。

また、関連してTGraphicにCanLoadFromStreamというクラスメソッドが追加され、これを使うことで事前にロード可能か判定できるようになったようです。ざっと見たところ全てのTGraphic派生クラスで実装されているようなので、気にせず使っても大丈夫そうです。

2017-01-23

Style Selector for TFrame

01:40 | Style Selector for TFrameを含むブックマーク

f:id:tales:20170123013834p:image

これは何?

FMX.Forms.TFrameでスタイルセレクタを表示させるためのプラグインです。TFormと違い、TFrameではスタイルセレクタが表示されないため、デフォルトでは各プラットフォームごとの見た目を確認できません。

ダウンロード

https://github.com/lynatan/StyleSelectorForTFrame