<?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, 8 Jun 2026 19:52: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: A Sharper, More Modern Scripting Engine for Delphi]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2483</link>
		<pubDate>Mon, 8 Jun 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		<p>If you&#39;ve opened a Delphi visual designer on a 4K laptop or a high-resolution external monitor, you know the feeling: blurry icons, toolbar buttons that are too small to read, controls that land in the wrong place when you drop them on a form. Embedding a scripting IDE into your own application means inheriting all of those problems &#151; and exposing them to your end users.</p>

<p><a href="https://www.tmssoftware.com/site/scriptstudiopro.asp">TMS Scripter</a> is a scripting engine for Delphi applications. It lets you add a full Pascal or Basic scripting language to your software, complete with an embeddable IDE: a code editor with syntax highlighting and code completion, a visual form designer, an object inspector, and a debugger. Because that IDE runs <em>inside</em> your application, the way it renders on modern displays is the way your users experience it.</p>

<p>TMS Scripter 8.0 is, above all, about making that experience crisp. This release brings full High DPI support throughout the Scripter IDE and the visual scripter components, alongside a new platform target, a reorganized documentation set, and a more modern codebase. Let&#39;s walk through what changed.</p><p><br></p>

<h3>Sharp on High DPI displays</h3>

<p>High DPI support is the headline of this release. On a standard 96-DPI monitor everything looked fine; the trouble started at 150%, 200%, and beyond &#151; exactly the scaling factors most modern laptops and 4K screens use by default. Version 8.0 addresses this across the board.</p>

<p><strong>Modern, scalable icons.</strong> Every icon in the Scripter IDE &#151; the toolbar, the menus, and the component palette &#151; has been redrawn as a modern, High DPI-aware set. The object inspector&#39;s own glyphs (expand/collapse, the drop-down and dialog buttons, the spin buttons) were updated too, so they render correctly instead of as tiny smudges on a scaled display.</p>

<p><strong>A form designer that behaves at any scale.</strong> The visual designer received the bulk of the fixes:</p>

<ul>
  <li><strong>Controls now drop where you put them.</strong> Previously, inserting a control by clicking or dragging on the form designer placed it at roughly twice the intended position and size on a scaled monitor. That double-scaling bug is gone &#151; what you draw is what you get.</li>
  <li><strong>The grid and snap-to-grid respect DPI.</strong> Grid spacing, grab-handle size, and the arrow-key move/resize step are now treated as logical (96-DPI) values and scaled to the physical display. The grid stays visually consistent at any scaling factor, and the grid dots remain visible on High DPI screens.</li>
  <li><strong>Grab handles resize correctly when you move a form between monitors</strong> with different DPI settings.</li>
  <li><strong>Non-visual components and the component palette render at the right size.</strong> Both now load the High DPI (32px) icon variants when available, with a clean fallback to the standard icons.</li>
</ul>

<p><strong>The little things, too.</strong> The IDE splitter now starts at a sensible position on High DPI monitors, several dialogs that weren&#39;t being centered on screen now are, and the splitter-drag cursor shows up in the correct spot even when the host application is marked DPI-unaware while running on a High DPI monitor.</p>

<p><img src="https://download.tmssoftware.com/business/images/blog/26-06-08-scripter-high-dpi-ide.png" alt="The TMS Scripter IDE rendering crisply on a High DPI display" style="width: 75%;"></p><p><br></p>

<h3>A new platform: WinArm64EC</h3>

<p>TMS Scripter 8.0 adds support for the <strong>WinArm64EC</strong> platform introduced in Delphi 13.1. <a href="https://learn.microsoft.com/en-us/windows/arm/arm64ec">ARM64EC</a> ("Emulation Compatible") is Microsoft&#39;s ABI for building native ARM64 apps on Windows that can interoperate with existing x64 code in the same process. If you&#39;re targeting Windows on ARM, you can now ship scripting capabilities there too.</p><p><br></p>

<h3>Documentation, reorganized around how you actually use Scripter</h3>

<p>The user guide has been rewritten and split by audience into three clear chapters:</p>

<ul>
  <li><strong><a href="https://doc.tmssoftware.com/biz/scripter/guide/scripter.html">Working with Scripter</a></strong> &#151; running, executing, and debugging scripts.</li>
  <li><strong><a href="https://doc.tmssoftware.com/biz/scripter/guide/extending.html">Extending Scripter</a></strong> &#151; integrating the engine with your host application: registering classes, methods, properties, functions, and libraries. This is now presented <em>RTTI-first</em>, matching the recommended modern approach.</li>
  <li><strong><a href="https://doc.tmssoftware.com/biz/scripter/guide/language.html">Writing Scripts</a></strong> &#151; the supported Pascal and Basic languages, plus declaring forms and classes from within a script.</li>
</ul>

