<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PC Application Development Archives | DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/category/application-development/pc-application-development/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Fri, 04 Sep 2026 14:37:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>PC Application Development Archives | DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Electrifying Desktop Application Development with Electron</title>
		<link>https://static.dmcinfo.com/blog/15849/electrifying-desktop-application-development-with-electron/</link>
		
		<dc:creator><![CDATA[DMC]]></dc:creator>
		<pubDate>Mon, 18 Nov 2024 10:58:38 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Web Application Development]]></category>
		<category><![CDATA[Electron]]></category>
		<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/15849/electrifying-desktop-application-development-with-electron/</guid>

					<description><![CDATA[<p>You&#8217;ve already used an Electron application.&#160;So, what is Electron? You’ve already used an Electron application; you just might not have known it. Electron applications provide the basis for some of the apps you use daily—Microsoft Teams, Slack, and Visual Studio Code. Electron is a modern, open-source framework for building desktop applications. Since it runs on [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/15849/electrifying-desktop-application-development-with-electron/">Electrifying Desktop Application Development with Electron</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-you-ve-already-used-an-electron-application-nbsp-so-what-is-electron" class="wp-block-heading">You&#8217;ve already used an Electron application.&nbsp;So, what is Electron?</h2>



<p class="wp-block-paragraph">You’ve already used an Electron application; you just might not have known it. Electron applications provide the basis for some of the apps you use daily—Microsoft Teams, Slack, and Visual Studio Code.</p>



<p class="wp-block-paragraph"><a href="https://www.electronjs.org/" target="_blank" rel="noreferrer noopener">Electron</a> is a modern, open-source framework for building desktop applications. Since it runs on the Node.js runtime, Electron makes it possible to harness JavaScript—typically a web technology—for the development of desktop applications. That’s a powerful way to take our expertise in web technology and apply it to desktop development.</p>



<p class="wp-block-paragraph">Let’s talk about why DMC loves building with Electron!</p>



<h2 id="h-why-dmc-develops-with-electron" class="wp-block-heading">Why DMC develops with Electron</h2>



<p class="wp-block-paragraph"><strong>Electron is cross-platform.</strong> Have a use case that calls for compatibility with Windows, macOS, and Linux? Your Electron application will run on all three, so you need just one code base. You’ll benefit from a smaller, tighter engineering team focused on a single application for minimized time to market and maximized market reach across multiple platforms. A singular code base means that bug fixes, updates, and new features need only be implemented once.</p>



<p class="wp-block-paragraph"><strong>Built on the Node.js runtime.</strong> Equally as important is the runtime itself. Electron runs on Node.js, a JavaScript runtime environment, taking advantage of JavaScript’s position as the<a href="https://survey.stackoverflow.co/2023/#technology-most-popular-technologies"> world’s eminent web technology</a>. Not only does this align with DMC’s sweeping base of expertise in developing JavaScript-based applications, but it also means that you’ll be able to involve your developers in the technical process during an application’s development and support, including post-handoff of the application. That common technical understanding means all parties are speaking the same language and are geared up for success for the lifetime of the application.</p>



<p class="wp-block-paragraph"><strong>Prototyping and proof-of-concepting. </strong>An often-understated utility of Electron development is its handiness for prototyping. Since Electron applications can be spun up quickly — and because you&#8217;ll have access to the vast community of established libraries and frameworks built for JavaScript — an Electron application is ideally suited to building out a viable proof of concept perfect for getting projects off to a promising start.</p>



<p class="wp-block-paragraph"><strong>Trying out new ideas with Electron Fiddle.</strong> Sometimes, all you need is a playground to test out that idea that’s been bouncing around in your head. Electron Fiddle is Electron’s take on <a href="https://jsfiddle.net/">JSFiddle</a>. It’s a lightweight desktop studio for quickly running a simple Electron project. Maybe you want to experiment with a new feature for your application. Or maybe you want to download someone else’s demo and click around on your own. Fiddle allows you&nbsp;to run an Electron application without the overhead of initializing a completely new project. I love this capability and use it both for exploring new features and testing out small tweaks to colleagues’ code. You can read up on <a href="https://www.electronjs.org/fiddle">Electron Fiddle</a> here.</p>



<p class="wp-block-paragraph"><strong>Packaging and Distributing with Electron Forge. </strong>Electron Forge is the all-in-one tool for initiating, configuring, pipelining, and distributing an Electron application. At the start of your project, you’ll use the Electron Forge CLI to spin up a template application. Later, when it’s time to distribute, Electron Forge facilitates three core steps in the distribution sequence:</p>



<ol class="wp-block-list">
<li>The <em>packaging</em> of the application to a bundled executable</li>



<li>The <em>making</em> of the bundled executable into a distributable (such as a .zip or an .exe)</li>



<li>The <em>publishing</em> of the distributable so that your application is available for users to download</li>
</ol>



<h2 id="h-users-love-it" class="wp-block-heading">Users love it</h2>



<p class="wp-block-paragraph">Developing for the web means recognizing the dominant design for the everyday user’s experience. Developers and non-developers alike have come to expect a certain standard: a modern browser serving webpages on a modern JavaScript framework—think React with Material UI or Angular with Angular UI. Why shouldn’t this expectation carry over to the desktop application experience? For consistency and seamlessness, it makes sense to craft desktop applications that look and feel like their web counterparts.</p>



<p class="wp-block-paragraph">An Electron application fluently brings the desktop application must-haves—offline capabilities, local system access, process launching, hardware discovery—and merges them with the familiar user interface elements of the web.</p>



<p class="wp-block-paragraph">When it comes to picking JavaScript frameworks and libraries, Electron lets you choose your own tools. You can keep things simple and lean on basic HTML and CSS. Or you can scaffold up a full-fledged React application via Next.js.</p>



<h2 id="h-case-study-a-desktop-application-for-high-throughput-data-analysis-and-visualization" class="wp-block-heading">Case Study: A desktop application for high-throughput data analysis and visualization</h2>



<p class="wp-block-paragraph"><b>The problem: </b>Our client, a global aerospace and defense technology company, sought to modernize their in-house data analysis and visualization workflow. Their existing workflow relied on several de-coupled applications for processing and exhibiting their data, and there was no official means of warehousing the data. Their team needed a successor platform to provide stronger scalability and faster workflow throughput while still retaining the data reporting functionalities of the legacy system. In modernizing the legacy platform, they laid out a set of key requirements:</p>



<ul class="wp-block-list">
<li>The solution must provide for the storage of captured data.</li>



<li>The solution must ingest large datasets and visualize them clearly, and it must do so performantly.
 
 
<ul class="wp-block-list">
<li>Presentation of data must support advanced visualization controls such as time-shifting, zooming, and time-trending.</li>



<li>Plotting must be capable of processing up to 1 million data points.</li>
</ul>
</li>



<li>The solution must support the validation of data.</li>



<li>The solution’s data processing must be offline-capable. If data is available locally, no network connection shall be required for the core functionality of the application.</li>
</ul>



<p class="wp-block-paragraph"><strong>The solution:</strong> DMC identified a path forward to replace the legacy workflow platform with a single cohesive solution. The bedrock of the solution is a data analysis desktop application built on Electron. Here’s why we landed on Electron for building out this application:</p>



<ul class="wp-block-list">
<li>Electron gives our development team access to a range of powerful JavaScript packages for plotting, UI, and navigation. These are the packages that supply the familiar, expected experience of the web, and Electron brings them to the desktop environment.</li>



<li>Electron’s access to the native OS empowers us to launch a local Python service upon Electron startup. This allows us to easily take advantage of Python’s proven libraries for data crunching, validation, and report generation.</li>
</ul>



<p class="wp-block-paragraph">To round out the solution, we then spun up cloud-based data storage hosted on Amazon’s S3 service. As a whole, DMC delivered an application to meet the&nbsp;client&#8217;s need for a comprehensive data intelligence and reporting platform.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Bring Modern Web Experiences to the Desktop</strong></h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/application-development/" data-type="page" data-id="434">Application Development</a> expertise in Electron, JavaScript, and cross-platform architectures for building scalable <a href="https://static.dmcinfo.com/services/application-development/desktop-application-development/" data-type="page" data-id="680">desktop applications</a> from a unified codebase.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/15849/electrifying-desktop-application-development-with-electron/">Electrifying Desktop Application Development with Electron</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Custom Image Provider Implementation in PySide</title>
		<link>https://static.dmcinfo.com/blog/17084/custom-image-provider-implementation-in-pyside/</link>
		
		<dc:creator><![CDATA[DMC]]></dc:creator>
		<pubDate>Thu, 02 Nov 2023 14:34:05 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[User Interface Design]]></category>
		<category><![CDATA[Computer Vision]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[image rendering]]></category>
		<category><![CDATA[UI]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17084/custom-image-provider-implementation-in-pyside/</guid>

					<description><![CDATA[<p>Introduction In application development, projects require various depths of involvement. Some projects may need you to interconnect a bunch of trendy frameworks and open-source libraries, while other projects will require full-scale development in a lesser-known framework not even designed for the task at hand. A framework that is particularly interesting to work with that occasionally [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17084/custom-image-provider-implementation-in-pyside/">Custom Image Provider Implementation in PySide</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">In application development, projects require various depths of involvement. Some projects may need you to interconnect a bunch of trendy frameworks and open-source libraries, while other projects will require full-scale development in a lesser-known framework not even designed for the task at hand. A framework that is particularly interesting to work with that occasionally lacks proper documentation is PySide.</p>



<p class="wp-block-paragraph">I’ve had an opportunity to explore some image rendering capabilities of the framework and would like to share some tips and best practice standards for your custom application.</p>



<h2 id="h-image-provider" class="wp-block-heading">Image Provider</h2>



<p class="wp-block-paragraph">Before we dive deeper into the topic, I want to note that I am using PySide6, and the image provider lives under PySide6.QtQuick.</p>



<p class="wp-block-paragraph">See this decent, thorough <a href="https://doc.qt.io/qtforpython-6/PySide6/QtQuick/QQuickImageProvider.html" type="link" id="https://doc.qt.io/qtforpython-6/PySide6/QtQuick/QQuickImageProvider.html" target="_blank" rel="noreferrer noopener">documentation page on the QQuickImageProvider</a> that you may wish to examine to gain a better understanding of the concept. </p>



<p class="wp-block-paragraph">The primary purpose of the image provider is to allow the application to render images from sources other than the standard files. Good examples of such sources are the in-memory data and dynamically generated images.</p>



<p class="wp-block-paragraph">If you simply need to render a pre-existing image in a common format, then I highly recommend looking into the native QML Image component capabilities.</p>



<h2 id="h-setup" class="wp-block-heading">Setup</h2>



<p class="wp-block-paragraph"><strong>Disclosure:</strong> code casing might be inconsistent with what you are used to in Python, but I tried incorporating both QML and Python standards where applicable.</p>



<ul class="wp-block-list">
<li><strong>camelCase</strong> – function names (QML/C++ – inherited function override&nbsp;+ consistency)</li>



<li><strong>PascalCase</strong> – objects (QML/C++ – inherited base class + consistency)</li>



<li><strong>snake_case</strong> – variables (Python – standard)</li>
</ul>



<p class="wp-block-paragraph">To begin custom image provider implementation, you have to instantiate your image provider class that inherits <strong>PySide6.QtQuick.QQuickImageProvider.</strong></p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/1-custom-image-provider-definition.png" alt="Custom Image Provider Definition"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 1. custom image provider definition</em></p></p>


<pre class="”brush:python”">
class CustomImageProvider(QQuickImageProvider):
    def __init__(self, image_provider_id: str):
        super().__init__(QQuickImageProvider.ImageType.Image)
        &quot;&quot;&quot;Image provider metadata.&quot;&quot;&quot;
        self.provider_id = image_provider_id

        &quot;&quot;&quot;Image provider data.&quot;&quot;&quot;
        self._images: dict[str, np.ndarray] = dict()

        &quot;&quot;&quot;Suggested utility objects.&quot;&quot;&quot;
        # self.SharedConstants = SharedConstants()
        # self._imageConstructor = ImageConstructor()
        # self._idConstructor = IdConstructor()
</pre>


<p class="wp-block-paragraph">Notice that I created a public property <strong>provider_id.</strong> You do not technically need it, but I highly recommend introducing one, especially if you are anticipating multiple image provider instances. For example, I worked on an app that required multiple tabs to be open in parallel, each with access to an image provider. Since each tab needed its own library of custom images, with image IDs not necessarily <em>globally </em>unique, I had to instantiate a unique image provider per tab to avoid data conflicts. The <strong>provider_id</strong> really helps identify which image provider to use and, more importantly, which image provider can be cleared out for garbage collection purposes as the tab closes.</p>



<p class="wp-block-paragraph">You also need to create a data structure to hold your images. A dictionary has the convenience of id-data mapping. QML will always request an image using a string id and mapping hashable string ids to image data sounds like a perfect opportunity to use a dictionary. To avoid any unexpected behavior, I recommend instantiating the data structure as an internal property and, thus, I called it simply <strong>_images.</strong></p>



<p class="wp-block-paragraph">In the comments I am also suggesting the usage of the following objects:</p>



<ul class="wp-block-list">
<li><strong>SharedConstants</strong> – implement to store and use constants that are shared across the application. Remember, if anything is used in both QML and Python and is ultimately hardcoded, then you should instantiate it as a shared constant, and that constant could be used by both QML and Python. Otherwise, any change to a hardcoded value will become a living nightmare of chasing down all the instances of that value in the code. <strong>Remember, you cannot easily, if at all, debug QML.</strong></li>



<li><strong>ImageConstructor</strong> – use this class to define functions that could be used to construct pixel data that would be stored in the <strong>_images</strong> data structure.</li>



<li><strong>IdConstructor</strong> – use this class to define functions that could be used to construct ids that would be used to store data in the <strong>_images</strong> data structure.</li>
</ul>



<h2 id="h-method-override" class="wp-block-heading">Method Override</h2>



<p class="wp-block-paragraph"><strong>QQuickImageProvider </strong>has <strong>requestPixmap</strong>, <strong>requestImage</strong> and <strong>requestTexture</strong> that you can override&nbsp;to implement your custom functionality. Each method’s signature is similar to the others, so I will focus on the <strong>requestImage</strong> method as I have worked with it the most.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2-requestimage-override.png" alt="Requestimage Override"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 2. requestImage override</em></p></p>


<pre class="”brush:python”">
    def requestImage(self, image_id: str, size: QSize, requested_size: QSize) -&gt; QImage:
        if (image_id in self._images.keys()) and (self._images[image_id] is not None):
            &quot;&quot;&quot;Retrieve the image data from the image library.&quot;&quot;&quot;
            _pixels = self._images[image_id]

            &quot;&quot;&quot;According to the documentation: 
            In all cases, size must be set to the original size of the image. 
            This is used to set the width and height of the relevant Image if 
            these values have not been set explicitly.&quot;&quot;&quot;
            image_size = QSize(_pixels.shape[1], _pixels.shape[0])
            if size:
                size = image_size

            &quot;&quot;&quot;Construct the size of the returned image.&quot;&quot;&quot;
            width = (
                requested_size.width()
                if requested_size.width() &gt; 0
                else image_size.width()
            )
            height = (
                requested_size.height()
                if requested_size.height() &gt; 0
                else image_size.height()
            )

            &quot;&quot;&quot;Construct the image.&quot;&quot;&quot;
            img = QImage(
                _pixels.data,
                height,
                width,
                QImage.Format_RGBA8888,
            )

            return img
        else:
            raise ValueError(
                self.provider_id
                + &quot; image provider was unable to find image &quot;
                + image_id
            )
</pre>


<p class="wp-block-paragraph">Each of the three methods requires a signature that includes <strong>image_id</strong>, <strong>size</strong> and <strong>requested_size</strong>. It is okay to rename those input variables, but the typing must remain the same. Nevertheless, I do not recommend changing the names.</p>



<ul class="wp-block-list">
<li><strong>image_id</strong> – the string ID of the image by which you will be looking up the pixel data in the defined <strong>_images</strong> data structure.</li>



<li><strong>size</strong> – is not technically used for anything in the Python implementation of the image provider. According to the official documentation: “<em>In all cases, size must be set to the original size of the image. This is used to set the width and height of the relevant Image if these values have not been set explicitly.</em>” This is a remnant of the C++ framework. This variable is passed into this method by reference and must be updated. Every other input is passed in by value.</li>



<li><strong>requested_size</strong> – the size of the <strong>Image</strong> component in QML that requested the image from the custom image provider.</li>
</ul>



<p class="wp-block-paragraph">The return value of the overridden&nbsp;method must be a properly constructed <strong>QImage</strong> that QML will be able to display in the application. Notice that the first input for the <strong>QImage</strong> constructor is a <strong>buffer object pointing to the start of the array’s data</strong>. This is also a C++ memory management quirk that Python has to deal with. The format also plays a big role in image rendering. The current <strong>QImage.Format_RGBA8888</strong> decodes the given data array as though there are four <strong>8-bit unsigned integers for red, green, blue, and alpha channels per pixel</strong>. This is crucial, since, if you provide the wrong array type, the displayed image will either be incorrect or won’t show up at all. QML will not throw an error, so you might spend a lot of time trying to figure out why your image is not rendering. A friend of mine told me this, I am certainly not speaking from experience…</p>



<p class="wp-block-paragraph">The sizing of the image is another topic for discussion. Depending on your implementation, you might want to keep the original size of the image in any rendering case, or, on the contrary, you might always want to resize the image to the window size. Sometimes, you might need to implement the sizing so that it is dynamic and dependent on the window state/size. Basically, what I am saying is that the sizing implementation in the code above is subject to change depending on your application needs; however, you must assign the current image original size to the passed by reference <strong>size</strong> variable.</p>



<h2 id="h-data-management" class="wp-block-heading">Data Management</h2>



<p class="wp-block-paragraph">Now that we’ve implemented the basic image provider functionality, we have to develop data management capabilities. Image data must be somehow stored in the image provider, and, since the data structure <strong>_images</strong> was defined internally, we have to define functions that will allow insertion of the new data into the dictionary as well as its removal.</p>



<p class="wp-block-paragraph">It is also important to implement data validation code that could be broken down into helper functions. This is where you make sure that the data provided for addition could be displayed using the defined <strong>QImage </strong>format.</p>



<p class="wp-block-paragraph">You can also include some of your data manipulation and ID construction code. Basically, this is the perfect opportunity to get the best use out of your predefined <strong>ImageConstructor </strong>and <strong>IdConstructor </strong>objects. You still have to make sure that the data on the output is suitable for the defined <strong>QImage </strong>format.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/3-main-image-provider-management-function.png" alt="Main Image Provider Management Function"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 3. main image provider management function</em></p></p>



<p class="wp-block-paragraph">The following implementation is an example of how you can develop the data removal functionality.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/4-supplemental-management-function.png" alt="Supplemental Management Function"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 4. supplemental management function</em></p></p>


<pre class="”brush:python”">

    def addOrUpdateLayer(self, layer_id: str, pixel_data: np.ndarray) -&gt; dict:
        &quot;&quot;&quot;Data validation and manipulations to prep for the render-ready format&quot;&quot;&quot;
        # Insert your data validation code
        # Insert your data manipulation code

        &quot;&quot;&quot;Map the new data to the desired id and save to the library.&quot;&quot;&quot;
        layer = {layer_id: pixel_data}
        self._images.update(layer)

        return layer

    def removeLayer(self, layer_id: str) -&gt; None:
        &quot;&quot;&quot;Apply key-value pair deletion logic&quot;&quot;&quot;
        del self._images[layer_id]
</pre>


<h2 id="h-helper-functionality" class="wp-block-heading">Helper Functionality</h2>



<p class="wp-block-paragraph">I highly recommend keeping your custom image provider as lightweight as possible for scaling and maintainability purposes; however, you may still want to implement some basic helper functions like:</p>



<ul class="wp-block-list">
<li><strong>isIDTaken</strong> – to check whether a given ID already exists in the image provider to avoid overriding&nbsp;stored data<strong>.</strong></li>



<li><strong>isImageLoaded</strong> – to check whether the data is present in the data structure before querying it.</li>



<li><strong>clearImageProviderInstance</strong> – to manage the memory that the image provider occupies.</li>
</ul>



<p class="wp-block-paragraph">Example implementation for each is presented below:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/5-supplemental-image-provider-functionality.png" alt="Supplemental Image Provider Functionality"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 5. supplemental image provider functionality</em></p></p>


<pre class="”brush:python”">
    def isIDTaken(self, image_id: str) -&gt; bool:
        return image_id in self._images.keys()

    def isImageLoaded(self, image_id: str) -&gt; bool:
        return self._images[image_id] is not None

    def clearImageProviderInstance(self):
        self._qml_engine.removeImageProvider(self._id)
</pre>


<h2 id="h-in-action" class="wp-block-heading">In Action</h2>



<p class="wp-block-paragraph">The following code snippets do not require much explanation but are a good starting point in learning how to use the custom image provider in the scope of any application. Note, we must add an image provider to the application engine.</p>



<p class="wp-block-paragraph">Since we defined the image provider class with a unique ID property, you must provide one to each image provider you insert into the application. Keep in mind that QML engine requires you to provide a unique ID in the first place, so you should just store that ID in the image provider itself.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/6-image-provider-usage-in-an-example.png" alt="Image Provider Usage in an Example"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 6. image provider usage in an example</em></p></p>


<pre class="”brush:python”">
if __name__ == &quot;__main__&quot;:
    &quot;&quot;&quot;Set up the application.&quot;&quot;&quot;
    app = QApplication([])
    engine = QQmlApplicationEngine()

    &quot;&quot;&quot;Instantiate an image provider.&quot;&quot;&quot;
    unique_id = &quot;unique_image_provider_id&quot;
    image_provider = CustomImageProvider(unique_id)
    engine.addImageProvider(unique_id, image_provider)

    &quot;&quot;&quot;Add an image to the image provider.&quot;&quot;&quot;
    image_provider.addOrUpdateLayer(
        &quot;unique_image_id&quot;,
        np.array(
            [
                [
                    [255, 0, 0, 255],
                    [255, 0, 0, 255],
                    [255, 0, 0, 255],
                ],
                [
                    [0, 255, 0, 255],
                    [0, 255, 0, 255],
                    [0, 255, 0, 255],
                ],
                [
                    [0, 0, 255, 255],
                    [0, 0, 255, 255],
                    [0, 0, 255, 255],
                ],
            ],
            dtype=np.uint8,
        ),
    )

    &quot;&quot;&quot;Load and tun the app.&quot;&quot;&quot;
    engine.load(&quot;main.qml&quot;)
    app.exec()
</pre>


<p class="wp-block-paragraph">According to the code above, the example image we are defining has a 3-pixel height and a 3-pixel width. It’s a square of three colored stripes: red, green, blue with full opacity (4<sup>th</sup> alpha channel).</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/7-example-qml-code.png" alt="Example QML Code"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 7. example QML code</em></p></p>


<pre class="”brush:html”">
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 600
    height: 600
    title: &quot;Custom Image App&quot;

    Image {
        anchors.fill: parent
        // Set the source to the custom image provider.
        // Include the image id if you would like to show a particular image.
        source: &quot;image://unique_image_provider_id/unique_image_id&quot;
    }
}
</pre>


<p class="wp-block-paragraph">I am choosing to keep the QML code fairly simple and straightforward. This code snippet does not necessarily follow any coding standards, but rather serves as a quick and dirty playground to show off some custom image provider capabilities.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/8-produced-result.png" alt="Produced Result"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 8. produced result</em></p></p>



<p class="wp-block-paragraph">The produced result is just as we expected, a stretched out 3&#215;3 pixel image! Notice that the <strong>Image </strong>component anchors onto its parent, so the <strong>requestedSize</strong> will be inherited from the <strong>ApplicationWindow</strong> component size.</p>



<p class="wp-block-paragraph">We could also set the <strong>requestedSize</strong> manually in QML. This way, the size of the constructed image will not change dynamically with the <strong>ApplicationWindow</strong>.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/9-example-of-setting-the-image-size-manually.png" alt="Example of Setting the Image Size Manually"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 9. example of setting the image size manually</em></p></p>


<pre class="”brush:html”">
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 600
    height: 600
    title: &quot;Custom Image App&quot;

    Image {
        width: 450
        height: 300
        // Set the source to the custom image provider.
        // Include the image id if you would like to show a particular image.
        source: &quot;image://unique_image_provider_id/unique_image_id&quot;
    }
}
</pre>


<p class="wp-block-paragraph">Notice the difference in the newly rendered result:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/10-result-of-setting-the-image-size-manually.png" alt="Result of Setting the Image Size Manually"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 10. result of setting the image size manually</em></p></p>



<h2 id="h-alternative-setup" class="wp-block-heading">Alternative Setup</h2>



<p class="wp-block-paragraph">A less recommended, but valid nonetheless, implementation is to let the custom image provider insert itself into the application upon instantiation.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/11-alternative-custom-image-provider-definition.png" alt="Alternative Custom Image Provider Definition"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 11. alternative custom image provider definition</em></p></p>


<pre class="”brush:python”">
class CustomImageProvider(QQuickImageProvider):
    def __init__(
        self, image_provider_id: str, qml_application_engine: QQmlApplicationEngine
    ):
        super().__init__(QQuickImageProvider.ImageType.Image)
        &quot;&quot;&quot;Image provider metadata.&quot;&quot;&quot;
        self.provider_id = image_provider_id

        &quot;&quot;&quot;Handle image provider self insertion into the application.&quot;&quot;&quot;
        self._qml_application_engine = qml_application_engine
        self._qml_application_engine.addImageProvider(self.provider_id, self)

        &quot;&quot;&quot;Image provider data.&quot;&quot;&quot;
        self._images: dict[str, np.ndarray] = dict()

        &quot;&quot;&quot;Suggested utility objects.&quot;&quot;&quot;
        # self.SharedConstants = SharedConstants()
        # self._imageConstructor = ImageConstructor()
        # self._idConstructor = IdConstructor()
</pre>


<p class="wp-block-paragraph">The following is an example of such an image provider in action.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/12-example-usage-of-the-alternative-cuatom-image-provider.png" alt="Example Usage of the Alternative Custom Image Provider"/></figure>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Image 12. example usage of the alternative custom image provider</em></p></p>


<pre class="”brush:python”">
if __name__ == &quot;__main__&quot;:
    &quot;&quot;&quot;Set up the application.&quot;&quot;&quot;
    app = QApplication([])
    engine = QQmlApplicationEngine()

    &quot;&quot;&quot;Instantiate an image provider.&quot;&quot;&quot;
    unique_id = &quot;unique_image_provider_id&quot;
    image_provider = CustomImageProvider(unique_id, engine)

    &quot;&quot;&quot;Add an image to the image provider.&quot;&quot;&quot;
    image_provider.addOrUpdateLayer(
        &quot;unique_image_id&quot;,
        np.array(
            [
                [
                    [255, 0, 0, 255],
                    [255, 0, 0, 255],
                    [255, 0, 0, 255],
                ],
                [
                    [0, 255, 0, 255],
                    [0, 255, 0, 255],
                    [0, 255, 0, 255],
                ],
                [
                    [0, 0, 255, 255],
                    [0, 0, 255, 255],
                    [0, 0, 255, 255],
                ],
            ],
            dtype=np.uint8,
        ),
    )

    &quot;&quot;&quot;Load and tun the app.&quot;&quot;&quot;
    engine.load(&quot;main.qml&quot;)
    app.exec()
</pre>


<h2 id="h-notes-and-tips" class="wp-block-heading">Notes and Tips</h2>



<ol class="wp-block-list">
<li>Keep your image provider lightweight. Put all the functionality you think is relevant to it somewhere else, because chances are, it is not. The image provider should really be treated as a data structure that has functionality only to store and remove images.</li>



<li>Make your image provider usable in every place of your application. Avoid putting select-component/window-only functionality in here.</li>



<li>This implementation will also likely work in PySide2 since that is where I originally developed it.</li>
</ol>



<p class="wp-block-paragraph">&nbsp;<b data-stringify-type="bold">Learn more about&nbsp;</b><b data-stringify-type="bold"><a data-sk="tooltip_parent" data-stringify-link="https://static.dmcinfo.com/latest-thinking/blog/id/10393/resizing-uis-with-qml-layouts" delay="150" href="https://static.dmcinfo.com/latest-thinking/blog/id/10393/resizing-uis-with-qml-layouts" rel="noopener noreferrer" target="_blank">Resizing UIs with QML Layouts</a></b><b data-stringify-type="bold">&nbsp;and&nbsp;</b><b data-stringify-type="bold"><a data-sk="tooltip_parent" data-stringify-link="https://static.dmcinfo.com/contact" delay="150" href="https://static.dmcinfo.com/contact" rel="noopener noreferrer" target="_blank">contact us</a></b><b data-stringify-type="bold">&nbsp;today for your next project.</b></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17084/custom-image-provider-implementation-in-pyside/">Custom Image Provider Implementation in PySide</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using a QAbstractListModel in QML</title>
		<link>https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Mon, 27 Mar 2023 09:36:10 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Qt]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/</guid>

					<description><![CDATA[<p>The QAbstractListModel class provided by Qt can be used to organize data that will be presented visually as a list or table. Standardizing the interface with an abstract class like QAbstractListModel makes it easy to keep your model data completely isolated from your view (a software design principle known as &#8220;separation of concerns&#8220;). That abstraction [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/">Using a QAbstractListModel in QML</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The <code><a href="https://doc.qt.io/qt-6/qabstractlistmodel.html">QAbstractListModel</a></code> class provided by Qt can be used to organize data that will be presented visually as a list or table. Standardizing the interface with an abstract class like <code>QAbstractListModel</code> makes it easy to keep your model data completely isolated from your view (a software design principle known as &#8220;<a href="https://csrc.nist.gov/glossary/term/separation_of_concerns">separation of concerns</a>&#8220;). That abstraction makes it a powerful and flexible tool, but it also makes the learning curve steep.</p>



<p class="wp-block-paragraph">The goal of this post is to provide concrete examples, explanations, and definitions of terms so you can more easily make use of the <code>QAbstractListModel</code> class. For your reference, you can see the complete <a href="https://github.com/marklocascio/qml-listmodel-example">example code on GitHub</a>.</p>



<h2 class="wp-block-heading" id="h-example-gui">Example GUI</h2>



<p class="wp-block-paragraph">Let&#8217;s say we&#8217;ve got a list of devices with which our software interacts. The data we&#8217;ve got for each device is:</p>



<ul class="wp-block-list">
<li>A human-readable name (a string)</li>



<li>A serial number (an integer)</li>



<li>Whether or not the device is currently connected (a Boolean)</li>
</ul>



<p class="wp-block-paragraph">Our example GUI will look like this:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/app.png" alt="Example GUI"/></figure>



<p class="wp-block-paragraph">Part of the appeal of Qt is that you can make extremely slick UIs. We will not be doing that here in order to keep the focus on listmodel concepts. I&#8217;ve resisted the urge to add eye candy for the sake of clarity, and I have crafted the example to make it clear how you <strong><em>could</em></strong> stylize the list if you wanted to.</p>



<p class="wp-block-paragraph">Additionally, the example uses Qt&#8217;s Python bindings (<a href="https://pypi.org/project/PySide6/">PySide6</a>). Everything here is equally applicable to C++, but again, for the sake of simplicity, it is presented as a Python application. The QML is identical in both cases.</p>



<h2 class="wp-block-heading" id="h-the-qml-description">The QML Description</h2>



<p class="wp-block-paragraph">First, we&#8217;ll describe the visualization of our list of devices in QML. The <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html">QML <code>ListView</code> class</a> is a great start. We&#8217;ll set three properties:</p>



<ul class="wp-block-list">
<li>
<p class="wp-block-paragraph"><code><span style="color:#c00000">model</span></code>: this is what we&#8217;ll use to bind the QML <code>ListView</code> to a <code>QAbstractListModel</code> class defined in C++ or Python</p>
</li>



<li><code><span style="color:#2f5496">delegate</span></code>: this is used to define how each item in the list is rendered as a QML object</li>



<li><code><span style="color:#bf8f00">highlight</span></code>: this is not necessary to use a <code>ListView</code>, but it is generally useful to visualize a selected item in the list</li>
</ul>



<h2 class="wp-block-heading" id="h-basic-example">Basic Example</h2>



<p class="wp-block-paragraph">A rough first draft of the QML might look like this (for the final version, <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/main.qml">see here</a>):</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;id: deviceList<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#c00000">model: controller.listmodel</span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Item {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width: deviceList.width<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Text {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text: "Placeholder"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MouseArea {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anchors.fill: parent<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span>&nbsp;&nbsp;<span style="color:#538135">// Item delegate</span><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#bf8f00">highlight: Rectangle { color: "lightBlue" }</span><br>
}&nbsp;&nbsp;<span style="color:#538135">// ListView</span></span></code></p>



<h2 class="wp-block-heading" id="h-using-the-model-and-the-delegate">Using the Model and the Delegate</h2>



<p class="wp-block-paragraph">In my <code>main()</code> function, I <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/__main__.py#L18">set a context property</a> called <code>controller</code> that refers to an instance of my <code>Controller</code> class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>qml_app_engine = QQmlApplicationEngine()
qml_context = qml_app_engine.rootContext()
controller = Controller(parent=app)
qml_context.setContextProperty("controller", controller)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">qml_app_engine = QQmlApplicationEngine()</span></span>
<span class="line"><span style="color: #D4D4D4">qml_context = qml_app_engine.rootContext()</span></span>
<span class="line"><span style="color: #D4D4D4">controller = Controller(</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">=app)</span></span>
<span class="line"><span style="color: #D4D4D4">qml_context.setContextProperty(</span><span style="color: #CE9178">&quot;controller&quot;</span><span style="color: #D4D4D4">, controller)</span></span></code></pre></div>



<p class="wp-block-paragraph">The <code>Controller</code> class <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/controller.py#L12">exposes a Qt property</a> called <code>listmodel</code>. Note that that property is declared as a <code>QObject</code> in my Python code, and that it does not need a property change signal (i.e., I use <code>constant=True</code>). In the QML above, I bind the <code>ListView</code>&#8216;s <code>model</code> property to the <code>listmodel</code> property of my <code>controller</code> object:</p>



<p class="wp-block-paragraph"><code><span style="color:#c00000">model: controller.listmodel</span></code></p>



<p class="wp-block-paragraph">The <code><span style="color:#2f5496">delegate</span></code> property of <code>ListView</code> is like a template that defines how each item in the list is rendered as a QML object. For the sake of demonstration, we&#8217;ll keep it simple here. I made it a QML <code><a href="https://doc.qt.io/qt-6/qml-qtquick-item.html">Item</a></code> that is as wide as the <code>ListView</code> itself and contains a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-text.html">Text</a></code> object and a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-mousearea.html">MouseArea</a></code>, but you can make it anything you like (you&#8217;ll generally make it much fancier)! For example, you might instead have something like a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-rowlayout.html">RowLayout</a></code> containing a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-controls2-checkbox.html">Checkbox</a></code>, an <code><a href="https://doc.qt.io/qt-6/qml-qtquick-image.html">Image</a></code>, and a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-text.html">Text</a></code>. (Haven&#8217;t used layouts yet? <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10393/resizing-uis-with-qml-layouts">Start here!</a>) However you want each item in your list to be visualized, you can define it in your <code><span style="color:#2f5496">delegate</span></code>. For simplicity, I often start by just rendering it all in a <code>Text</code> item. We&#8217;ll look at how to access each item of data (name, serial number, and connection status) in the next section.</p>



<p class="wp-block-paragraph">Note also that the <code>MouseArea</code> in my <code><span style="color:#2f5496">delegate</span></code> is used to select an item in the list. Each item in the list is instantiated as a <code><span style="color:#2f5496">delegate</span></code> object, so each item in the list has a <code>MouseArea</code> that can handle click events. We&#8217;ll look at this in more detail later also.</p>



<h2 class="wp-block-heading" id="h-the-qabstractlistmodel-class">The QAbstractListModel Class</h2>



<p class="wp-block-paragraph">If you are managing a large quantity of data and you want to visualize it on your QML GUI, you have a few options. For simple cases, a <a href="https://doc.qt.io/qt-6/qml-qtquick-repeater.html"><code>Repeater</code></a> can usually get the job done just fine and is conceptually very easy to grasp. However, for very large lists, <a href="https://doc.qt.io/qt-6/qml-qtquick-repeater.html#considerations-when-using-repeater">a <code>Repeater</code> is not recommended</a> because it instantiates all visual items at once. In cases where you have a lot of data, you often only want to view or update a small section of it. For these cases, QML provides the <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html"><code>ListView</code></a> object, which expects to be bound to a <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html">QAbstractListModel</a> object in your C++ or Python application.</p>



<p class="wp-block-paragraph"><code>QAbstractListModel</code> is an abstract class that cannot be instantiated itself, so you need to create a new class that inherits from it and is specialized for your needs. I would first suggest reading the section of its documentation titled <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html#subclassing">&#8220;Subclassing,&#8221;</a> which states that:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">When subclassing QAbstractListModel, you must provide implementations of the <code>rowCount()</code> and <code>data()</code> functions. Well behaved models also provide a <code>headerData()</code> implementation.</p>



<p class="wp-block-paragraph">If your model is used within QML and requires roles other than the default ones provided by the <code>roleNames()</code> function, you must override it.</p>



<p class="wp-block-paragraph">For editable list models, you must also provide an implementation of <code>setData()</code>&nbsp;and implement the <code>flags()</code> function so that it returns a value containing Qt::ItemIsEditable.</p>
</blockquote>



<p class="wp-block-paragraph">It&#8217;s unlikely that those few sentences made it immediately obvious what you need to do. Let&#8217;s start with what confused me most when I got started: the concept of a &#8220;role.&#8221;</p>



<h2 class="wp-block-heading" id="h-roles">Roles</h2>



<p class="wp-block-paragraph">Think about the data we&#8217;re presenting. We have a list of devices, and each device in the list has three pieces of data (name, serial number, and connection status). You might think of each device&#8217;s data as a row in a table:</p>


<table border="0" cellpadding="1" cellspacing="1">
<thead>
<tr>
<th scope="col" style="text-align: left;">Serial Number</th>
<th scope="col" style="text-align: left;">Human-readable name</th>
<th scope="col" style="text-align: left;">Connected?</th>
</tr>
</thead>
<tbody>
<tr>
<td>123</td>
<td>name1</td>
<td>No</td>
</tr>
<tr>
<td>456</td>
<td>name2</td>
<td>Yes</td>
</tr>
<tr>
<td>789</td>
<td>name3</td>
<td>No</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">The simplest analogy is that the &#8220;role&#8221; is the piece of data that goes in each column. You might also think of it as identifying each piece of data in each object in the list. So, we will define our &#8220;roles&#8221; as <code>name</code>, <code>serial</code>, and <code>connected</code>.</p>



<p class="wp-block-paragraph">Notice also that Qt provides a <a href="https://doc.qt.io/qt-6/qt.html#ItemDataRole-enum">built-in <code>ItemDataRole</code> enum</a>. I initially found this very confusing, because it provides roles with names like <code>Qt::DisplayRole</code> and <code>Qt::EditRole</code>, which don&#8217;t really sound like individual data items to me. The built-in roles are intended for use with built-in classes like <code>QString</code> and <code>QIcon</code>, and they don&#8217;t necessarily make sense for this particular custom class, so don&#8217;t let it throw you off. Consider though, that you might have roles (items of data in your class) that aren&#8217;t pieces of data that you&#8217;d want to render as text but are instead pieces of data that determine how the display of that data behaves (like a background color or an icon).</p>



<p class="wp-block-paragraph">Roles that you want to define yourself for your own custom class can use enum values starting with <code>Qt::UserRole</code>, which has value 0x0100 = 256.</p>



<h2 class="wp-block-heading" id="h-an-aside-on-tables">An Aside on Tables</h2>



<p class="wp-block-paragraph">It&#8217;s worth mentioning that there is indeed a <a href="https://doc.qt.io/qt-6/qabstracttablemodel.html"><code>QAbstractTableModel</code></a> class as well. As shown above, we can use the role as the &#8220;second dimension&#8221; of our one-dimensional list, making it look like a table. So, when would you use <code>QAbstract<u>Table</u>Model</code>? You might use it when you have a 2D array of objects, where each object has a set of properties that you identify as &#8220;roles.&#8221;</p>



<p class="wp-block-paragraph">What makes the most sense as a data model will depend on your specific data, and it may be confusing to think about a list in terms of &#8220;rows&#8221; if your data doesn&#8217;t really seem like a table (you may not even arrange items vertically on your UI, which makes the terminology much worse!). We&#8217;re stuck with the &#8220;row&#8221; and &#8220;column&#8221; terminology used by Qt here, but the models can be used in whatever way makes the most sense for the data you need to represent.</p>



<p class="wp-block-paragraph">In most real-life applications (as well as in the example code here), I use a 1D array with multiple roles, because I find that to be the simplest and most natural data structure. However, both <code>QAbstract<u>Table</u>Model</code> and <code>QAbstract<u>Item</u>Model</code> are available to you if you need a more complex visualization of more complex data. Once you get a handle on <code>QAbstract<u>List</u>Model</code>, the more general classes will make more sense.</p>



<h2 class="wp-block-heading" id="h-how-are-roles-used-in-qml">How are Roles Used in QML?</h2>



<p class="wp-block-paragraph">As shown above, you will use the QML <code>ListView</code>&#8216;s model property to specify an object in your C++ or Python code that inherits from <code>QAbstractListModel</code>. The <code><span style="color:#2f5496">delegate</span></code> property is then used to define the QML object that will visualize that data. In this case, each item in the list has three roles (<code>name</code>, <code>serial</code>, and <code>connected</code>), and we&#8217;ll want to access each of those data items in QML independently.</p>



<p class="wp-block-paragraph">Looking at the GUI again:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/app-1.png" alt="GUI example"/></figure>



<p class="wp-block-paragraph">Each item in the list is visualized as a <code>Text</code> item where the content follows this pattern:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">[<span style="color:#00b050">index</span> of item]: [<span style="color:#00b050">name</span> role] ([<span style="color:#00b050">serial</span> role]) - [<span style="color:#00b050">connection</span> role]</span></code></p>



<p class="wp-block-paragraph">In our <span style="color:#2f5496"><code>delegate</code></span>, we can access each item of data using the name of the role:</p>



<p class="wp-block-paragraph"><span style="white-space: nowrap;"><code>ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#c00000">model: controller.listmodel</span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Text {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text: `${<span style="color:#00b050">index</span>}: ${<span style="color:#00b050">name</span>} (${<span style="color:#00b050">serial</span>}) - ${<span style="color:#00b050">connected</span> ? "OK" : "NOT FOUND"}`<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span><br>
}</code></span></p>



<p class="wp-block-paragraph">Inside the <span style="color:#2f5496"><code>delegate</code></span> object we can simply use <span style="color:#00b050"><code>index</code></span>, <span style="color:#00b050"><code>name</code></span>, <span style="color:#00b050"><code>serial</code></span> and <span style="color:#00b050"><code>connected</code></span> as if they are bound to the individual data items inside that element of the list. <span style="color:#00b050"><code>index</code></span> is provided out-of-the-box by <code>ListView</code>, but <span style="color:#00b050"><code>name</code></span>, <span style="color:#00b050"><code>serial</code></span>, and <span style="color:#00b050"><code>connected</code></span> are the <strong><em>names of roles we define ourselves</em></strong>. Within the <span style="color:#2f5496"><code>delegate</code></span> object, we can refer to those names, and our child class of <code>QAbstractListModel</code> will provide methods that QML can use to link those names to specific pieces of data.</p>



<p class="wp-block-paragraph">The <span style="color:#00b050"><code>index</code></span> value is also useful in our <code>MouseArea</code>. We added the <code>MouseArea</code> so that the user could click an item in the list and manipulate it. Since the <code>MouseArea</code> is inside the <span style="color:#2f5496"><code>delegate</code></span>, we have access to the index value. In the <code>MouseArea</code>&#8216;s signal handler <code>onClicked</code>, we will want to set <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html#currentIndex-prop">the <code>currentIndex</code> property</a> of the <code>ListView</code> to the index of the item in the list that was clicked:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;id: deviceList<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Item {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MouseArea {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onClicked: deviceList.currentIndex = index<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp; // MouseArea<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span>&nbsp;&nbsp;<span style="color:#538135">// Item delegate</span><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#bf8f00">highlight: Rectangle { color: "lightBlue" }</span><br>
}&nbsp;&nbsp;<span style="color:#538135">// ListView</span></span></code></p>



<p class="wp-block-paragraph">Setting the <code>currentIndex</code> of the <code>ListView</code> enables the <code>ListView</code> to automatically animate the <span style="color:#bf8f00"><code>highlight</code></span> object that we defined. When the user clicks an item in the list, it will move the <code>Rectangle</code> to highlight the selected list item.</p>



<p class="wp-block-paragraph">As an exercise for the reader, try making the <span style="color:#2f5496"><code>delegate</code></span> more interesting. Instead of indicating the state of the connected role with just a <code>Text</code>, try using the <code>connected</code> role to set the text color of the <span style="color:#2f5496"><code>delegate</code></span>, or add an icon to each row that indicates whether or not the device is connected.</p>



<h2 class="wp-block-heading" id="h-setting-up-the-roles">Setting Up the Roles</h2>



<p class="wp-block-paragraph">Let&#8217;s circle back to this statement in the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">If your model is used within QML and requires roles other than the default ones provided by the <code>roleNames()</code> function, you must override it.</p>
</blockquote>



<p class="wp-block-paragraph">We want to use our own role names for this, so we can use names that make sense in our <span style="color:#2f5496"><code>delegate</code></span> (like <code>name</code>, <code>serial</code>, and <code>connected</code>). The mapping from integer role enum values (like <code>Qt::UserRole</code>) to strings of characters is established with the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#roleNames"><code>QAbstractItemModel::roleNames()</code></a> method, which your custom listmodel will inherit. All classes that inherit <code>QAbstractListModel</code> need to implement this method, which returns the map from integers to byte arrays. In C++, this map is a <code>QHash&lt;int, QByteArray&gt;</code>, and in Python it is a basic <code>dict</code>. The integer is the role enum value, and the byte array is the string name used in QML to access that role in each item of the listmodel.</p>



<p class="wp-block-paragraph">I like to set up my roles by doing two things: creating an enum (starting with the value <code>Qt::UserRole</code> and incrementing from there) that enumerates my custom roles, and then creating a dictionary that maps the role enum values to byte arrays (the names used by QML to access elements of the model). In our example, I might do:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>class DeviceItemRoles(IntEnum):    
  NAME = Qt.UserRole
  SERIAL = auto()
  CONNECTED = auto()
  _role_names = {
      DeviceItemRoles.NAME: b'name',
      DeviceItemRoles.SERIAL: b'serial',
      DeviceItemRoles.CONNECTED: b'connected'
  }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DeviceItemRoles</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">IntEnum</span><span style="color: #D4D4D4">):    </span></span>
<span class="line"><span style="color: #D4D4D4">  NAME = Qt.UserRole</span></span>
<span class="line"><span style="color: #D4D4D4">  SERIAL = auto()</span></span>
<span class="line"><span style="color: #D4D4D4">  CONNECTED = auto()</span></span>
<span class="line"><span style="color: #D4D4D4">  _role_names = {</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.NAME: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.SERIAL: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;serial&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.CONNECTED: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;connected&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span></code></pre></div>



<p class="wp-block-paragraph">Note again that in Python, the values are byte arrays (<code>b''</code>), not strings.</p>



<p class="wp-block-paragraph">With this setup, the delegate of our <code>ListView</code> can access each piece of data in each list item using the strings <code>name</code>, <code>serial</code>, and <code>connected</code>. QML knows how the role integers (from the enum) map to the names because it knows that a <code>QAbstractListModel</code> must have a <code>roleNames()</code> method, so now we just need to give it a way to access each piece of data given the list index and the role. That is the job of the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#data"><code>QAbstractItemModel::data()</code></a> method, which we will get to shortly.</p>



<h2 class="wp-block-heading" id="h-subclassing-a-qabstractlistmodel">Subclassing a QAbstractListModel</h2>



<p class="wp-block-paragraph">Recall from the <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html#subclassing">documentation</a> that subclasses of <code>QAbstractListModel</code> need to implement the <code>rowCount()</code> and <code>data()</code> methods, plus <code>roleNames()</code> if the listmodel is used in QML. We&#8217;ll cover these one-by-one, but first let&#8217;s define how we&#8217;ll store our data.</p>



<h2 class="wp-block-heading" id="h-data-storage">Data Storage</h2>



<p class="wp-block-paragraph">I find that the easiest way to store the data (for a Python application) is with a list of dictionaries, where each dictionary uses the role enum as the key for each data value. This is by no means the only way, but it is very simple and often sufficient. So, you might start developing your custom listmodel class like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>class DeviceListModel(QAbstractListModel):

    def __init__(self):

        super().__init__()

        self._data = []



    def add_device(self, name, serial, connected):

        new_row = {

            DeviceItemRoles.NAME: name,

            DeviceItemRoles.SERIAL: serial,

            DeviceItemRoles.CONNECTED: connected

        }



        self._data.append(new_row)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DeviceListModel</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">QAbstractListModel</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__init__</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">super</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">__init__</span><span style="color: #D4D4D4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data = []</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">add_device</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">serial</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">connected</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        new_row = {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.NAME: name,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.SERIAL: serial,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.CONNECTED: connected</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data.append(new_row)</span></span></code></pre></div>



<p class="wp-block-paragraph">When we create a new listmodel, the list of data, <code>self._data</code>, is just an empty list. We can then add device data to the list with the <code>add_device()</code> method, which takes the name, serial number, and connection status, puts them in a dictionary with the appropriate role enum values as keys, and then appends that dictionary to the data list.</p>



<p class="wp-block-paragraph">Now that we&#8217;ve established how the data is stored, we can fill out the required methods.</p>



<h2 class="wp-block-heading" id="h-the-rolenames-method">The roleNames() Method</h2>



<p class="wp-block-paragraph"><code>roleNames()</code> is the easiest to implement, because it&#8217;s already done! The <code>_role_names</code> dictionary from above is exactly what <code>roleNames()</code> should return, so this one&#8217;s a no-brainer:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def roleNames(self):
    return _role_names</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">roleNames</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> _role_names</span></span></code></pre></div>



<p class="wp-block-paragraph">That&#8217;s it!</p>



<h2 class="wp-block-heading" id="h-the-rowcount-method">The rowCount() Method</h2>



<p class="wp-block-paragraph"><code>rowCount()</code> is similarly straightforward. The number of rows is just the number of elements in our <code>self._data</code> list. We don&#8217;t need to do much here either:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def rowCount(self, parent=QModelIndex()):
    return len(self._data)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">rowCount</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">=QModelIndex()):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">len</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data)</span></span></code></pre></div>



<p class="wp-block-paragraph">The only thing to address is that weird <code>parent</code> argument. What&#8217;s that about?</p>



<p class="wp-block-paragraph">It comes from the base class, <code>QAbstract<u>Item</u>Model</code>. The base class is more general. Whereas <code>QAbstract<u>List</u>Model</code> represents a one-dimensional list of items that all have the same type of elements, <code>QAbstract<u>Item</u>Model</code> can describe trees and other complex hierarchical structures. In those cases, you need to provide the index of a parent object in the tree so the <code>rowCount()</code> method can return the number of children <strong><em>of that parent</em></strong>. Once you get your bearings with the <code>QAbstract<u>List</u>Model</code>, you can dig into the <code>QAbstract<u>Item</u>Model</code>, but for now, let&#8217;s just ignore <code>parent</code>, because it doesn&#8217;t apply to a one-dimensional list. Just give it a default <code>QModelIndex</code>.</p>



<h2 class="wp-block-heading" id="h-the-data-method">The data() Method</h2>



<p class="wp-block-paragraph">Finally, we need to implement a method that will return data values when QML asks for them. <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#data">The C++ signature of this method is:</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C++</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>QVariant QAbstractItemModel::data(const QModelIndex &amp;index, int role = Qt::DisplayRole) const</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #4EC9B0">QVariant</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">QAbstractItemModel</span><span style="color: #D4D4D4">::</span><span style="color: #DCDCAA">data</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">QModelIndex</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&amp;</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">int</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">Qt</span><span style="color: #D4D4D4">::</span><span style="color: #4EC9B0">DisplayRole</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">const</span></span></code></pre></div>



<p class="wp-block-paragraph">So, our implementation of the method needs to take the index of the row we want (as a <code>QModelIndex</code> object) and the role of the individual data item we want (as an integer, like our convenient <code>DeviceItemRoles</code> enum), and it will return the data as a <code>QVariant</code>. With the PySide6 bindings, there is no <code>QVariant</code>. We can return whatever Python object we want, and if there&#8217;s no data at that index or with that role, we can just return <code>None</code>. A simple implementation in Python looks like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def data(self, index, role):

    if role not in list(DeviceItemRoles):

        return None



    try:

        device = self._data&#91;index.row()&#93;

    except IndexError:

        return None



    if role in device:

        return device&#91;role&#93;

    return None</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">data</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">list</span><span style="color: #D4D4D4">(DeviceItemRoles):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">try</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        device = </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data&#91;index.row()&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IndexError</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> device:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> device&#91;role&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span></code></pre></div>



<p class="wp-block-paragraph">There&#8217;s a little more meat here than in our <code>roleNames()</code> and <code>rowCount()</code> methods. First, we check that the role integer that was passed in is an item in our <code>DeviceItemRoles</code> enum. If it isn&#8217;t, then something is looking for a role we aren&#8217;t providing, so we&#8217;ll just return <code>None</code>.</p>



<p class="wp-block-paragraph">Next, we&#8217;ll try to get the index of the item in the list. Note that you can&#8217;t index the <code>self._data</code> list using the <code>index</code> argument directly. You need to call <code>index.row()</code>, which is a consequence of the fact that <code>QAbstract<u>List</u>Model</code> is a child of the more general <code>QAbstract<u>Item</u>Model</code> class, which is not necessarily a 1D list. If you look at the <a href="https://doc.qt.io/qt-6/qmodelindex.html"><code>QModelIndex</code></a> class, you&#8217;ll see that, in addition to <code>row()</code>, it also provides <code>column()</code>, as well as various other methods that only apply to more complex structures.</p>



<p class="wp-block-paragraph">Anyway, if the given index is out of bounds, then something is looking for an invalid row, and we return <code>None</code>. Beyond that, both the index and the role are ok, so we return the appropriate value by indexing the list to get a dictionary, and then looking up the value of the role key in that dictionary. Whatever data was stored there gets returned.</p>



<h2 class="wp-block-heading" id="h-updating-inserting-and-removing-data">Updating, Inserting, and Removing Data</h2>



<p class="wp-block-paragraph">What was implemented above is sufficient for a listmodel that will never change, but that&#8217;s probably in the minority of use cases. If you only have a small-ish amount of static data, it would probably be easier to use a <code>Repeater</code>. More likely, you&#8217;ll want to add data to your list, remove data, or change data at runtime, and <code>QAbstractListModel</code> is a much better fit in these situations. In order to manipulate our list contents, we need to understand a few additional concepts.</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-existing-row-data-from-the-application">Signaling Changes to Existing Row Data from the Application</h2>



<p class="wp-block-paragraph">In general, when we bind properties to QML, we provide a signal that we emit when the property changes. QML listens for that signal, and when it gets emitted, it calls the property getter to refresh the value. This is done by a <code>QAbstractListModel</code> by using the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#dataChanged"><code>dataChanged</code> signal</a> provided by its parent, <code>QAbstract<u>Item</u>Model</code>, which specifies which elements of the model changed (in terms of rows, columns, and roles).</p>



<p class="wp-block-paragraph">In some cases, you need to emit this signal yourself. For example, we might want our listmodel class to have a method that <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/device_listmodel.py#L73">sets all devices to &#8220;disconnected.&#8221;</a> That would look like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def set_all_disconnected(self):

    for d in self._data:

        d&#91;DeviceItemRoles.CONNECTED&#93; = False

    self.dataChanged.emit(self.index(0), self.index(self.rowCount() - 1), [])</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">set_all_disconnected</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> d </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        d&#91;DeviceItemRoles.CONNECTED&#93; = </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.dataChanged.emit(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.index(</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">), </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.index(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.rowCount() - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">), [])</span></span></code></pre></div>



<p class="wp-block-paragraph">In this method, we first loop over all items in the data list and set the <code>connected</code> value to <code>False</code>. Then, we only need to emit a single signal that says that all items in the list have changed (i.e., every index from 0 to <code>rowCount() - 1</code>). The empty list in the last parameter of the signal is a list of roles that changed, which can be left empty to indicate that all roles have changed. In this case, you can specify <code>[DeviceItemRoles.CONNECTED]</code> if you prefer. This only makes a difference if you have many roles.</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-the-collection-of-rows">Signaling Changes to the Collection of Rows</h2>



<p class="wp-block-paragraph">Even if you don&#8217;t <strong><em>change any existing data</em></strong>, you might <strong><em>add or remove entire rows</em></strong>, and QML will need to know what to update when that happens. In this case, we use a pair of methods, <code>beginInsertRows()</code> and <code>endInsertRows()</code>, to specify that we&#8217;re adding data (and how many rows we&#8217;re adding).</p>



<p class="wp-block-paragraph">Let&#8217;s say we want to add a new element to the list after the selected index. <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/device_listmodel.py#L54">We can do that with a method like:</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def add_device_after_index(self, idx, name, serial, connected):

    index_of_new_device = idx + 1

    new_device = {

        DeviceItemRoles.NAME: name,

        DeviceItemRoles.SERIAL: serial,

        DeviceItemRoles.CONNECTED: connected

    }



    self.beginInsertRows(QModelIndex(), index_of_new_device, index_of_new_device)

    self._data.insert(index_of_new_device, new_device)

    self.endInsertRows()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">add_device_after_index</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">idx</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">serial</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">connected</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    index_of_new_device = idx + </span><span style="color: #B5CEA8">1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    new_device = {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.NAME: name,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.SERIAL: serial,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.CONNECTED: connected</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.beginInsertRows(QModelIndex(), index_of_new_device, index_of_new_device)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data.insert(index_of_new_device, new_device)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.endInsertRows()</span></span></code></pre></div>



<p class="wp-block-paragraph"><code>beginInsertRows()</code> needs three things: the <code>QModelIndex</code> of the parent into which rows are inserted (for 1D listmodels that we&#8217;re talking about here, just give it a default one), the row number that <strong><em>the first new row will have after insertion</em></strong>, and the row number that <strong><em>the last new row will have after insertion</em></strong>. I&#8217;m never able to remember this, so I almost always consult the helpful diagrams in <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#beginInsertRows">the documentation for this method</a>.</p>



<p class="wp-block-paragraph">After that, we&nbsp;insert the new data into our list, and then we call <code>endInsertRows()</code>. The <code>beginInsertRows()</code> method handles emitting a signal for you (<code>rowsAboutToBeInserted</code>), and <code>endInsertRows()</code> handles emitting a different signal for you (<code>rowsInserted</code>) so you don’t have to emit any signals yourself! These signals are used to notify QML that it’s time to refresh the <code>ListView</code> with new rows, and which ones need to be updated (if the model contains large quantities of data, we obviously only want to update as few as possible).</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-existing-row-data-from-the-gui">Signaling Changes to Existing Row Data from the GUI</h2>



<p class="wp-block-paragraph">The final scenario we&#8217;ll discuss addresses the last part of the <code>QAbstractListModel</code> documentation on subclassing:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">For editable list models, you must also provide an implementation of <code>setData()</code>&nbsp;and implement the <code>flags()</code> function so that it returns a value containing <code>Qt::ItemIsEditable</code>.</p>
</blockquote>



<p class="wp-block-paragraph">As you can see above, if <strong><em>the application</em></strong> manipulates data in the <code>QAbstractListModel</code>, it simply needs to emit a signal (<code>dataChanged</code>) to notify QML that there&#8217;s something new. The <code>setData()</code> method is used when information goes the opposite direction, <strong><em>from the UI to the application</em></strong>. For example, say the delegate contains a checkbox. If the user clicks the checkbox in a particular row, QML needs to tell the <code>QAbstractListModel</code> that there is a new value for the checkbox&#8217;s role at a particular list index. It does this by calling the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#setData"><code>setData()</code></a> method, which takes three arguments: the <code>index</code>, the new <code>value</code>, and the <code>role</code>. It will look very similar to the <code>data()</code> method above, perhaps like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def setData(self, index, value, role):

    if role != MyRoleEnum.SOME_EDITABLE_ROLE:

        return False



    try:

        data_row = self._data_list&#91;index.row()&#93;

    except IndexError:

        return False



    data_row&#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93; = value

    self.dataChanged.emit(index, index, &#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93;)

    return True</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">setData</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role != MyRoleEnum.SOME_EDITABLE_ROLE:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">try</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        data_row = </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data_list&#91;index.row()&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IndexError</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    data_row&#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93; = value</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.dataChanged.emit(index, index, &#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">True</span></span></code></pre></div>



<p class="wp-block-paragraph">In short, you use the <code>index</code> and <code>role</code> arguments to find the data you’re looking for in the model, you set that data to the new <code>value</code>, and then you emit <code>dataChanged</code>.</p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p class="wp-block-paragraph">The <code>QAbstract<u>List</u>Model</code> (and its base class, <code>QAbstract<u>Item</u>Model</code>) is a powerful way to present a list of data to a user interface, but the extensive abstraction can make the documentation hard to parse. A <a href="https://github.com/marklocascio/qml-listmodel-example">simple example</a> should help clarify, as well as a small number of important concepts:</p>



<ul class="wp-block-list">
<li>Many of <code>QAbstract<u>List</u>Model</code>&#8216;s methods are inherited from its base classes, and consequently involve a parent <code>QModelIndex</code> that doesn&#8217;t apply to a simple list and can be very confusing.</li>



<li>A &#8220;role&#8221; is simply a way to specify individual pieces of data in a list item.</li>



<li>Roles can be used to make a list seem like a table, and that&#8217;s fine&#8230; you can still use <code>QAbstractListModel</code>!</li>



<li>When adding items to the list or removing items from it, call the <code>beginInsertRows()</code> and <code>endInsertRows()</code> methods before making your changes, and the correct signals will be emitted for you at the right times.</li>



<li>If your application updates the model by changing data in an existing item in the list (or multiple existing items in the list), make sure you emit the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#dataChanged"><code>dataChanged</code> signal</a> after the changes are made to notify QML that it needs to update its views.</li>



<li>If the user interacts with the QML UI and modifies data in the model, you will need to implement <code>setData()</code> to store the new information in the model object, and then you will need to emit <code>dataChanged.</code></li>
</ul>



<h2 class="wp-block-heading" id="h-building-a-qt-app">Building a Qt app?</h2>



<p class="wp-block-paragraph">I&#8217;d love to help! Give us a call or <a href="mailto:sales@localhost?subject=Let's%20build%20a%20Qt%20app!">send us an email</a> to discuss!&nbsp;</p>



<p class="wp-block-paragraph"><strong>Learn more about our&nbsp;<a href="https://static.dmcinfo.com/services/application-development">Application Development</a>&nbsp;expertise and&nbsp;<a href="https://static.dmcinfo.com/contact#get-in-touch">contact us</a>&nbsp;for your next project.</strong></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/">Using a QAbstractListModel in QML</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Resizing UIs with QML Layouts</title>
		<link>https://static.dmcinfo.com/blog/18019/resizing-uis-with-qml-layouts/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Fri, 09 Dec 2022 09:40:04 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/18019/resizing-uis-with-qml-layouts/</guid>

					<description><![CDATA[<p>Overview When I was first getting exposed to QML as a language for describing user interfaces, almost everything was easy to grasp except the concept of layouts. Their behavior never seemed natural, and I spent a lot of time fighting with them before I was finally able to identify the things that didn’t do what [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18019/resizing-uis-with-qml-layouts/">Resizing UIs with QML Layouts</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-overview" class="wp-block-heading">Overview</h2>



<p class="wp-block-paragraph">When I was first getting exposed to QML as a language for describing user interfaces, almost everything was easy to grasp except the concept of <a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-layout.html" target="_blank">layouts</a>. Their behavior never seemed natural, and I spent a lot of time fighting with them before I was finally able to identify the things that didn’t do what I wanted them to do.</p>



<p class="wp-block-paragraph">This blog aims to give you the head start I didn’t have by walking you through a series of simple examples that&nbsp;illustrate most of the principles.</p>



<h2 id="h-a-brief-introduction-to-layouts" class="wp-block-heading">A Brief Introduction to Layouts</h2>



<p class="wp-block-paragraph">In short, layouts are QML elements that control how their children&nbsp;(the elements that they contain) are positioned and resized. A layout has no visible characteristics itself. There are a few types of layouts:</p>



<ul class="wp-block-list">
<li><code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-rowlayout.html" target="_blank">RowLayout</a></code>: positions its children in a single row, either left to right (default) or right to left</li>



<li><code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-columnlayout.html" target="_blank">ColumnLayout</a></code>: positions its children&nbsp;in a single column, either top to bottom (default) or bottom to top</li>



<li><code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-gridlayout.html" target="_blank">GridLayout</a></code>: positions children in successive cells of a grid
 
 
<ul class="wp-block-list">
<li>Cells in the grid are rearranged when the <code>GridLayout</code> is resized.</li>



<li><code>RowLayout</code> and <code>ColumnLayout</code> are special cases of a <code>GridLayout</code> with only one row or column.</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">The purpose of this walkthrough is to familiarize you with the <strong><em>behaviors</em></strong> of layouts (particularly behaviors that are unintuitive), not to describe all of their features. As such, we will largely focus on the <code>ColumnLayout</code>.</p>



<h2 id="h-a-side-note-on-the-stacklayout" class="wp-block-heading">A Side Note on the StackLayout</h2>



<p class="wp-block-paragraph">There is one more QML Layout type called <code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-stacklayout.html" target="_blank">StackLayout</a></code>. This is most closely related to the concept of “tabs” or “pages,” where different sets of controls can be grouped together and displayed in the same area, and&nbsp;only one group is visible at a time. The <code>StackLayout</code> behaves much like the others, but isn’t primarily for positioning and resizing its content. Since the positioning and resizing behaviors are the interesting ones, we’ll focus on those here, and you will be more than capable of figuring out the <code>StackLayout</code> on your own.</p>



<h3 id="h-walkthrough" class="wp-block-heading">Walkthrough</h3>



<p class="wp-block-paragraph">Let’s step through a series of tests to understand the behavior of layouts.</p>



<h2 id="h-step-0-start-a-nbsp-new-qtquick-project-in-qtcreator" class="wp-block-heading">Step 0: Start a&nbsp;New QtQuick Project in QtCreator</h2>



<p class="wp-block-paragraph">I am using Qt Creator 8.0.2 on Windows, but any recent version on any platform will do. Simply create a new project from the QtQuick application template. I will be using Qt 6.2.1, but Qt5 should be nearly the same.</p>



<p class="wp-block-paragraph">When your template application is generated, you’ll have a file main.qml file that looks like the code on the left. When you build and run the project, you’ll see an empty window like the one on the right. The only change I made to the template QML file is the width of the <code>Window</code>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex1.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">As a side note, you do not need to build the application to see how the window will behave. You can use the “QML utility” to visualize and interact with the currently-active QML file. This utility can be launched from the Tools menu:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/tools-menu.png" alt=""/></figure>



<p class="wp-block-paragraph">For convenience, I have that utility mapped to Ctrl-Q, <em><strong>which is not the default</strong></em>. By default, Ctrl-Q exits Qt Creator. In a default setup, don’t just hit Ctrl-Q and expect to see your QML object.</p>



<h2 id="h-step-1-add-a-rectangle" class="wp-block-heading">Step 1: Add a Rectangle</h2>



<p class="wp-block-paragraph">Let’s put something in that window, maybe just a colored rectangle to start with. In my example code, I&#8217;ll highlight changes from the previous example <code><span style="color:#0066ff">in blue</span></code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "lightBlue"
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex2.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">We’ve already hit a snag. Where’s my rectangle?!</p>



<p class="wp-block-paragraph">It’s there&#8230; but Qt has no way of knowing how big a rectangle you want, so, naturally, it chose 0x0 pixels. The rectangle is there, but it has no height or width. It is conceptual&#8230; the <strong><em>essence</em></strong> of a rectangle, wafting in the breeze, elusive.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Lesson 1: The implicit height and width of a QML Rectangle object is zero.</p>
</blockquote>



<h2 id="h-step-2-make-it-a-much-better-rectangle" class="wp-block-heading">Step 2: Make it a Much Better Rectangle</h2>



<p class="wp-block-paragraph">We’ll specify the height and width of the rectangle so we can actually see it. Now we have:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "lightBlue"
        height: 64
        width: 64
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        width: 64</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex3.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<h2 id="h-step-3-more-rectangles-more-nbsp" class="wp-block-heading">Step 3: More Rectangles. MORE.&nbsp;</h2>



<p class="wp-block-paragraph">Next,&nbsp;add another two rectangles and vary the sizes and colors a little.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "pink"
        height: 256
        width: 256
    }

    Rectangle {
        color: "lightGreen"
        height: 128
        width: 128
    }

    Rectangle {
        color: "lightBlue"
        height: 64
        width: 64
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        height: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        width: 256</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        height: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        width: 128</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        width: 64</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex4.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">This isn’t necessarily what we wanted. In the absence of any specific information about where to put those rectangles, the only reasonable thing to do is just layer them on top of each other in the upper left corner, pink on the bottom, then light green, then light blue on top. If we hadn’t made them different sizes in exactly that order, we wouldn’t have even known, and the smaller rectangles would’ve been hidden under the larger one! We might have gotten incredibly frustrated and hurled our laptop into a fire! Boy, would IT have been mad in this extremely hypothetical example!</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Lesson 2:&nbsp;Unless you tell Qt where to put stuff, it won’t know, and it has no problem letting things overlap.</p>
</blockquote>



<h2 id="h-step-4-add-a-column-layout" class="wp-block-heading"><a id="add-column-layout" name="add-column-layout">Step 4</a>: Add a Column Layout</h2>



<p class="wp-block-paragraph">As stated above, a layout object’s job is to handle the positioning and sizing of its children. So, let’s put our three rectangles into a column layout and change nothing else. Note that, in order to bring in the layout objects, we need to import the QtQuick.Layouts module at the top:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        Rectangle {
            color: "pink"
            height: 256
            width: 256
        }

        Rectangle {
            color: "lightGreen"
            height: 128
            width: 128
        }

        Rectangle {
            color: "lightBlue"
            height: 64
            width: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 256</span></span>
<span class="line"><span style="color: #D4D4D4">            width: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 128</span></span>
<span class="line"><span style="color: #D4D4D4">            width: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">            width: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex5-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Now we’re cookin’! The <code>ColumnLayout</code> object takes its children and arranges them in a column, in the order that they were declared. Try resizing the window, though. You’ll notice that the window resizes, but the rectangles don’t. They stay the same size, and more blank space fills the areas below and to the right of the rectangles.</p>



<h2 id="h-step-5-filling-all-available-space" class="wp-block-heading">Step 5: Filling All Available Space</h2>



<p class="wp-block-paragraph">If we want the rectangles to keep our specified <strong><em>height&nbsp;</em></strong>but always resize to be the <strong><em>width</em></strong> of the window, we can set the <code>Layout.fillWidth</code> property to true in our rectangles. This is the rectangle telling its parent layout,&nbsp;“set my width so that I’m always as wide as possible.”</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        Rectangle {
            color: "pink"
            height: 256
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightGreen"
            height: 128
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightBlue"
            height: 64
            Layout.fillWidth: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 256</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 128</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex6.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">WHAT. WHY?</p>



<p class="wp-block-paragraph">Instead of specifying the width of the rectangles, we told it to make them as wide as possible, and now they’re gone! You might throw several more PCs into a fire before realizing that this is because the <code>ColumnLayout</code> <strong><em>did</em></strong> in fact make them as wide as it could, but the layout can only make its children as wide as it itself is: and, by default, a layout is&#8230; 0 pixels wide.</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 3: Layouts, like rectangles, have zero implicit width/height.</p>


</blockquote>



<h2 id="h-step-6-make-the-layout-wider-than-0-pixels" class="wp-block-heading">Step 6: Make the Layout Wider Than 0 Pixels</h2>



<p class="wp-block-paragraph">We want that layout to be pinned to the width of the window, so&nbsp;let’s anchor the layout to its parent’s boundaries (i.e., make the <code>ColumnLayout</code> fill the <code>Window</code> left-to-right and top-to-bottom) by setting the layout’s <code>anchors.fill</code> property to <code>parent</code> (which is the <code>Window</code> object):</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            height: 256
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightGreen"
            height: 128
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightBlue"
            height: 64
            Layout.fillWidth: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 256</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 128</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex7-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">That’s better! Now, the user can resize the window to be as wide or as narrow as needed, and the rectangles will always reach from the far left to the far right. If you stretch the window vertically, you’ll see that the rectangles always stay their specified height and they just get spaced farther apart as the window gets taller. Let’s see if we can fill their heights also!</p>



<h2 id="h-step-7-use-the-layout-to-fill-height" class="wp-block-heading">Step 7: Use the Layout to Fill Height</h2>



<p class="wp-block-paragraph">Now, let’s replace the <code>height</code> property of each rectangle with <code>Layout.fillHeight</code> so the layout will know to resize all of them in both width and height:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            height: 256
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightGreen"
            height: 128
            Layout.fillWidth: true
        }

        Rectangle {
            color: "lightBlue"
            height: 64
            Layout.fillWidth: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 256</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 128</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            height: 64</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex8-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Well&#8230; now, if we resize the window, the rectangles <strong><em>do</em></strong> always fill both width and height, so that&#8217;s great!&nbsp;But, now we’ve lost the ratio of heights that made these rectangles so special to begin with! How do we get that back so they fill left &amp; right but maintain a specified ratio vertically?</p>



<h2 id="h-step-8-preferred-minimum-and-maximum-sizes" class="wp-block-heading">Step 8: Preferred, Minimum, and Maximum Sizes</h2>



<p class="wp-block-paragraph">There’s a nuance here that is easy to overlook and definitely confused me. When <code>Layout.fillWidth</code> or <code>Layout.fillHeight</code> is true, how does the layout decide what proportion of that dimension is allocated to each element? By default, it makes sense to&nbsp;divide it up evenly, which is what happened. But what happens if there are other constraints, like when we specified <code>width</code> or <code>height</code>? Well, first, consider that <code>width</code> and <code>height</code> are properties of the <code>Rectangle</code> object, whereas <code>Layout.fillHeight</code> talks to the <code>Rectangle</code>’s <strong><em>parent layout</em></strong>. You should not use explicit position or size properties (like <code>x</code>, <code>y</code>, <code>width</code>, or <code>height</code>) in objects managed by a layout. The layout should be free to manage positions and sizes for you.</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 4: If an object is a child of a <code>Layout</code>, don&apos;t set explicit size properties of the child. Only use the <code>Layout.*</code> properties to delegate those duties to the <code>Layout</code>.</p>


</blockquote>



<p class="wp-block-paragraph">We can, however, give the layout more information so it can make better decisions. Each element that sets <code>Layout.fillWidth</code> or <code>Layout.fillHeight</code> to true can also set:</p>



<ul class="wp-block-list">
<li><code>Layout.minimumWidth</code> and <code>Layout.minimumHeight</code></li>



<li><code>Layout.maximumWidth</code> and <code>Layout.maximumHeight</code></li>



<li><code>Layout.preferredWidth</code> and <code>Layout.preferredHeight</code></li>
</ul>



<p class="wp-block-paragraph">The minimum and maximum properties are self-explanatory, but what does “preferred” mean in the context of elements that the layout wants to stretch to fit all available space? As it turns out, <a href="https://doc.qt.io/qt-6/qtquicklayouts-overview.html#size-constraints" target="_blank">it specifies the proportions</a>! Let’s put our original heights back in there as <strong><em>preferred</em></strong> heights:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 256
        }

        Rectangle {
            color: "lightGreen"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 128
        }

        Rectangle {
            color: "lightBlue"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex9-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">That’s what we wanted, and now when we stretch the window vertically, the rectangles always maintain the given ratio. The green rectangle is always half the height of the pink one, and the blue one is always half the height of the green one (and therefore a quarter of the height of the pink one).</p>



<p class="wp-block-paragraph">In the absence of maximum or minimum values, you can even simplify the preferred heights down to the proportions you want. For example, the example above will behave exactly the same if you change the <code>Layout.preferredHeight</code> values to 4, 2, and 1! Sometimes I’ll do that if I don’t care about the absolute sizes and just want to express that elements should be sized in a 4:2:1 ratio (or whatever ratio you’re looking to achieve). But be aware that this is only the case when all child elements of the layout are set to fill in that direction, and they all have a preferred size in that direction.</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 5: If the layout itself has a specified size, AND all child objects use <code>Layout.fillWidth/Height</code>, AND all child elements have a <code>preferredWidth/Height</code> set, then the proportion of the fill allocated to each child will be the ratios of the <code>preferredWidth/Height</code>!</p>


</blockquote>



<p class="wp-block-paragraph">Let’s dig a little deeper. What if we don’t specify the vertical height of the layout? In other words, what if we only anchor the layout’s left and&nbsp;right sides to the window?</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.left: parent.left
        anchors.right: parent.right

        Rectangle {
            color: "pink"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 256
        }

        Rectangle {
            color: "lightGreen"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 128
        }

        Rectangle {
            color: "lightBlue"
            Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.left: parent.left</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.right: parent.right</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex10-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">At the bottom, there’s some white space now. The layout isn’t anchored to the window’s top and bottom, so the rectangles are always their “preferred” heights, even when we stretch the window vertically. The <code>Layout.fillHeight</code> properties don’t do anything here. The layout doesn’t know what space it has available to fill if it doesn’t have a parent controlling its size in that direction! The only information the layout can use to control its own height is the sum of the implicit heights of its children (provided by their <code>Layout.preferredHeight</code> properties). The <code>Layout.fillHeight</code> properties are simply ignored.</p>



<p class="wp-block-paragraph">OK, so what happens if we <strong><em>don’t</em></strong> fill height, but we <strong><em>do</em></strong> have the layout anchored to all four sides of the window?</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 256
        }

        Rectangle {
            color: "lightGreen"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 128
        }

        Rectangle {
            color: "lightBlue"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex11-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Now the layout knows what height the rectangles like to be, but it was not told to resize them with a <code>Layout.fillHeight</code>. So, the layout stretches, and the rectangles get repositioned, but they do <strong><em>not</em></strong> get resized vertically. If you reduced the <code>Layout.preferredHeight</code> values to 4, 2, and 1, what happens?</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 256
        }

        Rectangle {
            color: "lightGreen"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 128
        }

        Rectangle {
            color: "lightBlue"
            // Layout.fillHeight: true
            Layout.fillWidth: true
            Layout.preferredHeight: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex12.png" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Interesting. You get very slim horizontal lines that are not spaced out uniformly. The layout uses the <code>Layout.preferredHeight</code> properties to allocate proportional chunks of space (see the annotated figure below), but the rectangle placed in that space has exactly the height specified by the <code>Layout.preferredHeight</code> property.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex12-spacing.png" alt=""/></figure>



<p class="wp-block-paragraph">Now, the last test we’ll do on sizing: setting the <code>Layout.preferredHeight</code> of a subset of child elements. For example, let’s say we put <code>Layout.fillHeight</code> back in there and remove the <code>Layout.preferredHeight</code> from the pink rectangle only (and restore the larger sizes 256, 128, and 64):</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            Layout.fillWidth: true
            Layout.fillHeight: true
            // Layout.preferredHeight: 256
        }

        Rectangle {
            color: "lightGreen"
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 128
        }

        Rectangle {
            color: "lightBlue"
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 64
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.preferredHeight: 256</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 128</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 64</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex13-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">The pink rectangle is very small&#8230; it&#8217;s easy to miss, but it&#8217;s there.&nbsp;It <em><strong>does</strong></em> grow and shrink a little when resizing, but not much. The proportions of the green &amp; blue rectangles are correctly maintained, but the QML engine doesn’t really have much information about what to do with the pink one. For the sake of being thorough, let’s reduce the <code>Layout.preferredHeight</code> of the green &amp; blue rectangles to a simple 2:1 ratio:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>ColumnLayout {
    anchors.fill: parent

    Rectangle {
        color: "pink"
        Layout.fillWidth: true
        Layout.fillHeight: true
        // Layout.preferredHeight: 4
    }

    Rectangle {
        color: "lightGreen"
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.preferredHeight: 2
    }

    Rectangle {
        color: "lightBlue"
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.preferredHeight: 1
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">    anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        // Layout.preferredHeight: 4</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.preferredHeight: 2</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">        color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        Layout.preferredHeight: 1</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex14-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">The QML engine still maintains the correct ratio for the child elements with a specified <code>Layout.preferredHeight</code>, but the amount of space allocated for the pink one is different. This is all academic&#8230; I don&#8217;t know what the use case for this would be. My only goal is to demonstrate how things behave and establish some rules.</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 6: If all children of a layout are set to fill in the direction of the layout, either give all of them a <code>Layout.preferredHeight/Width</code>, or none of them.</p>


</blockquote>



<h2 id="h-step-9-additional-constraints" class="wp-block-heading">Step 9: Additional Constraints</h2>



<p class="wp-block-paragraph">The minimum/maximum height and width properties can also be set. This allows the rectangles to&nbsp;resize, but only to a certain minimum or maximum size.&nbsp;In this case, we’ll put a maximum height on the pink rectangle. When resized, that rectangle will grow up to 384 pixels. It won’t get any taller, but the blue and&nbsp;green rectangles will continue to maintain their 2:1 ratio. In this case, it doesn’t matter if we use 256:128:64 or 4:2:1, the behavior is the same:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            color: "pink"
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 4
            Layout.maximumHeight: 384
        }

        Rectangle {
            color: "lightGreen"
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 2
        }

        Rectangle {
            color: "lightBlue"
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 1
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 4</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.maximumHeight: 384</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 2</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">            color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 1</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex15-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">We can continue to add additional constraints, and the QML engine will do its best to resize based on all of them.</p>



<h2 id="h-step-10-a-fancy-window-with-nested-layouts" class="wp-block-heading">Step 10: A Fancy Window with Nested Layouts</h2>



<p class="wp-block-paragraph">Let’s take it up a notch by maintaining the 4:2:1 ratio of rows, and in each of those, let’s put a <code>RowLayout</code>. We’ll add more rectangles in various proportions in each of those rows. Let’s also set the spacing on the rows to zero, but leave the column layout’s spacing as the default value (5 pixels). A lot changed here, so I&#8217;ll skip the highlighting:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        RowLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 4

            spacing: 0

            Rectangle {
                color: "pink"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }

            Rectangle {
                color: "darkRed"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }
        }

        RowLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 2

            spacing: 0

            Rectangle {
                color: "lightGreen"
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.preferredWidth: 1
            }

            Rectangle {
                color: "green"
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.preferredWidth: 2
            }

            Rectangle {
                color: "darkGreen"
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.preferredWidth: 3
            }
        }

        RowLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            Layout.preferredHeight: 1

            spacing: 0

            Rectangle {
                color: "lightBlue"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }

            Rectangle {
                color: "steelBlue"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }

            Rectangle {
                color: "blue"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }

            Rectangle {
                color: "darkBlue"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }

            Rectangle {
                color: "midnightBlue"
                Layout.fillWidth: true
                Layout.fillHeight: true
            }
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 640</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        RowLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 4</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            spacing: 0</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;pink&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;darkRed&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        RowLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 2</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            spacing: 0</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;lightGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.preferredWidth: 1</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;green&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.preferredWidth: 2</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;darkGreen&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.preferredWidth: 3</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        RowLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.preferredHeight: 1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            spacing: 0</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;lightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;steelBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;blue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;darkBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Rectangle {</span></span>
<span class="line"><span style="color: #D4D4D4">                color: &quot;midnightBlue&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">                Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex16-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<h2 id="h-step-11-using-real-controls" class="wp-block-heading">Step 11: Using Real Controls</h2>



<p class="wp-block-paragraph">I mentioned previously that <code>Rectangle</code> objects don’t have an implicit size, but <a href="https://doc.qt.io/qt-6/qml-qtquick-controls2-button.html" target="_blank"><code>Button</code></a> objects do (note that we need to add <code>import QtQuick.Controls</code> to get the <code>Button</code> object). Let’s see how that changes things. First, let’s strip it down to something like what we had <a href="#add-column-layout">way back in Step 4</a>. The layout is <em><strong>not</strong></em> anchored, and it contains two buttons:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {

        Button {
            text: "Top button is...short"
        }

        Button {
            text: "Bottom button is...long"
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button is...short&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button is...long&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex17-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Nothing&nbsp;terribly interesting there, but&nbsp;when the layout wasn&#8217;t anchored before (with just <code>Rectangle</code>s), we couldn&#8217;t see the contents at all. However, <code>Button</code>s <strong><em>do</em></strong> have an implicit size, whereas <code>Rectangle</code>s don&#8217;t. You can see that the <code>Button</code>s are as wide as they need to be to accommodate their text, and the layout stays &#8220;fitted&#8221; to the implicit size of those buttons. As a consequence, the buttons neither resize nor reposition when the window is resized.</p>



<p class="wp-block-paragraph">Now let’s say we want to fill the buttons in both directions:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {

        Button {
            text: "Top button is...short"

            Layout.fillWidth: true
            Layout.fillHeight: true
        }

        Button {
            text: "Bottom button is...long"

            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button is...short&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button is...long&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex18-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">That’s a little more interesting. The buttons don’t fill the height and width of the whole <code>Window</code>, they fill the height and width of the <code>ColumnLayout</code>, and we didn’t say anything about how big the <code>ColumnLayout</code> should be. That is, we didn’t anchor it to the <code>Window. </code>It&nbsp;is not itself inside another layout, etc. The layout therefore just has its implicit size. The implicit height is the sum of its children’s implicit heights (plus spacing), and its implicit width is the maximum width of its children. So, when the children tell their parent layout to resize them to fill the layout’s height, nothing changes (because they already do), and, when they tell the parent layout to fill the layout’s width, really all that happens is that the narrower button fills to the same width as the wider one. Obviously, a <code>RowLayout</code> will behave the same, but in the horizontal direction instead.</p>



<p class="wp-block-paragraph">Let’s anchor the layout to the <code>Window</code> and fill width (but not height):</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {

        Button {
            text: "Top button is...short"

            Layout.fillWidth: true
            Layout.fillHeight: true
        }

        Button {
            text: "Bottom button is...long"

            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button is...short&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button is...long&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex19-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Now both buttons stretch across the <code>Window</code> and are spaced out such that the column is divided in half vertically, and each button is in the vertical center of its half.</p>



<h2 id="h-step-12-alignment" class="wp-block-heading">Step 12: Alignment</h2>



<p class="wp-block-paragraph">We probably don’t want those buttons to be haphazardly floating in the middle of their space. We probably want them either both at the top, both at the bottom, or one at the top and the other at the bottom. All of this can be achieved, but first we need to understand the quirks of the <code>Layout.alignment</code> property.</p>



<p class="wp-block-paragraph">First, look at the window we created and notice the language I used above. The two buttons are not spaced equally relative to the height of the layout. There&#8217;s twice as much space between the buttons as there is between the top button and the top of the window. Conceptually, the layout’s full height is divided in half, creating two &#8220;cells,&#8221; and each&nbsp;button hovers in the center of its “cell.” This is important because the <code>Layout.alignment</code> property will align a button within its “cell,” not relative to the whole layout! So, if we align both buttons to the top, they <b><i>will not</i></b> both be at the top of the window. They are each at the top of their own “cell,” which leaves the bottom button hovering somewhere in the middle of the window:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Button {
            text: "Top button is...short"
            Layout.fillWidth: true
            Layout.alignment: Qt.AlignTop
        }

        Button {
            text: "Bottom button is...long"
            Layout.fillWidth: true
            Layout.alignment: Qt.AlignTop
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button is...short&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.alignment: Qt.AlignTop</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button is...long&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.alignment: Qt.AlignTop</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex20-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">If the bottom button had instead used <code>Layout.alignment: Qt.AlignBottom</code>, then the top button would be at the top of the window&nbsp;and the bottom button would be at the bottom of the window. If we want both at the top,&nbsp;<code>Layout.alignment</code> isn’t what we want. We have a couple options for that:</p>



<ol class="wp-block-list">
<li>Instead of anchoring the ColumnLayout with <code>anchors.fill: parent</code>, we can just anchor it to the <code>Window</code>’s left and right sides. Then, the buttons will fill the width of the <code>Window</code>, but the layout will only take up as much height as the two buttons (plus any <code>ColumnLayout</code> spacing). The buttons will both&nbsp;be stacked at the top.</li>



<li>We can let the layout take up the whole height, and then add a dummy <a href="https://doc.qt.io/qt-6/qml-qtquick-item.html" target="_blank"><code>Item</code></a> after the second button that will eat up all available height, pushing the two buttons up to the top, like this:</li>
</ol>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Button {
            text: "Top button"
            Layout.fillWidth: true
        }

        Button {
            text: "Middle button"
            Layout.fillWidth: true
        }

        Item {
            Layout.fillHeight: true
        }

        Button {
            text: "Bottom button"
            Layout.fillWidth: true
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Middle button&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Item {</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex22-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">This is useful&nbsp;because now we can also add a button at the very bottom. We wouldn’t be able to achieve this with the alignment properties unless we divided up the layout’s “cells” just right, which wouldn’t be worth the hassle. Otherwise,&nbsp;we&#8217;d have to nest another <code>ColumnLayout</code> inside the outer&nbsp;one and use it to keep the top two buttons together.</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 7: Dummy <code>Item</code>s used as spacers can often give you better control of where things are positioned in the direction of the layout&rsquo;s ordering of elements.</p>


</blockquote>



<p class="wp-block-paragraph">I prefer to use spacer <code>Item</code>s instead of <code>Layout.alignment</code>, at least with regard to alignment in the direction of the layout. If we wanted things to be aligned left or right in a <code>ColumnLayout</code> (or top/bottom in a <code>RowLayout</code>), then <code>Layout.alignment</code>&nbsp;makes sense:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import QtQuick
import QtQuick.Layouts
import QtQuick.Controls

Window {
    width: 320
    height: 480
    visible: true
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Button {
            text: "Top button"

            // Layout.fillWidth: true
            Layout.alignment: Qt.AlignRight
        }

        Button {
            text: "Middle button"

            // Layout.fillWidth: true
            Layout.alignment: Qt.AlignLeft
        }

        Item {
            Layout.fillHeight: true
        }

        Button {
            text: "Bottom button"

            // Layout.fillWidth: true
            Layout.alignment: Qt.AlignHCenter
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import QtQuick</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Layouts</span></span>
<span class="line"><span style="color: #D4D4D4">import QtQuick.Controls</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Window {</span></span>
<span class="line"><span style="color: #D4D4D4">    width: 320</span></span>
<span class="line"><span style="color: #D4D4D4">    height: 480</span></span>
<span class="line"><span style="color: #D4D4D4">    visible: true</span></span>
<span class="line"><span style="color: #D4D4D4">    title: qsTr(&quot;Hello World&quot;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    ColumnLayout {</span></span>
<span class="line"><span style="color: #D4D4D4">        anchors.fill: parent</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Top button&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.alignment: Qt.AlignRight</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Middle button&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.alignment: Qt.AlignLeft</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Item {</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.fillHeight: true</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        Button {</span></span>
<span class="line"><span style="color: #D4D4D4">            text: &quot;Bottom button&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            // Layout.fillWidth: true</span></span>
<span class="line"><span style="color: #D4D4D4">            Layout.alignment: Qt.AlignHCenter</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/ex23-small.gif" alt=""/></figure>


</td>
</tr>
</tbody>
</table>


<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Lesson 8: When positioning items on the axis that the layout does <strong><em>not</em></strong> control, the <code>Layout.alignment</code> property is more&nbsp;useful than it is on the axis that the layout <em><strong>does</strong></em> control.</p>


</blockquote>



<h3 id="h-summary" class="wp-block-heading">Summary</h3>



<p class="wp-block-paragraph">Layouts are a&nbsp;modern and flexible method for creating nicely-resizable QML user interfaces. Until&nbsp;you have some time to play with them and see how they behave, they can seem unintuitive. I have learned several lessons the hard way, and I will reiterate them here for your comfort and convenience:</p>



<ol class="wp-block-list">
<li>The implicit height and width of <code>Rectangle</code>s are zero.</li>



<li>Unless you tell Qt where to put stuff, it won’t know, and it has no problem letting things overlap.</li>



<li><code>Layout</code>s, like <code>Rectangle</code>s, have zero implicit width/height.</li>



<li>If an object is a child of a <code>Layout</code>, don&#8217;t set explicit size properties of the child: only use the <code>Layout.*</code> properties to delegate those duties to the <code>Layout</code>.</li>



<li>If the layout itself has a specified size, AND all child objects use <code>Layout.fillWidth/Height</code>, AND all child elements have a <code>Layout.preferredWidth/Height</code> set, then the proportion of the fill allocated to each child will be the ratios of the <code>preferredHeight/Widths</code>!</li>



<li>If all children of a layout are set to fill in the direction of the layout, either give all of them a <code>preferredHeight/Width</code>, or give them neither.</li>



<li>Dummy <code>Item</code>s used as spacers can often give you better control of where things are positioned in the direction of the layout’s ordering of elements.</li>



<li>When positioning items on the axis that the layout does <strong><em>not</em></strong> control, the <code>Layout.alignment</code> property is more&nbsp;useful than it is on the axis that the layout <em><strong>does</strong></em> control.</li>
</ol>



<h3 id="h-building-a-qt-app" class="wp-block-heading">Building a Qt App?</h3>



<p class="wp-block-paragraph">I’d love to help! Give us a call or&nbsp;<a href="mailto:sales@localhost?subject=Let's%20build%20a%20Qt%20app!">send us an email</a>&nbsp;to discuss! Learn more about our <a href="https://static.dmcinfo.com/services/application-development/web-application-development">Web Application Development</a> solutions and <a href="https://static.dmcinfo.com/contact">contact us</a> today for your next project!</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18019/resizing-uis-with-qml-layouts/">Resizing UIs with QML Layouts</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A Brief Tutorial on Qt’s Resource Files</title>
		<link>https://static.dmcinfo.com/blog/18025/a-brief-tutorial-on-qts-resource-files/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Wed, 07 Dec 2022 17:32:56 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/18025/a-brief-tutorial-on-qts-resource-files/</guid>

					<description><![CDATA[<p>One of the many tools Qt provides for you is what’s known as the “resource compiler.” The idea is that you might have some data (say, an icon or image file) that your application needs. You could place that file in a particular location on the file system, and your application could load it at [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18025/a-brief-tutorial-on-qts-resource-files/">A Brief Tutorial on Qt’s Resource Files</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">One of the many tools Qt provides for you is what’s known as the “<a href="https://doc.qt.io/qt-6/rcc.html">resource compiler</a>.” The idea is that you might have some data (say, an icon or image file) that your application needs. You could place that file in a particular location on the file system, and your application could load it at run time, but you would need to either ensure that it’s there every time the app runs or ensure that the app will still work without it. The resource compiler gives you an alternative: load that file at compile time and bake the data directly into your executable. Then&nbsp;you never need to worry about finding the file at runtime.</p>



<p class="wp-block-paragraph">It&#8217;s a simple and useful system that is worth understanding. The <a href="#details">first section</a> is an overview of what it is, why it’s useful, and how to use it. The <a href="#limitations">second section</a> details a few of the things I’ve run into that have tripped me up.</p>



<h2 id="h-part-1-details-of-the-qt-resource-system" class="wp-block-heading"><a id="details" name="details">Part 1:</a> Details of the Qt Resource System</h2>



<h3 id="h-the-guts-of-a-qrc-file" class="wp-block-heading">The Guts of a QRC File</h3>



<p class="wp-block-paragraph">A resource file (usually with a “.qrc” extension) is just XML that allows you to organize the app’s resources to your liking (you don’t need to write XML yourself if you’re using Qt Creator). When they’re compiled into the executable, you won’t have the luxury of a file system to help you organize and identify your files. This is the job of the QRC file, which is processed as follows at compile time:</p>



<ul class="wp-block-list">
<li>The resource compiler reads the QRC file</li>



<li>The resource compiler loads the resources listed in the QRC file</li>



<li>The resource compiler generates a C++ source file that contains a huge array of bytes containing the exact bytes of those resources</li>



<li>Your regular compiler toolchain compiles the generated C++ source code into an object file that can be linked with the rest of your application</li>



<li>Other parts of your code use a special URL notation to reference resource files, and Qt knows how to point it toward the right part of the compiled byte array</li>
</ul>



<p class="wp-block-paragraph">Let’s take a quick look at what it does behind the scenes. Let’s say I have a file that the app uses as a background image. The file is 1462239 bytes:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;RCC>
    &lt;qresource prefix="/">
        &lt;file>background.png&lt;/file>
    &lt;/qresource>
&lt;/RCC></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #808080">&lt;</span><span style="color: #F44747">RCC</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #F44747">qresource</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">prefix</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #F44747">file</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">background.png</span><span style="color: #808080">&lt;/</span><span style="color: #F44747">file</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;/</span><span style="color: #F44747">qresource</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #F44747">RCC</span><span style="color: #808080">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">In hex, 1462239 bytes is <code><span style="font-size:larger;"><strong>0x164fdf</strong></span></code>. The generated C++ source looks like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static const unsigned char qt_resource_data[] = {
    // /home/markl/path/to/background.png
    0x00, 0x16, 0x4f, 0xdf,
    0x89,
    0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
    ...
};</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">static const unsigned char qt_resource_data[] = {</span></span>
<span class="line"><span style="color: #D4D4D4">    // /home/markl/path/to/background.png</span></span>
<span class="line"><span style="color: #D4D4D4">    0x00, 0x16, 0x4f, 0xdf,</span></span>
<span class="line"><span style="color: #D4D4D4">    0x89,</span></span>
<span class="line"><span style="color: #D4D4D4">    0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,</span></span>
<span class="line"><span style="color: #D4D4D4">    ...</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span></code></pre></div>



<p class="wp-block-paragraph">Note that this byte array starts with the four-byte length of the file (<code><span style="font-size:larger;"><strong>0x00 0x16 0x4f 0xdf</strong></span></code>) followed by the exact bytes of the png file (which we can recognize by the standard magic bytes at the beginning of <strong><em>every</em></strong> png file, <code><span style="font-size:larger;"><strong>0x89 0x50 0x4e 0x47 0x0d 0x0a 0x1a 0x0a</strong></span></code>). Additional resource files are concatenated onto that same byte array after the background image. That is, the array contains the size of background.png, then the bytes of background.png, then four bytes denoting the length of the next file, then the bytes of the next file, and so on.</p>



<p class="wp-block-paragraph">Finally, at the bottom of the generated C++ file are some macros and other helpers. Note that you don’t need to interact with anything in this file directly. We’re only peeking behind the curtain here for educational purposes. Qt’s resource compiler will generate that C++ file for you, and your system’s toolchain will handle compiling and linking it. All you need to do is use URLs in your code to reference the resources you want to use.</p>



<h3 id="h-urls-and-aliases" class="wp-block-heading">URLs and aliases</h3>



<p class="wp-block-paragraph">Once the data is compiled into your application, it no longer has a filesystem path for you to refer to it in your code. So, Qt provides a URL format to conveniently identify resource files listed in your QRC file. You can optionally give each data file an easy-to-remember alias so you can refer to it more conveniently (and, later, you might map that same alias to a different resource file so you can change a resource without changing any of your source code). Neat!</p>



<p class="wp-block-paragraph">Let’s look at some examples. <a href="#resources-in-qrc">Figure 1</a> shows an example in which all QML resources are in a directory called “qml_dir,” and all images are in a directory called “image_dir.” In the QRC file (the right half of the figure shows Qt Creator’s QRC editor), the resources are organized into groups based on “prefixes” that you can define yourself. Here, I created three prefixes: ui, images, and icons.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/resources-on-disk-and-in-qrc.png" alt="Resources as they are arranged on the file system (left side) and in the QRC file (right side)."/></figure>



<p class="wp-block-paragraph"><em><a id="resources-in-qrc" name="resources-in-qrc">Figure 1:</a> Resources as they are arranged on the file system (left side) and in the QRC file (right side)</em></p>



<p class="wp-block-paragraph">The URL that you will use has the following general form: <code><span style="font-size:larger;"><strong>qrc:/prefix/file_path_relative_to_qrc_file</strong></span></code></p>



<p class="wp-block-paragraph">Based on the organization of my QRC file, I can refer to each resource using the following URLs:</p>


<table style="border: none; border-spacing: 16px;">
<tbody>
<tr style="border: none;">
<td style="border: none;"><span style="font-size:larger;"><strong>File path</strong></span></td>
<td style="border: none;"><span style="font-size:larger;"><strong>URL</strong></span></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>qml_dir/main.qml</code></td>
<td style="border: none;"><code>qrc:/ui/qml_dir/main.qml</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/background.png</code></td>
<td style="border: none;"><code>qrc:/images/image_dir/background.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/image1.png</code></td>
<td style="border: none;"><code>qrc:/images/image_dir/image1.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/image2.png</code></td>
<td style="border: none;"><code>qrc:/images/image_dir/image2.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/icon1.png</code></td>
<td style="border: none;"><code>qrc:/icons/image_dir/icon1.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/icon2.png</code></td>
<td style="border: none;"><code>qrc:/icons/image_dir/icon2.png</code></td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">For example, using a <a href="https://doc.qt.io/qt-6/qml-qtquick-image.html">QML Image</a> object to display background.png would look like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Image {
    source: "qrc:/images/image_dir/background.png"
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">Image {</span></span>
<span class="line"><span style="color: #D4D4D4">    source: &quot;qrc:/images/image_dir/background.png&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">You also have the option of applying aliases to items in the resource file. Imagine that your graphic design team gives you an image with a name like <code><span style="font-size:larger;"><strong>Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final.png.</strong></span></code> Perhaps you would black out with rage, and when you awoke, you would recognize this as a great use case for aliases. Let’s give that image a short (readable and type-able) alias, say, <code><span style="font-size:larger;"><strong>bckgnd</strong></span></code>, which will appear in parentheses at the end of the line in the QRC file (<a href="#qrc-alias">Figure 2</a>):</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/long-filename-with-alias.png" alt="An alias applied to an image with an absurd file name"/></figure>



<p class="wp-block-paragraph"><em><a name="qrc-alias">Figure 2:</a> An alias applied to an image with an absurd file name</em></p>



<p class="wp-block-paragraph">Now, instead of typing</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Image {
    source: "qrc:/images/image_dir/Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final.png"
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">Image {</span></span>
<span class="line"><span style="color: #D4D4D4">    source: &quot;qrc:/images/image_dir/Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final.png&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">We can use only the prefix and the alias. The alias takes the place of the relative file path in the URL, so it has form <code><span style="font-size:larger;"><strong>qrc:/prefix/alias</strong></span></code>. For example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Image {
    source: "qrc:/images/bckgnd"
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">Image {</span></span>
<span class="line"><span style="color: #D4D4D4">    source: &quot;qrc:/images/bckgnd&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">The next week, when you get <code><span style="font-size:larger;"><strong>Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final FINAL.png</strong></span></code>, you can update the QRC file with the new file path&nbsp;but keep the same alias (<code><span style="font-size:larger;"><strong>bckgnd</strong></span></code>). None of your code needs to change at all, because the URL&nbsp;you use to reference that file doesn&#8217;t need to change!</p>



<p class="wp-block-paragraph">Returning to my original example, I might apply the following aliases:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/resources-with-aliases.png" alt="Aliases applied to all resources"/></figure>



<p class="wp-block-paragraph"><em><a name="qrc-alias">Figure 3:</a> Aliases applied to all resources</em></p>



<p class="wp-block-paragraph">All resources can now be used in code using nice short URLs:</p>


<table style="border: none; border-spacing: 16px;">
<tbody>
<tr style="border: none;">
<td style="border: none;"><span style="font-size:larger;"><strong>File path</strong></span></td>
<td style="border: none;"><span style="font-size:larger;"><strong>URL</strong></span></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>qml_dir/main.qml</code></td>
<td style="border: none;"><code>qrc:/ui/main_screen</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/background.png</code></td>
<td style="border: none;"><code>qrc:/images/bckgnd</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/image1.png</code></td>
<td style="border: none;"><code>qrc:/images/img1</code></td>
</tr>
<tr>
<td style="border: none;"><code>image_dir/image2.png</code></td>
<td style="border: none;"><code>qrc:/images/img2</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/icon1.png</code></td>
<td style="border: none;"><code>qrc:/icons/start_symbol</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><code>image_dir/icon2.png</code></td>
<td style="border: none;"><code>qrc:/icons/stop_symbol</code></td>
</tr>
</tbody>
</table>


<h3 id="h-why-would-i-do-nbsp-this" class="wp-block-heading">Why Would I Do&nbsp;This?</h3>



<p class="wp-block-paragraph">For me, the most compelling reason is to manage QML files in a QtQuick application. You probably don’t want users to be exposed to the actual QML files that make up the UI, so I list all my QML files as resources. The application is probably useless without the QML files, so you’re likely to want to ensure that they can always be found, and that they can’t change.</p>



<p class="wp-block-paragraph">Another use case, as described above, is to provide some abstraction between the URL that you use to refer to resources in code and the file name that it has on disk. This helps you manage files with horrific names and allows you to easily update the file path of an aliased file without changing any of your code.</p>



<p class="wp-block-paragraph">You might also benefit from the reduced amount of code you need to write &amp; maintain. To load a file from disk, you need to know where to look for it. Is it at an absolute path? If so, how do you know what that path is on all systems? Is it a relative path? Relative to what? Once you know what path to look for, what do you do if the file isn’t there? What do you do if it <strong><em>is</em></strong> there, and there’s a problem with permissions? You can neatly sidestep all those problems if you can just compile the file directly into your app and refer to it with an easy-to-remember URL.</p>



<h2 id="h-part-2-limitations-and-gotchas" class="wp-block-heading"><a id="limitations" name="limitations">Part 2:</a> Limitations and Gotchas</h2>



<p class="wp-block-paragraph">This is all pretty simple, but there are a few things that can get you tripped up that aren’t addressed clearly in the documentation.</p>



<h3 id="h-using-a-full-url-instead-of-an-alias" class="wp-block-heading">Using a full URL instead of an alias</h3>



<p class="wp-block-paragraph">Let’s look at the example above. We’ve got the following:</p>


<table style="border: none; border-spacing: 16px;">
<tbody>
<tr style="border: none;">
<td style="border: none;"><span style="font-size:larger;"><strong>File path</strong></span></td>
<td style="border: none;"><code>image_dir/image2.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><span style="font-size:larger;"><strong>URL (without the alias)</strong></span></td>
<td style="border: none;"><code>qrc:/images/image_dir/image2.png</code></td>
</tr>
<tr style="border: none;">
<td style="border: none;"><span style="font-size:larger;"><strong>URL (using the alias)</strong></span></td>
<td style="border: none;"><code>qrc:/images/img2</code></td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Let’s say you’ve recently learned about aliases, and you applied the img2 alias to image2.png. You changed to the shorter URL several places in your code, but you missed one instance and left the full (non-aliased) URL somewhere. <strong><em>Qt’s resource URL resolver will fail to locate that resource</em></strong>. Even though the URL used to be valid, and the relative path to the file has not changed, <strong><em>if you apply an alias, you must use it</em></strong>.</p>



<p class="wp-block-paragraph">I find this unintuitive. The word “alias” to me implies that you <strong><em>can</em></strong> use it, but you don’t <strong><em>have</em></strong> to. Nevertheless, as of Qt 6.2.1, be aware that this is the case. Note that you <strong><em>can</em></strong> have a mix of aliased and not-aliased items in the same QRC file.</p>



<h3 id="h-accidental-alias-collisions" class="wp-block-heading">Accidental Alias Collisions</h3>



<p class="wp-block-paragraph">Suppose you accidentally apply the same alias to different items in a QRC file. For example, you alias both icon1.png and icon2.png as “start_symbol.” You will be able to use <code><span style="font-size:larger;"><strong>qrc:/icons/start_symbol</strong></span></code> as usual in your code, and no errors or warnings will appear at compile time or run time. This is, in fact, normal and useful behavior, but if you aren’t checking for it, it could cause a subtle bug.</p>



<p class="wp-block-paragraph">Why is this normal and useful? Because one of the common uses for resource files is to <a href="https://doc.qt.io/qt-6/resources.html#language-selectors">provide language translation features</a>. Say you have an image of a stop sign for users in the US (“STOP”) and an image of a stop sign for users in Mexico (“ALTO”). You can use the same alias to refer to one of many resources, and Qt will choose the appropriate image based on the user’s configured locale. We won’t dig into it any more here, but for more information on advanced usage and localization, <a href="https://doc.qt.io/qt-6/resources.html">see the Qt documentation</a>.</p>



<h3 id="h-multiple-qrc-files" class="wp-block-heading">Multiple QRC files</h3>



<p class="wp-block-paragraph">You might be tempted, as I was, to separate your resources into multiple QRC files. Each QRC file needs to generate a C++ source file, and then that file needs to be compiled, so you might think that it makes sense to put your QML files (which will change frequently as you develop) in their own QRC file separate from icons, images, and other relatively large files that won’t change much. This makes a lot of sense and seems like a perfectly fine practice&#8230; as long as you realize that there is no mechanism for addressing a particular QRC file in your URLs.</p>



<p class="wp-block-paragraph">Recall that the format of a URL is <code><span style="font-size:larger;"><strong>qrc:/prefix/file_path_relative_to_qrc_file</strong></span></code> or <code><span style="font-size:larger;"><strong>qrc:/prefix/alias</strong></span></code>. Nowhere in there do you have the option of specifying a particular QRC file, only the prefix and path or alias. It will still work, and it will <strong><em>not</em></strong> complain if both QRC files each contain the same prefix and the same alias, so be careful that you don’t have colliding prefixes or aliases. There’s not a lot of error checking being done, so be careful!</p>



<h3 id="h-big-files" class="wp-block-heading">Big Files</h3>



<p class="wp-block-paragraph">Recall from <a href="#details">the first section</a> that the resource compiler puts the bytes of your files into an array of bytes in a C++ source file. As you might imagine, that array can get pretty big. I have seen some instances (generally on smaller ARM systems) in which the generated (huge) C++ file fails to compile without a terribly helpful error message. In this case, you can direct the resource compiler to skip the C++ source file step and compile your resources directly into an object file.</p>



<p class="wp-block-paragraph">By default, in CMake, you can just list your resource file with the rest of your sources and tell it to automatically run the resource compiler (rcc):</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># Run Qt's Resource Compiler (rcc) automatically
set(CMAKE_AUTORCC ON)

# C++ source files
set(CPP_SRC
    main.cpp
)

# Qt resource files
set(QRC_SRC
    resources.qrc
)

# Create the executable
qt_add_executable(
    ${PROJECT_NAME}
    MANUAL_FINALIZATION
    ${CPP_SRC}
    ${QRC_SRC}
)
``</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"># Run Qt&apos;s Resource Compiler (rcc) automatically</span></span>
<span class="line"><span style="color: #D4D4D4">set(CMAKE_AUTORCC ON)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># C++ source files</span></span>
<span class="line"><span style="color: #D4D4D4">set(CPP_SRC</span></span>
<span class="line"><span style="color: #D4D4D4">    main.cpp</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Qt resource files</span></span>
<span class="line"><span style="color: #D4D4D4">set(QRC_SRC</span></span>
<span class="line"><span style="color: #D4D4D4">    resources.qrc</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Create the executable</span></span>
<span class="line"><span style="color: #D4D4D4">qt_add_executable(</span></span>
<span class="line"><span style="color: #D4D4D4">    ${PROJECT_NAME}</span></span>
<span class="line"><span style="color: #D4D4D4">    MANUAL_FINALIZATION</span></span>
<span class="line"><span style="color: #D4D4D4">    ${CPP_SRC}</span></span>
<span class="line"><span style="color: #D4D4D4">    ${QRC_SRC}</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">``</span></span></code></pre></div>



<p class="wp-block-paragraph">To skip the C++ generation step, use the <code><span style="font-size:larger;"><strong>qt_add_big_resources()</strong></span></code> function:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># Run Qt Resource Compiler automatically
set(CMAKE_AUTORCC ON)

# C++ source files
set(CPP_SRC
    main.cpp
)

# Generate resource source files from resources.qrc
qt_add_big_resources(
    QRC_SRC
    resources.qrc
)

# Create executable
qt_add_executable(
    ${PROJECT_NAME}
    MANUAL_FINALIZATION
    ${CPP_SRC}
    ${QRC_SRC}
)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"># Run Qt Resource Compiler automatically</span></span>
<span class="line"><span style="color: #D4D4D4">set(CMAKE_AUTORCC ON)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># C++ source files</span></span>
<span class="line"><span style="color: #D4D4D4">set(CPP_SRC</span></span>
<span class="line"><span style="color: #D4D4D4">    main.cpp</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Generate resource source files from resources.qrc</span></span>
<span class="line"><span style="color: #D4D4D4">qt_add_big_resources(</span></span>
<span class="line"><span style="color: #D4D4D4">    QRC_SRC</span></span>
<span class="line"><span style="color: #D4D4D4">    resources.qrc</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Create executable</span></span>
<span class="line"><span style="color: #D4D4D4">qt_add_executable(</span></span>
<span class="line"><span style="color: #D4D4D4">    ${PROJECT_NAME}</span></span>
<span class="line"><span style="color: #D4D4D4">    MANUAL_FINALIZATION</span></span>
<span class="line"><span style="color: #D4D4D4">    ${CPP_SRC}</span></span>
<span class="line"><span style="color: #D4D4D4">    ${QRC_SRC}</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span></code></pre></div>



<p class="wp-block-paragraph">The resource compiler will then produce object files without bothering with the intermediate C++ source. Most of the time, you might as well just use <code><span style="font-size:larger;"><strong>qt_add_big_resources()</strong></span></code>&#8230; you know what’s in the generated C++ source now, and it’s probably not that useful to you.</p>



<h2 id="h-summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">Qt’s resource compiler can be a nice way to simplify access to data files in your application. You just need to understand the rules regarding URL formats, and make note of a few potential pitfalls:</p>



<ul class="wp-block-list">
<li>Nothing’s stopping you from giving multiple things the same alias, so be careful</li>



<li>Nothing’s stopping you from having multiple QRC files, and nothing’s stopping you from duplicating prefixes and aliases across multiple files</li>



<li>If you applied an alias to a resource, you <strong><em>must</em></strong> use it in the URLs</li>



<li>Unless you’re especially interested in looking at the C++ source file generated by the resource compiler, feel free to skip that step and use the <code><span style="font-size:larger;"><strong>qt_add_big_resources()</strong></span></code> function in CMake</li>
</ul>



<h2 id="h-building-a-qt-app" class="wp-block-heading">Building a Qt app?</h2>



<p class="wp-block-paragraph">I’d love to help! Give us a call or <a href="mailto:sales@localhost?subject=Let's%20build%20a%20Qt%20app!">send us an email</a> to discuss!</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/18025/a-brief-tutorial-on-qts-resource-files/">A Brief Tutorial on Qt’s Resource Files</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Advantages of .NET and Python for Test &#038; Measurement Applications</title>
		<link>https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Wed, 07 Dec 2022 16:57:14 +0000</pubDate>
				<category><![CDATA[LabVIEW]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Test and Measurement Automation]]></category>
		<category><![CDATA[User Interface Design]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/</guid>

					<description><![CDATA[<p>Prologue In May 2019, at what would become the last NI Week ever, I led a session called “Learning to Love Text Again With Measurement Studio.” It was scheduled for 8:30 AM on the last day of the conference, so I was surprised that so many travel-weary engineers stumbled in, red-eyed, with clumps of taco [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/">Advantages of .NET and Python for Test &#038; Measurement Applications</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-prologue" class="wp-block-heading">Prologue</h2>



<p class="wp-block-paragraph">In May 2019, at what would become the last NI Week ever, I led a session called “<a href="https://forums.ni.com/ni/attachments/ni/niweeksessions/425/11/Learning%20to%20Love%20Text%20Again%20With%20Measurement%20Studio_Software.pdf">Learning to Love Text Again With Measurement Studio</a>.” It was scheduled for 8:30 AM on the last day of the conference, so I was surprised that so many travel-weary engineers stumbled in, red-eyed, with clumps of taco still stuck in their hair, ready to listen to me talk about what&nbsp;I thought was a fairly niche topic. I was wrong! The room was filled with enthusiasm, although some of it was already about where to get lunch.</p>



<p class="wp-block-paragraph">It is now December 2022. NI Week is gone, but the Test &amp; Measurement community is still hungry for Austin’s spectacular tacos and alternative software development platforms. A mere three-and-a-half years later, I have finally found time to convert that presentation into a series of blog posts for those of you who weren’t there (or who slept through it). The intent is to motivate the use of Python or Microsoft’s .NET platform as programming environments for&nbsp;<a href="https://static.dmcinfo.com/services/test-and-measurement-automation">Test &amp; Measurement Automation</a>, and to provide you with some guidance toward getting started.</p>



<h2 id="h-introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">The purpose of this post is to describe the advantages of Python and .NET software development for Test &amp; Measurement applications. The <em>de facto</em> standard is generally National Instruments LabVIEW, due to the shallow learning curve and the quality and&nbsp;feature set of NI hardware; however, NI is also very good about offering hardware APIs for other languages&nbsp;— which gives you the flexibility to choose another option if it’s the right tool for the job. This post will describe why, in certain circumstances, the right tool may be Python or .NET.</p>



<p class="wp-block-paragraph">In a separate post, <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10390/measurement-studio-net-programming-for-ni-enthusiasts">I also offer a brief overview of NI’s Measurement Studio</a>&nbsp;— which is a very useful set of tools that makes it easier for LabVIEW developers to get started with .NET.</p>



<p class="wp-block-paragraph">Measurement Studio offers:</p>



<ul class="wp-block-list">
<li>.NET classes and functions analogous to LabVIEW’s data analysis VIs</li>



<li>.NET data types analogous to those used by LabVIEW (e.g., analog and&nbsp;digital waveform types)</li>



<li>A .NET API for creating and managing TDMS files</li>



<li>Controls, indicators, and graph elements that can be used in .NET UIs</li>
</ul>



<p class="wp-block-paragraph">If I make a compelling case here and you’d like to try building your next Test &amp; Measurement application in .NET, I recommend getting started with the free trial of Measurement Studio. The familiar tools that it provides will help you leverage your existing LabVIEW knowledge to work efficiently in a new environment.</p>



<h2 id="h-strengths-of-labview-development" class="wp-block-heading">Strengths of LabVIEW Development</h2>



<p class="wp-block-paragraph">From the beginning, NI’s mission for LabVIEW was to make it easy for scientists and&nbsp;engineers to build Test &amp; Measurement applications. Since the mid-80s, the guiding principles of NI’s LabVIEW investment were to:</p>



<ul class="wp-block-list">
<li>Enable fast, easy software development</li>



<li>Make hardware integration as simple as possible</li>



<li>Provide a standard library with a focus on engineering functionality</li>



<li>Lower the barrier to producing graphical user interfaces</li>
</ul>



<p class="wp-block-paragraph">For these reasons, there are some use cases for which LabVIEW is an obvious win. If you need to get an application up and&nbsp;running quickly, if it needs to acquire, process, and visualize data, and if it will neither&nbsp;be deployed widely nor maintained for a very long time, then you will likely benefit from the productivity of developing in LabVIEW.</p>



<h2 id="h-where-can-we-do-better" class="wp-block-heading">Where Can We Do Better?</h2>



<p class="wp-block-paragraph">Larger projects require larger teams in order to meet delivery deadlines or maintain the software throughout its lifecycle. However, larger teams need to be able to work in parallel without stepping on each other’s toes. Beyond the initial delivery, maintaining the software can become a challenge as it ages and evolves&nbsp;and&nbsp;as developers drift in and&nbsp;out of the project. Here, we will discuss some advantages to Python and C# that reduce the complexity of maintaining a large software project throughout its lifecycle.</p>



<h3 id="h-enabling-technology" class="wp-block-heading">Enabling Technology</h3>



<p class="wp-block-paragraph">Oddly enough, the graphical nature of LabVIEW, which makes it one of the most beginner-friendly&nbsp;programming languages, also makes it extremely difficult to compare two pieces of code. It is easy to navigate and visually parse LabVIEW code, but very difficult to graphically represent <strong><em>the difference</em></strong> between two pieces of code.</p>



<p class="wp-block-paragraph">Consider the case in which you wrote a VI and shared it with a colleague. If that colleague edited it and gave you a new version, how would you find all of&nbsp;the differences? You can hunt them down on your own, but you’ll need to search every case structure and check the default value of every control. Even if it were a simple VI that could be visually compared easily, you’d have to identify each change as either functional or cosmetic (i.e., an additional wire bend is a difference, but an inconsequential one). <a href="https://labviewwiki.org/wiki/Set_up_differencing_capabilities" target="_blank">Some automated tools exist</a>, but they tend to be hard to configure and use. Furthermore, once all of the differences are identified, how do you visualize them concisely?</p>



<p class="wp-block-paragraph">Text source code is much more limited in terms of layout. It’s very easy for a computer to parse two chunks of text, compare them, and produce a simple visualization of the differences (often referred to as a “diff”). Simple as it may seem, this enables a substantial number of tools and techniques for managing source code that are not available for complex binary files like VIs. This is critical for code reviews as a quality assurance technique. Senior developers can easily review only the diffs, which are both concise and automatically generated.</p>



<h3 id="h-multi-developer-workflows" class="wp-block-heading">Multi-developer Workflows</h3>



<p class="wp-block-paragraph">Being able to see only the differences in two chunks of code is important for quality assurance, but it gets even better: a diff can also be used to easily merge changes into source code as well. This is a major enabler for multi-developer scenarios because it means that two or more developers can make changes to different parts of the same source file, and those changes can be blended together easily (i.e., they won’t collide unless there are different variations of the same lines of text).</p>



<p class="wp-block-paragraph">DMC’s platform of choice is <a href="https://about.gitlab.com/" target="_blank">GitLab</a>, which is a web-based software development management tool.</p>



<p class="wp-block-paragraph">GitLab provides:</p>



<ul class="wp-block-list">
<li>A revision-control system (<a href="https://git-scm.com/" target="_blank">git</a>)</li>



<li>Source code navigation, viewing, and comparing</li>



<li>Organized methods for users to track issues and resolutions</li>



<li>Automations for testing and building applications</li>



<li>Different user roles that enable better collaboration with both internal and&nbsp;external teams</li>



<li>Many other features</li>
</ul>



<p class="wp-block-paragraph">Of particular importance is the issue resolution workflow, which can be separated into individual workflows for different user roles, such as:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/workflow_1.png" alt=""/></figure>



<p class="wp-block-paragraph">In this diagram, we have several people working in parallel (from top to bottom): a technical lead, a few developers, and anyone else with access to the source. Anyone with access to the source code repository can test the code and report issues (bottom). Developers (center) can select a reported issue, create a branch of code dedicated to its resolution, resolve the issue, and submit a “merge request.” The technical lead of the project (top) can then review those merge requests, ensure the code meets quality standards (by viewing the diff of that branch with the main line of development), and merge the updated code into the main line of development independently and in parallel with the developers.</p>



<p class="wp-block-paragraph">We have found that the cadence of code reviews is easier to maintain with this workflow. GitLab serves as a portal to view only the parts of the code that have changed and allows you to discuss those changes with the developer (asynchronously, via the web interface) before merging them. Instead of sitting down in a conference room and having the developer&nbsp; walk the technical lead through all the changes in a branch, the technical lead can simply view a diff those changes&nbsp;and enter comments or suggestions that the developer can then address later (as shown below). Once all comments have been addressed and the technical lead is satisfied with the changes, they can be merged.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/gitlab-diff.png" alt="The GitLab comparison tool shows the source code diff and allows a code reviewer to leave comments &amp; questions for the code's author."/></figure>



<p class="wp-block-paragraph">This substantially improves the code review workflow, allowing the tech lead and developers to work asynchronously, communicate effectively, and collaborate productively.</p>



<h3 id="h-separation-of-concerns" class="wp-block-heading">Separation of Concerns</h3>



<p class="wp-block-paragraph">One objective of good software design is “separation of concerns.” One must break down the problem into smaller and smaller problems, and then those into <strong><em>even smaller</em></strong> problems, continuing until there is a tree of convenient fun-size problems that can each be solved easily. Implicit in this methodology is the idea that each problem should be independent of the others. The programmer should establish clear interfaces between problems to keep them logically separate.</p>



<p class="wp-block-paragraph">If you haven’t heard of SOLID design principles, consider <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10019/dmc-attends-2019-national-instruments-cla-summit">reading through a blog post</a> by my colleague and LabVIEW <em>aficionado</em> <a href="https://static.dmcinfo.com/about/employee-bios/steven-dusing">Steven Dusing</a>. The “S” in SOLID stands for “single responsibility,” meaning that, as you separate your concerns into smaller, more manageable problems, you should end up with chunks of code that do one thing and one thing <strong><em>only</em></strong>.</p>



<p class="wp-block-paragraph">If different pieces of code are truly separated, you can even delegate them to people with different skill sets. Wouldn’t it be nice to have a UI designer handle the graphical layout of your application while engineers develop the business logic? A LabVIEW VI has a user interface (front panel) that is inextricably tied to the business logic (block diagram). This makes it dirt-simple to produce a GUI application, but it also&nbsp;makes it very hard to have a complex UI that is separate from the business logic and can be developed by a different person.</p>



<p class="wp-block-paragraph">As an example, consider <a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/?view=netdesktop-6.0">WPF</a>, one of the graphical frameworks available on the .NET platform. The UI layout is specified with <a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/?view=netdesktop-6.0" target="_blank">an XML-style language</a> completely separately from the run-time logic. Since the graphical layout is specified as a text language, diffs are supported for easy review, and, since it exists in its own file, your UI/UX designer can work in parallel with the engineering team. We have effectively separated our concerns: UI things get handled in a UI file by a UI expert, and business logic things get handled in C# code by C# experts.</p>



<p class="wp-block-paragraph">Outside of the .NET framework, various graphical toolkits are available&nbsp;but&nbsp;my personal preference is <a href="https://www.qt.io/" target="_blank">Qt</a>&nbsp;—&nbsp;which is available under the terms of the LGPL (mostly). Qt provides QML, which is conceptually similar to XAML (i.e., like XAML, QML is a declarative UI description language), and gets used in the same way to separate your UI design from business logic. Business logic can be implemented in Python, C++, or some other languages with third-party bindings to Qt.</p>



<h3 id="h-best-practices-for-software-development" class="wp-block-heading">Best Practices for Software Development</h3>



<p class="wp-block-paragraph">In LabVIEW 8.2, object-oriented design principles were introduced to LabVIEW, which was a difficult balancing act. NI’s team aimed to make Object-Oriented Programming (OOP) accessible to scientists and engineers who didn’t necessarily have a computer scientist’s background, without compromising the fundamentals on which LabVIEW was built.</p>



<p class="wp-block-paragraph"><a href="https://www.ni.com/en-us/support/documentation/supplemental/06/labview-object-oriented-programming--the-decisions-behind-the-de.html" target="_blank">This was done out of a recognition that</a>:</p>



<blockquote class="is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph">Object-oriented programming has demonstrated its superiority over procedural programming as an architecture choice in several programming languages. It encourages clear divisions between sections of the code, it is easier to debug, and it scales better for large programming teams. LabVIEW R&amp;D wanted this power to be accessible by our customers. We wanted the language to be able to enforce some of these software best-practices.</p>


</blockquote>



<p class="wp-block-paragraph">There are a lot of compelling reasons to use an object orientation (OO) approach, and NI needed to balance that with the need to maintain the concept of dataflow, and a recognition that this might not be the right approach for every person, every project, and every situation. Consequently, the implementation of OO in LabVIEW is very useful and&nbsp;very easy to use, but it is substantially different from traditional OO. For larger projects with larger teams, leveraging the scalability and modularity of a truly object-oriented language can lead to a better product and a more efficient development experience.</p>



<h3 id="h-deployment" class="wp-block-heading">Deployment</h3>



<p class="wp-block-paragraph">The three languages we’ve been discussing most (LabVIEW, C#, and Python) all need to run in the context of some set of libraries on the target machine. That may be the LabVIEW Runtime Engine, the .NET Framework, or the Python interpreter, respectively.</p>



<p class="wp-block-paragraph">Applications that will be widely distributed will generally benefit from the fact that all Windows installations either have the .NET runtime installed already, or they make it easy to do so automatically. Python even has the advantage of creating virtual environments&nbsp;which allow multiple Python interpreters to co-exist on a system, and for each project to have its own set of dependencies installed without any of them colliding with the others. Virtual environments are important for development, but they can also be used to ensure that applications are installed consistently across various systems.</p>



<p class="wp-block-paragraph">For large development projects, the deployment procedure should be considered early. In many cases, the .NET framework is already installed and available on Windows machines, and if the target will be Linux, Python is widely available and often distributed by default with an OS installation.</p>



<h3 id="h-community-support-and-nbsp-engagement" class="wp-block-heading">Community Support and&nbsp;Engagement</h3>



<p class="wp-block-paragraph">The largest and most active development communities generally produce some of the most useful third-party tools,&nbsp;often under open-source license terms. Based on the relatively informal <a href="https://www.tiobe.com/tiobe-index/">TIOBE index</a>, both C# and Python land in the top 5 most searched programming languages. Using that as a proxy for the activity of their respective communities, it is not surprising that the package managers for these platforms runneth over with useful tools and libraries that can be readily used in your projects.</p>



<p class="wp-block-paragraph">The .NET framework provides a package management system called <a href="https://www.nuget.org/" target="_blank">NuGet</a>, which currently hosts over 330,000 unique packages. For Python, the pip module is used to connect to <a href="https://pypi.org/" target="_blank">pypi</a> (over 420,000 unique packages) or other package repositories. While LabVIEW does have both JKI’s VI Package Manager (with over 1000 packages) and NI’s own package manager, neither offers the same level of community engagement.</p>



<p class="wp-block-paragraph">As projects get larger, the availability and quality of reusable tools and&nbsp;libraries can help drive down the amount of code you need to maintain yourself.</p>



<h2 id="h-summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">In recent years, NI has made substantial investments in support for .NET and Python. While LabVIEW continues to be a good option for some Test &amp; Measurement applications, many (particularly large projects) could benefit from different tools.</p>



<p class="wp-block-paragraph">For some engineers, the learning curve of training up on a language other than LabVIEW may be prohibitive. In these cases, I recommend considering NI Measurement Studio as a stepping stone. Read <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10390/measurement-studio-net-programming-for-ni-enthusiasts" type="link" id="https://static.dmcinfo.com/latest-thinking/blog/id/10390/measurement-studio-net-programming-for-ni-enthusiasts">my overview</a> of how Measurement Studio can fill the gaps between LabVIEW and C#.</p>



<p class="wp-block-paragraph"><strong>Learn more about DMC&#8217;s <a href="https://static.dmcinfo.com/services/test-and-measurement-automation">Test &amp; Measurement Automation solutions</a>, and <a href="https://static.dmcinfo.com/contact">contact us</a> today for your next project.</strong></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/">Advantages of .NET and Python for Test &#038; Measurement Applications</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Measurement Studio: .NET Programming for NI Enthusiasts</title>
		<link>https://static.dmcinfo.com/blog/18038/measurement-studio-net-programming-for-ni-enthusiasts/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Tue, 06 Dec 2022 11:56:45 +0000</pubDate>
				<category><![CDATA[LabVIEW]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Test and Measurement Automation]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/18038/measurement-studio-net-programming-for-ni-enthusiasts/</guid>

					<description><![CDATA[<p>Prologue In May 2019, at what would become the last NI Week ever, I led a session called “Learning to Love Text Again With Measurement Studio.” It was scheduled for 8:30 AM on the last day of the conference, so I was surprised that so many travel-weary engineers stumbled in, red-eyed, with clumps of taco [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18038/measurement-studio-net-programming-for-ni-enthusiasts/">Measurement Studio: .NET Programming for NI Enthusiasts</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-prologue" class="wp-block-heading">Prologue</h2>



<p class="wp-block-paragraph">In May 2019, at what would become the last NI Week ever, I led a session called “<a href="https://forums.ni.com/ni/attachments/ni/niweeksessions/425/11/Learning%20to%20Love%20Text%20Again%20With%20Measurement%20Studio_Software.pdf">Learning to Love Text Again With Measurement Studio</a>.” It was scheduled for 8:30 AM on the last day of the conference, so I was surprised that so many travel-weary engineers stumbled in, red-eyed, with clumps of taco still stuck in their hair, ready to listen to me talk about what&nbsp;I thought was a fairly niche topic. I was wrong! The room was filled with enthusiasm, although some of it was already about where to get lunch.</p>



<p class="wp-block-paragraph">It is now December 2022. NI Week is gone, but the Test &amp; Measurement community is still hungry for Austin’s spectacular tacos and alternative software development platforms. A mere three-and-a-half years later, I have finally found time to convert that presentation into a series of blog posts for those of you who weren’t there (or who slept through it). The intent is to motivate the use of Python or Microsoft’s .NET platform as programming environments for <a href="https://static.dmcinfo.com/services/test-and-measurement-automation/">Test &amp; Measurement Automation</a>, and to provide you with some guidance toward getting started.</p>



<h2 id="h-introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">This post will focus on NI’s Measurement Studio, what it is, and why it’s useful. In another post, <a href="https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/">I will write about the procedural advantages</a> (code reviews, separation of concerns, multi-developer teams) of developing Test &amp; Measurement applications in .NET or Python. In&nbsp;yet another, I will write about the abundance of tools available to you in those environments (like ORM, web applications, etc.).</p>



<h2 id="h-motivation-using-the-right-tool-for-the-job" class="wp-block-heading">Motivation: Using the Right Tool for the Job</h2>



<p class="wp-block-paragraph">As engineers, we know that there are many ways of doing a thing, and that there are many tradeoffs that need to be considered before deciding how the thing will get done. Programming languages are a dime a dozen, and they all pretty much do the same thing. So, if you’ve already got LabVIEW competency, why invest the time in learning Microsoft’s (extremely large, complicated, and intimidating) .NET stack? On the face of it, that doesn’t seem like a worthwhile tradeoff if both languages get the thing done.</p>



<p class="wp-block-paragraph">LabVIEW’s dataflow language is a great way for engineers without programming experience to write data-acquisition software quickly, without fear of encountering some freakish horror like:</p>



<p class="wp-block-paragraph"><span style="color:#990000;"><span style="font-size:larger;"><strong><code>/usr/lib/../lib/crt1.o: In function `_start':&nbsp;(.text+0x20): undefined reference to `main'</code></strong></span></span></p>



<p class="wp-block-paragraph">This is one reason why LabVIEW will always occupy a 32&#215;32 pixel area of our hearts: quickly acquiring and visualizing data just doesn’t get any easier; however, as software reaches a certain level of maturity or complexity, other development environments can substantially simplify the management of the software’s lifecycle. DMC develops <em><strong>extremely large</strong></em> Test &amp; Measurement applications with deep inheritance hierarchies and complex sets of dependencies. As a project gets larger and larger, it becomes more and more difficult to manage LabVIEW code, work on it with a team, control &amp; assure its quality, and deploy it to customers.</p>



<p class="wp-block-paragraph">Over the last several decades, the software community (separately from the engineering community) built itself many tools to achieve those same goals. As the two communities have converged over the years, their tools have become available off-the-shelf not just for the software developers, but for the mechanical engineers, the electrical engineers, and everyone else. Test &amp; Measurement applications can stand to benefit from improved processes that already exist thanks to those tools. For example, .NET, Python, and other languages offer:</p>



<ul class="wp-block-list">
<li>The humble and&nbsp;mighty&nbsp;diff (an easy way to compare two versions of textual source code)</li>



<li>Tools for multi-developer scenarios (for example, GitLab and GitHub —&nbsp;which work much better with text than with VIs)</li>



<li>Tools for code quality (such as linters, formatters, and so on)</li>



<li>Huge libraries of third-party packages</li>



<li>Really sweet dynamic UIs</li>



<li>More opportunities to adhere to good programming practices with object-oriented programming paradigms</li>
</ul>



<p class="wp-block-paragraph"><strong>Note:</strong> again,&nbsp;there will always be a time and a place for LabVIEW, but it is not “all the time” and “everywhere.”</p>



<h2 id="h-what-s-the-problem-then" class="wp-block-heading">What&#8217;s the Problem, Then?</h2>



<p class="wp-block-paragraph">The problem is that you’re busy, and you don’t have time for this.</p>



<p class="wp-block-paragraph">But what if I told you that getting started with .NET doesn’t mean you have to start from scratch? What if I told you that NI predicted this traumatic event and already provided the tools that you, a LabVIEW developer, would need to productively develop an application in C#?</p>



<h2 id="h-measurement-studio" class="wp-block-heading">Measurement Studio</h2>



<p class="wp-block-paragraph">A lot of people familiar with NI’s ecosystem have never used <a href="https://www.ni.com/en-us/shop/electronic-test-instrumentation/application-software-for-electronic-test-and-instrumentation-category/what-is-measurement-studio.html" target="_blank">Measurement Studio</a> and don’t really know what it is. Here’s what it is <strong><em>not</em></strong>:</p>



<ul class="wp-block-list">
<li>Measurement Studio is <strong><em>not</em></strong> a new language (it’s regular ol’ C#)</li>



<li>Measurement Studio is <strong><em>not</em></strong> a new IDE (it’s regular ol’ Microsoft Visual Studio)</li>



<li>Measurement Studio is <strong><em>not</em></strong> the same as LabWindows/CVI</li>



<li>Measurement Studio is <strong><em>not</em></strong> a set of hardware drivers</li>
</ul>



<p class="wp-block-paragraph">What Measurement Studio <strong><em>is</em></strong>, however, is the bridge between LabVIEW and .NET. It is a set of tools that are analogous to the ones you’re used to in LabVIEW, so you can take everything you know about LabVIEW programming and easily transition it to C#.</p>



<h3 id="h-same-concepts-different-angle" class="wp-block-heading">Same Concepts, Different Angle</h3>



<p class="wp-block-paragraph">You already know how to acquire data from DAQmx in LabVIEW:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/daqmx-labview.png" alt="Typical usage of the LabVIEW DAQmx API."/></figure>



<p class="wp-block-paragraph">If you wanted to do the same thing in .NET, your code would conceptually be the same, just&#8230; rotated 90 degrees clockwise:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/daqmx-dotnet.png" alt="The .NET API for DAQmx is directly analogous to the LabVIEW API."/></figure>



<p class="wp-block-paragraph">As you can see, NI provides a consistent API for DAQmx across multiple languages. There’s the familiar LabVIEW API for DAQmx and a parallel .NET API (i.e., there&#8217;s basically a one-to-one mapping of LabVIEW VIs to class methods in C#), as shown above. For Python fans, I would also recommend NI&#8217;s Python API for DAQmx, <a href="https://static.dmcinfo.com/our-work/ni-data-acquisition-library-and-calibration-utility-in-python/">which we&#8217;ve also used</a>.</p>



<h3 id="h-hardware-drivers-are-already-available" class="wp-block-heading">Hardware Drivers are Already Available!</h3>



<p class="wp-block-paragraph">Any time you install the DAQmx drivers, you have the option of installing the .NET language bindings, too. The DAQmx drivers are <strong><em>not</em></strong> a part of Measurement Studio (they come with all NI hardware that supports DAQmx). The same goes for some other drivers, so DAQmx, NI-VISA, and NI-GPIB drivers all have “.NET Development Support” <a href="https://knowledge.ni.com/KnowledgeArticleDetails?id=kA03q000000x0QqCAI&amp;l=en-US" target="_blank">options in the installers</a>&nbsp;—&nbsp;even without Measurement Studio. Some have even been open-sourced and hosted on GitHub, like the <a href="https://github.com/ni/vdm-dotnet" target="_blank">Vision Development Module for .NET</a>! Others are available as a separate download, like .NET APIs for NI-Switch, NI-DMM, and NI-FGEN. See <a href="https://www.ni.com/en/support/documentation/supplemental/13/national-instruments--net-support.html" type="link" id="https://www.ni.com/en/support/documentation/supplemental/13/national-instruments--net-support.html">NI’s documentation</a> here for more information.</p>



<h3 id="h-measurement-studio-s-value-add" class="wp-block-heading">Measurement Studio&#8217;s Value-Add</h3>



<p class="wp-block-paragraph">You already have your hardware drivers available, and you already know how to use them, so whether you’re working in LabVIEW or C#, you can acquire some data. Now&nbsp;you need to do stuff to it. In LabVIEW, you know how to do stuff to data. You do stuff to data all day long;&nbsp;<b><i>it’s literally your job</i></b>! So, if you’re going to work productively in C#, you’ll need to know how to do the same things. This is where Measurement Studio makes your life substantially easier. Once you install Measurement Studio, you will have access to .NET libraries that provide <b><i>the</i></b> <b><i>same functionality</i></b> that you’re already using in LabVIEW, and they’re even organized in <b><i>the same way</i></b>:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/namespace-palette.png" alt="The NationalInstruments namespace includes APIs for data acquisition, data analysis, TDMS logging, and more."/></figure>



<h4 id="h-butterworth-filter-example" class="wp-block-heading">Butterworth Filter Example</h4>



<p class="wp-block-paragraph">The parallels extend down to individual VIs, which typically map to a single .NET function or class. For example, consider the <a href="https://www.ni.com/docs/en-US/bundle/labview-api-ref/page/vi-lib/analysis/3filter-llb/butterworth-filter-vi.html" target="_blank" rel="noreferrer noopener">Butterworth Filter VI</a>.</p>



<p class="wp-block-paragraph">The analogous .NET library is provided by Measurement Studio&nbsp;and would be used like this in C#:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap; font-size:larger;"><strong><span style="color:#0070C0;">using</span> NationalInstruments.Analysis.Dsp.Filters;<br>
<span style="color: rgb(0, 112, 192);">var</span> <span style="color: rgb(113, 64, 121);">newFilter</span> = ButterworthLowpassFilter(<span style="color: rgb(0, 112, 192);">order</span>, <span style="color: rgb(191, 89, 0);">fs</span>, <span style="color:#BF5900;">fh</span>);<br>
<span style="color: rgb(0, 112, 192);">var</span> <span style="color: rgb(191, 89, 0);">filteredX</span> = <span style="color: rgb(113, 64, 121);">newFilter</span>.FilterData(<span style="color: rgb(191, 89, 0);">X</span>);</strong></span></code></p>



<p class="wp-block-paragraph">You simply create a <strong><code><span style="font-size:larger;">ButterworthLowpassFilter</span></code></strong> object with the same <code><span style="font-size:larger;"><strong><span style="color: rgb(0, 112, 192);">order</span></strong></span></code>, <code><span style="font-size:larger;"><strong><span style="color: rgb(191, 89, 0);">fs</span></strong></span></code>, and <code><span style="font-size:larger;"><strong><span style="color:#BF5900;">fh</span></strong></span></code>&nbsp;parameters that you’d wire to the VI. You then call that object’s <code><span style="font-size:larger;"><strong>FilterData()</strong></span></code>&nbsp;method on the input array (<code><span style="font-size:larger;"><strong><span style="color: rgb(191, 89, 0);">X</span></strong></span></code>), and it gives you the filtered output array (<code><span style="font-size:larger;"><strong><span style="color: rgb(191, 89, 0);">filteredX</span></strong></span></code>).</p>



<p class="wp-block-paragraph">There’s a specific lowpass filter class, so you don’t need the <code><span style="font-size:larger;"><strong><span style="color: rgb(0, 112, 192);">filter type</span></strong></span></code>&nbsp;input, and the <code><span style="font-size:larger;"><strong><span style="color: rgb(191, 89, 0);">fl</span></strong></span></code>&nbsp;value isn’t applicable. Nor do you need the <span style="color:#538135;"><code><span style="font-size:larger;"><strong>init/cont</strong></span></code></span>&nbsp;input (the nature of object-oriented programming means you don’t need to manipulate the filter’s state at the same time you’re trying to use it). I would make the bold claim that, while this may seem unfamiliar at first, the VI is trying to do too much, so the C# code is ultimately more intuitive &amp; readable than the VI is!</p>



<h4 id="h-tdms-example" class="wp-block-heading">TDMS example</h4>



<p class="wp-block-paragraph">Whether you’re using LabVIEW or C# to glue the parts together, you’ve acquired your data with DAQmx, filtered it with a Butterworth filter, and now you need to store that data in a TDMS file. In LabVIEW, you might have something like:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/tdms-labview.png" alt="Typical usage of the LabVIEW TDMS API."/></figure>



<p class="wp-block-paragraph">And the equivalent C# code, using libraries provided by Measurement Studio, is:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap; font-size:larger;"><strong><span style="color:#0070C0;">using</span> NationalInstruments.Tdms;<br>
<span style="color: rgb(0, 112, 192);">var</span> tdmsFile = <span style="color: rgb(0, 112, 192);">new</span> TdmsFile(<span style="color: #C00000;">"C:\filePath.tdms"</span>, <span style="color: rgb(0, 112, 192);">new</span> TdmsFileOptions());<br>
<span style="color: rgb(0, 112, 192);">var</span> channel = tdmsFile.AddChannelGroup(<span style="color: #C00000;">"DataGroup"</span>).AddChannel(<span style="color: #C00000;">"DataChannel"</span>, TdmsDataType.Double);<br>
channel.AppendData&lt;<span style="color: #9656A1;">double</span>&gt;(<span style="color: rgb(191, 89, 0);">123.45</span>);<br>
tdmsFile.Close();</strong></span></code></p>



<p class="wp-block-paragraph">Unlike hardware drivers, which already have .NET language bindings available in their installers, the data analysis and TDMS methods in C# <em><strong>are provided by Measurement Studio</strong></em>.</p>



<p class="wp-block-paragraph">What I hope is evident from these examples is that you don’t need to start from scratch when you use Measurement Studio. It provides equivalent classes &amp; functions to the VIs you already know you want. Measurement Studio is just a parallel universe where the only difference is that you develop data acquisition software with your keyboard instead of your mouse!</p>



<h3 id="h-debugging" class="wp-block-heading">Debugging</h3>



<p class="wp-block-paragraph">The code itself is only one piece of the puzzle. The IDE is a big part of the programming experience, too. As I mentioned before, Measurement Studio <strong><em>is not</em></strong> a new language or a new IDE. The language is just standard C#, and Measurement Studio installs a few extensions to Microsoft’s Visual Studio IDE, which is an extremely powerful programming and debugging environment.</p>



<p class="wp-block-paragraph">It’s easy to acquire, filter, and write your data to disk, but, as you develop your application, you’ll eventually need to debug a problem. In LabVIEW, you’ve got your trusty probes, breakpoints, and execution highlighting. What do you do in Visual Studio? Exactly the same things!</p>



<p class="wp-block-paragraph">You can <a href="https://learn.microsoft.com/en-us/visualstudio/debugger/debugger-feature-tour?view=vs-2019#set-a-breakpoint-and-start-the-debugger" target="_blank">easily add breakpoints</a> (yes, conditional ones, too) and step through your code line-by-line. This is standard in Visual Studio (and virtually all IDEs).</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="752" height="420" src="https://static.dmcinfo.com/wp-content/uploads/2022/12/dbg-tour-set-a-breakpoint.gif" alt="Microsoft Visual Studio's breakpoint feature" class="wp-image-35312"/></figure>



<p class="wp-block-paragraph">Since Measurement Studio’s .NET class libraries provide data types like the ones you’re familiar with (for example, <code><span style="font-size:larger;"><strong>AnalogWaveform</strong></span></code>), <a href="https://learn.microsoft.com/en-us/visualstudio/debugger/debugger-feature-tour?view=vs-2019#inspect-variables-with-data-tips" target="_blank">it also provides “data tips”</a> so that the debugger can inspect (“probe”) those data types while you’re debugging.</p>



<p class="wp-block-paragraph">Visual Studio brings some of its own fun debugging tricks, too. For example, you can:</p>



<ul class="wp-block-list">
<li>Edit code while it’s running in debug mode!</li>



<li>Change the flow of execution (i.e., manually move to particular lines of code) while you’re debugging!</li>



<li>Change the values of variables in memory while you’re debugging!</li>
</ul>



<h3 id="h-visualization" class="wp-block-heading">Visualization</h3>



<p class="wp-block-paragraph">When it comes to graphs &amp; charts, nothing’s easier than LabVIEW. In Test &amp; Measurement applications, you’re almost certainly going to need to plot some data, and Measurement Studio makes it easy.</p>



<p class="wp-block-paragraph">The current standard for GUI development in .NET is <a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/?view=netdesktop-6.0" target="_blank">WPF</a>, which provides standard controls and indicators like buttons, text boxes, sliders, etc; however, as a LabVIEW developer, you’re going to want some LEDs, some touchscreen-friendly switches, and some high-visibility dial gauges alongside your graphs and charts. Measurement Studio has you covered here, too, by providing <a href="https://www.ni.com/en-us/shop/electronic-test-instrumentation/application-software-for-electronic-test-and-instrumentation-category/what-is-measurement-studio.html" target="_blank">familiar controls and indicators for your WPF user interface</a>:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/wpf-controls.png" alt="A sample of WPF controls. Some are provided by the .NET framework, others are provided by Measurement Studio."/></figure>



<p class="wp-block-paragraph">And, as a bonus, WPF provides plenty of customization and styling options&nbsp;—&nbsp;as well as hardware-accelerated vector graphics and easy re-sizing.</p>



<h2 id="h-summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">Measurement Studio gives you the following tools to help ease your transition from LabVIEW to .NET:</p>



<ul class="wp-block-list">
<li>Convenient NI-style controls, indicators, and graphs that you’re used to</li>



<li>Convenient NI-style data types that you’re used to</li>



<li>Convenient NI-style APIs for all the analysis functions you’re used to</li>



<li>A convenient NI-style TDMS API that you’re used to</li>
</ul>



<p class="wp-block-paragraph">These tools can help you continue to work productively as you migrate from LabVIEW to an unfamiliar programming environment. With all these convenient, NI-style tools available, you can ease right into it.</p>



<p class="wp-block-paragraph">All I’ve done here is talk about how Measurement Studio can help <strong><em>if you choose</em></strong> to develop your application in .NET instead of LabVIEW. We haven’t even discussed <strong><em>why</em></strong> you would choose that! If this post has made C# seem more accessible with Measurement Studio but you&#8217;re still not sure why it&#8217;s worth trying, then I would suggest reading through <a href="https://static.dmcinfo.com/blog/18030/advantages-of-net-and-python-for-test-measurement-applications/">this related post</a>&nbsp;to give you more insight on how platforms like .NET and Python can help you build and maintain your Test &amp; Measurement software.</p>



<p class="wp-block-paragraph"><strong>Learn more about DMC&#8217;s <a href="https://static.dmcinfo.com/services/test-and-measurement-automation">Test &amp; Measurement Automation solutions</a>, and <a href="https://static.dmcinfo.com/contact">contact us</a> today for your next project.</strong></p>
<p>The post <a href="https://static.dmcinfo.com/blog/18038/measurement-studio-net-programming-for-ni-enthusiasts/">Measurement Studio: .NET Programming for NI Enthusiasts</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>NUnit Testing and Using Moq in C#</title>
		<link>https://static.dmcinfo.com/blog/18302/nunit-testing-and-using-moq-in-c/</link>
		
		<dc:creator><![CDATA[Kevin Service]]></dc:creator>
		<pubDate>Tue, 30 Aug 2022 11:55:10 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/18302/nunit-testing-and-using-moq-in-c/</guid>

					<description><![CDATA[<p>*To the tune of Willy Wonka singing* Come with me, and you&#8217;ll be, in a world of unit testing informationnnnn. Unit testing! Unit testing is a great way to ensure that any updates or new functionality added to your code runs smoothly. With well-written tests, you can&#160;catch anything that may have been broken by changed [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/18302/nunit-testing-and-using-moq-in-c/">NUnit Testing and Using Moq in C#</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph"><em>*To the tune of Willy Wonka singing*</em><br />
Come with me, and you&rsquo;ll be, in a world of unit testing informationnnnn.</p>

<p class="wp-block-paragraph">Unit testing! Unit testing is a great way to ensure that any updates or new functionality added to your code runs smoothly. With well-written tests, you can&nbsp;catch anything that may have been broken by changed methods.&nbsp;</p>

<p class="wp-block-paragraph">Getting started can be a little tricky&nbsp;as there are some caveats and neat tricks that are hard to identify at first. In this blog you&rsquo;ll learn how to get started with NUnit unit testing in C#, use Moq to help enhance these tests, and get testing like a pro.&nbsp;</p>

<h2 class="wp-block-heading">Getting Started</h2>

<p class="wp-block-paragraph">To begin, open your project in Visual Studio Enterprise. If the project is opened in a Community or other edition of Visual Studio, you will not be able to view specific breakdowns of the code coverage by section. If you are not&nbsp;concerned with looking at code coverage, this shouldn&rsquo;t be an issue.</p>

<p class="wp-block-paragraph">Once the project is opened, select &ldquo;Test&rdquo; in the top menu and navigate down to &ldquo;Test Explorer&rdquo; to view a layout of all tests.</p>

<p class="wp-block-paragraph">&nbsp;<figure class="wp-block-image"><img decoding="async" alt="Navigating to the Test explorer in Visual Studio Enterprise" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-1.png"  /></figure></p>

<p class="wp-block-paragraph">This should open the Test Explorer for you.</p>

<h2 class="wp-block-heading">The Test Explorer</h2>

<p class="wp-block-paragraph">The Test Explorer is where you can view all the tests built in your project. To run all the tests at once, select the multi-layered play button in the top left corner. To run individual groups of tests, you can open nested tests and run either groups or individual tests by using the right-hand play button&nbsp;shown in the snapshot below.</p>

<p class="wp-block-paragraph">Once you run all the tests using the play button in the top left corner, you will be able to view the passing or failing status of the test grouping based on the icon next to the grouping.&nbsp;</p>

<p class="wp-block-paragraph">In this case, all our tests have passed and have a green check mark next to them. If a test inside the grouping fails, the grouping will be marked by a red X.&nbsp;</p>

<p class="wp-block-paragraph">You can also use the highlighted icons to filter by passing tests, failing tests, or tests that are not run.</p>

<h3 class="wp-block-heading">Where Tests Are Located</h3>

<p class="wp-block-paragraph">To locate tests, drill down into the test explorer and double click a test. This will take you to its location in your project</p>

<h3 class="wp-block-heading">Viewing Code Coverage</h3>

<p class="wp-block-paragraph">To view the code coverage, once you have successfully opened the Test Explorer and run all your tests, return to the &ldquo;Test&rdquo; menu and select &ldquo;Analyze Code Coverage for All Tests.&rdquo;<br />
&nbsp;<br />
This will bring up the &ldquo;Code Coverage Results&rdquo; window, which you can drill down into to view coverage by sections of the project.</p>

<h2 class="wp-block-heading">Installing Necessary NuGet Packages</h2>

<p class="wp-block-paragraph">In order to run the tests, you will need to have a few NuGet packages installed in each section of the project with tests present. In this case, tests are present in both Mars.NUnit and in NUnit under Reports, so we will want our packages installed in both sections.</p>

<p class="wp-block-paragraph">To do this, right click on the portion of the project you would like to install the packages and select &ldquo;Manage NuGet Packages.&quot;</p>

<p class="wp-block-paragraph">From here, the packages you will need to install are:</p>

<ul class="wp-block-list">
 <li>Microsoft.CodeCoverage</li>
 <li>Microsoft.NET.Test.Sdk</li>
 <li>MSTest.TestAdapter</li>
 <li>NUnit</li>
 <li>NUnit3TestAdapter</li>
</ul>

<p class="wp-block-paragraph">These packages and their respective versions are also listed in the screenshot below.</p>

<h2 class="wp-block-heading">Writing your Tests and Using Moq</h2>

<p class="wp-block-paragraph">Writing unit tests is straightforward; the process can be as simple or as complex as you would like. For example, I created a method called AddTwo, which does exactly as the name implies: adds two to my input.&nbsp;</p>

<figure class="wp-block-image"><img decoding="async" alt="Writing tests using Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests.png"  /></figure>

<p class="wp-block-paragraph">I also wrote a test with three test cases. This checks that&nbsp;when I add two&nbsp;my answer is what I expected.</p>

<p class="wp-block-paragraph"><figure class="wp-block-image"><img decoding="async" alt="Writing tests using Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests-2.png"  /></figure><br />
&nbsp;As you can see, this is&nbsp;straightforward, and my tests are all passing. Let&rsquo;s say, however, I had a method called &lsquo;AddThree,&rsquo;&nbsp;which depended on a function &lsquo;AddOne&rsquo; (the desired result), and &lsquo;AddOne&rsquo; had either not been completed&nbsp;or was still in development.&nbsp;</p>

<p class="wp-block-paragraph">This situation is a great example of where we can use Moq to make our lives easier. As shown below, I&rsquo;ve created my function AddThree():</p>

<p class="wp-block-paragraph"><figure class="wp-block-image"><img decoding="async" alt="Writing tests using Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests-3_1.png"  /></figure><br />
I&rsquo;ve also defined AddOne to be a virtual method. This is so that we can use Moq to mock the method. If your method is private and unable to be scoped to, or virtual, you won&rsquo;t be able to use Moq to get around this issue. I&rsquo;ve now written a new test called TestAddThreeMethod and used Moq to mock a call of our AddOne method.</p>

<p class="wp-block-paragraph">In this case, I&rsquo;ve updated my AddOne function to erroneously try and add 5 to my function, which would throw off our expected addition of just one.</p>

<p class="wp-block-paragraph"><figure class="wp-block-image"><img decoding="async" alt="Writing tests using Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests-4.png"  /></figure><br />
Using Moq, we can get around the AddOne method that our AddThree method uses and isolate AddThree, only testing AddThree&apos;s functionality.</p>

<p class="wp-block-paragraph"><figure class="wp-block-image"><img decoding="async" alt="Writing tests using Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests-5.png"  /></figure><br />
What I&rsquo;ve done in the above image is set up a Mock class of my AddingFunctionsClass. In the line below, I&rsquo;ve instantiated that whenever AddOne gets called inside my mockFunctionClass, it will instead default to using what I have entered in my Returns(), which is my number + 1, the correct output of AddOne.&nbsp;</p>

<p class="wp-block-paragraph">You could also hardcode AddOne to return a single value each time, however, this would then no longer show our tests as passing.</p>

<h2 class="wp-block-heading">Using SetupSequence</h2>

<p class="wp-block-paragraph">SetupSequence is another powerful tool to use in Moq. Let&rsquo;s say we have a method that gets called multiple times in a function, but we want it to return variable elements for each time it is called. This can be accomplished by using the SetupSequence. For example, we could set our AddOne method to return different results for each time it was called, as shown below.</p>

<figure class="wp-block-image"><img decoding="async" alt="Using SetupSequence in Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-writing-tests-6.png"  /></figure>

<p class="wp-block-paragraph">If we had called AddOne three times in this scenario, the first time it would return our input plus one, then 3, then 5.&nbsp;</p>

<p class="wp-block-paragraph">Another handy feature of Moq is being able to simply skip over methods altogether. Let&rsquo;s say you have a method that doesn&rsquo;t return a result but does some Initialization features. You could follow this process, and simply not set a return for the method. This will then simply skip over the method any time it is called.&nbsp;</p>

<p class="wp-block-paragraph">&nbsp;<figure class="wp-block-image"><img decoding="async" alt="Using SetupSequence in Moq" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-3.png"  /></figure></p>

<p class="wp-block-paragraph">This statement says any time we call AddOne, don&rsquo;t return anything, and don&rsquo;t run AddOne either.</p>

<p class="wp-block-paragraph">These are&nbsp;some basics and helpful tricks with Moq to get you going. There is a multitude of&nbsp;more options that can make Moq a powerful tool. With these tools, you&rsquo;ll be able to write effective and useful tests quickly and make sure that your code runs smoothly and as expected!</p>

<figure class="wp-block-image"><img decoding="async" alt="NuGet NUnit window " src="https://static.dmcinfo.com/wp-content/uploads/2025/05/using-nunit-and-moq-in-C-4.png"  /></figure>

<h2 class="wp-block-heading">Troubleshooting</h2>

<p class="wp-block-paragraph">It is possible that&nbsp;when unzipping the project&nbsp;and after building the project, the tests refuse to run. Here are some common tricks that we used to ensure the tests were properly compiling:</p>

<ul class="wp-block-list">
 <li>Attempt a Rebuild of the whole project&nbsp;and&nbsp;then&nbsp;try running all tests again.</li>
 <li>Attempt doing a Clean of the whole project&nbsp;and then building.&nbsp;</li>
 <li>Try uninstalling the NUnit and NUnit 3 Adapters Nuget packages&nbsp;and clean the project. Reinstall the packages, rebuild the full project, and then try running all tests again.</li>
 <li>Some tests may not run&nbsp;after getting errors about other sections in the Report folder. Try building these sections in the Reports folder individually, doing a Build all, then running the tests.</li>
</ul>

<p class="wp-block-paragraph"><strong>Learn more about DMC&apos;s <a href="https://static.dmcinfo.com/services/application-development/pc-application-development/c-net">C# programming</a> services and <a href="https://static.dmcinfo.com/contact">contact us</a>&nbsp;today for your next project!</strong></p>
<p>The post <a href="https://static.dmcinfo.com/blog/18302/nunit-testing-and-using-moq-in-c/">NUnit Testing and Using Moq in C#</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>6 Tips for Working with Legacy Code That You Did Not Write</title>
		<link>https://static.dmcinfo.com/blog/19746/6-tips-for-working-with-legacy-code-that-you-did-not-write/</link>
		
		<dc:creator><![CDATA[Rafael Rejuso]]></dc:creator>
		<pubDate>Tue, 08 Sep 2020 12:09:30 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Web Application Development]]></category>
		<category><![CDATA[Legacy Code]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/19746/6-tips-for-working-with-legacy-code-that-you-did-not-write/</guid>

					<description><![CDATA[<p>There may come a time as a programmer that you have to support a crucial existing system and while the task is daunting, there are still many actions you can take to make that process a lot easier. When you have to work with a deep expansive legacy system it&#8217;s easy to get lost in [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/19746/6-tips-for-working-with-legacy-code-that-you-did-not-write/">6 Tips for Working with Legacy Code That You Did Not Write</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">There may come a time as a programmer that you have to support a crucial existing system and while the task is daunting, there are still many actions you can take to make that process a lot easier. When you have to work with a deep expansive legacy system it&rsquo;s easy to get lost in a lot of the details. Here are six tips that definitely would&rsquo;ve saved me, and hopefully you, some time when getting introduced to a legacy system&nbsp;regardless of web or desktop.</p>

<h2 class="wp-block-heading">1. Ask as Many Questions as Possible</h2>

<p class="wp-block-paragraph">One of the fastest ways of getting familiarized with legacy code is to simply ask a lot of questions from the previous developer if that is possible. Starting off, it might not be possible to know of all the specific questions someone may have about the code. Ideally, you should try to ask questions about the overall structure and algorithm of the program. This helps create a more defined mental map and allows you to quickly identify places to edit/change in the event you need to implement new features.</p>

<p class="wp-block-paragraph">It is also valuable to ask about previous places the developer had difficulty in or code they believe is not as robust. There is no reason to try and resolve an issue twice if the previous developer already identified the problem. Simply keeping this information in the back of your mind whenever you write a new code that interacts with that section can help to debug smoother. However, not everyone can have the luxury of having the previous developer available, in which case you can try your luck with the client or userbase actively using the code for any history they may remember, in regards to bugs or already implemented fixes.</p>

<p class="wp-block-paragraph">If either way is not possible, there are still many things you can do to make working with legacy code a lot less painful, which I will cover in the following tips.</p>

<h2 class="wp-block-heading">2. Document Call Hierarchy</h2>

<p class="wp-block-paragraph">When working with a large spanning piece of code, it can get difficult tracing down how a function or procedure works, especially when you have to make edits that may have far extending effects that you need to account for. You can create a simple call hierarchy by noting down the specific function you call, and all the subsequent calls that function makes in sequential order.</p>

<p class="wp-block-paragraph">For instance, if you have a function <em>Run </em>which calls function <em>GetItems </em>and later on <em>SortItems</em>, you can keep track of the process and all the necessary places you have to change the code to maintain functionality. This is especially true if there are further calls in the subfunctions such as <em>GetItems </em>calling its own subfunction of <em>LoadFile</em>. This process of documenting the hierarchy should be kept relatively simple and easy to read, something as simple as the name of the function should work.</p>

<ul class="wp-block-list">
 <li>Run
 <ul class="wp-block-list" style="list-style-type:circle;">
  <li>GetItems
  <ul class="wp-block-list">
   <li>LoadFile</li>
  </ul>
  </li>
  <li>SortItems</li>
 </ul>
 </li>
 <li>&hellip;</li>
</ul>

<p class="wp-block-paragraph">A key point is that you should not go in-depth as to what each function does, especially if you are looking at the code for the first time. The main reason you want to avoid that is that by documenting excessively, you might develop biases or assumptions that are initially inaccurate.</p>

<p class="wp-block-paragraph">After working with the legacy code, you&rsquo;ll discover nuances and small details that you may have overlooked in the first pass through that change how the process runs, like a flag being set on a variable or specific timings that may have been missed.</p>

<p class="wp-block-paragraph">Keeping the documentation of hierarchy simple will allow you to identify and understand the structure, but allow you to avoid common pitfalls and red herrings that comes from not being unacquainted with the code. As you get familiarized with the code, you can develop more diagrams and discover how your new or edited code interacts with the rest of the system.</p>

<h2 class="wp-block-heading">3. Double Check Your Assumptions</h2>

<p class="wp-block-paragraph">A subsequent tip that was briefly touched on is that you may develop assumptions after going through the first pass of the code. When bugs occur, one of the first things you should immediately look at when you have reached a dead-end is to re-check your initial assumptions. In most cases, a lot of bugs that come from editing legacy code comes from a misunderstanding over what you believe something does, and what it actually does. After reviewing changes that you implemented and making sure it works, the next step should always be to double-check assumptions that you previously made.</p>

<p class="wp-block-paragraph">For example, this can occur in conditional statements, where you believe the code would go into a specific conditional branch but instead goes somewhere else entirely. Another place this can occur is in function calls that have side effects that might have been missed, setting flags in other places of the code.</p>

<figure class="wp-block-image"><img decoding="async" alt="" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/CRT-Monitor-with-code.png" /></figure>

<h2 class="wp-block-heading">4. Testing, Testing, Testing</h2>

<p class="wp-block-paragraph">This might be obvious, but after you make any changes, extensive testing should be done to guarantee not only that the new function works, but that everything else behaves as it did before the changes. Setting up unit testing and functional testing ahead of time can go a long way in saving time and effort by having a set of pre-defined test suites to verify that everything is working in tip-top shape.</p>

<p class="wp-block-paragraph">In cases where the code is too old and setting up testing cases do not make sense, setting up logging in as many places as possible can be key in debugging potential issues. Debugging and viewing the watch screen is very viable. Having logging sequences in place for each event is just as valuable as an issue could occur and you aren&apos;t around to watch it happen, logging will help narrow down <span style="font-size: calc(var(--rem) * 1px * 1.0625); letter-spacing: 0px;">exactly where the issue occurred, and some valuable replicable context that can help track down the issue.</span></p>

<p class="wp-block-paragraph"><span style="font-size: calc(var(--rem) * 1px * 1.0625); letter-spacing: 0px;">For each new addition, there should be at the very least, a new line of logging or monitoring that should stay until it has been verified that the program is running consistently.</span></p>

<h2 class="wp-block-heading">5. Start Small and Go Slow</h2>

<p class="wp-block-paragraph">Starting small goes hand in hand with testing everything. Sometimes, a change doesn&rsquo;t require knowing every single nook and cranny of a large piece of code. In other cases, it requires changing code in many different places that you may not have been identified yet. It can get overwhelming changing code in multiple places and remembering to do it all.</p>

<p class="wp-block-paragraph">Making sure to go slow and looking at one section of code at a time can reduce a lot of mistakes. This isn&rsquo;t just applicable to legacy code but can also be applied to most aspects of coding.</p>

<h2 class="wp-block-heading">6. Document Everything</h2>

<p class="wp-block-paragraph">Lastly, you want to document everything. From the discoveries you&rsquo;ve made at looking at the legacy code, to the new additions that you&rsquo;ve added. This can as simple as notes that you can print out, or comments in the code itself. Documentation not only helps you but also helps future developers that may have to work on this software. This information will create stability for yourself when months down the line, you may have to go back and look at your own work. By setting it in a sort of electronic stone, it legitimizes the changes done to the project, beyond just being key points to remember internally.</p>
<p>The post <a href="https://static.dmcinfo.com/blog/19746/6-tips-for-working-with-legacy-code-that-you-did-not-write/">6 Tips for Working with Legacy Code That You Did Not Write</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Sorting Multiple Columns in a Table with React</title>
		<link>https://static.dmcinfo.com/blog/19887/sorting-multiple-columns-in-a-table-with-react/</link>
		
		<dc:creator><![CDATA[Rafael Rejuso]]></dc:creator>
		<pubDate>Tue, 02 Jun 2020 14:08:49 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Web Application Development]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[React]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/19887/sorting-multiple-columns-in-a-table-with-react/</guid>

					<description><![CDATA[<p>Tables are a fast way to show a lot of valuable data. There are many guides on sorting tables by a column in React, but sorting multiple columns is something that can take some effort. At the end of this guide, you should have a sortable table where you can click multiple columns. For this [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/19887/sorting-multiple-columns-in-a-table-with-react/">Sorting Multiple Columns in a Table with React</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Tables are a fast way to show a lot of valuable data. There are many guides on sorting tables by a column in React, but sorting multiple columns is something that can take some effort. At the end of this guide, you should have a sortable table where you can click multiple columns.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/sorting-mulitple-columns-react.gif" alt="Sorting mulitple columns with React"/></figure>



<p class="wp-block-paragraph">For this tutorial, we will be leveraging the use of LINQ&nbsp;to make sorting quite painless, and optionally Material-UI for styling the table. At the very end, I will also briefly describe a way of implementing this table without using LINQ&nbsp;and only using base React.</p>



<h2 class="wp-block-heading" id="h-tools-used-in-this-blog">Tools Used in this Blog:</h2>



<ul class="wp-block-list">
<li>LINQ&nbsp;</li>



<li><a href="https://static.dmcinfo.com/latest-thinking/blog/id/10114/a-simple-guide-to-material-ui-grids">Material-UI </a><em>(Optional)</em></li>



<li>Basic React hooks</li>



<li>Functional React</li>
</ul>



<h2 class="wp-block-heading" id="h-setup">Setup</h2>



<p class="wp-block-paragraph">First, we need some data.&nbsp;For the purposes of this example, I just created some dummy data that we can sort. I also defined an interface for the data type and an enum for departments that a person can belong to.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> const dataList=&#91;
    {name: 'Ryan H.',   hours: 30, startDate: new Date('2019-01-14'), department: Department.Marketing},
    {name: 'Ariel P.',  hours: 22, startDate: new Date('2017-03-12'), department: Department.Sales},
    {name: 'Ryan Y.',   hours: 31, startDate: new Date('2015-09-12'), department: Department.Marketing},
    {name: 'Ed T.',     hours: 22, startDate: new Date('2017-03-12'), department: Department.Engineering},
    {name: 'Matt G.',   hours: 30, startDate: new Date('2017-03-12'), department: Department.Marketing},
    {name: 'Olivia H.', hours: 32, startDate: new Date('2018-05-10'), department: Department.Engineering}&#93; as TableData[];</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">dataList</span><span style="color: #D4D4D4">=&#91;</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Ryan H.&apos;</span><span style="color: #D4D4D4">,   </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">30</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2019-01-14&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Marketing</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Ariel P.&apos;</span><span style="color: #D4D4D4">,  </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">22</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2017-03-12&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Sales</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Ryan Y.&apos;</span><span style="color: #D4D4D4">,   </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">31</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2015-09-12&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Marketing</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Ed T.&apos;</span><span style="color: #D4D4D4">,     </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">22</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2017-03-12&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Engineering</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Matt G.&apos;</span><span style="color: #D4D4D4">,   </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">30</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2017-03-12&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Marketing</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #9CDCFE">name:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Olivia H.&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">hours:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">32</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">startDate:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Date</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;2018-05-10&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">department:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Engineering</span><span style="color: #D4D4D4">}&#93; </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">[];</span></span></code></pre></div>



<p class="wp-block-paragraph">Our interface is <strong>TableData.</strong>&nbsp;We have an enum called <strong>Departments</strong> which lists the available department&#8217;s people may be in:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>interface TableData{
    name: String,
    hours: number,
    startDate: Date,
    department: Department
}

enum Department{
    Marketing = 'Marketing',
    Sales = 'Sales',
    Engineering = 'Engineering',
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">String</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">hours</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">number</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">startDate</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Date</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">department</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Department</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">enum</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Department</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4FC1FF">Marketing</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;Marketing&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4FC1FF">Sales</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;Sales&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4FC1FF">Engineering</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;Engineering&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">It’s important to note that this interface and enum is solely for structuring our table, and not for sorting. We will eventually add another interface and enum for sorting.</p>



<p class="wp-block-paragraph">For the case of simulation, I’ll be placing the data into another file and returning it from a function call called <strong>fetchData.</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export const fetchData = () => { return dataList;}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">fetchData</span><span style="color: #D4D4D4"> = () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> { </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">;}</span></span></code></pre></div>



<p class="wp-block-paragraph">For the table, we’ll be using Material-UI, but this method will absolutely work with just a regular table. If you don’t want to use Material-UI tables, just replace the tags with the corresponding ones below. Everything but TableBody has a corresponding tag which just contains a collection of &lt;tr&gt; tags.</p>


<table style="width:100%; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; margin-bottom:40px;">
<tbody>
<tr style="text-align:center;">
<th style="border-collapse:collapse; border-style:solid; border-width:medium; border-spacing: 20px; border:1px solid black; height:100px; font-size:24px; border-right-style:solid; border-right-width:medium; border-left-style:solid; border-left-width:medium;">MaterialUI</th>
<th style="border-collapse:collapse; border-style:solid; border-width:medium; border-spacing: 20px; border:1px solid black; height:100px; font-size:24px; border-right-style:solid; border-right-width:medium; border-left-style:solid; border-left-width:medium;">HTML</th>
</tr>
<tr>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">TableHead</td>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">th</td>
</tr>
<tr>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">TableBody</td>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">&#8211;</td>
</tr>
<tr>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">TableRow</td>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">tr</td>
</tr>
<tr>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">TableCell</td>
<td style="text-align:center; vertical-align:middle; border-collapse:collapse; border:1px solid black; border-style:solid; border-width:medium; font-size:24px;">td</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">Now we pull our data from the other file by calling <strong>fetchData</strong>, set up the appropriate table headers, and map our data to the corresponding table cells. For the sake of keeping clean code, I moved the sortable table headers to another component, so our code should look somewhat like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import React from "react";
import {Table, TableRow, TableCell, TableBody } from '@material-ui/core';
import { fetchData, TableData } from "./fetchData";
import { SortableHeader } from "./sortableTableHeader";

export const TableExample = () => {
    const dataList = fetchData();
    return(
        &lt;div>
            &lt;Table>
                &lt;SortableHeader/>
                &lt;TableBody>
                    {dataList.map((data) => { return(
                        &lt;TableRow>
                            &lt;TableCell>
                                {data.name}
                            &lt;/TableCell>
                            &lt;TableCell>
                                {data.hours}
                            &lt;/TableCell>
                            &lt;TableCell>
                                {data.startDate.toDateString()}
                            &lt;/TableCell>
                            &lt;TableCell>
                                {data.department}
                            &lt;/TableCell>
                        &lt;/TableRow>
                        )})}
                &lt;/TableBody>
            &lt;/Table>
        &lt;/div>
    )
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">React</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;react&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">Table</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableBody</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;@material-ui/core&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">fetchData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableData</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;./fetchData&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">SortableHeader</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;./sortableTableHeader&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">TableExample</span><span style="color: #D4D4D4"> = () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">dataList</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">fetchData</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #4EC9B0">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #4EC9B0">Table</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">SortableHeader</span><span style="color: #D4D4D4">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableBody</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    {</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) =&gt; { </span><span style="color: #DCDCAA">return</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">hours</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">startDate</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">toDateString</span><span style="color: #D4D4D4">()}</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">department</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;/</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                        )})}</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;/</span><span style="color: #9CDCFE">TableBody</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">Table</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import React from "react";
import { TableHead, TableRow, TableCell } from "@material-ui/core";

export const SortableHeader = () => {
    return (
        &lt;TableHead>
            &lt;TableRow>
                &lt;TableCell>Name&lt;/TableCell>
                &lt;TableCell>Hours&lt;/TableCell>
                &lt;TableCell>Date&lt;/TableCell>
                &lt;TableCell>Department&lt;/TableCell>
            &lt;/TableRow>
        &lt;/TableHead>
    );
};</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">React</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;react&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">TableHead</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;@material-ui/core&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SortableHeader</span><span style="color: #D4D4D4"> = () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #4EC9B0">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #4EC9B0">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Hours</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span></code></pre></div>



<p class="wp-block-paragraph">Which outputs something like this:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/1-react-tables-bad.png" alt=""/></figure>



<h2 class="wp-block-heading" id="h-implementation">Implementation</h2>



<p class="wp-block-paragraph">Our overall goal is to create an ordered queue of sorting configurations that will apply each sorting in order through the use of LINQ&nbsp;to our data. We will then update our data list which will be re-rendered and displayed in the desired sorting configuration.</p>



<p class="wp-block-paragraph"><p style="font-size:110%"><strong>Step 1: Define a Sorting configuration</strong></p></p>



<p class="wp-block-paragraph">A sorting configuration is essentially an object that has two things. The property/column of the list that we’re trying to sort, and the sorting type such as ascending or descending. Since our rows are all the same object type: TableData, we can use keyof TableData to get all the properties/columns of our table. The only thing we’d have to do is make an enum of the different sorting types that we need, in this case we’ll stick with the standard ascending/descending.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>interface SortingConfiguration{
    propertyName: keyof TableData,
    sortType: SortingType,
}

enum SortingType{
    Ascending,
    Descending,</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">SortingType</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">enum</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SortingType</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4FC1FF">Ascending</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4FC1FF">Descending</span><span style="color: #D4D4D4">,</span></span></code></pre></div>



<p class="wp-block-paragraph"><p style="font-size:110%"><strong>Step 2: Utilize useState to Maintain a List of our Sorting Configurations</strong></p></p>



<p class="wp-block-paragraph">If you’ve never used <strong>useState</strong>, it’s a React hook that returns an array of two things: the first being the state of an object that is being maintained. The second is basically a function/dispatch that you can call to change the state. Here we’re initializing our list of sorting configurations to be an empty list since we want to start off with an unsorted table.</p>



<p class="wp-block-paragraph">Note: If you do want a table column to be sorted by default, all you would need to do is add a <strong>SortConfiguration </strong>object in between the square brackets</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>  const &#91;sortConfig, updateSortConfig&#93; = useState&lt;SortingConfiguration[]>([]);
    //Below is an example of sorting columns by name by default.
    const &#91;initializedSortConfig, updateInitSortConfig&#93; = useState&lt;SortingConfiguration[]>(&#91;
        {propertyName: 'name', sortConfig: SortingType.Descending}
    &#93;);</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">sortConfig</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">updateSortConfig</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">[]&gt;([]);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//Below is an example of sorting columns by name by default.</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">initializedSortConfig</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">updateInitSortConfig</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">[]&gt;(&#91;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortConfig:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">    &#93;);</span></span></code></pre></div>



<p class="wp-block-paragraph"><p style="font-size:110%"><strong>Step 3: Create a Function That Adds, Modifies, and Removes Our Sorting Configurations</strong></p></p>



<p class="wp-block-paragraph">Most sortable headers are clickable headers that switch between a cycle of unsorted -&gt; descending -&gt; ascending -&gt; unsorted -&gt; etc, but there are many different orders or ways in which you may wish to set this up. You should be able to create your own custom function that edits the sorting configuration given three basic criteria:</p>



<ol class="wp-block-list">
<li>Add a sorting configuration to a column when there is not an existing configuration</li>



<li>Modify a sorting configuration when there is an existing configuration
 
 
<ul class="wp-block-list">
<li>For example, going from descending to ascending, or vice versa</li>
</ul>
</li>



<li>Provide a way to remove a sorting configuration
 
 
<ul class="wp-block-list">
<li>This can be a part of criteria #2, but it is important enough to warrant its own criteria</li>
</ul>
</li>
</ol>



<p class="wp-block-paragraph">We’ll be following a cycle of unsorted -&gt; descending -&gt; ascending -&gt; unsorted -&gt; etc… for the purposes of this guide.</p>



<p class="wp-block-paragraph">We will be making a function called sortBy that will take in a key of TableData. Basically, whenever we call sortBy on a given property/column, it should automatically fulfill our three criteria: add a new configuration when we don’t have one, alter a configuration if we do, and remove a configuration when we need to. We wrap it in a <strong>useCallback </strong>because, in the next step, we will pass this function down to a prop.</p>



<p class="wp-block-paragraph">So our basic structure looks like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> const sortBy = useCallback(
        (propertyName: keyof TableData) => {
            let pendingChange = &#91;...sortConfig&#93;;
            const index = pendingChange.findIndex((config) => 
       						config.propertyName === 	propertyName)
            if(index > -1){
                // Existing configuration
            } else {
                // No existing configuration
            }
            updateSortConfig(&#91;...pendingChange&#93;);
        },
        &#91;sortConfig&#93;
    )</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">sortBy</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        (</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;...</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">index</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findIndex</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">       						</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4"> === 	</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> &gt; -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">// Existing configuration</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">// No existing configuration</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #DCDCAA">updateSortConfig</span><span style="color: #D4D4D4">(&#91;...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        &#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span></code></pre></div>



<p class="wp-block-paragraph">We use the spread operator to make a copy of the <strong>sortConfiguration</strong>. Then we use <strong>findIndex </strong>to search our copied list and see if there is an existing configuration that’s in our list. We’ll be using a lot of the spread operator, so if you aren’t experienced with using it, you can see more examples in <a href="https://static.dmcinfo.com/latest-thinking/blog/id/9668/categoryid/95/5-great-uses-of-the-spread-operator-in-a-react-app">this blog by Jacob Bruce</a>. Now that we have the basic structure laid out, let’s address the first criteria. This one is relatively simple since all we’re doing is adding a sorting configuration given the propertyName that was passed in:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>  if(index > -1){
                // Existing configuration
            } else {
                pendingChange = &#91;
                    ...pendingChange,
                    { propertyName: propertyName, sortType: SortingType.Descending },
                &#93;;
            }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> &gt; -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">// Existing configuration</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                    ...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                    { </span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortType:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">                &#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span></code></pre></div>



<p class="wp-block-paragraph">Here we use the spread operator to push a new sorting configuration to the end of the queue.</p>



<p class="wp-block-paragraph">Now we have to handle our second and third criteria which you can only do on an existing configuration. We can remove extra code if instead of modifying we save the existing sort direction, remove the current configuration and lastly, add in a new configuration if it’s necessary to ‘modify’. Technically, our modification is removing a configuration of a property and adding in the same property back, but with a different sortType, but doing it this way makes it so we don’t have to write two similar code branches.</p>



<p class="wp-block-paragraph">In short, we use our index we found to store the existing sort type. We then remove that configuration using splice. Lastly, we use our saved sorting type to determine if we needed to add a new configuration. In this example, we check if it’s Descending, and if that’s the case, we change that sortType to Ascending. Note how, if the sorting type is Ascending, we do not ‘modify’ our sorting configuration list, but rather just let the code remove the configuration.</p>



<p class="wp-block-paragraph">Put together we have:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> const sortBy = useCallback(
        (propertyName: keyof TableData) => {
            let pendingChange = &#91;...sortConfig&#93;;
            const index = pendingChange.findIndex((config) => 
                                    config.propertyName === propertyName)
            if(index > -1){
                //Save the sortType
                var currentSortType = pendingChange&#91;index&#93;.sortType;
                //Remove existing config
                pendingChange.splice(index, 1);
                //check if the sort type we saved is descending
                if (currentSortType === SortingType.Descending) {
                    pendingChange = &#91;
                        ...pendingChange,
                        { propertyName: propertyName, sortType: SortingType.Ascending },
                    &#93;;
                }
            } else {
                pendingChange = &#91;
                    ...pendingChange,
                    { propertyName: propertyName, sortType: SortingType.Descending },
                &#93;;
            }
            updateSortConfig(&#91;...pendingChange&#93;);
        },
        &#91;sortConfig&#93;
    )</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">sortBy</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        (</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;...</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">index</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findIndex</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">                                    </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> &gt; -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Save the sortType</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">currentSortType</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Remove existing config</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">splice</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//check if the sort type we saved is descending</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">currentSortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                        ...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                        { </span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortType:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">                    &#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">                }</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                    ...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                    { </span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortType:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">                &#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #DCDCAA">updateSortConfig</span><span style="color: #D4D4D4">(&#91;...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        &#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span></code></pre></div>



<p class="wp-block-paragraph">Now the loop of unsorted -&gt; descending -&gt; ascending -&gt; unsorted -&gt; etc… is complete.</p>



<p class="wp-block-paragraph"><p style="font-size:110%"><strong>Step 4: Attach Our Function to the Table Headers</strong></p></p>



<p class="wp-block-paragraph">Now that we created our function, we need to attach it to table headers and make them something we can click. Since we’re passing down our props to our <strong>SortableTableHeader </strong>don’t forget to export our interfaces and enums to avoid errors. We’ll need to pass two things to our SortableTableHeader the first is our newly created sortBy function, and the second is our current <strong>sortConfig </strong>which will be used for arrow indicators to show what direction we’re sorting.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Table>
    &lt;SortableHeader 
          sortBy={sortBy} 
          sortConfig={sortConfig}
          />
      &lt;TableBody>
		………
&lt;/Table>
&lt;/TableBody>
&lt;/pre>

&lt;pre class="brush:js">
interface SortableHeaderProps{
    sortBy: (string: keyof TableData) => void;
    sortConfig: SortingConfiguration[];
}

export const SortableHeader = ({sortBy, sortConfig}:SortableHeaderProps) => {
    return (
        &lt;TableHead>
            &lt;TableRow>
                &lt;TableCell>Name&lt;/TableCell>
                &lt;TableCell>Hours&lt;/TableCell>
                &lt;TableCell>Date&lt;/TableCell>
                &lt;TableCell>Department&lt;/TableCell>
            &lt;/TableRow>
        &lt;/TableHead>
    );
};</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">Table</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">SortableHeader</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">sortBy</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">sortBy</span><span style="color: #D4D4D4">} </span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">          /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;</span><span style="color: #4EC9B0">TableBody</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">		………</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">Table</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableBody</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">pre</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">pre</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;brush:js&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SortableHeaderProps</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">: (</span><span style="color: #9CDCFE">string</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">[];</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SortableHeader</span><span style="color: #D4D4D4"> = ({</span><span style="color: #9CDCFE">sortBy</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">}:</span><span style="color: #4EC9B0">SortableHeaderProps</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #4EC9B0">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #4EC9B0">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Hours</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #4EC9B0">TableCell</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Department</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span></code></pre></div>



<p class="wp-block-paragraph">To keep code DRY, I’ll be making a list of the columns that we want, and the TableData property they correspond to. This way, we can map over the function without copy/pasting TableCell four times.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const tableColumn = &#91;
        {label:'Name', property:'name'},
        {label:'Hours', property:'hours'},
        {label:'Date', property:'startDate'},
        {label:'Department', property:'department'}
        &#93; as TableColumn[];</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">tableColumn</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Name&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Hours&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;hours&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Date&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;startDate&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Department&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;department&apos;</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">        &#93; </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableColumn</span><span style="color: #D4D4D4">[];</span></span></code></pre></div>



<p class="wp-block-paragraph">Now, we can attach our function to the tablecell by adding the onClick prop to the <strong>TableCell</strong>, and pass in the corresponding property for the column being mapped. We can also apply our CSS stylings to the table cells at this point. Here I use Material UI’s styling solution, but the key point when doing this is setting the CSS property <em>cursor: ‘pointer’</em>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> &lt;TableHead>
            &lt;TableRow>
                {tableColumn.map((column, index) => {
                    return(
                        &lt;TableCell key={index} 
                            className={headerCell}
                            onClick={()=>sortBy(column.property)}
                        >
                            {column.label}
                        &lt;/TableCell>
                    )
                })}
            &lt;/TableRow>
        &lt;/TableHead></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> &lt;</span><span style="color: #4EC9B0">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #4EC9B0">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                {</span><span style="color: #9CDCFE">tableColumn</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">} </span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">headerCell</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #9CDCFE">onClick</span><span style="color: #D4D4D4">={()=&gt;</span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">(column.property)}</span></span>
<span class="line"><span style="color: #D4D4D4">                        &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            {</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    )</span></span>
<span class="line"><span style="color: #D4D4D4">                })}</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">TableHead</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">Lastly, we’ll add an indicator arrow through conditional rendering to show the current sort direction. To do this, we have two conditionals to check what we render. The first is checking if there is an existing sorting configuration, and the second is checking if the sortType is ascending/descending. To make this easier, we can write a simple function that gets us the current sortType from the sorting configuration and returns an appropriate icon.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> const getSortDirection = (property:keyof TableData) => {
        var config = sortConfig.find((sortConfig) => sortConfig.propertyName === property)
        if(config){
            if(config.sortType === SortingType.Descending){
                return &lt;ArrowDownwardIcon/>
            }
            else {
                return &lt;ArrowUpwardIcon/>
            }
        }
        return null;
    }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getSortDirection</span><span style="color: #D4D4D4"> = (</span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4">:</span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">find</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> &lt;</span><span style="color: #4EC9B0">ArrowDownwardIcon</span><span style="color: #D4D4D4">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            else {</span></span>
<span class="line"><span style="color: #D4D4D4">                return &lt;</span><span style="color: #4EC9B0">ArrowUpwardIcon</span><span style="color: #D4D4D4">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        return null;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span></code></pre></div>



<p class="wp-block-paragraph">Our <strong>SortableTableHeader </strong>code should now look like this, with the appropriate CSS stylings applied in place of <strong>headerCell </strong>and <strong>sortLabel</strong>, if you aren’t using Material-UI.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export const SortableHeader = ({sortBy, sortConfig}:SortableHeaderProps) => {

    const {headerCell, sortLabel} = useStyles();

    const tableColumn = &#91;
        {label:'Name', property:'name'},
        {label:'Hours', property:'hours'},
        {label:'Date', property:'startDate'},
        {label:'Department', property:'department'}
        &#93; as TableColumn[];

    const getSortDirection = (property:keyof TableData) => {
        var config = sortConfig.find((sortConfig) => sortConfig.propertyName === property)
        return config ?
            config.sortType === SortingType.Descending ?
                &lt;ArrowDownwardIcon/>
                :&lt;ArrowUpwardIcon/>
            :null
    }

    return (
        &lt;TableHead>
            &lt;TableRow>
                {tableColumn.map((column, index) => {
                    return(
                        &lt;TableCell key={index} 
                            className={headerCell}
                            onClick={()=>sortBy(column.property)}
                        >
                            &lt;span className={sortLabel}>
                                {column.label}
                                {getSortDirection(column.property)}
                            &lt;/span>
                        &lt;/TableCell>
                    )
                })}
            &lt;/TableRow>
        &lt;/TableHead>
    );
};</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SortableHeader</span><span style="color: #D4D4D4"> = ({</span><span style="color: #9CDCFE">sortBy</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">}:</span><span style="color: #4EC9B0">SortableHeaderProps</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> {</span><span style="color: #4FC1FF">headerCell</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">sortLabel</span><span style="color: #D4D4D4">} = </span><span style="color: #DCDCAA">useStyles</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">tableColumn</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Name&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Hours&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;hours&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Date&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;startDate&apos;</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span><span style="color: #9CDCFE">label:</span><span style="color: #CE9178">&apos;Department&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">property:</span><span style="color: #CE9178">&apos;department&apos;</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">        &#93; </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableColumn</span><span style="color: #D4D4D4">[];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getSortDirection</span><span style="color: #D4D4D4"> = (</span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4">:</span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">find</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4"> ?</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4"> ?</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">ArrowDownwardIcon</span><span style="color: #D4D4D4">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                :&lt;</span><span style="color: #4EC9B0">ArrowUpwardIcon</span><span style="color: #D4D4D4">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            :</span><span style="color: #569CD6">null</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #4EC9B0">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #4EC9B0">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                {</span><span style="color: #9CDCFE">tableColumn</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">) =&gt; {</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #DCDCAA">return</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">} </span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">headerCell</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #9CDCFE">onClick</span><span style="color: #D4D4D4">={()=&gt;</span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">(column.property)}</span></span>
<span class="line"><span style="color: #D4D4D4">                        &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">sortLabel</span><span style="color: #D4D4D4">}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">                                {</span><span style="color: #9CDCFE">getSortDirection</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4">)}</span></span>
<span class="line"><span style="color: #D4D4D4">                            &lt;/</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                        &lt;/</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    )</span></span>
<span class="line"><span style="color: #D4D4D4">                })}</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">TableRow</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">TableHead</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span></code></pre></div>



<p class="wp-block-paragraph">If everything went correctly, you should now see table headers with arrows showing the corresponding sort directions on them.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2-react-tables-good.png" alt=""/></figure>



<p class="wp-block-paragraph"><p style="font-size:110%"><strong>Step 5: Applying the Sort Configuration with LINQ</strong></p></p>



<p class="wp-block-paragraph">The last step is to finally apply the sorting in order.</p>



<p class="wp-block-paragraph">We’ll be defining our variable, ‘sortedRows’ by a useMemo function, with a dependency on sortConfig and dataList, so that we re-render our sortedRows whenever our sort configuration changes, or whenever our data list changes.</p>



<p class="wp-block-paragraph">Next, we can turn our standard list of items into a LINQ list by putting it in a LINQ.from call. Now, in order to leverage LINQ&#8217;s sorting functionality, our list of values has to be an <strong>IOrderedEnumerable</strong> object. We can get this by applying a basic orderBy function to our LINQ and passing it a lambda that just returns one, which basically says sort this by the key ‘nothing’, and return ‘1’ in all cases to avoid doing any comparison. This returns us our list in an unmodified order, but as an IOrderedEnumerable type.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> //Set up default ordering
        let sorted = linq.from(dataList).orderBy(() => 1);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955">//Set up default ordering</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">linq</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">The reason why we want to do this is so we can apply multiple sorts on top of this which is possible by using the thenBy and <strong>thenyByDescending </strong>functions from LINQ and using those requires an IOrderedEnumerable. Now that that’s established, we can loop through each of the sorting configurations in order and check which sort type we’re applying.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>let sorted = linq.from(dataList).orderBy(() => 1);
        //Loop through the queue
        sortConfig.forEach((sortConfig) => {
            if (sortConfig.sortType === SortingType.Ascending) {
                //Ascending sorting
            } else {
                //Descending sorting
            }
        });</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">linq</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Loop through the queue</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">forEach</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Ascending sorting</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Descending sorting</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span></code></pre></div>



<p class="wp-block-paragraph">Our last step in sorting is to apply the thenBy conditional on the corresponding property, and LINQ will handle the complicated sorting for us, including comparing integers, strings, and dates. You can read more on LINQ functions, and even pass in your own comparable which is useful for handling sorting between more complex types, but in the case of this guide, we’ll just stick to basic sorting since we’re handling primitive types. If it’s descending, we can use <strong>thenByDescending</strong> which makes things easier.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> if (sortConfig.sortType === SortingType.Ascending) {
                sorted = sorted
                    .thenBy((dataRow) => dataRow&#91;sortConfig.propertyName&#93;);
            } else {
                sorted = sorted
                    .thenByDescending((dataRow) => dataRow&#91;sortConfig.propertyName&#93;);
            }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sorted</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sorted</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenByDescending</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span></code></pre></div>



<p class="wp-block-paragraph">In the code above, you see that the arguments for the lambda are an element of the <strong>sortedList. </strong>In this case, I named it <strong>dataRow</strong> because each item in the sorted is a LINQ&nbsp;from our dataList so we can think of it as one row. It then selects a corresponding key via the propertyName of our sortConfig, which as you recall is either “name”, “hours”, “startDate’, and “department”. In short, this is us targeting that specific column.</p>



<p class="wp-block-paragraph">Now, if you have null data, Javascript will run into the null comparison issue, which will mess up the sorting. A quick workaround I do is to sort all the data that is null on the bottom, but prefacing this <strong>thenBy</strong> with another thenBy that basically checks if the property is null.</p>



<p class="wp-block-paragraph">Here we return -1 when the data is null which means the object is ‘less than’ an object which does have data, and is sorted lower on the list.</p>



<p class="wp-block-paragraph">The last thing we need to do is to turn our LINQ into an array by calling <strong>.toArray()</strong>, and returning that value so that our <strong>sortedRows</strong> has an array of a sorted <strong>dataList</strong>. Putting it all together, we have this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const sortedRows = useMemo(() => {
        //Set up default ordering
        let sorted = linq.from(dataList).orderBy(() => 1);
        //Loop through the queue
        sortConfig.forEach((sortConfig) => {
            if (sortConfig.sortType === SortingType.Ascending) {
                sorted = sorted
                    .thenBy((dataRow) => (dataRow&#91;sortConfig.propertyName&#93; === null ? -1 : 1))
                    .thenBy((dataRow) => dataRow&#91;sortConfig.propertyName&#93;);
            } else {
                sorted = sorted
                    .thenByDescending((dataRow) =>
                    dataRow&#91;sortConfig.propertyName&#93; === null ? -1 : 1
                    )
                    .thenByDescending((dataRow) => dataRow&#91;sortConfig.propertyName&#93;);
            }
        });
        return sorted.toArray();
    }, &#91;sortConfig, dataList&#93;);</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">sortedRows</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Set up default ordering</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">linq</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Loop through the queue</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">forEach</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sorted</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93; === </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> ? -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4"> : </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sorted</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenByDescending</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93; === </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> ? -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4"> : </span><span style="color: #B5CEA8">1</span></span>
<span class="line"><span style="color: #D4D4D4">                    )</span></span>
<span class="line"><span style="color: #D4D4D4">                    .</span><span style="color: #DCDCAA">thenByDescending</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dataRow</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">toArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }, &#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">&#93;);</span></span></code></pre></div>



<p class="wp-block-paragraph">You should now have a functioning table with multiple sortable headers! A key thing to note is that you can pass a comparable to the thenBy function instead of just selecting by key. If for some reason things are not sorting as they should, that should be the first point of debugging.</p>



<h2 class="wp-block-heading" id="h-bonus-sorting-without-linq">Bonus: Sorting without LINQ</h2>



<p class="wp-block-paragraph">If you’ve made it this far, or just skipped ahead to this section, we’ll be going over the basics of sorting rows based on multiple headers by just using basic comparators.</p>



<p class="wp-block-paragraph">Comparators are just functions that take in two objects and return a number depending on how they relate to one another. A zero means they’re equal, positive numbers are greater, and negative numbers are less. Now let’s work on converting our sorting function to one that doesn’t use LINQ at all.</p>



<p class="wp-block-paragraph">Sorting multiple columns may seem complex, but in practice, it’s not really that difficult. Given a list of sorting configurations in the desired order, all you have to check is if the <strong>first comparator</strong> results in an equality, use the <strong>next comparator</strong> in order. If we have a list of {a ,1 }, {a , 2}, and {b, 1}. Sorting by the first column results in the equality of a’s. All we’d need to do is check our second column’s comparator to determine what order they should go in.</p>



<p class="wp-block-paragraph">In this guide, we’ve already implemented a function that can order our sort configurations for us. All we need to do is add a comparators to our SortConfiguration so we can keep track of which compare function we need to use.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export interface SortingConfiguration{
    propertyName: keyof TableData,
    sortType: SortingType,
    compareFunction: TableDataComparable
}

export type TableDataComparable = ((a: TableData, b:TableData) => number);</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">SortingType</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">compareFunction</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableDataComparable</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">type</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableDataComparable</span><span style="color: #D4D4D4"> = ((</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">:</span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">number</span><span style="color: #D4D4D4">);</span></span></code></pre></div>



<p class="wp-block-paragraph">Next, we add that field to our sortBy function:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const sortBy = useCallback(
        (propertyName: keyof TableData, compareFunction: TableDataComparable) => {
            let pendingChange = &#91;...sortConfig&#93;;
            const index = pendingChange.findIndex((config) => config.propertyName === propertyName)
            if(index > -1){
                //Save the sortType
                var currentSortType = pendingChange&#91;index&#93;.sortType;
                //Remove existing config
                pendingChange.splice(index, 1);
                //check if the sort type we saved is descending
                if (currentSortType === SortingType.Descending) {
                    pendingChange = &#91;
                        ...pendingChange,
                        { propertyName: propertyName, sortType: SortingType.Ascending , compareFunction: compareFunction},
                    &#93;;
                }
            } else {
                pendingChange = &#91;
                    ...pendingChange,
                    { propertyName: propertyName, sortType: SortingType.Descending, compareFunction: compareFunction },
                &#93;;
            }
            updateSortConfig(&#91;...pendingChange&#93;);
        },
        &#91;sortConfig&#93;
    )</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">sortBy</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        (</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">compareFunction</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableDataComparable</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;...</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">index</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findIndex</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> &gt; -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Save the sortType</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">currentSortType</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//Remove existing config</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">splice</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #6A9955">//check if the sort type we saved is descending</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">currentSortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                        ...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                        { </span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortType:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">compareFunction</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">                    &#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">                }</span></span>
<span class="line"><span style="color: #D4D4D4">            } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4"> = &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                    ...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                    { </span><span style="color: #9CDCFE">propertyName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">propertyName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sortType:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Descending</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">compareFunction</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">                &#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #DCDCAA">updateSortConfig</span><span style="color: #D4D4D4">(&#91;...</span><span style="color: #9CDCFE">pendingChange</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        &#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span></code></pre></div>



<p class="wp-block-paragraph">Next, we update our interfaces and define comparators for each of our table columns.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>interface SortableHeaderProps {
    sortBy: (string: keyof TableData, compareFunction: TableDataComparable) => void;
    sortConfig: SortingConfiguration[];
}
...
const CompareByEquality = (column: keyof TableData) => (a: TableData, b: TableData) => {
        if(a&#91;column&#93; === b&#91;column&#93;){
            return 0
        } else{
            if (a&#91;column&#93; > b&#91;column&#93;){
                return 1;
            }
            return -1;
        }
    }

    const tableColumn = [
        {   label: 'Name', 
            property: 'name', 
            compareFunction: 
            (a: TableData, b: TableData) => {
                 return a&#91;'name'&#93;.localeCompare(b&#91;'name'&#93; as string) 
            } 
        },
        {
            label: 'Hours', 
            property: 'hours',
            compareFunction: CompareByEquality('hours')
        },
        {   label: 'Date', 
            property: 'startDate', 
            compareFunction: CompareByEquality('startDate') 
        },
        {   label: 'Department',
            property: 'department', 
            compareFunction: CompareByEquality('department') }
    ] as TableColumn[];</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SortableHeaderProps</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">: (</span><span style="color: #9CDCFE">string</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">compareFunction</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableDataComparable</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">SortingConfiguration</span><span style="color: #D4D4D4">[];</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">...</span></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CompareByEquality</span><span style="color: #D4D4D4"> = (</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">keyof</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">&#93; === </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">&#93;){</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0</span></span>
<span class="line"><span style="color: #D4D4D4">        } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">&#93; &gt; </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4">&#93;){</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> -</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">tableColumn</span><span style="color: #D4D4D4"> = [</span></span>
<span class="line"><span style="color: #D4D4D4">        {   </span><span style="color: #9CDCFE">label:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Name&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">property:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">            (</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                 </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #DCDCAA">localeCompare</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">&#93; </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #D4D4D4">            } </span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">label:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Hours&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">property:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;hours&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CompareByEquality</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;hours&apos;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        {   </span><span style="color: #9CDCFE">label:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Date&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">property:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;startDate&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CompareByEquality</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;startDate&apos;</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #D4D4D4">        },</span></span>
<span class="line"><span style="color: #D4D4D4">        {   </span><span style="color: #9CDCFE">label:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;Department&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">property:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;department&apos;</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">compareFunction:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CompareByEquality</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;department&apos;</span><span style="color: #D4D4D4">) }</span></span>
<span class="line"><span style="color: #D4D4D4">    ] </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TableColumn</span><span style="color: #D4D4D4">[];</span></span></code></pre></div>



<p class="wp-block-paragraph">Here, I just use equality for returning the values, but for more complex objects, you can define your own comparator function. As for the name, I used string’s localCompare as an example of an in-line lambda comparator.</p>



<p class="wp-block-paragraph">After that, we just add the compareFunction into our onClick, so that our sortBy function has all the correct parameters.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;TableCell key={index}
    		className={headerCell}
   	 	onClick={() => sortBy(column.property, column.compareFunction)}
>
&lt;TableCell key={index}
    		className={headerCell}
   	 	onClick={() => sortBy(column.property, column.compareFunction)}
></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">    		</span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">headerCell</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">   	 	</span><span style="color: #9CDCFE">onClick</span><span style="color: #D4D4D4">={() =&gt; </span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">(column.property, column.compareFunction)}</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">TableCell</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">    		</span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">headerCell</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">   	 	</span><span style="color: #9CDCFE">onClick</span><span style="color: #D4D4D4">={() =&gt; </span><span style="color: #DCDCAA">sortBy</span><span style="color: #D4D4D4">(column.property, column.compareFunction)}</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">Lastly, we need to change our definition for sortedRows:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const sortedRows = useMemo(() => {
        if(sortConfig.length === 0){
            return &#91;...dataList&#93;;
        }
        let sorted = &#91;...dataList&#93;.sort(
            (a: TableData, b:TableData) =>{
                 for(const config of sortConfig){
                    const result = (config.compareFunction(a,b))
                    if(result !== 0){
                        if(config.sortType === SortingType.Ascending){
                            return result;
                        }
                        else{
                            return -result;
                        }
                    }
                }
                return 0;
            }
        )
        return(sorted)
    }, &#91;sortConfig, dataList&#93;);</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">sortedRows</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">length</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> &#91;...</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">let</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4"> = &#91;...</span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">            (</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">:</span><span style="color: #4EC9B0">TableData</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">                 </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">config</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">of</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">result</span><span style="color: #D4D4D4"> = (</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">compareFunction</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">,</span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> !== </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">config</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sortType</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">SortingType</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Ascending</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                        }</span></span>
<span class="line"><span style="color: #D4D4D4">                        </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">                            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> -</span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                        }</span></span>
<span class="line"><span style="color: #D4D4D4">                    }</span></span>
<span class="line"><span style="color: #D4D4D4">                }</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        )</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">sorted</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    }, &#91;</span><span style="color: #9CDCFE">sortConfig</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">dataList</span><span style="color: #D4D4D4">&#93;);</span></span></code></pre></div>



<p class="wp-block-paragraph">First, we check if there’s a sort configuration and if there isn’t we want to go back to our unsorted state by returning a copy of the data list.</p>



<p class="wp-block-paragraph">If there is a sort configuration, we make a copy of dataList using the spread operator and sort with a lambda function that goes over our loop of sortConfigs. It then tests each a, and b, TableData against the compareFunction in the sortConfig. If we have an equal match, we try to and apply the subsequent sort config until we have a result that isn’t 0. Next, we check if it’s ascending or descending, and so we flip the result to its opposite value with a negative sign. Lastly, we finish going through our sort configuration and find that all matches, then we know both rows are equal.</p>



<p class="wp-block-paragraph">That about does it for sorting multiple columns in React.</p>



<p class="wp-block-paragraph"><strong>Learn more about DMC&#8217;s <a href="https://static.dmcinfo.com/services/application-development">Application Development expertise</a>.</strong></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/19887/sorting-multiple-columns-in-a-table-with-react/">Sorting Multiple Columns in a Table with React</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
