<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">	<channel>	<title>TMS Software</title>	<link>https://www.tmssoftware.com</link>	<language>en</language>	<pubDate>Mon, 1 Jun 2026 17:14:00 +0100</pubDate>	<docs>http://blogs.law.harvard.edu/tech/rss</docs>	<ttl>1440</ttl>	<generator>TMS Software</generator>	<image>		<url>https://www.tmssoftware.com/site/img/tmslogo.png</url>		<title>TMS Software</title>		<link>https://www.tmssoftware.com</link>	</image>
	<item>
		<title><![CDATA[﻿TMS Scripter 8 drops legacy Delphi support]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2480</link>
		<author>Wagner Landgraf</author>
		<pubDate>Mon, 1 Jun 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		<p><a href="https://www.tmssoftware.com/site/scriptstudiopro.asp">TMS Scripter</a> 8.0 &#151; the scripting engine that lets Delphi applications execute Pascal and Basic scripts at runtime &#151; is coming soon, and it will be the first release to require <strong>Delphi XE2 or later</strong>. Delphi 7, Delphi 2007, Delphi 2010, and Delphi XE are no longer in the supported range.</p>

<p><a href="https://www.tmssoftware.com/site/diagram.asp">TMS Diagram Studio</a>, <a href="https://www.tmssoftware.com/site/workflow.asp">TMS Workflow Studio</a>, and <a href="https://www.tmssoftware.com/site/qs.asp">TMS Query Studio</a> will make the same change in their own upcoming releases.</p>

<h3>A Long-Running Commitment</h3>

<p>TMS Scripter has supported Delphi 7 since day one &#151; the library was born in that era, and the compatibility was maintained for roughly twenty-five years. Many TMS customers run production applications on legacy Delphi versions that cannot be easily migrated, and keeping them supported was always a deliberate choice.</p>

<p>But that chapter is now closed. The time has come to move forward.</p>

<h3>Why We Are Dropping Support</h3>

<p>Delphi 7 predates two of the most important additions to the Object Pascal language: <strong>generics</strong> and <strong>anonymous methods</strong>, both introduced in Delphi 2009. Every line of code in TMS Scripter had to be written as if those features did not exist, which created real and lasting constraints:</p>

<ul>
  <li><strong>Untyped collections everywhere.</strong> Without generics, lists of objects use untyped <code>TList</code> with manual typecasts at every access site. Code that would naturally be <code>TList&lt;TScript&gt;</code> must instead work with raw pointers and explicit casts throughout.</li>
  <li><strong>Callback boilerplate.</strong> Registering Delphi methods for use in scripts requires separate named wrapper procedures for each one. Anonymous methods would allow the logic to be co-located with its registration, but that was not an option.</li>
  <li><strong>No advanced RTTI.</strong> The <code>System.Rtti</code> unit &#151; which allows full runtime inspection of classes, methods, properties, and their types &#151; arrived in Delphi 2010. It is unavailable on Delphi 7 entirely, which means a scripting engine cannot use it to automatically discover and register Delphi classes. Everything has to be done manually.</li>
  <li><strong>API surface limited by the lowest common denominator.</strong> Public APIs that would benefit from typed generics or procedure references had to fall back to <code>TObject</code>, <code>Pointer</code>, or method-of-object types.</li>
</ul>

<p>This is not a criticism of Delphi 7. It had its time, and it served its users well. But maintaining compatibility with a compiler from 2002 while trying to build a modern, maintainable library is a genuine tension, and it has accumulated over the years.</p>

<h3>What This Opens Up</h3>

<p>TMS Scripter 8.0 itself does not yet take full advantage of the new baseline &#151; this is the first step, not the finished product. We want to see how the community reacts before committing to deeper API changes.</p>

<p>What the change does is remove a hard constraint. Going forward, the codebase can evolve toward:</p>

<p><strong>Generics.</strong> Internal collections can become properly typed. <code>TList&lt;T&gt;</code> instead of <code>TList</code>. Fewer typecasts, stronger compile-time checks, better IDE code completion.</p>

<p><strong>Advanced RTTI.</strong> The <code>System.Rtti</code> unit gives a program full runtime knowledge of any class &#151; its methods, parameters, return types, properties, and attributes. For a scripting engine, this is particularly powerful: instead of manually writing a wrapper procedure for every method you want to expose to scripts, the engine can potentially walk the RTTI of a class and register everything automatically. This is the kind of capability that can dramatically reduce the boilerplate burden on users and make Scripter integration with arbitrary Delphi classes much more seamless.</p>

<p><strong>Anonymous methods.</strong> Callback types can be changed from method-of-object references to <code>reference to procedure</code>. To illustrate the kind of improvement this enables &#151; today, registering a Delphi class method for use in a script looks like this:</p>

<pre><code>// Current approach: a dedicated class with one wrapper method per Delphi method
type
  TMyLibrary = class(TatScripterLibrary)
  protected
    procedure CalculateProc(AMachine: TatVirtualMachine);
    procedure Init; override;
  end;

procedure TMyLibrary.Init;
begin
  with Scripter.AddDelphiClass(TMyCalculator) do
    DefineMethod(&#39;Calculate&#39;, 1, tkInteger, nil, CalculateProc);
end;

procedure TMyLibrary.CalculateProc(AMachine: TatVirtualMachine);
begin
  with AMachine do
    ReturnOutputArg(TMyCalculator(CurrentObject).Calculate(GetInputArgAsInteger(0)));
end;</code></pre>

<p>Once the callback type supports procedure references, the same registration can be written inline:</p>

<pre><code>// Future direction: registration and implementation in one place
with Scripter.AddDelphiClass(TMyCalculator) do
  DefineMethod(&#39;Calculate&#39;, 1, tkInteger, nil,
    procedure(AMachine: TatVirtualMachine)
    begin
      AMachine.ReturnOutputArg(
        TMyCalculator(AMachine.CurrentObject).Calculate(
          AMachine.GetInputArgAsInteger(0)));
    end);</code></pre>

<p>None of this was possible while the library had to compile on Delphi 7. Now it is.</p>

<h3>You Are Not Stranded: TMS Smart Setup Versioning</h3>

<p>If you are still on Delphi 7, or on one of the other versions now leaving the support window, you are not left without options.</p>

<p><a href="https://doc.tmssoftware.com/smartsetup/">TMS Smart Setup</a> &#151; the TMS Software installer and package manager &#151; includes a <a href="https://doc.tmssoftware.com/smartsetup/guide/versioning.html">versioned installation feature</a> designed exactly for this situation. When you use Smart Setup to install TMS Scripter, it detects which Delphi version you have and serves you the last release that supports it &#151; automatically, without you having to track which archive corresponds to which compiler.</p>

<p>The end of active compatibility maintenance does not mean the last supported version disappears.</p>

<h3>What About the Other Products?</h3>

<p>TMS Scripter gets the headline, but it is not the only product affected.</p>

<p><a href="https://www.tmssoftware.com/site/diagram.asp">TMS Diagram Studio</a> &#151; the framework for building diagram and flowchart editors in Delphi VCL applications &#151; will drop the same set of legacy versions in an upcoming release.</p>

<p><a href="https://www.tmssoftware.com/site/workflow.asp">TMS Workflow Studio</a> &#151; a workflow engine for defining and executing business processes within Delphi applications &#151; follows the same path.</p>

<p><a href="https://www.tmssoftware.com/site/qs.asp">TMS Query Studio</a> &#151; the visual query builder component for building complex queries at runtime &#151; likewise.</p>

<p>All four products share the same compatibility floor going forward: Delphi XE2 or later.</p>

<h3>What This Means for You</h3>

<p><strong>If you are on Delphi XE2 or later:</strong> you are in the supported range. TMS Scripter 8.0 will install and work as usual when it releases.</p>

<p><strong>If you are on Delphi 7, 2007, 2010, or XE:</strong> the last version of each product that supported your compiler remains fully functional. Use Smart Setup to install it cleanly &#151; it will continue to work exactly as it always has.</p>

<h3>Links</h3>

<ul>
  <li><a href="https://www.tmssoftware.com/site/scriptstudiopro.asp">TMS Scripter product page</a></li>
  <li><a href="https://doc.tmssoftware.com/biz/scripter">TMS Scripter documentation</a></li>
  <li><a href="https://doc.tmssoftware.com/smartsetup/">TMS Smart Setup</a></li>
  <li><a href="https://doc.tmssoftware.com/smartsetup/guide/versioning.html">Smart Setup version compatibility guide</a></li>
</ul>

		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Editing, sorting, filtering and paging in TWeb(DB)DataGrid]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2479</link>
		<author>Bruno Fierens</author>
		<pubDate>Thu, 28 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		
		
		
		
		
		
		
		<h4><img src="https://www.tmssoftware.com/site/img/blog/webdatagridbanner.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></h4><h4>Introduction</h4><div>When building business web applications with <a href="https://www.embarcadero.com/products/delphi/" target="_blank">Delphi</a> and <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a>, showing data is only the first step.</div><div><br></div><div>Users also expect to work with that data: edit values directly in the grid, sort columns, filter large lists and navigate records page by page. With TWebDataGrid and TWebDBDataGrid, these common data workflows are available directly in the component, while still keeping the programming model familiar for Delphi developers.</div><div><br></div><div>In this blog post, we take a closer look at four everyday grid operations:</div><div><br></div><ul><li>Editing cells</li><li>Sorting columns</li><li>Filtering columns</li><li>Paging through records</li></ul><div>The examples are based on the TMS WEB Core demo projects for cell editors, column sorting, column filtering and record paging.</div><div><br></div><h4>Editing Data with Built-in Cell Editors</h4><div>TWebDataGrid supports different editor types per column. This makes it possible to provide the right editor for the right data type: text editing for strings, checkboxes for Boolean values, numeric editors for numbers, date editors for date values and combobox editors for predefined selections.</div><div><br></div><div>When cells in a column can be edited by setting grid.ColumnDefs[index].Editable = true, the type of inplace cell editors available are:</div><div><br></div><table class="table table-bordered"><tbody><tr><td>cetDefault</td><td>Default HTML INPUT element to enter any characters</td></tr><tr><td>cetText</td><td>Alphanumeric input control</td></tr><tr><td>cetNumber</td><td>Numeric input control</td></tr><tr><td>cetDate</td><td>Date picker input control</td></tr><tr><td>cetTime</td><td>Time picker input control</td></tr><tr><td>cetRange</td><td>Range picker input control</td></tr><tr><td>cetColor</td><td>Color picker input control</td></tr><tr><td>cetWeek</td><td>Week selector</td></tr><tr><td>cetMonth</td><td>Month selector</td></tr><tr><td>cetURL</td><td>Input control with regular expression check for valid URL entry</td></tr><tr><td>cetEmail</td><td>Input control with regular expression check for valid email entry</td></tr><tr><td>cetTel</td><td>Input control with regular expression check for valid telephone entry</td></tr><tr><td>cetMemo</td><td>Multiline text edit control</td></tr><tr><td>cetCheckbox</td><td>Checkbox control</td></tr><tr><td>cetCombobox</td><td>Combobox control</td></tr><tr><td>cetDateTime</td><td>Date/time picker input control</td></tr></tbody></table><div>For the cetCombobox input control type, per column, you can define the values for the combobox via grid.ColumnDefs[index].SelectOptions. This is a collection of combobox text &amp; values. If the displayed text is the same as the data you handle in the grid, using grid.ColumnDefs[index].SelectOptions[index].Text is sufficient. Otherwise, specify a Text value as well as the Value. The Value is what is then written to the grid cell upon editing.<br><br>This is all that is needed to make the grid feel much more like a real data-entry surface than a static table.</div><div><br></div><div>For TWebDBDataGrid, the same idea applies in a data-aware scenario. The grid columns are bound to dataset fields and editing happens through the connected dataset. This gives Delphi developers a classic RAD workflow in the browser: configure fields, choose the editor behavior and let the dataset handle the data updates.</div><div><br></div><h4>Sorting Columns</h4><div>Sorting is one of the first things users expect from a grid. In TWebDataGrid, sorting can be enabled or disabled per column. When enabled, the user can click on the column header to invoke consecutively an ascending sort, a descending sort and then restore the original row order.&nbsp;</div><div><img src="https://www.tmssoftware.com/site/img/blog/webdatagridsort.png" style="width: 615px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br></div><div>The user can sort the sortable column directly in the grid UI this way. But sorting can also be controlled from code.</div><div><br></div><div><pre class="delphi" name="code">procedure TForm1.btnSortSortableColumnClick(Sender: TObject);
var
  SO: TDGSortOrder;
begin
  case cmbSortOrder.ItemIndex of
    0: SO := TDGSortOrder.soAscending;
    1: SO := TDGSortOrder.soDescending;
    else SO := TDGSortOrder.soNone;
  end;

  WebDataGrid1.ColumnDefs.FindColumn(&#39;SortableColumn&#39;).Sort(
    SO,
    chkAddToExistingSortOrder.Checked);
end;</pre></div><div><br></div><div>The second parameter makes it possible to add this sort operation to an existing sort order. This is useful when building applications where users need multi-column sorting, for example by category first and by name or date afterwards.</div><div><br></div><div>Clearing sorting is equally direct:<br></div><div><pre class="delphi" name="code">procedure TForm1.btnClearSortingClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.ClearSorting;
end;</pre></div><div>This programming model is especially useful when the grid is controlled by external UI elements such as toolbar buttons, menu items or predefined views.</div><div><br></div><div>With TWebDBDataGrid, the same sorting experience can be offered on top of dataset-backed data. This keeps the user interaction consistent whether the data comes from JSON, CSV, custom row data or a live dataset.</div><div><br></div><h4>Filtering Columns</h4><div>Filtering helps users focus on the records that matter. Just like sorting, filtering can be enabled per column.</div><div><img src="https://www.tmssoftware.com/site/img/blog/webdatagridfilter.png" style="width: 609px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br></div><div>In the filtering demo, a text column and a number column are made filterable:</div><div><br></div><div><pre class="delphi" name="code">procedure TForm1.WebFormCreate(Sender: TObject);
var
  Data, IStr, IStr2: String;
  I: Integer;
begin
  WebDataGrid1.ColumnDefs[1].Filter := True;
  WebDataGrid1.ColumnDefs[2].Filter := True;

  Data := &#39;&#39;;
  for I := 1 to 50 do
  begin
    IStr := IntToStr(I);
    IStr2 := IntToStr(Random(100));
    Data := Data + IStr + &#39;,"Item #&#39; + IStr + &#39;", &#39; + IStr2 + #$D#$A;
  end;

  WebDataGrid1.LoadFromCSVString(Data);
end;</pre></div><div><br>When filtering is enabled for a column, the filter icon appears in the column header and it can be clicked by the user at runtime to show the popup for filter condition entry.</div><div><br></div><div>Programmatic filtering can be applied with ApplyFilter.</div><div><br></div><div>For a text column:</div><div><pre class="delphi" name="code">procedure TForm1.btnFilterTextColumnClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.FindColumn(&#39;TextColumn&#39;).ApplyFilter(
    TDGFilterType.gftText,
    TDGFilterOperation.foContains,
    &#39;#1&#39;);
end;</pre><br></div><div>For a numeric column:</div><div><pre class="delphi" name="code">procedure TForm1.btnFilterNumberColumnClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.FindColumn(&#39;NumberColumn&#39;).ApplyFilter(
    TDGFilterType.gftNumber,
    TDGFilterOperation.foGreaterThanOrEqual,
    25);
end;</pre></div><div>This gives full control from application code. A filter can be applied from a button, from a form field, from a saved search, or from any other application state.</div><div><br></div><div>Filters can also be cleared globally:</div><div><pre class="delphi" name="code">procedure TForm1.btnClearFiltersClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.ClearFilters;
end;</pre><br></div><div>Or per column:</div><div><pre class="delphi" name="code">procedure TForm1.btnClearTextColumnClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.FindColumn(&#39;TextColumn&#39;).ClearFilter;
end;

procedure TForm1.btnClearNumberColumnClick(Sender: TObject);
begin
  WebDataGrid1.ColumnDefs.FindColumn(&#39;NumberColumn&#39;).ClearFilter;
end;</pre><br></div><div>This makes filtering easy to combine with application-specific actions such as "show open orders", "show high priority tickets" or "show customers from this region".</div><div><br></div><h4>Pagination</h4><div>When a grid contains many records, pagination keeps the user interface manageable. Instead of showing all rows at once, the grid can display a page of records and provide navigation between pages.</div><div><img src="https://www.tmssoftware.com/site/img/blog/webdatagridpaging.png" style="width: 607px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br></div><div>The record paging demo fills the grid with 300 records:</div><div><pre class="delphi" name="code">procedure TForm1.WebFormCreate(Sender: TObject);
var
  Data, IStr: String;
  I: Integer;
begin
  Data := &#39;&#39;;
  for I := 1 to 300 do
  begin
    IStr := IntToStr(I);
    Data := Data + IStr + &#39;,"Item #&#39; + IStr + &#39;","Item #&#39; + IStr + &#39;"&#39; + #$D#$A;
  end;

  WebDataGrid1.LoadFromCSVString(Data);
  UpdatePagination;
end;</pre><br></div><div>Pagination is controlled through a small set of properties:</div><div><pre class="delphi" name="code">procedure TForm1.UpdatePagination;
begin
  WebDataGrid1.Pagination := true;
  WebDataGrid1.PaginationPageSizeSelector := &#39;20,40,80&#39;;
  WebDataGrid1.PaginationPageSize := 40;
end;</pre></div><div><br></div><div>WebDataGrid.PaginationPageSizeSelector is a comma separated list of the page sizes a user can select from.<br><br></div><div>This gives users a simple way to choose how many records they want to see on each page. Set the selector to 0 when the page size selector should be hidden.</div><div><br></div><div>For data-aware applications using TWebDBDataGrid, pagination is especially useful when the dataset represents a larger result set. The grid remains responsive, and the user keeps a clear sense of where they are in the data.</div><div><br></div><h4>Combining the Features</h4><div>The real strength of TWebDataGrid and TWebDBDataGrid is that these features are not isolated.</div><div><br></div><div>A business grid will often use several of them together:</div><div><br></div><ul><li>Editable columns for quick data entry</li><li>Sorting for comparing records</li><li>Filtering for narrowing down large lists</li><li>Pagination for keeping the UI fast and readable</li></ul><div>For example, an order overview can allow editing the order status, filtering on customer name, sorting by order date and paging through the result set. A product list can use numeric filtering for stock levels, combobox editing for categories and pagination for larger catalogs.</div><div><br></div><div>All of this remains close to the Delphi style of development: configure columns, set properties, connect events and write clear Object Pascal code.<br><h3><br></h3></div><h4>Conclusion</h4><div>TWebDataGrid and TWebDBDataGrid bring modern grid behavior to <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> applications while preserving the RAD development experience Delphi developers know well.</div><div><br></div><div>With only a few properties and method calls, you can turn a basic grid into an interactive data workspace:</div><div><br></div><div>Editable, CellDataType and EditModeType enable rich cell editing</div><div>Sortable and Sort provide user-driven and programmatic sorting</div><div>Filter, ApplyFilter and ClearFilter make it easy to focus data</div><div>Pagination, PaginationPageSize and PaginationPageSizeSelector keep larger datasets manageable</div><div>Whether data is loaded directly into TWebDataGrid or exposed through a dataset in TWebDBDataGrid, the result is the same: a fast, familiar and highly productive way to build data-centric web applications with Delphi and TMS WEB Core.<br><br><h4>Video</h4></div><div><iframe frameborder="0" src="//www.youtube.com/embed/ocht5OmZdcA" width="640" height="360" class="note-video-clip"></iframe></div><div><br></div><div><h3 style="color: rgb(51, 51, 51);">Beta available now</h3><p style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;"><img src="https://www.tmssoftware.com/site/img/blog/betasoft.avif" alt="TMS Software Delphi  Components tmswebcore" style="max-width: 100%; width: 235.47px; height: 235.094px;"></p><p style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;">The TMS WEB Core v3.0 beta is available now for all <a href="https://www.tmssoftware.com/site/tmsallaccess.asp" target="_blank" style="color: rgb(0, 158, 227); text-decoration: none;">TMS ALL-ACCESS</a> users and can be downloaded from the My Products page after login on our website!</p></div>
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Wrapping Your Own REST API with TMS FNC Cloud Pack]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2477</link>
		<author>Bart Holvoet</author>
		<pubDate>Wed, 27 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		<p>
		
		
		
		
		
		</p><p><a href="https://www.tmssoftware.com/site/tmsfnccloudpack.asp" style="background-color: white; font-family: sans-serif;">TMS FNC Cloud Pack</a> is well known for its ready-made components to services like <b>Google Drive</b>, <b>Microsoft Outlook Mail</b>. What is often overlooked is that the same package also gives you a lightweight, asynchronous HTTP engine called <code>TTMSFNCCloudBase</code>. With just a few lines of code, this engine lets you talk to any REST API, even when there is no pre-built wrapper available.</p>
<p>In this blog post we will show how easy this is by building a working currency converter from scratch in a short time. Thanks to the FNC framework, the same code runs on <b>VCL</b>, <b>FMX</b>, and <b>TMS WEB Core</b>.</p><p><br></p><p><span style="color: inherit; font-family: Montserrat, sans-serif; font-size: 24px; font-weight: bold;">Picking an API</span></p>
<p>If you are looking for a free API to experiment with, <a href="https://www.freepublicapis.com/tags/development">Free Public APIs</a> is a handy curated directory. For this example we picked <strong>UniRateAPI</strong>, a currency rate service with a generous free tier and a clean REST interface.</p>
<p>Create a free account at <a href="https://unirateapi.com/">unirateapi.com</a> and copy your API key from the dashboard. One detail to keep in mind right away: the API requires a <code>User-Agent</code> header on every request and will return HTTP 403 if it is missing. We will come back to this in step 3.<br><br></p>
<h3>The Building Block: <code>TTMSFNCCloudBase</code></h3>
<p>For APIs that authenticate with a simple key (rather than OAuth 2.0), <code>TTMSFNCCloudBase</code> is all you need. The workflow is the same every time. You create an instance, populate its <code>Request</code> object with the host, path, query, headers, and HTTP method, and then call <code>ExecuteRequest</code> with a callback procedure. The call returns immediately and the response arrives on the callback, which keeps the UI fully responsive.</p><p><br></p><p><img src="https://www.tmssoftware.com/site/img/blog/tms-fnc-cloud-pack-diagram.svg" style="width: 680px;" alt="TMS Software Delphi  Components tmsfnccloudpack"><br></p><p><br></p>
<h3><span style="color: inherit;">Step-by-Step Implementation</span></h3>
<h4>1. Create the instance</h4>
<pre class="delphi" name="code">procedure TForm1.FormCreate(Sender: TObject);
begin
  FCloudService := TTMSFNCCloudBase.Create;
end;
</pre>
<p>No setup is needed at construction time. The same instance can be reused for any number of subsequent requests.<br><br></p>
<h4>2. Build the request</h4>
<p>The conversion endpoint is <code>GET /api/rates</code> on <code>https://api.unirateapi.com</code>. The required query parameters are <code>api_key</code>, <code>amount</code>, <code>from</code>, <code>to</code>, and <code>format</code>.</p>

The full UniRateAPI endpoint reference is available at <a href="https://unirateapi.com/apidocs/">unirateapi.com/apidocs</a>.<p></p><p>

</p><pre class="delphi" name="code">FCloudService.Request.Clear;
FCloudService.Request.Name   := &#39;GET CURRENCY CONVERT&#39;;
FCloudService.Request.Host   := &#39;https://api.unirateapi.com&#39;;
FCloudService.Request.Path   := &#39;/api/rates&#39;;
FCloudService.Request.Query  := &#39;api_key=&#39;  + TTMSFNCUtils.URLEncode(edAPIKey.Text)
                               + &#39;&amp;amount=&#39; + edAmount.Text
                               + &#39;&amp;from=&#39;   + edFrom.Text
                               + &#39;&amp;to=&#39;     + edTo.Text
                               + &#39;&amp;format=json&#39;;
FCloudService.Request.Method := rmGET;
</pre>
<p><code>TTMSFNCUtils.URLEncode</code> (from TMS FNC Core) safely escapes any special characters in the API key.<br><br></p>
<h4>3. Add the required <code>User-Agent</code> header</h4>
<p>As mentioned earlier, UniRateAPI will reject requests without a <code>User-Agent</code> header. Adding one takes a single line:</p>
<pre class="delphi" name="code">FCloudService.Request.AddHeader(&#39;User-Agent&#39;, &#39;TMSFNCCloudAPIWrapper&#39;);
</pre>
<p><code>AddHeader</code> can be called repeatedly to attach any number of custom headers. This is useful whenever an API requires bearer tokens, header-based API keys, or vendor-specific values.<br><br></p>
<h4>4. Execute asynchronously</h4>
<pre class="delphi" name="code">FCloudService.ExecuteRequest(DoAPIRequestResult);
</pre>
<p>The call is non-blocking. The UI thread stays free while the HTTP round-trip completes in the background.<br><br></p>
<h4>5. Handle the response</h4>
<p>The callback receives a <code>TTMSFNCCloudBaseRequestResult</code> record that carries the HTTP status code and the raw response body. When the request succeeds, the JSON payload is parsed with <code>TJSONObject.ParseJSONValue</code>, and the relevant numeric values are extracted and assigned to the <code>TLabel</code> controls on the form so the user sees the converted amount and the exchange rate. The JSON object is freed in a <code>try..finally</code> block to avoid memory leaks.</p>
<pre class="delphi" name="code">procedure TForm1.DoAPIRequestResult(
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  LJson: TJSONObject;
begin
  if ARequestResult.ResponseCode = 200 then
  begin
    LJson := TJSONObject.ParseJSONValue(ARequestResult.ResultString) as TJSONObject;
    if Assigned(LJson) then
    try
      lbToAmount.Text := FormatFloat(&#39;0.00&#39;,
        (LJson.GetValue(&#39;result&#39;) as TJSONNumber).AsDouble);
      lbRate.Text := FormatFloat(&#39;0.00&#39;,
        (LJson.GetValue(&#39;rate&#39;) as TJSONNumber).AsDouble);
    finally
      LJson.Free;
    end;
  end;
end;
</pre>
<p>A successful response looks like this:</p>
<pre><code class="language-json">{
  "base": "USD",
  "target": "EUR",
  "rate": 0.9187,
  "amount": 100,
  "result": 91.87
}
</code></pre>
<p>The <code>result</code> field holds the converted amount and is assigned to <code>lbToAmount</code>, while the <code>rate</code> field holds the exchange rate used and is assigned to <code>lbRate</code>. Both values pass through <code>FormatFloat</code> with the <code>&#39;0.00&#39;</code> mask so they always display with two decimal places.&nbsp;<br><br></p>

<h4>6. Clean up</h4>
<pre class="delphi" name="code">procedure TForm1.FormDestroy(Sender: TObject);
begin
  FCloudService.Free;
end;
</pre>
<br><p><a href="https://download.tmssoftware.com/samples/tmsfnccloudapiwrapper.zip">Download</a> the complete sample project shown in this post. 

<br><br>
</p><h3>Conclusion</h3>
<p>With a handful of lines of Delphi code, <a href="https://www.tmssoftware.com/site/tmsfnccloudpack.asp">TMS FNC Cloud Pack</a> gives you access to any REST API. The same <code>TTMSFNCCloudBase</code> class that powers the built-in <b>Google Drive</b>, <b>Microsoft Outlook Mail</b> wrappers is available to you for any service you want to integrate, whether public or in-house. And when an API uses OAuth 2.0 instead of a static key, <code>TTMSFNCCloudOAuth</code> picks up where this example leaves off and handles the full three-step authentication flow for you.&nbsp;</p>

		
		
		
		
		
		<p></p>
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿TMS Data Modeler source code now available!]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2476</link>
		<author>Wagner Landgraf</author>
		<pubDate>Mon, 18 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		<p>After more than two decades of life as a closed-source product, the full source code of <a href="https://www.tmssoftware.com/site/tmsdm.asp">TMS Data Modeler</a> is now publicly available on GitHub:</p>

<p><strong><a href="https://github.com/tmssoftware/datamodeler">github.com/tmssoftware/datamodeler</a></strong></p>

<p>This post is a quick tour of what the release means, what&#39;s in the repo, and why opening it up took more than just flipping a switch.&nbsp;</p><p><b>Don&#39;t forget to start the repository!</b></p><p><br></p>

<h3>A quick refresher on TMS Data Modeler</h3>

<p>TMS Data Modeler is a visual database design tool for Windows, written in Delphi.</p>

<p><img src="https://www.tmssoftware.com/site/img/screen_diagram.png" alt="TMS Data Modeler diagram editor"></p>

<p>In a nutshell, it lets you:</p>

<ul>
  <li>Design databases visually with entity-relationship diagrams</li>
  <li>Generate DDL scripts to create databases</li>
  <li>Generate ALTER scripts to update existing databases between versions</li>
  <li>Reverse-engineer existing databases</li>
  <li>Compare versions and projects</li>
  <li>Archive history and validate models</li>
  <li>Convert models between different database systems</li>
</ul>

<p>Supported databases: SQL Server, Firebird, Oracle, PostgreSQL, MySQL, and SQLite.</p>

<p>And for <a href="https://www.tmssoftware.com/site/aurelius.asp">TMS Aurelius</a> users &#151; the ORM framework for Delphi &#151; Data Modeler is the most natural way to work in a <strong>database-first</strong> style: model your schema visually, then generate the Aurelius entity-mapped classes directly from the model.</p><p><br></p>

<h3>A bit of history</h3>

<p>Data Modeler has been around far longer than its current name suggests:</p>

<ul>
  <li><strong>Late 1990s / early 2000s</strong> &#151; originally developed by a Brazilian company as an internal tool. It wasn&#39;t even primarily a modeling tool back then: it was a CRUD form generator, and the database modeling part existed only to feed metadata into the form generation.</li>
  <li><strong>Mid 2000s</strong> &#151; acquired by Devgems and rebranded as DevGems Data Modeler.</li>
  <li><strong>Later</strong> &#151; acquired by TMS Software and re-released as TMS Data Modeler.</li>
</ul>

<p>More than twenty years of evolution land in the repository you can now <code>git clone</code>.</p><p><br></p>

<h3>Why we&#39;re releasing the source</h3>

<p>Several motivations led to opening up the source:</p>

<ul>
  <li><strong>Transparency</strong> &#151; customers can read, audit, and trust the code they rely on.</li>
  <li><strong>Flexibility</strong> &#151; teams can adapt the tool to specific needs that wouldn&#39;t make sense as features in the mainstream product.</li>
  <li><strong>Community contributions</strong> &#151; pull requests are welcome, and improvements from the community flow back to all users.</li>
  <li><strong>Ecosystem alignment</strong> &#151; it brings Data Modeler in line with the rest of the TMS BIZ products, where source access is the norm.</li>
  <li><strong>Learning</strong> &#151; both as a way to understand Data Modeler internals, and as a real-world example of a non-trivial Delphi application built on top of TMS components.</li>
</ul>

<p>That last point deserves its own section.</p><p><br></p>

<h3>A real-world example of TMS components in action</h3>

<p>If you&#39;ve ever wanted to see how TMS components fit together in a serious, production-grade Delphi application, the repository is a good place to read code. Among other things, Data Modeler uses:</p>

<ul>
  <li><strong><a href="https://www.tmssoftware.com/site/diagram.asp">TMS Diagram Studio</a></strong> &#151; powers the entity-relationship diagram editor: the canvas, shapes, connectors, and the whole visual editing experience.</li>
  <li><strong><a href="https://www.tmssoftware.com/site/scriptstudiopro.asp">TMS Scripter</a></strong> &#151; the entire scripting extensibility layer. Users can write scripts that automate tasks against the model, and that fully customize the class-generation output.</li>
  <li><strong><a href="https://www.tmssoftware.com/site/aurelius.asp">TMS Aurelius</a></strong> &#151; used specifically in the export / code-generation task, to reuse types declared in Aurelius units when emitting entity classes. (Data Modeler does not use Aurelius as its own persistence layer.)</li>
  <li><strong><a href="https://www.tmssoftware.com/site/tmsvcluipack.asp">TMS VCL UI Pack</a></strong> &#151; the entire desktop UI: ribbon, menus, edits, grid, and the rest of the controls that make up the application&#39;s look and feel.</li>
</ul>

<p>Plus a non-TMS dependency: <a href="https://github.com/JAM-Software/Virtual-TreeView">JAM Software Virtual TreeView</a>.</p>

<p>For a Delphi developer evaluating these components, reading how they&#39;re wired up in a real product is often more useful than any tutorial.</p><p><br></p>

<h3>What it took to get here</h3>

<p>Releasing the source of a closed-source product is not just flipping a visibility flag. A closed-source codebase grows under the assumption that nobody else will ever read it &#151; and that assumption shows up in lots of small places.</p>

<p>To get Data Modeler ready for the public, we did a substantial pass over the code:</p>

<ul>
  <li>Reviewed and sanitized the entire codebase</li>
  <li>Reorganized units into category folders &#151; <code>Model</code>, <code>Connectors</code>, <code>Dialogs</code>, and others. Before the cleanup, every unit lived flat in a single folder</li>
  <li>Broke remaining circular unit references so the structure makes sense as you navigate it</li>
  <li>Translated remaining Portuguese comments and identifiers (leftovers from the tool&#39;s origins)</li>
  <li>Removed several third-party dependencies, both to reduce licensing surface and to make ongoing upgrades to newer Delphi versions easier</li>
  <li>Removed the internal license-control machinery &#151; no longer meaningful with the source in the open</li>
  <li>Rebuilt the full CI/CD deployment pipeline</li>
</ul>

<p>One deliberate non-decision: we did <strong>not</strong> rename units to follow a stricter naming convention. The folder structure already conveys scope, and renaming hundreds of units would be a lot of churn for very little gain. Over time, individual units may get renamed as we revisit them, but no top-down rename was warranted.</p><p>Given all the effort above, this new milestone of source code availability, the removal/refactor of several parts of application (no more licensing tool, splash screen removed, automatic updates disabled, among others), we updated the version number and the current available source code, binary and installers are now released as <b>TMS Data Modeler 4.0</b></p><p><b><br></b></p>

<h3>Built with TMS Smart Setup</h3>

<p>The repository is fully prepared for TMS Smart Setup, which gives you two convenient flows.</p>

<p><strong>Build from the command line</strong> &#151; from the repository root, run:</p>

<pre><code>tms restore tms.snapshot.yaml -skip-register</code></pre>

<p>Smart Setup resolves all third-party dependencies and builds the <code>dm.exe</code> binary.</p>

<p><strong>Set up for development in the IDE</strong> &#151; running <code>tms restore</code> from an existing valid Smart Setup folder installs the required components into the IDE, so you can open <code>dm/dm.dproj</code> in Delphi 13 to build, debug, and explore the code interactively.</p>

<p>For end users who simply want to install the tool, the new CI/CD pipeline publishes installer binaries automatically to the GitHub <a href="https://github.com/tmssoftware/datamodeler/releases">Releases</a> page &#151; no source involved.</p><p><br></p>

<h3>A note on licensing</h3>

<p>TMS Data Modeler is <strong>source-available</strong>, not open source. In short:</p>

<ul>
  <li>Non-commercial use (learning, evaluation, education, open-source projects) does not require a paid license</li>
  <li>Commercial use still requires a paid license, available from the <a href="https://www.tmssoftware.com/site/tmsdm.asp">product page</a></li>
  <li>Contributions are accepted under the terms in the repository&#39;s <a href="https://github.com/tmssoftware/datamodeler/blob/main/LICENSE.txt">LICENSE.txt</a></li>
</ul>

<p>Support continues through the <a href="https://support.tmssoftware.com/c/business/tms-data-modeler/35">TMS Support Center</a> as before.</p><p><br></p>

<h3>Where to go from here</h3>

<ul>
  <li>Repository: <a href="https://github.com/tmssoftware/datamodeler">github.com/tmssoftware/datamodeler</a>. <b>Give a star to it!</b></li>
  <li>Product page: <a href="https://www.tmssoftware.com/site/tmsdm.asp">tmssoftware.com/site/tmsdm.asp</a></li>
  <li>Documentation: <a href="https://doc.tmssoftware.com/biz/datamodeler/">doc.tmssoftware.com/biz/datamodeler</a></li>
</ul>

<p>Clone it, build it, explore the code, file a PR &#151; or just keep using the binary releases. The choice is yours, and that&#39;s the point.</p>

		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Next Generation Data Grid for Delphi: Headless Data Layer]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2474</link>
		<author>Pieter Scheldeman</author>
		<pubDate>Fri, 15 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		<p><span style="font-family: Montserrat, sans-serif; font-size: 18px; font-weight: bold;">Intro</span></p>
<p>Not every data operation belongs on screen. Sometimes you need to load a data set,
filter it, sort it, group it, calculate summaries, and export the result without showing
a grid at all.</p>

<p>That is where the headless data layer in <a href="https://www.tmssoftware.com/site/blog.asp?post=1256" target="_blank">TMS FNC Data Grid </a>becomes especially useful. With <code>TTMSFNCDataGridData</code>, the same engine that powers
the visual <code>TTMSFNCDataGrid</code> can be used independently in services, background jobs,
console tools, tests, batch exports, or startup data preparation routines.</p>

<p>In other words: you can use the grid&#39;s data intelligence without needing the grid&#39;s
visual surface.</p><p><br></p>

<h4>Why Use a Headless Data Layer?</h4>
<p>A visual grid is perfect when users need to inspect and interact with data. But many
application workflows happen before, after, or completely outside the UI:</p>

<ul>
<li><p><strong>Background processing:</strong> load data, transform it, and prepare results while the UI remains responsive</p></li>
<li><p><strong>Batch exports:</strong> generate filtered CSV files without creating a form or control</p></li>
<li><p><strong>Automated tests:</strong> verify sorting, filtering, grouping, and aggregation logic without UI automation</p></li>
<li><p><strong>Services and console apps:</strong> reuse familiar grid data operations in non-visual applications</p></li>
</ul>

<p>The important part is that <code>TTMSFNCDataGridData</code> owns the data operations. Methods
such as <code>Filter.Add</code>, <code>ApplyFilter</code>, <code>Sort</code>, <code>Group</code>,
<code>SaveToCSVData</code>, and the typed accessors like <code>Strings[]</code>,
<code>Floats[]</code>, and <code>Ints[]</code> are available directly on the data layer.</p><p><br></p>

<h4>Creating the Data Object</h4>
<p>Because <code>TTMSFNCDataGridData</code> is a regular Delphi object, it can be created anywhere
you would normally create an object. There is no need for a form, parent control, or
rendering context.</p>

<pre class="delphi" name="code">type
  TMyForm = class(TForm)
  private
    FData: TTMSFNCDataGridData;
  end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  FData := TTMSFNCDataGridData.Create;
end;

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  FData.Free;
end;
</pre>

<p>That small setup gives you a reusable in-memory data layer with the same core operations
you would expect from the visual grid.</p><p><br></p>

<h4>Loading Data</h4>
<p>The headless layer supports the same practical loading scenarios as the grid. You can
load JSON, load CSV, or fill cells manually when you want full control.</p>

<pre class="delphi" name="code">// From JSON
FData.Options.IO.StartRow := 1;
FData.LoadFromJSONData(
  JSONPath,
  &#39;&#39;, &#39;&#39;,
  [&#39;id&#39;, &#39;name&#39;, &#39;category&#39;, &#39;price&#39;, &#39;stock&#39;, &#39;is_organic&#39;, &#39;is_vegan&#39;]);

// From CSV
FData.LoadFromCSVData(&#39;products.csv&#39;, &#39;,&#39;);
</pre>

<p>Setting <code>Options.IO.StartRow</code> to <code>1</code> before loading JSON is a useful detail:
row 0 can receive the JSON key names as column headers, while the actual records start
below that fixed header row.</p>

<p>Manual population is equally direct:</p>

<pre class="delphi" name="code">FData.ColumnCount   := 3;
FData.FixedRowCount := 1;
FData.RowCount      := 4;

FData.Cells[0, 0] := &#39;ID&#39;;
FData.Cells[1, 0] := &#39;Name&#39;;
FData.Cells[2, 0] := &#39;Price&#39;;

FData.Cells[0, 1] := 1;
FData.Cells[1, 1] := &#39;Widget A&#39;;
FData.Cells[2, 1] := 9.99;
</pre><p><br></p>

<h4>Filtering Without a Grid</h4>
<p>Filtering works the same way it does with the visual grid. Add one or more filter
conditions, apply them, and then work with the displayed rows.</p>

<pre class="delphi" name="code">FData.Filter.Clear;
FData.Filter.Add(COL_CATEGORY, gftEqual, &#39;Meat&#39;);
FData.ApplyFilter;
</pre>

<p>After calling <code>ApplyFilter</code>, rows that do not match are hidden from the active view.
You can use <code>IsRowDisplayed</code> when iterating the data:</p>

<pre class="delphi" name="code">var Count := 0;

for var R := FData.FixedRowCount to FData.RowCount - 1 do
  if FData.IsRowDisplayed(R) then
    Inc(Count);

Log(Format(&#39;%d matching rows&#39;, [Count]));
</pre>

<p>To return to the full data set, call <code>RemoveFilter</code>.</p>

<pre class="delphi" name="code">FData.RemoveFilter;
</pre><p><br></p>

<h4>Sorting the Active Result</h4>
<p>Sorting is just as compact. The data layer can sort a column in ascending or descending
order without requiring any visual repaint cycle.</p>

<pre class="delphi" name="code">FData.Sort(COL_PRICE, gsdDescending);
</pre>

<p>Sorting and filtering work together. When a filter is active, the sort operation reorders
the displayed rows while keeping filtered-out rows hidden.</p><p><br></p>

<h4>Grouping and Aggregations</h4>
<p>The headless layer is also useful when you need summary data. You can group by a column
and calculate values such as count, sum, average, minimum, or maximum.</p>

<pre class="delphi" name="code">FData.Group(COL_CATEGORY);
FData.GroupCount(COL_CATEGORY);
FData.GroupSum(COL_PRICE);
</pre>

<p>Once grouped, the result contains group header rows and summary rows that can be inspected
programmatically:</p>

<pre class="delphi" name="code">for var R := FData.FixedRowCount to FData.RowCount - 1 do
begin
  if FData.IsRowNode(R) then
    Log(FData.Strings[FData.FixedColumnCount, R])
  else if FData.IsRowSummary(R) then
    Log(Format(&#39;  count=%.0f   sum=$%.2f&#39;,
      [FData.Floats[COL_CATEGORY, R], FData.Floats[COL_PRICE, R]]));
end;

FData.Ungroup;
</pre>

<p>This makes it convenient to build reporting features where the final output is a log,
CSV file, dashboard feed, or another data structure rather than an interactive grid.</p><p><br></p>

<h4>Exporting the Result</h4>
<p>After loading, filtering, sorting, and grouping, you can export the processed result
directly to CSV.</p>

<pre class="delphi" name="code">FData.SaveToCSVData(&#39;output.csv&#39;, &#39;,&#39;);
</pre>

<p>This is a practical pattern for server-side export buttons, scheduled reporting tasks,
or command-line tools that need the same data behavior as your application&#39;s UI.</p><p><br></p>

<h4>Reading Typed Values</h4>
<p>When looping over rows, use the typed accessors to keep your code clear and efficient.
Instead of reading everything as text and converting manually, read the expected data
type directly from the layer.</p>

<pre class="delphi" name="code">var Name  := FData.Strings[COL_NAME,  Row];
var Price := FData.Floats [COL_PRICE, Row];
var Stock := FData.Ints   [COL_STOCK, Row];
</pre>

<p>For tight processing loops, this keeps the intent of the code obvious: names are strings,
prices are floating point values, stock counts are integers.</p><p><br></p>

<h4>Showing the Data Later</h4>
<p>Using the headless layer does not lock you into a non-visual workflow. If the processed
data later needs to be shown in a <code>TTMSFNCDataGrid</code>, you can load the same source into
the grid or copy values into the visual component.</p>

<pre class="delphi" name="code">// Let the grid load its own visual copy
TMSFNCDataGrid1.LoadFromJSONData(
  JSONPath,
  &#39;&#39;, &#39;&#39;,
  [&#39;id&#39;, &#39;name&#39;, &#39;price&#39;]);
</pre>

<p>For database-backed scenarios, <code>TTMSFNCDataGridDatabaseAdapter</code> can be used with a
shared <code>TDataSet</code>. You can bind that adapter to a visual grid, or bind it directly to
<code>TTMSFNCDataGridData</code> when the workflow is headless and the data operations matter
more than immediate visual interaction.</p><p><br></p>

<h4>Binding a Dataset</h4>
<p>For a non-visual workflow, assign the database adapter to the data layer. The result is still a dataset-backed data engine, but without any
renderer or control on screen.</p>

<pre class="delphi" name="code">procedure TMyService.LoadProducts;
begin
  FData := TTMSFNCDataGridData.Create;

  Adapter.DataSource := DataSource1;     // TDataSource attached to FDQuery1
  Adapter.AutoCreateColumns := True;

  FData.Adapter := Adapter;

  FDQuery1.Open;                         // records are loaded into FData

  FData.Filter.Clear;
  FData.Filter.Add(COL_CATEGORY, gftEqual, &#39;Meat&#39;);
  FData.ApplyFilter;

  FData.Sort(COL_PRICE, gsdDescending);
  FData.SaveToCSVData(&#39;filtered-products.csv&#39;, &#39;,&#39;);
end;
</pre>

<p>This is the database equivalent of loading JSON or CSV into <code>FData</code>: once the
records are in the data layer, the rest of the processing code can stay the same. For
exports, reports, validation jobs, and tests, that can be cleaner than creating a visual
grid just to reach the data operations.</p><p><br></p>

<h4>Choosing Fields and Column Behavior</h4>
<p>Automatic columns are convenient during prototyping, but production screens often need
a more curated shape: hide technical fields, reorder columns, use friendly headers, or
render specific field types with richer UI.</p>

<p>The adapter column collection lets you decide exactly which fields appear and how they
are presented:</p>

<pre class="delphi" name="code">Adapter.AutoCreateColumns := False;

with Adapter.Columns.Add do
begin
  FieldName := &#39;ProductName&#39;;
  Header := &#39;Product&#39;;
end;

with Adapter.Columns.Add do
begin
  FieldName := &#39;Price&#39;;
  Header := &#39;Unit Price&#39;;
end;
</pre>

<p>Individual adapter columns can also be configured for more specialized rendering:</p>

<pre class="delphi" name="code">Adapter.Columns[2].HTMLTemplate    := &#39;&lt;b&gt;{Name}&lt;/b&gt;&#39;;
Adapter.Columns[3].PictureField    := True;   // BLOB field as image
Adapter.Columns[4].ProgressField   := True;   // numeric field as progress bar
Adapter.Columns[5].UseLookupEditor := True;   // lookup field as drop-down
</pre>

<p>For conversion logic, the adapter exposes <code>OnFieldToData</code> and
<code>OnDataToField</code>. That gives you a clean place to format database values before
they enter the grid, or to translate edited grid values before they are posted back to
the dataset.</p><p><br></p>

<h4>A Complete Processing Flow</h4>
<p>A typical headless workflow can be surprisingly small: load records, apply business
filters, sort the result, and export it.</p>

<pre class="delphi" name="code">FData.ClearData;
FData.FixedRowCount       := 1;
FData.Options.IO.StartRow := 1;

FData.LoadFromJSONData(
  JSONPath,
  &#39;&#39;, &#39;&#39;,
  [&#39;id&#39;, &#39;name&#39;, &#39;category&#39;, &#39;price&#39;, &#39;stock&#39;]);

FData.Filter.Clear;
FData.Filter.Add(COL_CATEGORY, gftEqual, &#39;Meat&#39;);
FData.ApplyFilter;

FData.Sort(COL_PRICE, gsdDescending);
FData.SaveToCSVData(&#39;filtered-products.csv&#39;, &#39;,&#39;);
</pre>

<p>No visual control is required, but the data behavior remains consistent with the grid
users see elsewhere in the application.</p><p></p><h4><br></h4><h4>Sample</h4><p></p><p><font color="#000000"><a href="http://tmssoftware.com/download/DataLayerDemo.zip" target="_blank"></a><a href="https://www.tmssoftware.com/download/DataLayerDemo.zip" target="_blank">https://www.tmssoftware.com/download/DataLayerDemo.zip</a></font></p>

<h4><br></h4><h4>Conclusion</h4>
<p><code>TTMSFNCDataGridData</code> gives you the data-processing side of
<code>TTMSFNCDataGrid</code> as a standalone layer. You can load JSON or CSV, apply filters,
sort rows, group data, calculate aggregations, read typed values, and export the result
without placing a grid on screen.</p>

<p>For applications that need background exports, automated tests, reporting pipelines,
or non-visual data preparation, this is a clean way to reuse the same proven data layer
that powers the visual grid. It keeps your logic testable, your UI optional, and your
data workflow ready for both desktop and web applications.</p>

<ul>
</ul>

		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Connecting TWebDataGrid & TWebDBDataGrid to Your Data]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2475</link>
		<author>Bruno Fierens</author>
		<pubDate>Thu, 14 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		
		
		
		
		
		
		<h3><img src="https://www.tmssoftware.com/site/img/blog/webdatagridbanner.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></h3><h3>Introduction</h3><div>One of the most common questions Delphi developers ask when building modern web applications with <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> is simple: What is the best way to connect data to a web grid component?</div><div><br></div><div>With the introduction of the new <b>TWebDataGrid </b>and <b>TWebDBDataGrid </b>controls, developers now have several powerful options available, ranging from fully programmatic data access to direct live cloud-backed datasets.</div><div><br></div><div>In this article, we&#146;ll explore the different approaches for loading, displaying, editing, and managing data in TWebDataGrid and TWebDBDataGrid, and show practical examples of how each approach can be implemented.</div><div><br></div><div><h3>Different Ways to Connect Data</h3><div>This&nbsp;article discusses four primary ways to connect data to TWebDataGrid and TWebDBDataGrid:</div><div><br></div><ul><li>Programmatic data access</li><li>Loading JSON or CSV files</li><li>Using a custom data adapter</li><li>Connecting directly to datasets</li></ul><div>Each of these methods will be discussed in this article.</div><div><br></div><div><br></div><h3>1. Programmatic Data Access</h3><div>One of the simplest approaches is to fully manage the grid content in Delphi code.</div><div><br></div><div>This gives complete control over:</div><div><br></div><ul><li>Column definitions</li><li>Editors</li><li>Validation</li><li>Cell formatting</li><li>Calculated values</li><li>Dynamic row creation</li></ul><div>The demo application initializes the grid entirely in Pascal code:</div><div><pre class="delphi" name="code">procedure TForm2.InitGrid;
begin
  WebDataGrid1.Clear;
  WebDataGrid1.ColumnDefs.Clear;

  WebDataGrid1.ColumnDefs.Add;
  WebDataGrid1.ColumnDefs.Add;
  WebDataGrid1.ColumnDefs.Add;
  WebDataGrid1.ColumnDefs.Add;

  WebDataGrid1.ColumnDefs[0].HeaderName := &#39;Product&#39;;
  WebDataGrid1.ColumnDefs[0].EditModeType := cetCombobox;
  WebDataGrid1.ColumnDefs[0].Editable := true;
  WebDataGrid1.ColumnDefs[0].Field := &#39;Product&#39;; 
  ....
end;</pre><br></div><div><div>This example dynamically creates grid columns and configures the first column as a combobox editor.</div><div><br></div><div>The combobox values are also defined programmatically:<br><pre class="delphi" name="code" style="line-height: 1.42857;">var
  dgs: TDGSelectCellEditorCollectionItem;

  begin
    dgs := WebDataGrid1.ColumnDefs[0].SelectOptions.Add;
    dgs.Value := &#39;Delphi&#39;;
    dgs := WebDataGrid1.ColumnDefs[0].SelectOptions.Add;
    dgs.Value := &#39;C++Builder&#39;;
    ....
  end;</pre></div></div><div><br></div><div><span style="color: inherit; font-family: Montserrat, sans-serif; font-size: 24px; font-weight: bold;">Live calculations</span></div><div>The demo also shows how calculated fields can be implemented.</div><div><p>When a cell edit completes, totals are recalculated dynamically:</p><p></p><pre class="delphi" name="code">procedure TForm2.Recalc(RowIndex: integer);
var
  q,p: integer;
begin
  p := integer(WebDataGrid1.CellByName[RowIndex, &#39;Price&#39;]);
  q := integer(WebDataGrid1.CellByName[RowIndex, &#39;Quantity&#39;]);
  WebDataGrid1.CellByName[RowIndex, &#39;Total&#39;] := p * q;
end;</pre><p></p><p>This style of programming feels very natural to Delphi developers and enables highly interactive applications with minimal code. Note that CellByName is accessible as TJSValue, so you can assign an integer, float, string, boolean, ...</p><p><br></p><h3>Dynamically Adding Rows and setting Cell Data</h3><p>Adding rows is equally straightforward:</p><p></p><pre class="delphi" name="code">procedure TForm2.WebButton1Click(Sender: TObject);
begin
  WebDataGrid1.InsertNewRow;
  WebDataGrid1.CellByName[WebDataGrid1.RowCount - 1, &#39;Product&#39;] := &#39;Delphi&#39;;
  WebDataGrid1.CellByName[WebDataGrid1.RowCount - 1, &#39;Quantity&#39;] := 0;
  WebDataGrid1.CellByName[WebDataGrid1.RowCount - 1, &#39;Price&#39;] := 123;
end;</pre><p></p><p>The API remains intuitive and closely aligned with familiar Delphi development patterns.</p><p><span style="color: inherit; font-family: Montserrat, sans-serif; font-size: 24px; font-weight: bold;">Loading JSON Data</span></p><p>Modern web applications frequently consume JSON data from REST services or external APIs.</p><p><code>TWebDataGrid</code> includes built-in support for loading JSON directly.</p><p>This code snippet shows how easy this can be:</p><p></p><pre class="delphi" name="code">procedure TForm2.WebButton2Click(Sender: TObject);
begin
  WebDataGrid1.Clear;
  WebDataGrid1.ColumnDefs.Clear;

  WebDataGrid1.ColumnDefs.Add.Field := &#39;Brand&#39;;
  WebDataGrid1.ColumnDefs.Add.Field := &#39;Model&#39;;
  WebDataGrid1.ColumnDefs.Add.Field := &#39;Type&#39;;

  WebDataGrid1.LoadFromJSON(
    &#39;https://download.tmssoftware.com/tmsweb/demos/&#39; +
    &#39;TMSWEB_ResponsiveGrid/carsfull.json&#39;);
end;</pre><p></p><p>This instantly loads remote JSON data into the grid. No manual parsing is required.</p><p>This approach is ideal when:</p><ul data-spread="false"><li>Consuming REST APIs</li><li>Working with external cloud services</li><li>Loading static datasets</li><li>Building dashboard applications</li></ul><p><br></p><h3>Using an adapter&nbsp;</h3><p>With TWebDataGrid come 3 different adapters: <b>TDBXDataRestDataAdapter</b>, <b>TDBStellarRestDataAdapter </b>and <b>TDBCustomDataAdapter</b>. TDBXDataRestDataAdapter is a ready to use adapter that can connect a TWebDataGrid to data made accessible through a <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank">TMS XData</a> REST API server. TDBStellarRestDataAdapter is the same for our data in the cloud as a service solution <a href="https://stellards.io" target="_blank">https://stellards.io</a>&nbsp;<br><br>In this example, we show the generic <b>TDBCustomDataAdapter </b>that you can use to bind to any REST API.<br><br>Instead of loading everything at once, rows are fetched dynamically whenever needed.</p><p>This is especially useful for:</p><ul data-spread="false"><li>Very large datasets</li><li>Infinite scrolling</li><li>Server-side paging</li><li>Virtualized datasets</li><li>High-performance cloud applications<br><br></li></ul><p>To feed the data for the TWebDataGrid, implement the TDBCustomDataAdapter.OnGetRows event:</p><p></p><pre class="delphi" name="code">procedure TForm2.DGCustomDataAdapter1GetRows(Params: TJSGetRowsParams);
var
  I: Integer;
  RowData: TJSArray;
begin
  RowData := TJSArray._of;

  for I := Params.StartRow to Params.EndRow do
  begin
    RowData.push(new([
      &#39;Id&#39;, I + 1,
      &#39;Data&#39;, &#39;Row Number &#39; + IntToStr(I)
    ]));
  end;

  DGCustomDataAdapter1.GetRowsSuccessful(
    RowData,
    10000,
    Params);
end;</pre><p></p><p>The grid automatically retrieves the rows it needs.<br><br></p><p></p><h3>Connecting a TWebDBDataGrid to a TDataSet</h3><p></p><p>For Delphi developers who prefer a classic data-aware workflow, TWebDBDataGrid provides direct TDataSet integration.</p><p>This enables:</p><ul><li>Live editing</li><li>Field-aware controls</li><li>Existing business logic reuse</li><li>Familiar data binding</li><li>Rapid application development</li></ul><p>You can of course connect any TDataSet via a TWebDataSource to a TWebDBDataGrid. But let&#39;s in particular zoom-in on using the data in the cloud as a service offered by <a href="https://stellards.io" target="_blank">https://stellards.io</a>&nbsp; With this service, you do not need to write a REST API server yourself to connect to your data nor bother about managing the hosting of your database. StellarDS.io offers all this with a simple web interface to setup your tables and control the access.<br><br></p><p>TWebDBDataGrid can directly connect to cloud-hosted backend data using:</p><ul><li>TWebStellarDataStoreClientDataset</li><li>TWebDataSource</li><li>TWebDBDataGrid</li></ul><p>Drop these components on the form. Connect the TWebStellarDataStoreClientDataset to TWebDataSource.DataSet and connect the TWebDataSource to TWebDBDataGrid.DataSet.<br>Specify either the OAuth2 based access-token in TWebStellarDataStoreClientDataset or set the client ID &amp; client secret to follow an OAuth2 authentication &amp; authorization.&nbsp;<br>Set the TWebStellarDataStoreClientDataset.TableName to specify the StellarDS.io table you want to connect to.<br><br>After this setup, connecting is as simple as:</p><p></p><pre class="delphi" name="code">procedure TForm2.WebButton1Click(Sender: TObject);
begin
  WebStellarDataStoreClientDataset1.Active := true;
end;</pre><p></p><p>Once activated, the dataset automatically retrieves cloud data and the grid immediately displays it.<br>This gives Delphi developers the same RAD experience they are familiar with from desktop development, but now directly connected to cloud data.<br><br></p><h3>Choosing the Right Approach</h3><p>Each data connection approach serves different application scenarios.</p></div></div><table class="table table-bordered"><tbody><tr><td><b>Approach<span style="white-space: pre;">	</span></b></td><td><b>Best For</b></td></tr><tr><td>Programmatic access</td><td>Full control and dynamic UI behavior</td></tr><tr><td>JSON lor CSV oading</td><td>REST APIs and external services for viewing data</td></tr><tr><td>Custom DataAdapter</td><td>Access to a REST API (XData, StellarDS, other...) for CRUD</td></tr><tr><td>TDataSet integration</td><td>RAD data-aware business applications for CRUD</td></tr></tbody></table><div><p>The flexibility offered by TWebDataGrid and TWebDBDataGrid makes them suitable for a very wide range of modern Delphi web applications.</p><h3><br>Video</h3><p><iframe frameborder="0" src="//www.youtube.com/embed/AvrJNG2wTVE" width="640" height="360" class="note-video-clip"></iframe><br><br></p><h3 style="color: rgb(51, 51, 51);">Beta available now</h3><p style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;"><img src="https://www.tmssoftware.com/site/img/blog/betasoft.avif" alt="TMS Software Delphi  Components tmswebcore" style="max-width: 100%; width: 235.47px; height: 235.094px;"></p><p>The TMS WEB Core v3.0 beta is available now for all <a href="https://www.tmssoftware.com/site/tmsallaccess.asp" target="_blank" style="color: rgb(0, 158, 227); text-decoration: none;">TMS ALL-ACCESS</a> users and can be downloaded from the My Products page after login on our website!<br></p><p><br style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;"></p></div>
		
		
		
		
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿TMS Dashboard updated with TMS Smart Setup v3.3 support]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2473</link>
		<author>Bart Holvoet</author>
		<pubDate>Tue, 12 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		
		
		
		
		<p>
		
		
</p><p>
We are pleased to announce the release of <strong>TMS Dashboard v1.4.0.0</strong>. TMS Dashboard is a free utility for TMS Software customers that provides a central hub for managing your TMS component installations, monitoring available updates, and keeping your Delphi development environment in sync. This update delivers compatibility with the latest TMS Smart Setup v3.3 and brings an improved, reorganized options dialog for a more streamlined configuration experience.</p><p><br></p><p>
</p>

<img src="https://www.tmssoftware.com/site/img/dev/dashboard/tmsdashboard.png" alt="TMS Dashboard" style="width: 70%;">


<br><br>
<h3><span style="color: inherit;"><br></span></h3><h3><span style="color: inherit;">What&#39;s new in v1.4.0.0</span></h3>



<h4><br></h4><h4>TMS Smart Setup v3.3 compatibility</h4>

<p>
TMS Dashboard v1.4.0.0 adds compatibility with <strong>TMS Smart Setup v3.3</strong>. Since TMS Smart Setup uses an auto-update system, you are always running the latest version automatically. This update ensures TMS Dashboard stays in sync with those automatic upgrades.
</p>

<p>
For a full overview of what&#39;s new in the TMS Smart Setup 3.x series, please refer to the dedicated blog post:
</p>

<p><a href="https://www.tmssoftware.com/site/blog.asp?post=2469"><strong>Announcing SmartSetup 3.0&#133; and 3.1&#133; and 3.2&#133; and 3.3!</strong></a></p><p><a href="https://www.tmssoftware.com/site/blog.asp?post=2469"><strong><br></strong></a>
</p>

<h4>Reorganized options dialog</h4>

<p>
The TMS Dashboard Options dialog has been reorganized for clarity. Settings are now grouped more logically across the sidebar navigation, making it easier to find and configure exactly what you need.
</p>

<img src="https://www.tmssoftware.com/site/img/blog/tmsdashboard_options_tms_smart_setup.png" alt="TMS Dashboard v1.4.0.0 &#150; Reorganized Options dialog" style="width: 70%;">

<p>
</p><p><br></p><p>The sidebar navigation now clearly separates the key areas: <strong>My Account</strong>, <strong>TMS Smart Setup</strong>, <strong>Delphi Versions</strong>, <strong>Notifications</strong>, <strong>Status</strong>, and <strong>About</strong>. The <em>My Account</em> section displays a live validation indicator, confirming at a glance whether your email address and registration code are valid. The <i>TMS Smart Setup </i>section indicates if the TMS Smart Setup executable was detected correctly in the provided path.</p><p><br></p><p>
</p>

<h3>Getting the update</h3>

<p>
TMS Dashboard v1.4.0.0 is available now. The update can be downloaded directly via the built-in update link in TMS Dashboard, or from the <a href="https://www.tmssoftware.com/site/tmsdashboard.asp">TMS Dashboard product page</a>.
</p>


		
		<p></p><p><br></p>
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿TMS WEB Core v3.0 Beta: introducing the DB-aware TWebDataGrid]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2472</link>
		<author>Bruno Fierens</author>
		<pubDate>Wed, 6 May 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		
		
		
		
		
		<div><img src="https://www.tmssoftware.com/site/img/blog/webdatagridbanner.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br>We are thrilled to announce the beta release of <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core v3.0</a>.</div><div><br></div><div>This new version marks another important step forward in our mission to bring RAD-style Delphi web development to modern browser applications. With TMS WEB Core, Delphi developers can build rich client-side web applications in Object Pascal, using familiar tools, components and development patterns.</div><div><br></div><div>One of the major new features in TMS WEB Core v3.0 is the introduction of a <b>DB-aware version of TWebDataGrid: TWebDBDataGrid</b>.</div><div><br></div><div><img src="https://www.tmssoftware.com/site/img/blog/webdbdatagrid.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br></div><h3>Why TWebDBDataGrid?</h3><div>When building business applications, data is almost always at the center of the application. Whether it is customer data, orders, invoices, products, planning information or analytics, developers need a powerful way to present and manipulate structured data.</div><div><br></div><div>With TWebDataGrid, TMS WEB Core already offers a modern, high-performance grid component based on the popular MIT licenses&nbsp;<a href="https://www.ag-grid.com/" target="_blank">AgGrid</a> technology. It brings powerful grid functionality to web client applications, including sorting, filtering, formatting and advanced visualization capabilities.</div><div><br></div><div>But for Delphi developers, there is one development model that remains extremely productive and familiar: the <b>TDataSource / TDataSet</b> paradigm.</div><div><br></div><div>That is exactly where <b>TWebDBDataGrid </b>comes in.</div><div><br></div><div>It brings the power of TWebDataGrid together with the familiar Delphi database-aware architecture. This means that web client applications can now bind grid data directly to datasets using the same conceptual model Delphi developers have been using for decades in VCL and FMX applications.</div><div><br></div><h3>Delphi-style databinding for web client applications</h3><div>The new TWebDBDataGrid uses the standard Delphi-style databinding approach:<br></div><div></div><pre class="delphi" name="code">WebDBDataGrid1.DataSource := WebDataSource1;
WebDataSource1.DataSet := WebClientDataSet1;</pre><div></div><div>That is all it takes to connect the grid to a dataset.</div><div>From there, the grid can automatically display the data from the dataset and provide rich interaction capabilities out of the box.</div><div>This makes it possible to work with data in a very natural Delphi way, also in a browser-based TMS WEB Core application.</div><div>The TWebDBDataGrid can connect to datasets such as:</div><div></div><pre class="delphi" name="code">TWebClientDataSet
TXDataDataSet
TStellarDataStoreClientDataSet
</pre><div>and other dataset-based data sources.</div><div><br></div><div>This opens the door to directly working with data coming from a backend database through TMS WEB Core client-side applications. Whether the data is local in a client dataset, retrieved from a TMS XData backend, or provided by StellarDS.io, the grid can consume it through the familiar dataset abstraction.</div><div><br></div><h3>Less glue code, more productivity</h3><div>One of the main goals behind TWebDBDataGrid is simple: reduce the amount of code needed to build data-driven web applications.</div><div><br></div><div>Without DB-aware binding, developers often need to manually transfer data from backend responses into grid structures, handle updates, synchronize records, process sorting or filtering, and keep UI state aligned with data state.</div><div><br></div><div>With TWebDBDataGrid, much of this becomes automatic.</div><div><br></div><div>The component provides out-of-the-box support for:</div><div><br></div><ul><li>displaying dataset records</li><li>editing data</li><li>sorting</li><li>formatting</li><li>paging</li><li>filtering</li></ul><div>This means developers can focus more on the application logic and less on the repetitive wiring between data and UI.</div><div><br></div><div>Example: connecting a dataset to the grid</div><div><br></div><div>A typical setup can be as compact as this:</div><div><br></div><div></div><pre class="delphi" name="code">procedure TForm1.WebFormCreate(Sender: TObject);
begin
&nbsp; WebDataSource1.DataSet := WebClientDataSet1;
&nbsp; WebDBDataGrid1.DataSource := WebDataSource1;
&nbsp; WebClientDataSet1.Open;
end;</pre><div></div><div><br></div><div>The grid automatically receives its data through the datasource.</div><div><br></div><div>When working with a backend dataset like <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank">TMS XData</a> dataset, the same concept applies:</div><div><br></div><div></div><pre class="delphi" name="code">procedure TForm1.WebFormCreate(Sender: TObject);
begin
&nbsp; WebDataSource1.DataSet := XDataDataSet1;
&nbsp; WebDBDataGrid1.DataSource := WebDataSource1;
&nbsp; XDataDataSet1.Open;
end;</pre><div></div><div><br></div><div>And when using <a href="https://stellards.io/" target="_blank">StellarDS.io</a>, our data in the cloud as a service solution:</div><div><br></div><div></div><pre class="delphi" name="code">procedure TForm1.WebFormCreate(Sender: TObject);
begin
&nbsp; WebDataSource1.DataSet := StellarDataStoreClientDataSet1;
&nbsp; WebDBDataGrid1.DataSource := WebDataSource1;
&nbsp; StellarDataStoreClientDataSet1.Open;
end;</pre><div></div><div><br></div><div>The dataset can represent data coming from a real backend database, while the web client application continues to work with a clean Delphi dataset model.</div><div><br></div><h3>Editing data in the browser</h3><div>TWebDBDataGrid is not limited to displaying data. It also supports editing with various inplace editor types, making it possible to build real data management screens directly in the browser.</div><div><br></div><div>For example, a typical edit flow can remain dataset-driven:</div><div></div><pre class="delphi" name="code">procedure TForm1.btnSaveClick(Sender: TObject);
begin
&nbsp; if WebDataSource1.DataSet.State in dsEditModes then
&nbsp; &nbsp; WebDataSource1.DataSet.Post;
end;

procedure TForm1.btnCancelClick(Sender: TObject);
begin
&nbsp; if WebDataSource1.DataSet.State in dsEditModes then
&nbsp; &nbsp; WebDataSource1.DataSet.Cancel;
end;</pre><div></div><div><br></div><div>This is the kind of code Delphi developers already know.</div><div><br></div><div>The advantage is that the same productive programming model now becomes available in modern web client applications created with TMS WEB Core.</div><div><br></div><h3>A natural fit for database-backed web applications</h3><div>The addition of TWebDBDataGrid is especially useful for applications that need to work directly with structured backend data.</div><div><br></div><div>Typical use cases include:</div><div><br></div><ul><li>admin dashboards</li><li>ERP-style browser applications</li><li>customer portals</li><li>order management systems</li><li>reporting screens</li><li>planning tools</li><li>database maintenance applications</li></ul><div><br></div><div>Combined with datasets such as <b>TXDataDataSet </b>and <b>TStellarDataStoreClientDataSet</b>, TWebDBDataGrid makes it easier to build full client-side web applications that interact with backend databases in a structured and maintainable way.</div><div><br></div><div>The result is a productive development experience where the backend, dataset layer and grid UI work together with minimal boilerplate code.</div><div><br></div><h3>Built-in power of TWebDataGrid</h3><div>Because TWebDBDataGrid is based on the TWebDataGrid technology, it benefits from the rich grid functionality already available in TMS WEB Core.</div><div><br></div><div>This includes advanced grid behavior such as:</div><div><br></div><ul><li>column formatting</li><li>sorting</li><li>filtering</li><li>paging &amp; continuous data loading during scroll</li><li>editing with multiple editor types</li><li>custom cell content</li><li>responsive browser rendering</li><li>large data presentation</li></ul><div><br></div><div>This makes TWebDBDataGrid not just a DB-aware grid, but a powerful modern web grid with Delphi-style databinding.</div><div><br></div><h3>Conclusion</h3><div>With TMS WEB Core v3.0, we are taking another step in making web development with Delphi more powerful, more productive and more familiar.</div><div><br></div><div>The new TWebDBDataGrid brings the well-known TDataSource / TDataSet paradigm to a modern browser grid component. It enables developers to connect directly to datasets such as TWebClientDataSet, TXDataDataSet, TStellarDataStoreDataSet and others, while benefiting from built-in editing, sorting, formatting, paging and filtering.</div><div><br></div><div>For Delphi developers building database-oriented web applications, this means less code, faster development and a much more natural development experience.</div><div><br></div><h3>Beta available now</h3><p><img src="https://www.tmssoftware.com/site/img/blog/betasoft.avif" style="width: 235.47px; height: 235.094px;" alt="TMS Software Delphi  Components tmswebcore"><br></p><div>The TMS WEB Core v3.0 beta is available now for all <a href="https://www.tmssoftware.com/site/tmsallaccess.asp" target="_blank">TMS ALL-ACCESS</a> users and can be downloaded from the My Products page after login on our website!<br><p><br></p><h3>Introducing the TMS WEB Core WebDataGrid Video Series</h3><p>To help you get the most out of the new TWebDataGrid, we&#146;re launching a dedicated video series featuring Bruno Fierens. In this series, Bruno walks you through the capabilities of the WebDataGrid, sharing practical insights and hands-on demonstrations to accelerate your development.<br><br>A new video will be released every Thursday over the coming weeks, gradually guiding you from the fundamentals to more advanced data handling techniques in modern web applications.<br><br></p><h4><b>WebDataGrid #1: Managing Tabular Data in Delphi Web Applications</b></h4>In the first episode, we explore the different approaches to displaying and managing tabular data in TMS WEB Core applications. You&#146;ll discover how components like TWebStringGrid, TWebDBGrid, TWebTableControl, TWebDBTableControl, FNC Data Grid, TWebDataGrid, and TWebDBDataGrid compare in terms of features, rendering, and styling.<br><br>Through live demos, the video highlights the strengths of each option and clarifies where the WebDataGrid and its DB-aware counterpart fit within your web UI toolkit.<p></p><p><iframe frameborder="0" src="//www.youtube.com/embed/Z2arsRkfMmQ" width="640" height="360" class="note-video-clip"></iframe><br></p><p>Stay tuned for the next episodes in this series.</p><div><br><br></div></div>
		
		
		
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Announcing SmartSetup 3.0... and 3.1... and 3.2... and 3.3!]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2469</link>
		<author>Adrian Gallero</author>
		<pubDate>Sat, 18 Apr 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		<p>Time flies when you are having fun. We have been so busy lately adding new stuff to SmartSetup that we haven&#39;t had enough time to tell you about everything that&#39;s new. So, in this post, we wanted to give you a recap of the main new features.</p><div><br></div><h4>Versioning</h4><p>In SmartSetup 3.0 we added&nbsp;<a href="https://doc.tmssoftware.com/smartsetup/guide/versioning.html" target="_blank">versioning</a>. This means that you can now install older versions of our components (and components from the community server) just by right-clicking them in tmsgui and selecting a version. </p><p>Because SmartSetup was designed from the start to support versioning, this change is retroactive across all versions of TMS components ever published with SmartSetup. So even though we only released versioning support a month ago, you can get a component from a year ago without issues.</p><div><br></div><p><img src="https://doc.tmssoftware.com/smartsetup/images/tmsgui-install-version.png" style="width: 828px;" alt="TMS Software Delphi  Components "></p><p><br></p><p>For open-source components in Git, you can get any tagged version in the repository (shown here in dark mode, so we can also show off another great feature in SmartSetup 3.0):<img src="https://doc.tmssoftware.com/smartsetup/images/tmsgui-install-version-community_dark.png" alt="TMS Software Delphi  Components " style="width: 828px;"></p><p><br></p><p>This is, of course, also supported from the command line.</p><p><b>Note: versioning is not yet supported in tms dashboard. Until that support is added, you&#39;ll need tmsgui or tms directly to work with older versions.</b></p><div><br></div><p>Besides adding the much-requested support for older versions, SmartSetup 3.0 added the following features to make it simpler to work with versions:</p><ul><li><a href="https://doc.tmssoftware.com/smartsetup/guide/versioning.html#snapshots" target="_blank">Snapshots</a>: These work like a lock file in other package managers. They let you record the exact components and versions you are using at any given moment. You can generate snapshots automatically and store them in git, so if you want to roll a project back a year &#151; including the component versions you were using then &#151; you can simply restore the snapshot saved at that time. Snapshots also make it easy to reproduce the same environment across multiple machines.</li><li><a href="https://doc.tmssoftware.com/smartsetup/guide/versioning.html#version-pinning" target="_blank">Version Pinning</a>: You can now pin individual components, so they don&#39;t update automatically when you run an update. You can also pin all components, if you want to be sure nothing updates until you manually unpin them. As usual, this works with both TMS and non-TMS components. If you look back at the images at the beginning of this post, you can see that tms.fnc.uipack is pinned, in the leftmost column of the grid.</li><li><a href="https://github.com/agallero/multide" target="_blank">MultIDE</a>: There is a new tool available at <a href="https://github.com/agallero/multide" target="_blank">https://github.com/agallero/multide</a>&nbsp;that makes it simpler to have multiple versions of components installed on the same machine at the same time. I personally use it a lot to test different setups for customers.</li></ul><h4><br></h4><h4>Windows on ARM Support</h4><p>In version 3.1, SmartSetup added support for the new Windows on ARM platform, just days after it was introduced in RAD Studio. We did find the time to write about that one, so for more information see&nbsp;<a href="https://www.tmssoftware.com/site/blog.asp?post=2468" target="_blank">https://www.tmssoftware.com/site/blog.asp?post=2468</a></p><h4><br></h4><h4>SBOM</h4><p>Version 3.2 added a&nbsp;<a href="https://doc.tmssoftware.com/smartsetup/reference/tms-sbom-generate.html" target="_blank">sbom-generate</a>&nbsp;command that can produce a CycloneDX SBOM for every installed component, using the knowledge SmartSetup already has about dependencies and versions. The idea is not to replace an application-wide SBOM tool, but to feed it accurate component-level information that the app can use to generate the bigger SBOM for your application. If you are doing a SBOM application and think you can use the information SmartSetup can generate, do not hesitate to contact us.</p><p>SBOM is still a moving target, so expect changes in this area as the exact requirements become clearer. We plan to blog about more specific SBOM stuff in the future.</p><p><b>We would like to specially thank&nbsp;<a href="https://github.com/Maenken" target="_blank">Bj&#246;rn M&#228;nken</a> for contributing the SBOM-generating code.</b></p><p><b><br></b></p><h4>Automated Builds</h4><p>In release 3.3 we added many improvements for running automated builds on a server, along with a&nbsp;<a href="https://doc.tmssoftware.com/smartsetup/guide/continuous-integration.html" target="_blank" style="background-color: white; font-family: sans-serif;">document with information about how to set it up</a>. SmartSetup was originally created as a builder, and we have been using it that way for years. Fun fact: the original command-line project was called "tmsbuild", as a word play on "msbuild". When we added more commands, we renamed it to "tms" to avoid having to type "tmsbuild build" &#151; so you can still write "tms build", just with an extra space. But from the beginning, SmartSetup has been a builder, as you can infer from its original name.</p><p>In 3.3 we documented a lot more on how SmartSetup can be used to build your projects, not just install components. We also fixed many small things and added quality-of-life features to make the process smoother. We hope it helps speed up your build systems as much as it has ours. For complex projects, the parallel compilation in SmartSetup can shave literal hours off build times. In my own case, a build of FlexCel (the components themselves, plus more than 60 demos, tools, etc., built for Delphi XE through Delphi 13 on every platform) used to take about 8 hours. The new SmartSetup-based builder does the same work in 45 minutes. The speed gains are real.</p><p><br></p><h4>And a lot more</h4><p>You can find a full list of the smaller changes at <a href="https://doc.tmssoftware.com/smartsetup/guide/release-notes.html" target="_blank">https://doc.tmssoftware.com/smartsetup/guide/release-notes.html</a>. Things like much-improved installs in Delphi Community Edition, or credentials now being stored securely in the Windows Credential Manager. From an improved tms doctor that can find and fix more misconfigurations, to a new tms spec command that makes creating SmartSetup spec files much easier. From a new Dark Mode in tmsgui to greatly improved documentation. And so on.</p><p>Finally, let us once more remind you that SmartSetup is fully open source, and you can find the repository at&nbsp;<a href="https://github.com/tmssoftware/smartsetup." target="_blank">https://github.com/tmssoftware/smartsetup.</a>&nbsp;Feel free to open issues if you run into trouble or are missing functionality. All contributions are welcome, from improving the docs to bug fixes and new features. And if you find SmartSetup as useful as we do, don&#39;t forget to&nbsp;<a href="https://github.com/tmssoftware/smartsetup" target="_blank">give it a star</a>! Help us make SmartSetup great for the whole Delphi community.</p><p><br></p>
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿The Next Evolution of Charting in Delphi: Multipoint Series]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2466</link>
		<author>Pieter Scheldeman</author>
		<pubDate>Fri, 3 Apr 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		





<p><span style="font-family: Montserrat, sans-serif; font-size: 18px; font-weight: bold;">Intro</span></p>
<p>Stock data is one of the most demanding chart scenarios: dense OHLC bars, date labels that need rotating, interactive panning across months of history, and overlay series like trend lines and moving averages sitting on top. This post shows how <strong><a href="https://www.tmssoftware.com/site/tmsfncchart.asp" target="_blank">TMS FNC Chart</a></strong> handles all of it with a real NVIDIA price history dataset &#151; loading straight from CSV, switching between OHLC and line views, and adding a trend line and 30-day moving average in a few lines of code.</p>
<p><img src="https://tmssoftware.com/site/img/fnc/chart/FNCChartCandle.png" style="width: 828px;" alt="TMS Software Delphi  Components tmsfncchart"><br></p>

<h4>Watch The Video</h4>
<p>Watch the companion walkthrough on YouTube:<br><br><iframe frameborder="0" src="//www.youtube.com/embed/kU8jDPzJVLk" width="640" height="360" class="note-video-clip"></iframe><br></p>
<p><br></p>

<h4>Quick Highlights</h4>
<ul>
<li><p><strong>LoadFromCSVMultiPointData</strong> maps CSV columns to Open, High, Low, and Close in a single call &#151; no manual parsing.</p></li>
<li><p><strong>LoadFromCSVData</strong> pulls just the Close column for the line view, and <code>AddTrendLine</code> / <code>AddMovingAverage</code> add overlays over the same data immediately after.</p></li>
<li><p>Horizontal panning and zoom are enabled with three properties, and a point-based crosshair snaps to the nearest bar automatically.</p></li>
<li><p>The <code>BeforeDrawSerieXValue</code> event suppresses out-of-range date labels that appear when the scroll window extends past the data.</p></li>
</ul>

<h4>Loading OHLC Data from CSV</h4>
<p>The CSV has five columns: Date, Open, High, Low, Close. <code>LoadFromCSVMultiPointData</code> takes the file path, a load options object, and then the column index for each OHLC field followed by the label column. Setting <code>CSVFirstLine := cflFirstLineSkip</code> discards the header row automatically.</p>
<pre class="delphi" name="code">lo := TTMSFNCChartLoadOptions.Create;
lo.CSVDelimiter := &#39;,&#39;;
lo.CSVFirstLine := cflFirstLineSkip;

TMSFNCChart1.LoadFromCSVMultiPointData(STOCKDATAFILE, lo,
  2,   // Open
  3,   // High
  1,   // Low
  4,   // Close
  -1, -1,
  0);  // Label (Date)

TMSFNCChart1.Series[0].ChartType := ctOHLC;
</pre>
<p>After the load, setting <code>ChartType := ctOHLC</code> is the only change needed to render the bars. The same call works for <code>ctCandleStick</code> if you prefer filled bodies.</p>
<p><br></p>

<h4>Interaction: Panning and Zoom</h4>
<p>Six months of daily bars is too much to read at full width. Three properties turn on horizontal-only scaling and panning, and <code>MinX</code> / <code>MaxX</code> set the initial visible window to the full dataset so the scroll range is correct from the start.</p>
<pre class="delphi" name="code">TMSFNCChart1.Interaction := True;
TMSFNCChart1.InteractionOptions.ScaleMode := smHorizontal;
TMSFNCChart1.InteractionOptions.Panning   := True;

TMSFNCChart1.Series[0].AutoXRange := arDisabled;
TMSFNCChart1.Series[0].MinX := 0;
TMSFNCChart1.Series[0].MaxX := TMSFNCChart1.Series[0].Points.Count - 1;
</pre>
<p>A point-based crosshair snaps to the nearest bar on hover, showing the exact OHLC values without the crosshair drifting between bars.</p>
<pre class="delphi" name="code">TMSFNCChart1.Crosshair.Visible := True;
TMSFNCChart1.Crosshair.Modes   := [ccmPointBased];
</pre>
<p><span style="background-color: #eef6ff; border-left: 4px solid #2492d1; padding: 6px 10px; display: inline-block;"><strong>Highlight:</strong> <code>smHorizontal</code> locks vertical scale while panning, which is exactly what you want for stock charts &#151; the Y axis stays fixed to the full price range while you scroll through time.</span></p>
<p><br></p>

<h4>Line View: Trend Line and Moving Average</h4>
<p>The second button reloads the same CSV using <code>LoadFromCSVData</code>, pulling only the Close column. <code>AddTrendLine</code> and <code>AddMovingAverage</code> are then called over the full point range and each returns a <code>TTMSFNCChartSerie</code> that can be styled like any other series.</p>
<pre class="delphi" name="code">TMSFNCChart1.LoadFromCSVData(STOCKDATAFILE, lo, [4], -1, 0);

TMSFNCChart1.AddTrendLine(0, 0, TMSFNCChart1.Series[0].Points.Count - 1);
TMSFNCChart1.AddMovingAverage(0, 0, TMSFNCChart1.Series[0].Points.Count - 1, 30);

TMSFNCChart1.Series[1].Stroke.Color := gcRed;
TMSFNCChart1.Series[1].Stroke.Width := 3;

TMSFNCChart1.Series[2].Stroke.Color := gcBlue;
TMSFNCChart1.Series[2].Stroke.Width := 3;
</pre>
<p>The trend line (series index 1) is drawn in red, and the 30-day moving average (series index 2) in blue. Both get <code>AutoXRange := arDisabled</code> and matching <code>MinX</code> / <code>MaxX</code> so they align correctly with the price series during pan and zoom.</p>
<p><span style="background-color: #eef6ff; border-left: 4px solid #2492d1; padding: 6px 10px; display: inline-block;"><strong>Highlight:</strong> <code>AddTrendLine</code> and <code>AddMovingAverage</code> append new series to the chart and return them directly. No separate component, no manual calculation &#151; just style the result and move on.</span></p>
<p><br></p>

<h4>Suppressing Out-of-Range X Labels</h4>
<p>When <code>AutoXRange</code> is disabled and the chart draws axis labels beyond the data range, labels with invalid indices appear. The <code>BeforeDrawSerieXValue</code> event catches them before they render.</p>
<pre class="delphi" name="code">procedure TForm1.TMSFNCChart1BeforeDrawSerieXValue(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ASerie: TTMSFNCChartSerie;
  APosition: TTMSFNCChartXAxisPosition;
  var ADrawValue: TTMSFNCChartDrawXYValue; var ADefaultDraw: Boolean);
begin
  if ADrawValue.Value &lt; 0 then
    ADefaultDraw := False;

  if ADrawValue.Value &gt;= ASerie.Points.Count then
    ADefaultDraw := False;
end;
</pre>
<p>Setting <code>ADefaultDraw := False</code> skips the draw for that tick entirely, leaving the axis clean at both ends of the scroll range.</p>
<p><br></p>

<h4>Conclusion</h4>
<p><a href="https://www.tmssoftware.com/site/tmsfncchart.asp" target="_blank">TMS FNC Chart</a> reduces a real-world stock charting scenario to straightforward Delphi code: one call to load OHLC data from CSV, three properties for interactive panning, and two calls to add trend and moving average overlays. The same series model and the same <code>MinX</code> / <code>MaxX</code> scroll pattern apply across both chart types, so switching between the OHLC view and the close-price line view is a matter of reloading the data rather than rebuilding the chart.</p>

		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	</channel></rss>