<p>The content is cross-linked to the API Reference, outdated version-specific notes have been removed, and the modern <code>TatScripter</code> / <code>TIDEScripter</code> components and the RTTI-based registration approach are presented as the default throughout. If you&#39;ve been learning Scripter from older docs, this is a good moment to revisit them.</p><p><br></p>

<h3>A more modern codebase under the hood</h3>

<p>To move faster and write cleaner code, TMS Scripter 8.0 <strong>drops support for Delphi 7, 2009, 2010, and XE</strong> &#151; the minimum is now Delphi XE2. This unlocks generics, anonymous methods, and the modern <code>System.Rtti</code> unit, all of which were impossible to rely on while maintaining compatibility with a 2002-era compiler.</p>

<p>If you&#39;re still on one of those versions, you&#39;re not stuck: version 7.36 (the last release to support them) remains available for download via <a href="https://doc.tmssoftware.com/smartsetup/">TMS Smart Setup</a>, which automatically detects your Delphi version and installs the last compatible release.</p>

<p>We covered the reasoning, the trade-offs, and what it means for the codebase in a dedicated post &#151; <a href="https://www.tmssoftware.com/site/blog.asp?post=2480">Dropping Delphi 7 support in TMS Scripter</a> &#151; if you&#39;d like the full story.</p><p><br></p>

<h3>Developer-facing improvements</h3>

<p>A few changes target you, the developer integrating Scripter into your application.</p>

<p><strong>Generic array parameters in RTTI registration.</strong> When you register a class with <code>DefineClassByRTTI</code>, methods that take a generic array parameter &#151; for example <code>TArray&lt;string&gt;</code> &#151; are now handled correctly. Previously such methods needed manual workarounds; now they just work.</p>

<pre><code>type
  TReportService = class
  public
    // A method that takes a generic array parameter
    procedure Generate(const Sections: TArray&lt;string&gt;);
  end;

procedure TReportService.Generate(const Sections: TArray&lt;string&gt;);
var
  I: Integer;
begin
  for I := Low(Sections) to High(Sections) do
    WriteLn(&#39;Building section: &#39; + Sections[I]);
end;

// In your host application, register the class via RTTI:
Scripter.DefineClassByRTTI(TReportService);
</code></pre>

<p>Once registered, the script can call the method naturally, passing an array straight through:</p>

<pre><code>var
  Service: TReportService;
  Sections: array[0..2];
begin
  Sections[0] := &#39;Summary&#39;;
  Sections[1] := &#39;Details&#39;;
  Sections[2] := &#39;Appendix&#39;;

  Service := TReportService.Create;
  try
    Service.Generate(Sections);
  finally
    Service.Free;
  end;
end;
</code></pre>

<p><strong>Import tool supports Delphi 12 and 13.</strong> You can now import library units from the latest two IDE versions, so wrapping your own units and third-party libraries for use in scripts stays in step with your Delphi installation.</p>

<p><strong>More bookmarks in the editor.</strong> The code editor&#39;s <code>TIDEMemo.BookMarkMax</code> property lets you raise the bookmark limit beyond the previous fixed maximum (the default remains 10).</p>

<p>Alongside these, the release fixes a long-standing annoyance: cursor and selection position in the code editor are now correctly preserved when switching between files or saving.</p><p><br></p>

<h3>Getting it</h3>

<p>TMS Scripter 8.0 is available now on the <a href="https://www.tmssoftware.com/site/scriptstudiopro.asp">product page</a>. If you&#39;re already a customer, install it via <a href="https://doc.tmssoftware.com/smartsetup/">TMS Smart Setup</a>; the modern, High DPI-aware IDE is what your users will see the moment you rebuild.</p>

<p>For anyone who adds scripting to their Delphi applications, 8.0 removes a class of papercuts that mattered most on exactly the hardware your users run today &#151; and lays a more modern foundation for what comes next.</p>

<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/biz/scripter/about/whatsnew.html">What&#39;s new in 8.0</a></li>
  <li><a href="https://www.tmssoftware.com/site/blog.asp?post=2480">Dropping Delphi 7 support &#151; the reasoning</a></li>
</ul>

		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Dynamic cell style and cell data formatting in TWeb(DB)DataGrid]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2482</link>
		<pubDate>Fri, 5 Jun 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"></div><div><br></div><div>When presenting data in a web application, displaying raw values is often not enough. Users expect important information to stand out immediately, whether it is a high-performance car, an exceptional sales figure, or a value that requires attention.</div><div><br></div><div>With <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> and the powerful TWebDataGrid and TWebDBDataGrid components, dynamically styling and formatting cells based on their content is straightforward. In this article, we&#39;ll look at three different approaches for customizing cell appearance and formatting and examine a practical example built with TWebDBDataGrid.</div><div><br></div><h4>Three Ways to Customize Cell Appearance</h4><div><br></div><div>TWebDataGrid and TWebDBDataGrid provide three complementary mechanisms for dynamic presentation.</div><div><br></div><div><b>1. OnGetCellStyle</b></div><div><br></div><div>The OnGetCellStyle event allows you to return a JavaScript style object that is applied directly to the cell.</div><div><br></div><div>This is ideal when styling depends on data values and you want to generate styles programmatically.</div><div><br></div><div>Example:</div><div><pre class="delphi" name="code">function TForm2.WebDBDataGrid1Column_HpGetCellStyle(
  Params: TJSCellClassParams): TJSValue;
begin
  if Integer(Params.Value) &gt; 500 then
    Result := new([&#39;color&#39;, &#39;red&#39;,
                   &#39;backgroundColor&#39;, &#39;yellow&#39;]);
end;</pre></div><div>In this example, any horsepower value above 500 is displayed using red text on a yellow background.</div><div><br></div><div><b>2. OnGetCellClass</b></div><div><br></div><div>Instead of returning inline styles, you can return a CSS class name.</div><div><br></div><div>This approach keeps styling centralized in CSS and is often the preferred solution for larger applications.</div><div><pre class="delphi" name="code">function TForm2.WebDBDataGrid1Column_HpGetCellClass(
  Params: TJSCellClassParams): TJSValue;
begin
  if Integer(Params.Value) &gt; 500 then
    Result := &#39;supercar&#39;;
end;</pre></div><div>The CSS class can then be defined in your application&#39;s stylesheet:</div><div><pre class="delphi" name="code">.supercar {
  color: red;
  background-color: yellow;
}</pre></div><div>This separation of logic and presentation makes styling easier to maintain.</div><div><br></div><div><b>3. ValueFormatter</b></div><div><br></div><div>Sometimes the value itself should be displayed differently without modifying the underlying data.</div><div><br></div><div>The ValueFormatter event allows you to transform the value before it is rendered.</div><div><pre class="delphi" name="code">function TForm2.WebDBDataGrid1Column_PriceValueFormatter(
  Value: TJSValue): TJSValue;
var
  i: Integer;
begin
  i := Integer(Value);
  Result := FormatFloat(&#39;#,##0&#39;, i) + &#39;&#128;&#39;;
end;</pre></div><div>A numeric value such as:</div><div><br></div><div>125000</div><div><br></div><div>is rendered as:</div><div><br></div><div>125,000&#128;</div><div><br></div><div>making it significantly easier to read.</div><div><br></div><h4>Combining Formatting and Styling</h4><div>The sample application demonstrates how these techniques can work together.</div><div><br></div><div>For the Price column, values are right-aligned:</div><div><pre class="delphi" name="code">function TForm2.WebDBDataGrid1Column_PriceGetCellStyle(
  Params: TJSCellClassParams): TJSValue;
begin
  Result := new([&#39;text-align&#39;, &#39;right&#39;]);
end;</pre></div><div>This creates a more professional presentation for numeric data while the formatter ensures proper thousands separators.</div><div><br></div><div>At the same time, the Horsepower column receives special highlighting when values exceed 500 HP, helping users quickly identify high-performance vehicles.</div><div><br></div><div>With a couple of simple lines of code, we get the presentation of the data from this stale appearance:<br><img src="https://www.tmssoftware.com/site/img/blog/advwebgridstyle1.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div>to</div><div><img src="https://www.tmssoftware.com/site/img/blog/advwebgridstyle2.png" style="width: 828px;" alt="TMS Software Delphi  Components tmswebcore"><br></div><div><br></div><h4>Loading Data Dynamically</h4><div>The sample also demonstrates how easily external JSON data can be loaded into a TWebDBDataGrid.<br></div><div><pre class="delphi" name="code">procedure TForm2.WebFormShow(Sender: TObject);
begin
  WebClientConnection1.URI :=
    &#39;https://download.tmssoftware.com/tmsweb/demos/TMSWEB_ResponsiveGrid/carsfull.json&#39;;

  WebClientConnection1.Active := True;
end;<br>
</pre></div><div>When the form is displayed, the dataset automatically retrieves the JSON data and populates the grid.</div><div><br></div><div>Combined with dynamic styling and formatting, this creates a rich and responsive data visualization experience with only a small amount of code.</div><div><br></div><h4>Conclusion</h4><div>Dynamic cell styling is an important technique for improving usability and helping users focus on the information that matters most.</div><div><br></div><div>TWebDataGrid and TWebDBDataGrid offer several powerful customization mechanisms:</div><div><br></div><ul><li>OnGetCellStyle for dynamically generated inline styles</li><li>OnGetCellClass for CSS-based styling</li><li>ValueFormatter for custom value presentation</li></ul><div><br></div><div>These features can be combined to create visually appealing and highly informative data grids while keeping the implementation concise and maintainable.</div><div><br></div><div>Whether you need conditional formatting, custom number presentation, or responsive visual feedback, TMS WEB Core provides all the tools necessary to build modern web-based data applications.</div><div><br></div><div><h4>Video</h4><iframe frameborder="0" src="//www.youtube.com/embed/JXF16euejCQ" width="640" height="360" class="note-video-clip"></iframe><br></div><div><br></div><h4><br></h4><h4>Try it today</h4><div>Want to explore TWebDataGrid and TWebDBDataGrid yourself?</div><div><br></div><div>Download the latest version of <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> and discover how easy it is to build modern Delphi web applications with rich, responsive data grids.</div><div><br></div><div>Happy coding!</div><div><br></div><div><br></div><div><h4 style="color: rgb(0, 0, 0);">Get 10% Off TMS WEB Core &amp; TMS FNC UI Pack</h4><p>Enjoy 10% off when purchasing a new license of <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> or <a href="https://www.tmssoftware.com/site/tmsfncuipack.asp" target="_blank">TMS FNC UI Pack</a>. Simply use the promo code <span style="font-weight: 700;">WEBDATAGRID10</span> at checkout.</p><p>Offer valid until <span style="font-weight: 700;">June 15, 2026</span></p></div>
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿TMS VCL UI Pack: Installation, Modernized]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2481</link>
		<pubDate>Tue, 2 Jun 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		<p>
The installer for <strong><a href="https://www.tmssoftware.com/site/tmsvcluipack.asp" target="_blank">TMS VCL UI Pack</a></strong> is getting a major upgrade. Both the trial and the registered editions are moving to a new installation flow &#151; faster to set up, easier to keep updated, and ready for the latest RAD Studio target platforms.
</p>

<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjM2MCIgdmlld0JveD0iMCAwIDEwMDAgMzYwIiByb2xlPSJpbWciIGFyaWEtbGFiZWw9IlRNUyBWQ0wgVUkgUGFjayBtb2Rlcm4gaW5zdGFsbGVyIGV4cGVyaWVuY2UiPgogIDxkZWZzPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjEiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMwZjJmNDUiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjU1IiBzdG9wLWNvbG9yPSIjMTU1YzY4Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzIwN2Y3MyIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibmV3Q2FyZCIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjEiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNjNWY3ZTEiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjU1IiBzdG9wLWNvbG9yPSIjZTNmZmYyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2E5ZjBkNCIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibmV3SGVhZCIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjAiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMyN2MwOGEiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMWFhN2IwIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJsaW5lIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzQ5ZDZhNyIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM1NWI0ZmYiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8ZmlsdGVyIGlkPSJzaGFkb3ciIHg9Ii0yMCUiIHk9Ii0yMCUiIHdpZHRoPSIxNDAlIiBoZWlnaHQ9IjE0MCUiPgogICAgICA8ZmVEcm9wU2hhZG93IGR4PSIwIiBkeT0iMTIiIHN0ZERldmlhdGlvbj0iMTQiIGZsb29kLWNvbG9yPSIjMDYxMTFjIiBmbG9vZC1vcGFjaXR5PSIwLjI4Ii8+CiAgICA8L2ZpbHRlcj4KICA8L2RlZnM+CiAgPHJlY3Qgd2lkdGg9IjEwMDAiIGhlaWdodD0iMzYwIiByeD0iMTAiIGZpbGw9InVybCgjYmcpIi8+CiAgPHBhdGggZD0iTTAgMjc0IEMxNTAgMjA4IDI4NCAzMTggNDU0IDI1MiBDNjMwIDE4OCA3NjAgMjE0IDEwMDAgMTE4IFYzNjAgSDAgWiIgZmlsbD0iIzA3MTcyNyIgb3BhY2l0eT0iMC4zNiIvPgogIDxnIG9wYWNpdHk9IjAuMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIxIj4KICAgIDxwYXRoIGQ9Ik02NzQgMzAgaDIzOCIvPjxwYXRoIGQ9Ik03MDQgNTggaDIxMCIvPjxwYXRoIGQ9Ik03MzQgODYgaDE2MCIvPgogIDwvZz4KCiAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAgNDIpIiBmaWx0ZXI9InVybCgjc2hhZG93KSI+CiAgICA8cmVjdCB3aWR0aD0iMzMwIiBoZWlnaHQ9IjIyNCIgcng9IjE0IiBmaWxsPSIjZjdmYmZmIiBzdHJva2U9IiNkOGU2ZWUiLz4KICAgIDxyZWN0IHdpZHRoPSIzMzAiIGhlaWdodD0iNDAiIHJ4PSIxNCIgZmlsbD0iI2U4ZjFmNyIvPgogICAgPGNpcmNsZSBjeD0iMjYiIGN5PSIyMCIgcj0iNSIgZmlsbD0iI2VjNmI2YiIvPgogICAgPGNpcmNsZSBjeD0iNDYiIGN5PSIyMCIgcj0iNSIgZmlsbD0iI2YyYzk0YyIvPgogICAgPGNpcmNsZSBjeD0iNjYiIGN5PSIyMCIgcj0iNSIgZmlsbD0iIzQ5ZDZhNyIvPgogICAgPHRleHQgeD0iMzQiIHk9Ijc2IiBmb250LWZhbWlseT0iU2Vnb2UgVUksQXJpYWwsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzE2MzQ0YSI+T2xkIGV4cGVyaWVuY2U8L3RleHQ+CiAgICA8ZyBmb250LWZhbWlseT0iU2Vnb2UgVUksQXJpYWwsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzVkNzE4MSI+CiAgICAgIDxjaXJjbGUgY3g9IjM5IiBjeT0iMTEyIiByPSIzIiBmaWxsPSIjYjZjOWQ1Ii8+PHRleHQgeD0iNTIiIHk9IjExNiI+U2VwYXJhdGUgaW5zdGFsbGVyIGZsb3c8L3RleHQ+CiAgICAgIDxjaXJjbGUgY3g9IjM5IiBjeT0iMTQ2IiByPSIzIiBmaWxsPSIjYjZjOWQ1Ii8+PHRleHQgeD0iNTIiIHk9IjE1MCI+T2xkZXIgdGFyZ2V0IGNvdmVyYWdlPC90ZXh0PgogICAgICA8Y2lyY2xlIGN4PSIzOSIgY3k9IjE4MCIgcj0iMyIgZmlsbD0iI2I2YzlkNSIvPjx0ZXh0IHg9IjUyIiB5PSIxODQiPlN0YW5kYWxvbmUgdXBkYXRlIHBhdGg8L3RleHQ+CiAgICA8L2c+CiAgPC9nPgoKICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MTAgNDIpIiBmaWx0ZXI9InVybCgjc2hhZG93KSI+CiAgICA8cmVjdCB3aWR0aD0iMzMwIiBoZWlnaHQ9IjIyNCIgcng9IjE0IiBmaWxsPSJ1cmwoI25ld0NhcmQpIiBzdHJva2U9IiNmMGIzM2YiIHN0cm9rZS13aWR0aD0iMiIvPgogICAgPHJlY3Qgd2lkdGg9IjMzMCIgaGVpZ2h0PSI0MCIgcng9IjE0IiBmaWxsPSJ1cmwoI25ld0hlYWQpIi8+CiAgICA8Y2lyY2xlIGN4PSIyNiIgY3k9IjIwIiByPSI1IiBmaWxsPSIjZmZmZmZmIiBvcGFjaXR5PSIwLjkiLz4KICAgIDxjaXJjbGUgY3g9IjQ2IiBjeT0iMjAiIHI9IjUiIGZpbGw9IiNmZmZmZmYiIG9wYWNpdHk9IjAuNiIvPgogICAgPGNpcmNsZSBjeD0iNjYiIGN5PSIyMCIgcj0iNSIgZmlsbD0iI2ZmZmZmZiIgb3BhY2l0eT0iMC4zNSIvPgogICAgPHRleHQgeD0iMzQiIHk9Ijc2IiBmb250LWZhbWlseT0iU2Vnb2UgVUksQXJpYWwsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzBmM2QzMyI+TmV3IGV4cGVyaWVuY2U8L3RleHQ+CiAgICA8cmVjdCB4PSIzNCIgeT0iOTQiIHdpZHRoPSIyNjIiIGhlaWdodD0iMzIiIHJ4PSI3IiBmaWxsPSIjMTAyMzMzIi8+CiAgICA8dGV4dCB4PSI0NyIgeT0iMTE1IiBmb250LWZhbWlseT0iQ29uc29sYXMsTWVubG8sbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjUiIGZpbGw9IiM4MGU3YmYiPnRtcyBpbnN0YWxsIHRtcy52Y2wudWlwYWNrPC90ZXh0PgogICAgPGcgZm9udC1mYW1pbHk9IlNlZ29lIFVJLEFyaWFsLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZjVhNGEiPgogICAgICA8Y2lyY2xlIGN4PSI0MCIgY3k9IjE1MCIgcj0iMyIgZmlsbD0iIzFjOWU2ZSIvPjx0ZXh0IHg9IjUzIiB5PSIxNTQiPlRyaWFsOiBzbW9vdGhlciBldmFsdWF0aW9uPC90ZXh0PgogICAgICA8Y2lyY2xlIGN4PSI0MCIgY3k9IjE3OCIgcj0iMyIgZmlsbD0iIzFjOWU2ZSIvPjx0ZXh0IHg9IjUzIiB5PSIxODIiPlJlZ2lzdGVyZWQ6IFRNUyBTbWFydCBTZXR1cDwvdGV4dD4KICAgICAgPGNpcmNsZSBjeD0iNDAiIGN5PSIyMDYiIHI9IjMiIGZpbGw9IiMxYzllNmUiLz48dGV4dCB4PSI1MyIgeT0iMjEwIj5GdXR1cmUtcHJvb2YgSURFcyAmYW1wOyB0YXJnZXRzPC90ZXh0PgogICAgPC9nPgogIDwvZz4KCiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICAgIDxwYXRoIGQ9Ik00MzAgMTU0IEg1NzAiIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIzIiBvcGFjaXR5PSIwLjI4Ii8+CiAgICA8cGF0aCBkPSJNNDMwIDE1NCBINTcwIiBzdHJva2U9InVybCgjbGluZSkiIHN0cm9rZS13aWR0aD0iMyIvPgogICAgPGNpcmNsZSBjeD0iNTAwIiBjeT0iMTU0IiByPSIyNCIgZmlsbD0iIzEyMzM0NCIgc3Ryb2tlPSIjNjdkNmNhIiBzdHJva2Utd2lkdGg9IjIiIG9wYWNpdHk9IjAuOTYiLz4KICAgIDxwYXRoIGQ9Ik00OTEgMTU0IGgxOCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjMiLz4KICAgIDxwYXRoIGQ9Ik01MDMgMTQ3IGw4IDcgbC04IDciIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIzIi8+CiAgPC9nPgoKICA8dGV4dCB4PSI1MDAiIHk9IjMwNCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlNlZ29lIFVJLEFyaWFsLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMjkiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNmZmZmZmYiPlRNUyBWQ0wgVUkgUGFjazwvdGV4dD4KICA8dGV4dCB4PSI1MDAiIHk9IjMzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlNlZ29lIFVJLEFyaWFsLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTgiIGZpbGw9IiNjOWY0ZmYiPmEgc21vb3RoZXIgaW5zdGFsbGVyIGV4cGVyaWVuY2UgZm9yIHRyaWFsIGFuZCByZWdpc3RlcmVkIHVzZXJzPC90ZXh0Pgo8L3N2Zz4K" alt="TMS VCL UI Pack modern installer experience" style="width: 100%;">

<br><br>

<h3>A modern installer experience</h3>

<p>
The new installers make setup more consistent and easier to use, whether you are evaluating <a href="https://www.tmssoftware.com/site/tmsvcluipack.asp" target="_blank">TMS VCL UI Pack</a> with the trial version or installing the registered version as an existing customer.
</p>

<p>
The migration also updates the underlying installer technology to cover the latest RAD Studio target platforms, including <strong>Windows on Arm (Arm64EC).</strong>&nbsp;</p><p><br></p><p>
</p>

<h3>Installing TMS VCL UI Pack</h3>

<h4>Registered version (via TMS Smart Setup)</h4>

<p>
The registered version of <strong>TMS VCL UI Pack</strong> will be available exclusively through <strong>TMS Smart Setup</strong>. Before installing please uninstall the classic installation via the control panel.</p>

<p>
If you are new to TMS Smart Setup, see the <a href="https://doc.tmssoftware.com/smartsetup/">Smart Setup documentation</a> to install the <code>tms</code> command-line tool. Once it is set up, install TMS VCL UI Pack with:
</p>

<pre>tms install tms.vcl.uipack</pre>

<p>
</p><p><br></p><p>Prefer a visual workflow? You can also install the product through <strong><a href="https://www.tmssoftware.com/site/tmsdashboard.asp">TMS Dashboard</a></strong>, a central hub for managing TMS component installations and updates.</p><h6><img src="https://www.tmssoftware.com/site/img/dev/dashboard/tmsdashboard.png" alt="TMS Dashboard" style="font-size: 14px; width: 70%;"></h6><p><br></p><h4>Trial version</h4>

<p>
Evaluating TMS VCL UI Pack? Download the trial from the <a href="https://www.tmssoftware.com/site/tmsvcluipack.asp">TMS VCL UI Pack product page</a>: click <strong>Free Trial</strong>, enter your email address, and choose your IDE. The trial downloads instantly, and you&#39;ll receive a trial key which can be entered during setup.&nbsp;</p>

<h3><br></h3><h3>Moving forward</h3>

<p>
With this migration, <a href="https://www.tmssoftware.com/site/tmsvcluipack.asp" target="_blank">TMS VCL UI Pack</a> gains a cleaner installation experience, tighter alignment with TMS Smart Setup, and better support for today&#39;s multi-platform Delphi &amp; C++Builder development workflows.
</p>

<p>
This is part of a broader move across the entire TMS product portfolio. After TMS VCL UI Pack, the next product family planned for the modernized installer flow is the <strong>TMS FNC family</strong>.
</p>

<p>
As always, we welcome your feedback. Try the new way of installing TMS VCL UI Pack and <b>let us know</b> how we can keep improving the setup experience.
</p>

		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿Building a Photo Route Album with TMS FNC Maps, OpenStreetMap and EXIF GPS Data]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2478</link>
		<pubDate>Tue, 2 Jun 2026 12:00:00 +0000</pubDate>
		<description><![CDATA[<P>﻿
		
		
		
		
		
		
		
		<div><img src="https://www.tmssoftware.com/site/img/blog/fncmapalbum.png" style="width: 300.234px; height: 300.234px;" alt="TMS Software Delphi  Components tmsfncmaps"><br></div><div>There is something special about looking back at a trip and seeing not only the pictures you took, but also the exact route you followed on a map. In this article, we will build a small free &amp; open source Delphi application that combines:</div><div><br></div><ul><li><b>GPX route visualization</b></li><li><b>Free OpenStreetMap integration</b></li><li><b>JPEG photos with embedded GPS EXIF data</b></li><li><b>Interactive map markers with image previews</b></li></ul><div><br></div><div>The application is based on <a href="https://www.tmssoftware.com/site/tmsfncmaps.asp" target="_blank">TMS FNC Maps</a> and uses a lightweight open source EXIF parser to extract GPS coordinates from JPEG files.</div><div><br></div><div>The complete source code is available on GitHub:</div><div><a href="https://github.com/tmssoftware/TMSFNCMapsAlbum" target="_blank">https://github.com/tmssoftware/TMSFNCMapsAlbum</a><a href="https://github.com/tmssoftware/TMSFNCMapsAlbum" target="_blank"></a><br>and for your convenience, the free compiled app is also included in the repo.</div><div><br></div><div>The project demonstrates how easy it is to combine mapping, GPX loading and EXIF processing into a practical desktop application.</div><div><br></div><div><h3>The Result</h3><div>The application loads:</div><ul><li>GPX routes on top of a free OpenStreetMap layer</li><li>Photos with GPS metadata</li><li>Interactive image markers directly on the map</li></ul><div><br></div><div>A typical result looks like this:</div><div><img src="https://www.tmssoftware.com/site/img/blog/fotoroute.jpg" style="width: 780.234px; height: 730.292px;" alt="TMS Software Delphi  Components tmsfncmaps"><br></div><div><br></div><div><h3>The Main Ingredients</h3><div>The application is intentionally compact and easy to understand.</div><div><br></div><div>The core technologies used are:</div><div><br></div></div></div><table class="table table-bordered"><tbody><tr><td><b>Technology</b></td><td><b>Purpose</b></td></tr><tr><td>TTMSFNCMaps</td><td>Interactive cross-platform map component</td></tr><tr><td>OpenStreetMap</td><td>Free map tile provider</td></tr><tr><td>GPX support</td><td>Loading hiking/cycling/walking routes</td></tr><tr><td>EXIF4D parser</td><td>Reading GPS metadata from JPEG images</td></tr><tr><td>Delphi VCL</td><td>Building the desktop application</td></tr></tbody></table><div><div><br></div><div><h3>Using TTMSFNCMaps</h3><div>The application uses the TTMSFNCMaps component as the central map control. One of the nice things about TMS FNC Maps is that OpenStreetMap support works out of the box. This means you immediately have access to free online maps without requiring any commercial map provider.</div><div><br></div><div>Once the map is initialized, it triggers the OnMapInitialized event and it is safe to start working with the map so the application enables all controls:<br><pre class="delphi" name="code">procedure TMapAlbumForm.TMSFNCMaps1MapInitialized(Sender: TObject);
begin
  btnAddPhotos.Enabled := true;
  btnAddRoutes.Enabled := true;
  btnClear.Enabled := true;
end;</pre><br><h3>Loading GPX Routes</h3><div>One of the most powerful features of TMS FNC Maps is the built-in GPX support. The complete GPX loading logic is literally one line of code:<br><pre class="delphi" name="code">procedure TMapAlbumForm.btnAddRoutesClick(Sender: TObject);
begin
  if openRoute.Execute then
  begin
    TMSFNCMaps1.LoadGPXFromFile(openRoute.FileName, true, true, 3, clrPanel.Color);
  end;
end;</pre><div>This loads the GPX track and immediately renders it on the OpenStreetMap layer.</div><div><br></div><div>The parameters allow you to configure:</div><ul><li>Visibility</li><li>Automatic viewport adjustment</li><li>Line width</li><li>Route color</li></ul><div>This makes it very easy to visualize:</div><ul><li>Hiking trails</li><li>Bike rides</li><li>Travel routes</li><li>Running sessions</li><li>Road trips</li></ul><div>without writing custom GPX parsing code.</div><br><h3>Reading GPS Data from JPEG EXIF Metadata</h3><div>Modern smartphones and cameras often store GPS coordinates inside the JPEG EXIF metadata. The project uses the open source EXIF4D parser to extract this information.</div><div><br></div><div>The key code looks like this:<br><pre class="delphi" name="code">img := TAdvCloudExifImage.Create(MapAlbumForm);

try
  img.AutoLoad := false;
  img.FileName := fname;
  img.LoadData;

  if img.HasGPSData then
  begin
    lon := img.Longitude;
    lat := img.Latitude;
  end;
finally
  img.Free;
end;</pre><div>This gives direct access to:</div><ul><li>Latitude</li><li>Longitude</li><li>EXIF metadata</li><li>GPS availability</li></ul><div>without requiring any external libraries or APIs.</div><div><br></div><div><h3>Adding Photos as Interactive Map Markers</h3><div>Once the GPS coordinates are extracted, creating markers on the map becomes straightforward. The application dynamically adds a marker for each image:</div><div><pre class="delphi" name="code">m := Map.AddMarker(lat,lon);
m.IconURL := JpegToBase64(fname, 240);</pre></div><div>This is where things become really fun. Instead of using generic map pins, the marker icon itself becomes a thumbnail of the actual photo. The helper function converts the JPEG image into a Base64 encoded image stream:</div></div><div><pre class="delphi" name="code">&#39;data:image/jpeg;base64,&#39; +
TNetEncoding.Base64.EncodeBytesToString(
  OutStream.Memory,
  OutStream.Size
);</pre>As a result, the map instantly transforms into a visual travel diary. After all images are processed, the map automatically zooms to the correct region, i.e. the region calculated from all positions of the photes retrieved:<div><pre class="delphi" name="code">TMSFNCMaps1.ZoomToBounds(Coords.NorthEast, Coords.SouthWest);</pre></div><div>This creates a very polished user experience with minimal code.</div><div><br></div><div><h3>Cross-Platform Possibilities</h3><div>Because TMS FNC Maps is part of the FNC framework, the same concepts can be reused across:</div><ul><li>VCL</li><li>FMX</li><li>WEB Core</li></ul><div>This opens the door to building:</div><ul><li>Desktop travel journals</li><li>Web-based trip viewers</li><li>Mobile activity trackers</li><li>Corporate fleet tracking dashboards</li><li>Asset management applications</li></ul><div>using largely the same code base.</div><div><br></div><div><h3>Getting the Source Code</h3><div>The complete free source code of this demo application is available here:<br><a href="https://github.com/tmssoftware/TMSFNCMapsAlbum" target="_blank">https://github.com/tmssoftware/TMSFNCMapsAlbum</a></div><div><br></div><div>The project includes:</div><ul><li>Full Delphi source code</li><li>EXIF parser integration</li><li>GPX loading example</li><li>Dynamic image markers</li><li>OpenStreetMap integration</li></ul><h3><br>Conclusion</h3><p>TMS FNC Maps makes it remarkably easy to build visually rich mapping applications in Delphi.</p><p>By combining OpenStreetMap, GPX route loading, EXIF GPS metadata and Dynamic image markers, we can create an interactive photo route album with very little code.</p><p>The demo project shows how powerful the combination of mapping and metadata can be, while still keeping the implementation compact and readable.</p><p>If you are building applications involving:</p><ul><li>Geolocation</li><li>Travel logging</li><li>Asset tracking</li><li>Field service visualization</li><li>Tourism</li><li>Outdoor activities</li></ul><p>this approach offers an excellent starting point.</p><p>Happy coding!</p></div></div></div></div></div></div></div></div>
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	<item>
		<title><![CDATA[﻿TMS Scripter 8 drops legacy Delphi support]]></title>
		<link>https://www.tmssoftware.com/site/blog.asp?post=2480</link>
		<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>
		<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><p style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;"><br></p><h4 style="color: rgb(0, 0, 0);">Get 10% Off TMS WEB Core &amp; TMS FNC UI Pack</h4><p>Enjoy 10% off when purchasing a new license of <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> or <a href="https://www.tmssoftware.com/site/tmsfncuipack.asp" target="_blank">TMS FNC UI Pack</a>. Simply use the promo code <span style="font-weight: 700;">WEBDATAGRID10</span> at checkout.</p><p>Offer valid until <span style="font-weight: 700;">June 15, 2026</span></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>
		<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>
		<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>
		<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>
		<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!</p><p><br></p><h4 style="color: rgb(0, 0, 0);">Get 10% Off TMS WEB Core &amp; TMS FNC UI Pack</h4><p>Enjoy 10% off when purchasing a new license of <a href="https://www.tmssoftware.com/site/tmswebcore.asp" target="_blank">TMS WEB Core</a> or <a href="https://www.tmssoftware.com/site/tmsfncuipack.asp" target="_blank">TMS FNC UI Pack</a>. Simply use the promo code <span style="font-weight: 700;">WEBDATAGRID10</span> at checkout.</p><p>Offer valid until <span style="font-weight: 700;">June 15, 2026</span></p><p><br style="color: rgb(51, 51, 51); font-family: Roboto, sans-serif;"></p></div>
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		<br><br></P>]]></description>
	</item>
	</channel></rss>
