<?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>DesktopCanvas &#8211; Xojo Programming Blog</title>
	<atom:link href="https://blog.xojo.com/tag/desktopcanvas/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.xojo.com</link>
	<description>Blog about the Xojo programming language and IDE</description>
	<lastBuildDate>Wed, 08 Oct 2025 15:54:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>How To Create a Custom Button Control in Xojo – Part 5: Adding Text Alignment</title>
		<link>https://blog.xojo.com/2025/10/08/how-to-create-a-custom-button-control-in-xojo-part-5-adding-text-alignment/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Wed, 08 Oct 2025 15:54:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Custom Button Design]]></category>
		<category><![CDATA[Custom Classes]]></category>
		<category><![CDATA[Custom Control Design]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=15436</guid>

					<description><![CDATA[In our previous installment, we successfully wired up keyboard focus to our custom&#160;DesktopCanvas subclass, making it fully accessible and user-friendly. Now, we’re going to tackle&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In our previous installment, we successfully wired up keyboard focus to our custom&nbsp;<code>DesktopCanvas</code> subclass, making it fully accessible and user-friendly.</p>



<p>Now, we’re going to tackle another key feature: customizable text alignment. By the end of this tutorial, you’ll be able to set the button’s text to align left, center, or right, and we’ll introduce a small amount of padding to keep the text from touching the edges when it’s not centered. This approach uses a strongly typed&nbsp;<code>Enumeration</code>&nbsp;to ensure we only accept valid alignment values, maintaining the high quality of our custom control.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="314" height="182" src="https://blog.xojo.com/wp-content/uploads/2025/09/Screenshot-2025-09-30-150534.png" alt="" class="wp-image-15440" srcset="https://blog.xojo.com/wp-content/uploads/2025/09/Screenshot-2025-09-30-150534.png 314w, https://blog.xojo.com/wp-content/uploads/2025/09/Screenshot-2025-09-30-150534-300x174.png 300w" sizes="(max-width: 314px) 100vw, 314px" /></figure>



<h3 class="wp-block-heading" id="define-the-text-alignment-options">1. Define the Text Alignment Options</h3>



<p>To make our alignment property easy to use and type-safe, we will define a new enumeration inside the&nbsp;<code>CanvasButton</code>&nbsp;class.</p>



<p>Inside your&nbsp;<code>CanvasButton</code>&nbsp;class, go to&nbsp;<strong>Insert &gt; Enumeration</strong>&nbsp;and add the following:</p>



<pre class="wp-block-code"><code>  Left = 0
  Center = 1
  Right = 2</code></pre>



<h3 class="wp-block-heading" id="add-new-properties-for-alignment-and-padding">2. Add New Properties for Alignment and Padding</h3>



<p>Next, we need two new properties in the&nbsp;<code>CanvasButton</code>&nbsp;class to store the selected alignment and the padding value.</p>



<p>The&nbsp;<code>TextAlignment</code>&nbsp;property will use our new&nbsp;<code>TextAlign</code>&nbsp;enumeration, and, crucially, it defaults to&nbsp;<code>TextAlign.Center</code>.<br>The&nbsp;<code>TextPadding</code>&nbsp;property provides an adjustable inner margin, which is especially important for left and right alignment.</p>



<pre class="wp-block-code"><code>Public Property TextAlignment As TextAlign = TextAlign.Center
Public Property TextPadding As Integer = 8</code></pre>



<h3 class="wp-block-heading" id="update-the-paint-method">3. Update the Paint Method</h3>



<p>The core of this change happens in the&nbsp;<code>Paint</code>&nbsp;event handler. We need to modify the logic that calculates the horizontal position of the text (<code>tx</code>) to respect the value of our new&nbsp;<code>TextAlignment</code>&nbsp;property.</p>



<p>Locate the&nbsp;<code>Paint</code>&nbsp;event and replace the existing code with the following:</p>



<pre class="wp-block-code"><code>Sub Paint(g As Graphics, areas() As Rect) Handles Paint
  #Pragma unused areas
  
  // Use the custom CornerRadius property.
  Var currentCornerRadius As Integer = CornerRadius
  
  // Declare variables for the colors used in drawing.
  Var currentBgColor As Color
  Var currentBorderColor As Color
  Var currentTextColor As Color
  
  // Determine colors based on the button's current state (enabled, pressed, hovered).
  If Enabled Then
    If IsPressed Then
      // Use highlight color if pressed.
      currentBgColor = HighlightColor
      currentBorderColor = BorderColor
      currentTextColor = TextColor
    ElseIf IsHovered Then // Check for hover only if not pressed
      // Use hover color if hovered.
      currentBgColor = HoverColor
      currentBorderColor = BorderColor
      currentTextColor = TextColor
    Else
      // Use the custom background color for the default state.
      currentBgColor = BackgroundColor
      currentBorderColor = BorderColor
      currentTextColor = TextColor
    End If
  Else
    // Use appropriate system or standard gray colors for the disabled state.
    currentBgColor = Color.LightGray
    currentBorderColor = Color.Gray
    currentTextColor = Color.DisabledTextColor // Use system disabled text color
  End If
  
  // Set the drawing color and draw the background shape with rounded corners.
  g.DrawingColor = currentBgColor
  g.FillRoundRectangle(0, 0, g.Width, g.Height, currentCornerRadius, currentCornerRadius)
  
  // Set the drawing color and pen size for the border.
  g.DrawingColor = currentBorderColor
  g.PenSize = 2
  // Draw the border shape just inside the background rectangle.
  g.DrawRoundRectangle(0, 0, g.Width, g.Height, currentCornerRadius, currentCornerRadius)
  
  // Draw the focus ring
  If IsFocused And AllowFocusRing Then
    g.DrawingColor = Color.HighlightColor
    g.PenSize = 1
    g.DrawRoundRectangle(2, 2, g.Width-4, g.Height-4, CornerRadius-2, CornerRadius-2)
  End If
  
  // Enable anti-aliasing for smoother text rendering.
  g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
  g.AntiAliased = True
  // Calculate the width and height of the button text.
  Var tw As Double = g.TextWidth(ButtonText)
  Var th As Double = g.TextHeight
  // Calculate the X position to center the text horizontally.
  // Updated: compute X based on TextAlignment (Left, Center, Right) while preserving centered behavior as default.
  Var tx As Double
  Select Case TextAlignment
  Case TextAlign.Left
    tx = TextPadding
  Case TextAlign.Right
    tx = g.Width - tw - TextPadding
  Case TextAlign.Center
    tx = (g.Width - tw) / 2
  End Select
  // Calculate the Y position to center the text vertically, with a small adjustment.
  Var ty As Double = (g.Height + th) / 2 - 3
  // Set the drawing color for the text.
  g.DrawingColor = currentTextColor
  // Draw the button text at the calculated centered position.
  g.DrawText(ButtonText, tx, ty)
End Sub</code></pre>



<p>The magic happens at line 60 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h3 class="wp-block-heading" id="inspector-integration">4. Inspector Integration</h3>



<p>If you want the alignment to be easily adjustable in the Xojo Inspector, you should expose the&nbsp;<code>TextAlignment</code>&nbsp;and&nbsp;<code>TextPadding</code>&nbsp;properties. For&nbsp;<code>TextAlignment</code>, because it uses an&nbsp;<code>Enumeration</code>, Xojo will automatically display a helpful dropdown menu with Left, Center, and Right options once you expose it through the Inspector Behavior editor.</p>



<ul class="wp-block-list">
<li>Right-click the&nbsp;<code>CanvasButton</code>&nbsp;class in the Project Navigator.</li>



<li>Select ‘Inspector Behavior…’</li>



<li>Scroll through the property list and check the checkboxes for both&nbsp;<code>TextAlignment</code>&nbsp;and&nbsp;<code>TextPadding</code>.</li>
</ul>



<h3 class="wp-block-heading" id="try-it-out">Try It Out</h3>



<p>You can now set the text alignment directly in the code or via the Inspector.</p>



<p>For example, to set alignment in code:</p>



<pre class="wp-block-code"><code>// Set the button to right-aligned text
MyCanvasButton.TextAlignment = CanvasButton.TextAlign.Right</code></pre>



<p>Since the default value for&nbsp;<code>TextAlignment</code>&nbsp;is&nbsp;<code>Center</code>, no existing code needs to be modified, and all your current custom buttons will continue to render perfectly centered unless you explicitly change the alignment property.</p>



<p>Hope this update was useful.</p>



<p>You can grab the latest source on GitHub:&nbsp;<a href="https://github.com/xolabsro/CanvasButton">GitHub – CanvasButton</a></p>



<p>More in this series:</p>



<ul class="wp-block-list">
<li><a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo</a></li>



<li><a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 2</a></li>



<li><a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</a></li>



<li><a href="https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</a></li>



<li><a href="https://blog.xojo.com/2025/10/08/how-to-create-a-custom-button-control-in-xojo-part-5-adding-text-alignment/" target="_blank" rel="noreferrer noopener">How to Create a Custom Button Control in Xojo &#8211; Part 5: Adding Text Alignment</a></li>
</ul>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</title>
		<link>https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Mon, 23 Jun 2025 22:35:31 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Custom Button Design]]></category>
		<category><![CDATA[Custom Classes]]></category>
		<category><![CDATA[Custom Control Design]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=15007</guid>

					<description><![CDATA[In part 3 of this series&#160;we made our&#160;CanvasButton&#160;inspector-friendly by exposing all our colors, corner radius and other properties. Today we’re going to wire up real&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In <a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/" target="_blank" rel="noreferrer noopener">part 3 of this series</a>&nbsp;we made our&nbsp;<code>CanvasButton</code>&nbsp;inspector-friendly by exposing all our colors, corner radius and other properties. Today we’re going to wire up real keyboard focus so that you can:</p>



<ul class="wp-block-list">
<li>Tab to the button</li>



<li>See a neat focus ring when it has focus</li>



<li>Grab focus on click</li>
</ul>



<figure class="wp-block-video"><video height="254" style="aspect-ratio: 332 / 254;" width="332" autoplay loop muted preload="auto" src="https://blog.xojo.com/wp-content/uploads/2025/06/CanvasButton-Focus-Demo.mp4" playsinline></video></figure>



<h3 class="wp-block-heading" id="track-the-focus-state">1. Track the Focus State</h3>



<p>Add a private property to keep track of whether the button has focus:</p>



<pre class="wp-block-code"><code>  Private IsFocused As Boolean = False</code></pre>



<h3 class="wp-block-heading" id="respond-to-focus-events">2. Respond to Focus Events</h3>



<p>Implement&nbsp;<code>FocusReceived</code>&nbsp;and&nbsp;<code>FocusLost</code>&nbsp;so we can set/clear the flag and force a redraw:</p>



<pre class="wp-block-code"><code>  Sub FocusReceived()
    If Enabled And AllowFocusRing Then
      IsFocused = True
      Refresh(False)
    End If
  End Sub

  Sub FocusLost()
    If Enabled And AllowFocusRing Then
      IsFocused = False
      Refresh(False)
    End If
  End Sub</code></pre>



<h3 class="wp-block-heading" id="grab-focus-on-mouse-click">3. Grab Focus on Mouse Click</h3>



<p>Modify your&nbsp;<code>MouseDown</code>&nbsp;to call&nbsp;<code>SetFocus</code>. Now clicking the button also gives it focus immediately:</p>



<pre class="wp-block-code"><code>  Function MouseDown(x As Integer, y As Integer) As Boolean
    #Pragma unused x
    #Pragma unused y

    If Enabled Then
      IsPressed = True
      SetFocus       // ← new
      Refresh(False)
      Return True
    Else
      Return False
    End If
  End Function</code></pre>



<h3 class="wp-block-heading" id="draw-an-inset-focus-ring">4. Draw an Inset Focus Ring</h3>



<p>In the&nbsp;<code>Paint</code>&nbsp;event, after drawing the border, add:</p>



<pre class="wp-block-code"><code>// … after the border code …
If IsFocused And AllowFocusRing Then
  g.DrawingColor = Color.HighlightColor
  g.PenSize = 1
  // Inset by 2px so it sits inside the border
  g.DrawRoundRectangle(2, 2, g.Width-4, g.Height-4, CornerRadius-2, CornerRadius-2)
End If</code></pre>



<h3 class="wp-block-heading" id="enable-canvas-focus">5. Enable Canvas Focus</h3>



<p>By default,&nbsp;<code>DesktopCanvas</code>&nbsp;won’t accept focus, so make sure to check&nbsp;AllowFocusRing&nbsp;in the Inspector when you drop&nbsp;<code>CanvasButton</code>&nbsp;onto a window.</p>



<h2 class="wp-block-heading" id="try-it-out">Try It Out</h2>



<ol class="wp-block-list">
<li>Run the app:
<ul class="wp-block-list">
<li>Tab to the button → you’ll see the focus ring</li>
</ul>
</li>
</ol>



<h2 class="wp-block-heading" id="what’s-next">What’s Next?</h2>



<p>Stay tuned for more awesome updates!</p>



<p>You can grab the latest source on GitHub: <a href="https://github.com/xolabsro/CanvasButton" target="_blank" rel="noreferrer noopener">GitHub &#8211; CanvasButton</a></p>



<p>More in this series:</p>



<ul class="wp-block-list">
<li><a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo</a></li>



<li><a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 2</a></li>



<li><a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</a></li>



<li><a href="https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</a></li>



<li><a href="https://blog.xojo.com/2025/10/08/how-to-create-a-custom-button-control-in-xojo-part-5-adding-text-alignment/" target="_blank" rel="noreferrer noopener">How to Create a Custom Button Control in Xojo &#8211; Part 5: Adding Text Alignment</a></li>
</ul>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		<enclosure url="https://blog.xojo.com/wp-content/uploads/2025/06/CanvasButton-Focus-Demo.mp4" length="227730" type="video/mp4" />

			</item>
		<item>
		<title>How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</title>
		<link>https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Wed, 28 May 2025 17:54:20 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Custom Button Design]]></category>
		<category><![CDATA[Custom Classes]]></category>
		<category><![CDATA[Custom Control Design]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14925</guid>

					<description><![CDATA[In&#160;Part 1, we built a custom button control in Xojo by subclassing&#160;DesktopCanvas. In&#160;Part 2, we enhanced it with customizable colors, corner radius, and a disabled&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In&nbsp;<a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/">Part 1</a>, we built a custom button control in Xojo by subclassing&nbsp;<code>DesktopCanvas</code>. In&nbsp;<a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/">Part 2</a>, we enhanced it with customizable colors, corner radius, and a disabled state.</p>



<p>Now, let’s make the&nbsp;<code>CanvasButton</code>&nbsp;control even easier to use in the Xojo IDE by leveraging the&nbsp;<strong><a href="https://documentation.xojo.com/topics/custom_controls/changing_properties_with_the_inspector.html" target="_blank" rel="noreferrer noopener">Inspector Behavior</a></strong>&nbsp;feature. This allows you (and anyone using your control) to configure its appearance and behavior at design time, right in the Properties panel.</p>



<h2 class="wp-block-heading" id="why-inspector-behavior-matters">Why Inspector Behavior Matters</h2>



<p>When you create a custom control, its custom properties will not appear in the Inspector by default.&nbsp;<strong>Inspector Behavior</strong>&nbsp;lets you choose which properties are shown and how they’re grouped. This makes your control feel polished and professional, and saves users from digging into code to tweak settings.</p>



<h2 class="wp-block-heading" id="step-1-open-inspector-behavior">Step 1: Open Inspector Behavior</h2>



<p>To customize Inspector options for your&nbsp;<code>CanvasButton</code>:</p>



<ol class="wp-block-list">
<li>In the Xojo IDE, select the&nbsp;<code>CanvasButton</code>&nbsp;class in the Project Navigator.</li>



<li>Right-click it and choose&nbsp;<strong>Inspector Behavior…</strong></li>
</ol>



<figure class="wp-block-image size-full"><img decoding="async" width="342" height="512" src="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_01.jpg" alt="Inspector Behavior menu" class="wp-image-14929" srcset="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_01.jpg 342w, https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_01-200x300.jpg 200w" sizes="(max-width: 342px) 100vw, 342px" /></figure>



<h2 class="wp-block-heading" id="step-2-expose-and-organize-properties">Step 2: Expose and Organize Properties</h2>



<p>In the Inspector Behavior dialog, you’ll see a list of all public properties (inherited and custom).</p>



<ul class="wp-block-list">
<li><strong>Make the following properties visible:</strong>
<ul class="wp-block-list">
<li><code>ButtonText</code></li>



<li><code>CornerRadius</code></li>



<li><code>BackgroundColor</code>,&nbsp;<code>HoverColor</code>,&nbsp;<code>HighlightColor</code>,&nbsp;<code>BorderColor</code>,&nbsp;<code>TextColor</code>&nbsp;(all grouped under a custom “Button Colors” section)</li>
</ul>
</li>



<li><strong>Organize properties into logical groups</strong>&nbsp;for clarity.</li>
</ul>



<figure class="wp-block-image size-full"><img decoding="async" width="640" height="687" src="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_02.jpg" alt="Inspector Behavior window" class="wp-image-14930" srcset="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_02.jpg 640w, https://blog.xojo.com/wp-content/uploads/2025/05/inspector_behavior_02-279x300.jpg 279w" sizes="(max-width: 640px) 100vw, 640px" /></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Tip:</strong>&nbsp;Defining default values for your properties in the class code means they will be automatically populated in Inspector Behavior. However, Xojo gives you even more flexibility. You can override these default values directly in the Inspector Behavior dialog, so you have full control over what new instances of your control will look like by default.</p>
</blockquote>



<h2 class="wp-block-heading" id="step-3-enjoy-design-time-customization">Step 3: Enjoy Design-Time Customization</h2>



<p>With Inspector Behavior set up, you (or other developers) can now:</p>



<ul class="wp-block-list">
<li><strong>Change the button text</strong>&nbsp;right in the Properties panel.</li>



<li><strong>Pick custom colors</strong>&nbsp;for normal, hover, pressed, border, and text states using the color picker.</li>



<li><strong>Adjust the corner radius</strong>&nbsp;visually.</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="297" height="466" src="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_03.jpg" alt="Xojo Inspector showing custom CanvasButton properties" class="wp-image-14931" srcset="https://blog.xojo.com/wp-content/uploads/2025/05/inspector_03.jpg 297w, https://blog.xojo.com/wp-content/uploads/2025/05/inspector_03-191x300.jpg 191w" sizes="auto, (max-width: 297px) 100vw, 297px" /></figure>



<h2 class="wp-block-heading" id="step-4-best-practices-and-tips">Step 4: Best Practices and Tips</h2>



<ul class="wp-block-list">
<li><strong>Use clear group names</strong>&nbsp;(like “Button Colors”) to help users find related properties.</li>



<li><strong>Set sensible defaults</strong>&nbsp;for all properties in your code, but remember you can override them in Inspector Behavior.</li>
</ul>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>With Inspector Behavior, your custom controls feel like first-class citizens in Xojo. You and others can now design beautiful, custom buttons by simply dragging, dropping, and tweaking settings in the Inspector. No code required!</p>



<p><strong>Stay tuned for future parts</strong>&nbsp;where we’ll explore even more advanced features.</p>



<p id="block-59a92dbb-c0d1-4b75-b250-f331b6ceb4c4">More in this series:</p>



<ul id="block-c7594468-ed0c-4be1-a999-04ef9789f34c" class="wp-block-list">
<li><a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo</a></li>



<li><a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 2</a></li>



<li><a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</a></li>



<li><a href="https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</a></li>



<li><a href="https://blog.xojo.com/2025/10/08/how-to-create-a-custom-button-control-in-xojo-part-5-adding-text-alignment/" target="_blank" rel="noreferrer noopener">How to Create a Custom Button Control in Xojo &#8211; Part 5: Adding Text Alignment</a></li>
</ul>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To Create a Custom Button Control in Xojo &#8211; Part 2</title>
		<link>https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Wed, 14 May 2025 15:00:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Custom Button Design]]></category>
		<category><![CDATA[Custom Classes]]></category>
		<category><![CDATA[Custom Control Design]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14898</guid>

					<description><![CDATA[In the first part of this tutorial, we built a basic custom button control using the&#160;DesktopCanvas&#160;class, giving it a custom appearance and handling the basic&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In the <a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/" data-type="post" data-id="14858" target="_blank" rel="noreferrer noopener">first part of this tutorial</a>, we built a basic custom button control using the&nbsp;<code>DesktopCanvas</code>&nbsp;class, giving it a custom appearance and handling the basic mouse events to provide visual feedback and raise a&nbsp;<code>Pressed</code>&nbsp;event when clicked.</p>



<p>In this second part, we will enhance our&nbsp;<code>CanvasButton</code>&nbsp;by adding more customization with new properties to:</p>



<ul class="wp-block-list">
<li>set the button&#8217;s colors for different states (default, hovered, pressed)</li>



<li>adjust the corner radius of the button</li>



<li>implement a disabled state</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="278" height="146" src="https://blog.xojo.com/wp-content/uploads/2025/05/CanvasButton.gif" alt="" class="wp-image-14914"/></figure>



<p>Let&#8217;s get started!</p>



<h3 class="wp-block-heading" id="adding-new-properties-for-customization">Adding New Properties for Customization</h3>



<p>We need to add several properties to our&nbsp;<code>CanvasButton</code>&nbsp;class to store the custom colors for different states, the border color, the text color, the corner radius, and the enabled state.</p>



<ol class="wp-block-list">
<li>In the Project Navigator, select your&nbsp;<code>CanvasButton</code>&nbsp;class.</li>



<li>Go to&nbsp;<strong>Insert &gt; Property</strong>&nbsp;(or right-click the class and select Add to &#8220;CanvasButton&#8221; &gt; Property).</li>



<li>Add the following properties with the specified names, types, and default values:
<ul class="wp-block-list">
<li><strong>Name:</strong>&nbsp;<code>BackgroundColor</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Color</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>&amp;c5e2d8b</code></li>



<li><strong>Name:</strong>&nbsp;<code>HoverColor</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Color</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>&amp;c729fcf</code></li>



<li><strong>Name:</strong>&nbsp;<code>HighlightColor</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Color</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>&amp;c628eff</code></li>



<li><strong>Name:</strong>&nbsp;<code>BorderColor</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Color</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>&amp;c525252</code></li>



<li><strong>Name:</strong>&nbsp;<code>TextColor</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Color</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>&amp;ceeeeee</code></li>



<li><strong>Name:</strong>&nbsp;<code>CornerRadius</code>&nbsp;<strong>Type:</strong>&nbsp;<code>Integer</code>&nbsp;<strong>Default Value:</strong>&nbsp;<code>4</code></li>
</ul>
</li>
</ol>



<p>These properties will hold the values that determine the button’s appearance and behavior.</p>



<h3 class="wp-block-heading" id="updating-event-handlers-for-the-enabled-state">Updating Event Handlers for the Enabled State</h3>



<p>We need to modify the existing mouse event handlers (<code>MouseDown</code>,&nbsp;<code>MouseEnter</code>,&nbsp;<code>MouseExit</code>,&nbsp;<code>MouseUp</code>) to check if the button is&nbsp;<code>Enabled</code>&nbsp;before processing any mouse actions.</p>



<p>Select each of the following event handlers in the&nbsp;<code>CanvasButton</code>&nbsp;class and update the code as shown:</p>



<p><strong><code>MouseDown(x As Integer, y As Integer) As Boolean</code></strong></p>



<pre class="wp-block-code"><code>#Pragma unused x
#Pragma unused y

// Only process if the button is enabled.
If Enabled Then
  // Set internal state to indicate the button is being pressed.
  IsPressed = True
  // Refresh the control to show the pressed state visually.
  Refresh(False)
  // Return True to indicate that this event was handled.
  Return True
Else
  // If disabled, do not handle the event.
  Return False
End If</code></pre>



<p><strong><code>MouseEnter()</code></strong></p>



<pre class="wp-block-code"><code>// Only process if the button is enabled.
If Enabled Then
  // Set internal state to indicate the mouse is hovering over the button.
  IsHovered = True
  // Refresh the control to show the hover state visually.
  Refresh(False)
End If</code></pre>



<p><strong><code>MouseExit()</code></strong></p>



<pre class="wp-block-code"><code>// Only process if the button is enabled.
If Enabled Then
  // Set internal state to indicate the mouse is no longer hovering.
  IsHovered = False
  // Reset pressed state if mouse leaves while pressed (prevents accidental clicks).
  IsPressed = False
  // Refresh the control to revert from the hover state.
  Refresh(False)
End If</code></pre>



<p><strong><code>MouseUp(x As Integer, y As Integer) As Boolean</code></strong></p>



<pre class="wp-block-code"><code>#Pragma unused x
#Pragma unused y

// Only process if the button is enabled.
If Enabled Then
  // Check if the button was pressed down AND the mouse is still hovering over it.
  If IsPressed And IsHovered Then
    // If true, the button was successfully clicked. Raise the custom Pressed event.
    RaiseEvent Pressed
  End If
  // Reset the pressed state regardless of whether the click was successful.
  IsPressed = False
  // Refresh the control to revert from the pressed state.
  Refresh(False)
End If</code></pre>



<p>In these updated event handlers, we added an&nbsp;<code>If Enabled Then</code>&nbsp;check at the beginning. If the button is not enabled, the code inside the&nbsp;<code>If</code>&nbsp;block is skipped, preventing the button from reacting to mouse interactions. We also added&nbsp;<code>#Pragma unused</code>&nbsp;to the parameters&nbsp;<code>x</code>&nbsp;and&nbsp;<code>y</code>&nbsp;in&nbsp;<code>MouseDown</code>&nbsp;and&nbsp;<code>MouseUp</code>&nbsp;as they are not used in the code, which helps avoid compiler warnings. In&nbsp;<code>MouseExit</code>, we added&nbsp;<code>IsPressed = False</code>&nbsp;to ensure the pressed state is reset if the mouse leaves the button while the mouse button is held down.</p>



<h3 class="wp-block-heading" id="updating-the-paint-event-for-enhanced-appearance">Updating the Paint Event for Enhanced Appearance</h3>



<p>The most significant changes will be in the&nbsp;<code>Paint</code>&nbsp;event where we will use the new properties to draw the button based on its current state (enabled/disabled, hovered, and pressed).</p>



<p>Select the&nbsp;<code>Paint(g As Graphics, areas() As Rect)</code>&nbsp;event handler and replace its content with the following code:</p>



<pre class="wp-block-code"><code>#Pragma unused areas

// Use the custom CornerRadius property.
Var currentCornerRadius As Integer = CornerRadius

// Declare variables for the colors used in drawing.
Var currentBgColor As Color
Var currentBorderColor As Color
Var currentTextColor As Color

// Determine colors based on the button's current state (enabled, pressed, hovered).
If Enabled Then
  If IsPressed Then
    // Use highlight color if pressed.
    currentBgColor = HighlightColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  ElseIf IsHovered Then // Check for hover only if not pressed
    // Use hover color if hovered.
    currentBgColor = HoverColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  Else
    // Use the custom background color for the default state.
    currentBgColor = BackgroundColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  End If
Else
  // Use appropriate system or standard gray colors for the disabled state.
  currentBgColor = Color.LightGray
  currentBorderColor = Color.Gray
  currentTextColor = Color.DisabledTextColor // Use system disabled text color
End If

// Set the drawing color and draw the background shape with rounded corners.
g.DrawingColor = currentBgColor
g.FillRoundRectangle(0, 0, g.Width, g.Height, currentCornerRadius, currentCornerRadius)

// Set the drawing color and pen size for the border.
g.DrawingColor = currentBorderColor
g.PenSize = 2
// Draw the border shape just inside the background rectangle.
g.DrawRoundRectangle(1, 1, g.Width-2, g.Height-2, currentCornerRadius, currentCornerRadius)

// Enable anti-aliasing for smoother text rendering.
g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
g.AntiAliased = True
// Calculate the width and height of the button text.
Var tw As Double = g.TextWidth(ButtonText)
Var th As Double = g.TextHeight
// Calculate the X position to center the text horizontally.
Var tx As Double = (g.Width - tw) / 2
// Calculate the Y position to center the text vertically, with a small adjustment.
Var ty As Double = (g.Height + th) / 2 - 3
// Set the drawing color for the text.
g.DrawingColor = currentTextColor
// Draw the button text at the calculated centered position.
g.DrawText(ButtonText, tx, ty)</code></pre>



<p>Let’s break down the changes in the&nbsp;<code>Paint</code>&nbsp;event:</p>



<ul class="wp-block-list">
<li>We now use the&nbsp;<code>CornerRadius</code>&nbsp;property instead of a static constant for drawing the rounded rectangles.</li>



<li>We introduced an&nbsp;<code>If Enabled Then ... Else</code>&nbsp;block.</li>



<li>Inside the&nbsp;<code>If Enabled Then</code>&nbsp;block, we have a nested&nbsp;<code>If IsPressed Then ... ElseIf IsHovered Then ... Else</code>&nbsp;structure. This determines the&nbsp;<code>currentBgColor</code>:
<ul class="wp-block-list">
<li>If&nbsp;<code>IsPressed</code>&nbsp;is True,&nbsp;<code>currentBgColor</code>&nbsp;is set to&nbsp;<code>HighlightColor</code>.</li>



<li>If&nbsp;<code>IsPressed</code>&nbsp;is False but&nbsp;<code>IsHovered</code>&nbsp;is True,&nbsp;<code>currentBgColor</code>&nbsp;is set to&nbsp;<code>HoverColor</code>.</li>



<li>If neither&nbsp;<code>IsPressed</code>&nbsp;nor&nbsp;<code>IsHovered</code>&nbsp;is True,&nbsp;<code>currentBgColor</code>&nbsp;is set to&nbsp;<code>BackgroundColor</code>.</li>



<li>The&nbsp;<code>currentBorderColor</code>&nbsp;and&nbsp;<code>currentTextColor</code>&nbsp;are set directly from the&nbsp;<code>BorderColor</code>&nbsp;and&nbsp;<code>TextColor</code>&nbsp;properties when the button is enabled.</li>
</ul>
</li>



<li>Inside the&nbsp;<code>Else</code>&nbsp;block (when&nbsp;<code>Enabled</code>&nbsp;is False), we set the colors to standard gray values (<code>Color.LightGray</code>&nbsp;for background,&nbsp;<code>Color.Gray</code>&nbsp;for border, and&nbsp;<code>Color.DisabledTextColor</code>&nbsp;for text) to give the button a disabled appearance.</li>



<li>Finally, the drawing commands use these&nbsp;<code>currentBgColor</code>,&nbsp;<code>currentBorderColor</code>,&nbsp;<code>currentTextColor</code>, and&nbsp;<code>currentCornerRadius</code>&nbsp;variables.</li>
</ul>



<h3 class="wp-block-heading" id="using-the-enhanced-custom-control">Using the Enhanced Custom Control</h3>



<p>Now that our&nbsp;<code>CanvasButton</code>&nbsp;has more customization options and supports a disabled state, let’s see how to use these features.</p>



<ol class="wp-block-list">
<li>If you don’t already have an instance, drag the&nbsp;<code>CanvasButton</code>&nbsp;class from the Project Navigator onto a window in the Layout Editor.</li>



<li>You can now access and set the new properties programmatically. For example, in a button’s&nbsp;<code>Opening</code>&nbsp;event, you could add code like this:</li>
</ol>



<pre class="wp-block-code"><code>// Customize colors
Me.BackgroundColor = Color.RGB(200, 50, 50) // A shade of red
Me.HoverColor = Color.RGB(255, 100, 100) // A lighter red for hover
Me.HighlightColor = Color.RGB(150, 0, 0) // A darker red for pressed
Me.BorderColor = Color.Black
Me.TextColor = Color.White

// Adjust corner radius
Me.CornerRadius = 10</code></pre>



<p>You can experiment with different values to see how they affect the button’s appearance. To test the disabled state, you can set the&nbsp;<code>Enabled</code>&nbsp;property to&nbsp;<code>False</code>.</p>



<h3 class="wp-block-heading" id="conclusion">Conclusion</h3>



<p>We&#8217;ve now taken our custom button to the next level by adding extensive color customization, adjustable corners, and a disabled state.</p>



<p>This project can be downloaded from GitHub at:&nbsp;<a href="https://github.com/xolabsro/CanvasButton" target="_blank" rel="noreferrer noopener">https://github.com/xolabsro/CanvasButton</a></p>



<p>Hope you enjoyed making the button your own! While it&#8217;s much more flexible now, there might be other features we could add down the road, so stay tuned!</p>



<p id="block-59a92dbb-c0d1-4b75-b250-f331b6ceb4c4">More in this series:</p>



<ul id="block-c7594468-ed0c-4be1-a999-04ef9789f34c" class="wp-block-list">
<li><a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo</a></li>



<li><a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 2</a></li>



<li><a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</a></li>



<li><a href="https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/" target="_blank" rel="noreferrer noopener">How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</a></li>



<li><a href="https://blog.xojo.com/2025/10/08/how-to-create-a-custom-button-control-in-xojo-part-5-adding-text-alignment/" target="_blank" rel="noreferrer noopener">How to Create a Custom Button Control in Xojo &#8211; Part 5: Adding Text Alignment</a></li>
</ul>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To Create a Custom Button Control in Xojo</title>
		<link>https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Fri, 02 May 2025 14:00:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Custom Button Design]]></category>
		<category><![CDATA[Custom Classes]]></category>
		<category><![CDATA[Custom Control Design]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14858</guid>

					<description><![CDATA[This tutorial demonstrates how to create a custom button control in Xojo using the&#160;DesktopCanvas&#160;class. It covers defining properties, handling mouse events for visual feedback, and&#8230;]]></description>
										<content:encoded><![CDATA[
<p>This tutorial demonstrates how to create a custom button control in Xojo using the&nbsp;<code>DesktopCanvas</code>&nbsp;class. It covers defining properties, handling mouse events for visual feedback, and drawing the control in the&nbsp;<code>Paint</code>&nbsp;event.</p>



<p>You can read more about <code>DesktopCanvas</code> <a href="https://documentation.xojo.com/api/user_interface/desktop/desktopcanvas.html" target="_blank" rel="noreferrer noopener">in the Xojo Docs.</a></p>



<p>Let&#8217;s begin!</p>



<h3 class="wp-block-heading" id="add-a-custom-class">Add a Custom Class:</h3>



<ul class="wp-block-list">
<li>In the Project Navigator, add a new&nbsp;<strong>Class</strong>.</li>



<li>Name it&nbsp;<code>CanvasButton</code>.</li>



<li>Set its&nbsp;<strong>Super</strong>&nbsp;class to&nbsp;<code>DesktopCanvas</code>.</li>
</ul>



<h4 class="wp-block-heading" id="add-properties">Add Properties:</h4>



<ul class="wp-block-list">
<li><code>ButtonText As String</code>&nbsp;(Set Scope to&nbsp;<code>Public</code>, Default Value to&nbsp;<code>"Click Me"</code>. To make this property visible in the Inspector, right-click the&nbsp;<code>CanvasButton</code>&nbsp;class in the Project Navigator, select ‘Inspector Behaviour…’, scroll through the property list to find the&nbsp;<code>ButtonText</code>&nbsp;variable, and make sure its checkbox is checked.)</li>



<li><code>IsHovered As Boolean</code>&nbsp;(Set Scope to&nbsp;<code>Private</code>, Default Value to&nbsp;<code>False</code>)</li>



<li><code>IsPressed As Boolean</code>&nbsp;(Set Scope to&nbsp;<code>Private</code>, Default Value to&nbsp;<code>False</code>)</li>
</ul>



<h4 class="wp-block-heading" id="define-pressed-custom-event">Define&nbsp;<code>Pressed</code>&nbsp;Custom Event:</h4>



<ul class="wp-block-list">
<li>Go to&nbsp;<strong>Insert &gt; Event Definition</strong>.</li>



<li>Name the event&nbsp;<code>Pressed</code>. This is the event that will be raised when the button is clicked.</li>
</ul>



<h4 class="wp-block-heading" id="add-event-handlers">Add Event Handlers:</h4>



<ul class="wp-block-list">
<li>Select the&nbsp;<code>CanvasButton</code>&nbsp;class in the Project Navigator.</li>



<li>Go to&nbsp;<strong>Insert &gt; Event Handler</strong>. Add the following event handlers and paste the corresponding code into each:</li>
</ul>



<p><strong><code>MouseDown(x As Integer, y As Integer)</code></strong></p>



<pre class="wp-block-code"><code>// Set internal state to indicate the button is being pressed.
IsPressed = True
// Refresh the control to show the pressed state visually.
Me.Refresh(False)
// Return True to indicate that this event was handled.
Return True</code></pre>



<p><strong><code>MouseEnter</code></strong></p>



<pre class="wp-block-code"><code>// Set internal state to indicate the mouse is hovering over the button.
IsHovered = True
// Refresh the control to show the hover state visually.
Me.Refresh(False)</code></pre>



<p><strong><code>MouseExit</code></strong></p>



<pre class="wp-block-code"><code>// Set internal state to indicate the mouse is no longer hovering.
IsHovered = False
// Refresh the control to revert from the hover state.
Me.Refresh(False)</code></pre>



<p><strong><code>MouseUp(x As Integer, y As Integer)</code></strong></p>



<pre class="wp-block-code"><code>// Check if the button was pressed down AND the mouse is still hovering over it.
If IsPressed And IsHovered Then
   // If true, the button was successfully clicked. Raise the custom Pressed event.
  RaiseEvent Pressed
End If
// Reset the pressed state regardless of whether the click was successful.
IsPressed = False
// Refresh the control to revert from the pressed state.
Me.Refresh(False)</code></pre>



<p><strong><code>Paint(g As Graphics, areas() As Rect)</code></strong></p>



<pre class="wp-block-code"><code>// Corner radius of the button shape.
Static CornerRadius As Integer = 4

 // Declare variables for the colors used in drawing.
Var bgColor As Color
Var borderColor As Color = Color.DarkBevelColor
Var TextColor As Color = Color.LightTingeColor

 // Determine the background color based on the button's current state (pressed or hovered).
If IsPressed Or IsHovered Then
  // Use a highlight color if pressed or hovered.
  bgColor = Color.HighlightColor
Else
  // Use the accent theme color for the default state.
  bgColor = Color.AccentThemeColor
End If

// Set the drawing color and draw the background shape with rounded corners.
g.DrawingColor = bgColor
g.FillRoundRectangle(0, 0, g.Width, g.Height, CornerRadius, CornerRadius)

// Set the drawing color and pen size for the border.
g.DrawingColor = borderColor
g.PenSize = 2
// Draw the border shape just inside the background rectangle.
g.DrawRoundRectangle(1, 1, g.Width-2, g.Height-2, CornerRadius, CornerRadius)

// Enable anti-aliasing for smoother text rendering.
g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
g.AntiAliased = True
// Calculate the width and height of the button text.
Var tw As Double = g.TextWidth(ButtonText)
Var th As Double = g.TextHeight
// Calculate the X position to center the text horizontally.
Var tx As Double = (g.Width - tw) / 2
 // Calculate the Y position to center the text vertically, with a small adjustment.
Var ty As Double = (g.Height + th) / 2 - 3
// Set the drawing color for the text.
g.DrawingColor = TextColor
// Draw the button text at the calculated centered position.
g.DrawText(ButtonText, tx, ty)</code></pre>



<p><strong>Use the Custom Control:</strong></p>



<ul class="wp-block-list">
<li>Open&nbsp;<code>Window1</code>.</li>



<li>Drag the&nbsp;<code>CanvasButton</code>&nbsp;class from the Project Navigator onto the window.</li>
</ul>



<p><strong>Handle the Click Event:</strong></p>



<ul class="wp-block-list">
<li>Double-click the&nbsp;<code>CanvasButton</code>&nbsp;instance on the window layout.</li>



<li>Add the custom&nbsp;<code>Pressed</code>&nbsp;event handler.</li>



<li>Add code inside this handler:&nbsp;<code>MessageBox("Custom Button Clicked!")</code>.</li>
</ul>



<h4 class="wp-block-heading" id="run-the-project">Run the Project:</h4>



<ul class="wp-block-list">
<li>Run the project. Test the button by hovering, pressing, and clicking to see the visual feedback and trigger the&nbsp;<code>Pressed</code>&nbsp;event.</li>
</ul>



<p>And just like that, you’ve built your own custom button! Hope you had fun following along. Keep an eye out for our next tutorials, where we’ll take this button and give it some awesome upgrades!</p>



<p>This project can be downloaded from GitHub at: <a href="https://github.com/xolabsro/CanvasButton" target="_blank" rel="noreferrer noopener">https://github.com/xolabsro/CanvasButton</a></p>



<p id="block-59a92dbb-c0d1-4b75-b250-f331b6ceb4c4">More in this series:</p>



<ul id="block-c7594468-ed0c-4be1-a999-04ef9789f34c" class="wp-block-list">
<li><a href="https://blog.xojo.com/2025/05/02/how-to-create-a-custom-button-control-in-xojo/">How To Create a Custom Button Control in Xojo</a></li>



<li><a href="https://blog.xojo.com/2025/05/14/how-to-create-a-custom-button-control-in-xojo-part-2/">How To Create a Custom Button Control in Xojo – Part 2</a></li>



<li><a href="https://blog.xojo.com/2025/05/28/how-to-create-a-custom-button-control-in-xojo-part-3-make-your-controls-inspector-friendly/">How To Create a Custom Button Control in Xojo – Part 3: Make Your Controls Inspector-Friendly</a></li>



<li><a href="https://blog.xojo.com/2025/06/23/how-to-create-a-custom-button-control-in-xojo-part-4-adding-focus/">How To Create a Custom Button Control in Xojo – Part 4: Adding Focus</a></li>
</ul>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Create a Tic-Tac-Toe Game in Xojo &#8211; Step-by-Step Tutorial</title>
		<link>https://blog.xojo.com/2024/12/05/create-a-tic-tac-toe-game-in-xojo-step-by-step-tutorial/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Thu, 05 Dec 2024 15:30:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Custom Controls]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<category><![CDATA[Games]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=13994</guid>

					<description><![CDATA[Tic-tac-toe, a classic two-player strategy game where players take turns marking spaces in a 3&#215;3 grid. The objective is simple: be the first to get&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Tic-tac-toe, a classic two-player strategy game where players take turns marking spaces in a 3&#215;3 grid. The objective is simple: be the first to get three of your symbols (X or O) in a row, either horizontally, vertically, or diagonally.</p>



<p>By the end of this tutorial, you will learn how to:</p>



<ul class="wp-block-list">
<li>Create a desktop game application in Xojo</li>



<li>Use DesktopCanvas for game board rendering</li>



<li>Implement game logic and state management</li>



<li>Handle user interactions</li>



<li>Add visual effects and animations</li>
</ul>


<div class="wp-block-image is-style-default">
<figure class="aligncenter is-resized"><img decoding="async" src="https://storage.googleapis.com/co-writer/images/HRIwK4xjWLXvNRyAbzXbq5t8wJF3/-1731493084073.webp" alt="Modern TicTacToe game in Xojo" style="width:482px;height:auto"/></figure>
</div>


<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Setting Up the Project</h2>



<h3 class="wp-block-heading">Launch Xojo and Create a New Project</h3>



<ul class="wp-block-list">
<li>Open Xojo IDE</li>



<li>Select &#8220;Desktop&#8221; project type</li>



<li>Set an Application Name (e.g., &#8220;TicTacToe&#8221;)</li>



<li>Click &#8220;Create&#8221;</li>
</ul>



<h3 class="wp-block-heading">Creating the TicTacToeGame Custom Control</h3>



<p>To have a clear and modular code structure, the game logic and user interface will be implemented in a subclassed DesktopCanvas control.</p>



<p>Here are several key advantages for this approach:</p>



<ul class="wp-block-list">
<li><strong>Encapsulation:</strong> It neatly bundles all the game&#8217;s logic (like checking for wins or handling player turns) and visual elements (drawing the board, animating moves) into a single, self-contained unit. This makes your code cleaner, easier to understand, and simpler to maintain. You can reuse this control in other projects without rewriting everything.</li>



<li><strong>Organization:</strong> A custom control promotes better code organization by separating the game&#8217;s functionality from the rest of your application&#8217;s code. This reduces complexity, especially if your application grows larger and includes other features.</li>



<li><strong>Reusability:</strong> Once you&#8217;ve created the TicTacToeGame control, you can easily reuse it in other Xojo projects. Just drag and drop it onto a window!</li>



<li><strong>Abstraction:</strong> The custom control provides an abstraction layer. The rest of your application doesn&#8217;t need to know the internal workings of the TicTacToe game; it only needs to interact with the control&#8217;s interface (like starting a new game or getting the current score). This makes it easier to modify or update the game logic without affecting other parts of your application.</li>
</ul>



<p>Now, here are the steps to create this custom control based on DesktopCanvas:</p>



<ol class="wp-block-list">
<li>Click the &#8220;Insert&#8221; menu</li>



<li>Select &#8220;Class&#8221;</li>



<li>Name the class &#8220;TicTacToeGame&#8221;</li>



<li>Set the &#8220;Super&#8221; to &#8220;DesktopCanvas&#8221;</li>
</ol>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="618" height="342" src="https://blog.xojo.com/wp-content/uploads/2024/11/image.png" alt="" class="wp-image-13998" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/image.png 618w, https://blog.xojo.com/wp-content/uploads/2024/11/image-300x166.png 300w" sizes="auto, (max-width: 618px) 100vw, 618px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="596" height="236" src="https://blog.xojo.com/wp-content/uploads/2024/11/image-1.png" alt="" class="wp-image-13999" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/image-1.png 596w, https://blog.xojo.com/wp-content/uploads/2024/11/image-1-300x119.png 300w" sizes="auto, (max-width: 596px) 100vw, 596px" /></figure>
</div>
</div>



<h2 class="wp-block-heading">TicTacToeGame Class Structure</h2>


<div class="wp-block-image">
<figure class="alignright size-full is-resized"><img loading="lazy" decoding="async" width="554" height="950" src="https://blog.xojo.com/wp-content/uploads/2024/11/image-2.png" alt="" class="wp-image-14001" style="width:263px;height:auto" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/image-2.png 554w, https://blog.xojo.com/wp-content/uploads/2024/11/image-2-175x300.png 175w" sizes="auto, (max-width: 554px) 100vw, 554px" /></figure>
</div>


<p>First, we start by defining some important constants and properties that will be used by the game class.</p>



<h3 class="wp-block-heading">Constants</h3>



<ol class="wp-block-list">
<li><code>Private Const kBoardSize as Number = 3</code>
<ul class="wp-block-list">
<li>Defines the grid dimensions (3&#215;3)</li>



<li>Used for iterating through board cells</li>



<li>Provides flexibility for potential future grid size changes</li>
</ul>
</li>



<li><code>Private Const kCellsPadding as Number = 40</code>
<ul class="wp-block-list">
<li>Controls spacing around X and O symbols</li>



<li>Ensures symbols don&#8217;t touch cell borders</li>



<li>Provides visual breathing room in cell drawings</li>
</ul>
</li>
</ol>



<h3 class="wp-block-heading">Properties</h3>



<h4 class="wp-block-heading">Game State Properties</h4>



<ol class="wp-block-list">
<li><code>Public Property boardState(2,2) As Integer</code>
<ul class="wp-block-list">
<li>2D array representing game board</li>



<li>Values:
<ul class="wp-block-list">
<li>0 = Empty cell</li>



<li>1 = Player X</li>



<li>2 = Player O</li>
</ul>
</li>
</ul>
</li>



<li><code>Public Property currentPlayer As Integer = 1</code>
<ul class="wp-block-list">
<li>Tracks current turn</li>



<li>1 = Player X</li>



<li>2 = Player O</li>
</ul>
</li>



<li><code>Public Property isGameOver As Boolean = False</code>
<ul class="wp-block-list">
<li>Indicates game completion status</li>



<li>Prevents further moves after game ends</li>
</ul>
</li>
</ol>



<h4 class="wp-block-heading">Rendering Properties</h4>



<pre class="wp-block-code"><code>Public Property CellHeight As Integer
Get
  Return Height / 3
End Get

Set
  
End Set
End Property

Public Property CellWidth As Integer
Get
  Return Width / 3
End Get

Set
  
End Set
End Property</code></pre>



<ol class="wp-block-list">
<li><code>CellHeight</code>, <code>CellWidth As Integer</code>
<ul class="wp-block-list">
<li>Computed properties</li>



<li>Dynamically calculate cell dimensions based on canvas size</li>



<li>Divide width/height by 3 for equal grid cells</li>
</ul>
</li>



<li><code>ColorBoard</code>, <code>ColorX</code>, <code>ColorO</code><code>As Color</code>
<ul class="wp-block-list">
<li>Store color schemes for board elements</li>



<li>Support dark/light mode themes</li>
</ul>
</li>
</ol>



<h4 class="wp-block-heading">Animation Properties</h4>



<ol class="wp-block-list">
<li><code>Public Property animationProgress As Double</code>
<ul class="wp-block-list">
<li>Tracks symbol drawing animation</li>



<li>Ranges from 0 to 1</li>



<li>Controls symbol scaling during placement</li>
</ul>
</li>



<li><code>Public Property animationTimer As Timer</code>
<ul class="wp-block-list">
<li>Manages animation timing</li>



<li>Triggers smooth symbol rendering</li>
</ul>
</li>
</ol>



<h4 class="wp-block-heading">Interaction Tracking</h4>



<ol class="wp-block-list">
<li><code>Public Property HoverCol As Integer = -1</code>and <code>Public Property HoverCol As Integer = -1</code>
<ul class="wp-block-list">
<li>Track mouse position over grid</li>



<li>Enable hover effect on empty cells</li>



<li>Provide visual feedback during gameplay</li>
</ul>
</li>
</ol>



<h4 class="wp-block-heading">Scoring Properties</h4>



<ol class="wp-block-list">
<li><code>scoreX</code> and <code>scoreO</code><code>As Integer</code>
<ul class="wp-block-list">
<li>Track win counts for each player</li>



<li>Updated after each game</li>
</ul>
</li>
</ol>



<p>The constants and properties defined above will be crucial in the next steps, allowing us to implement the key features: encapsulation of game logic and rendering, flexible customization, responsive dynamic sizing and interactions, and an enhanced user experience through animations and hover effects.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Event Definitions</h2>


<div class="wp-block-image">
<figure class="alignright size-full is-resized"><img loading="lazy" decoding="async" width="552" height="236" src="https://blog.xojo.com/wp-content/uploads/2024/11/image-3.png" alt="" class="wp-image-14003" style="width:263px;height:auto" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/image-3.png 552w, https://blog.xojo.com/wp-content/uploads/2024/11/image-3-300x128.png 300w" sizes="auto, (max-width: 552px) 100vw, 552px" /></figure>
</div>


<p>To make sure our game class is complete, we will create two custom event definitions that will be used later throughout the game.</p>



<h3 class="wp-block-heading">GameStatus Event</h3>



<pre class="wp-block-code"><code>Event GameStatus(info As String, playerTurn As String = "", scoreX As Integer, scoreO As Integer)</code></pre>



<ul class="wp-block-list">
<li>Purpose: Tracks and communicates the current state of the game</li>



<li>Parameters:
<ol class="wp-block-list">
<li><code>info</code>: A string describing the current game status</li>



<li><code>playerTurn</code>: Optional parameter indicating which player&#8217;s turn it is</li>



<li><code>scoreX</code>: Current score for Player X</li>



<li><code>scoreO</code>: Current score for Player O</li>
</ol>
</li>
</ul>



<h3 class="wp-block-heading">GameOver Event</h3>



<pre class="wp-block-code"><code>Event GameOver(result As String, scoreX As Integer, scoreO As Integer)</code></pre>



<ul class="wp-block-list">
<li>Purpose: Signals the conclusion of the game with a winner or draw</li>



<li>Parameters:
<ol class="wp-block-list">
<li><code>result</code>: A string describing the game&#8217;s final outcome (e.g., &#8220;X Wins&#8221;, &#8220;O Wins&#8221;, &#8220;Draw&#8221;)</li>



<li><code>scoreX</code>: Final score for Player X</li>



<li><code>scoreO</code>: Final score for Player O</li>
</ol>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Event Handlers in Tic-Tac-Toe Game</h2>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="542" height="458" src="https://blog.xojo.com/wp-content/uploads/2024/11/image-4.png" alt="" class="wp-image-14005" style="width:222px;height:auto" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/image-4.png 542w, https://blog.xojo.com/wp-content/uploads/2024/11/image-4-300x254.png 300w" sizes="auto, (max-width: 542px) 100vw, 542px" /></figure>



<h3 class="wp-block-heading">Closing Event</h3>



<pre class="wp-block-code"><code>Sub Closing() Handles Closing
  // This event ensures that the animation timer is properly disabled and its handler is removed to prevent memory leaks or unexpected behavior.
  // Clean up the animation timer when the control is closing
  If animationTimer &lt;&gt; Nil Then
    animationTimer.Enabled = False
    RemoveHandler animationTimer.Action, AddressOf AnimationStep
    animationTimer = Nil
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Ensures clean resource management</li>



<li>Disables and removes timer to prevent memory leaks</li>



<li>Called when the control is being destroyed</li>
</ul>



<h3 class="wp-block-heading">MouseDown Event</h3>



<pre class="wp-block-code"><code>Function MouseDown(x As Integer, y As Integer) Handles MouseDown as Boolean
  // This event handles mouse click actions on the game board.
  // It checks if the game is over; if not, it calculates the row and column of the click.
  // If the clicked cell is empty, it records the player's move, checks for a winner, and toggles the current player.
  
  // Handle mouse clicks on the game board
  If isGameOver Then
    Return True
  End If
  
  // Calculate the row and column based on the click position
  Var row As Integer = y \ CellHeight
  Var col As Integer = x \ CellWidth
  
  // If the clicked cell is empty, make a move
  If boardState(row, col) = 0 Then
    boardState(row, col) = currentPlayer
    StartAnimation(row, col)
    
    // Reset hover position after a move
    hoverRow = -1
    hoverCol = -1
    
    // Refresh only the affected cell
    Refresh(col * CellWidth, row * CellHeight, CellWidth, CellHeight)
    
    // Check for a winner or a draw
    Var winner As Integer = CheckWinner()
    If winner &gt; 0 Then
      UpdateScore(winner)
      GameStatus("Player " + PlayerSymbol(winner) + " wins!", scoreX, scoreO)
      isGameOver = True
      GameOver("Player " + PlayerSymbol(winner) + " wins!", scoreX, scoreO)
      
      // Refresh the entire board to show the winning line
      Refresh(True)
    ElseIf Me.IsBoardFull() Then
      GameStatus("It's a draw!", scoreX, scoreO)
      isGameOver = True
      GameOver("Draw", scoreX, scoreO)
    Else
      // Switch to the other player
      currentPlayer = If(currentPlayer = 1, 2, 1)
      GameStatus("Player " + PlayerSymbol(currentPlayer) + "'s turn", PlayerSymbol(currentPlayer), scoreX, scoreO)
    End If
  End If
  
  Return True
End Function</code></pre>



<ul class="wp-block-list">
<li>Handles player moves</li>



<li>Validates move legality</li>



<li>Checks for win/draw conditions</li>



<li>Switches players</li>
</ul>



<h3 class="wp-block-heading">MouseExit and MouseMove Events</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Sub MouseExit() Handles MouseExit
  // This event is triggered when the mouse cursor exits the game board area.
  // It clears the hover effect to avoid leaving any visual artifacts on the board when the mouse is moved away.
  
  If hoverRow &gt;= 0 And hoverRow &lt; kBoardSize And hoverCol &gt;= 0 And hoverCol &lt; kBoardSize Then
    Var oldHoverRow As Integer = hoverRow
    Var oldHoverCol As Integer = hoverCol
    
    hoverRow = -1
    hoverCol = -1
    
    // Refresh only the cell that was previously hovered
    Refresh(oldHoverCol * CellWidth, oldHoverRow * CellHeight, CellWidth, CellHeight)
  End If
End Sub</code></pre>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Sub MouseMove(x As Integer, y As Integer) Handles MouseMove
  // This event handles mouse movement over the game board.
  // It updates the hover effect when the mouse moves to a new cell, providing visual feedback.
  
  If Not isGameOver Then
    Var newHoverRow As Integer = y \ CellHeight
    Var newHoverCol As Integer = x \ CellWidth
    
    If newHoverRow &lt;&gt; hoverRow Or newHoverCol &lt;&gt; hoverCol Then
      Var oldHoverRow As Integer = hoverRow
      Var oldHoverCol As Integer = hoverCol
      
      hoverRow = newHoverRow
      hoverCol = newHoverCol
      
      // Refresh the old hover cell (if it was valid)
      If oldHoverRow &gt;= 0 And oldHoverRow &lt; 3 And oldHoverCol &gt;= 0 And oldHoverCol &lt; 3 Then
        Refresh(oldHoverCol * CellWidth, oldHoverRow * CellHeight, CellWidth, CellHeight)
      End If
      
      // Refresh the new hover cell
      Refresh(hoverCol * CellWidth, hoverRow * CellHeight, CellWidth, CellHeight)
    End If
  End If
End Sub</code></pre>
</div>
</div>



<ul class="wp-block-list">
<li>Provides visual hover feedback</li>



<li>Tracks mouse movement across grid</li>



<li>Refreshes only changed cells</li>
</ul>



<h3 class="wp-block-heading">Opening Event</h3>



<pre class="wp-block-code"><code>Sub Opening() Handles Opening
  // This event initializes the board colors based on the current system theme (dark mode or light mode) and starts a new game.
  
  // Set up colors of the board lines, for the X's and O's
  If Color.IsDarkMode = True Then
    ColorBoard = Color.RGB(178, 161, 149)
    ColorX = Color.RGB(228, 182, 88)
    ColorO = Color.RGB(253, 161, 97)
  Else
    ColorBoard = Color.RGB(130, 110, 92)
    ColorX = Color.RGB(228, 182, 88)
    ColorO = Color.RGB(253, 161, 97)
  End If
  
  NewGame()
End Sub</code></pre>



<ul class="wp-block-list">
<li>Initializes color theme</li>



<li>Supports dark and light modes</li>



<li>Starts a new game automatically</li>
</ul>



<h3 class="wp-block-heading">Paint Event</h3>



<pre class="wp-block-code"><code>Sub Paint(g As Graphics, areas() As Rect) Handles Paint
  // This event is responsible for drawing the game board, hover effects, player symbols (X's and O's), and the winning line.
  // It is called whenever the game board needs to be redrawn.
  
  // Draw the board
  g.DrawingColor = ColorBoard
  g.DrawLine(Width/3, 0, Width/3, Height)
  g.DrawLine(2*Width/3, 0, 2*Width/3, Height)
  g.DrawLine(0, Height/3, Width, Height/3)
  g.DrawLine(0, 2*Height/3, Width, 2*Height/3)
  
  // Draw hover effect
  DrawHoverEffect(g)
  
  // Draw X's and O's
  g.PenSize = 8
  For row As Integer = 0 To 2
    For col As Integer = 0 To 2
      If boardState(row, col) = 1 Then
        DrawX(g, row, col)
      ElseIf boardState(row, col) = 2 Then
        DrawO(g, row, col)
      End If
    Next
  Next
  
  // Draw winning line if the game is over
  If isGameOver Then
    DrawWinningLine(g)
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Renders game board</li>



<li>Draws grid lines</li>



<li>Manages visual game state</li>



<li>Supports dynamic rendering</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Animation and Drawing Methods</h2>



<h3 class="wp-block-heading">1. AnimationStep</h3>



<pre class="wp-block-code"><code>Public Sub AnimationStep(sender As Timer)
  // This method is called by the animation timer (animationTimer property) to progress the animation of a newly placed symbol.
  // It increments the animation progress and stops the timer once the animation is complete.
  
  // Progress the animation
  animationProgress = animationProgress + 0.1
  If animationProgress &gt;= 1 Then
    animationTimer.Enabled = False
    animationProgress = 1
  End If
  
  // Refresh only the cell being animated
  Refresh(lastPlayedCol * CellWidth, lastPlayedRow * CellHeight, CellWidth, CellHeight)
  
  // If the animation is complete, stop the timer
  If animationProgress &gt;= 1 Then
    animationTimer.Enabled = False
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Manages symbol placement animation</li>



<li>Gradually scales symbol from 0 to 1</li>



<li>Updates only the recently played cell</li>
</ul>



<h3 class="wp-block-heading">2. DrawX and DrawO</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Sub MouseExit() Handles MouseExit
  // This event is triggered when the mouse cursor exits the game board area.
  // It clears the hover effect to avoid leaving any visual artifacts on the board when the mouse is moved away.
  
  If hoverRow &gt;= 0 And hoverRow &lt; kBoardSize And hoverCol &gt;= 0 And hoverCol &lt; kBoardSize Then
    Var oldHoverRow As Integer = hoverRow
    Var oldHoverCol As Integer = hoverCol
    
    hoverRow = -1
    hoverCol = -1
    
    // Refresh only the cell that was previously hovered
    Refresh(oldHoverCol * CellWidth, oldHoverRow * CellHeight, CellWidth, CellHeight)
  End If
End Sub</code></pre>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Sub MouseMove(x As Integer, y As Integer) Handles MouseMove
  // This event handles mouse movement over the game board.
  // It updates the hover effect when the mouse moves to a new cell, providing visual feedback.
  
  If Not isGameOver Then
    Var newHoverRow As Integer = y \ CellHeight
    Var newHoverCol As Integer = x \ CellWidth
    
    If newHoverRow &lt;&gt; hoverRow Or newHoverCol &lt;&gt; hoverCol Then
      Var oldHoverRow As Integer = hoverRow
      Var oldHoverCol As Integer = hoverCol
      
      hoverRow = newHoverRow
      hoverCol = newHoverCol
      
      // Refresh the old hover cell (if it was valid)
      If oldHoverRow &gt;= 0 And oldHoverRow &lt; 3 And oldHoverCol &gt;= 0 And oldHoverCol &lt; 3 Then
        Refresh(oldHoverCol * CellWidth, oldHoverRow * CellHeight, CellWidth, CellHeight)
      End If
      
      // Refresh the new hover cell
      Refresh(hoverCol * CellWidth, hoverRow * CellHeight, CellWidth, CellHeight)
    End If
  End If
End Sub</code></pre>
</div>
</div>



<ul class="wp-block-list">
<li>Draws X and O symbols with animation</li>



<li>Centers symbol in cell</li>



<li>Scales symbol based on animation progress</li>
</ul>



<h3 class="wp-block-heading">3. DrawHoverEffect</h3>



<pre class="wp-block-code"><code>Public Sub DrawHoverEffect(g As Graphics)
  // This method draws a semi-transparent hover effect over the cell that the mouse is currently hovering over.
  // It only draws the effect if the game is not over and the hovered cell is empty.
  
  If Not isGameOver And hoverRow &gt;= 0 And hoverRow &lt; 3 And hoverCol &gt;= 0 And hoverCol &lt; 3 Then
    If boardState(hoverRow, hoverCol) = 0 Then
      g.DrawingColor = Color.RGB(255, 255, 255, 250) // Semi-transparent white
      g.FillRectangle(hoverCol * CellWidth, hoverRow * CellHeight, CellWidth, CellHeight)
    End If
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Provides visual feedback on hoverable cells</li>



<li>Applies semi-transparent white overlay</li>



<li>Only affects empty, unplayed cells</li>
</ul>



<h3 class="wp-block-heading">4. DrawWinningLine</h3>



<pre class="wp-block-code"><code>Public Sub DrawWinningLine(g As Graphics)
  // This method draws a semi-transparent green line over the winning combination on the board if a player has won.
  
  // Draw the winning line if there is a winner
  If winningLine.Count = 6 Then
    g.DrawingColor = Color.RGB(0, 255, 0, 128) // Semi-transparent green
    g.PenSize = 2
    
    Var startX As Integer = winningLine(1) * cellWidth + cellWidth / 2
    Var startY As Integer = winningLine(0) * cellHeight + cellHeight / 2
    Var endX As Integer = winningLine(5) * cellWidth + cellWidth / 2
    Var endY As Integer = winningLine(4) * cellHeight + cellHeight / 2
    
    g.DrawLine(startX, startY, endX, endY)
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Draws a semi-transparent green line</li>



<li>Highlights the winning combination</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Game Logic Methods</h2>



<h3 class="wp-block-heading">1. CheckWinner</h3>



<pre class="wp-block-code"><code>Public Function CheckWinner() As Integer
  // This method checks the board for a winner by evaluating rows, columns, and diagonals.
  // It returns the winning player (1 or 2) or 0 if there is no winner.
  
  
  // Check rows
  For i As Integer = 0 To kBoardSize - 1
    If boardState(i, 0) &lt;&gt; 0 And boardState(i, 0) = boardState(i, 1) And boardState(i, 1) = boardState(i, 2) Then
      winningLine = Array(i, 0, i, 1, i, 2)
      Return boardState(i, 0)
    End If
  Next
  
  // Check columns
  For j As Integer = 0 To 2
    If boardState(0, j) &lt;&gt; 0 And boardState(0, j) = boardState(1, j) And boardState(1, j) = boardState(2, j) Then
      winningLine = Array(0, j, 1, j, 2, j)
      Return boardState(0, j)
    End If
  Next
  
  // Check diagonals
  If boardState(0, 0) &lt;&gt; 0 And boardState(0, 0) = boardState(1, 1) And boardState(1, 1) = boardState(2, 2) Then
    winningLine = Array(0, 0, 1, 1, 2, 2)
    Return boardState(0, 0)
  End If
  
  If boardState(0, 2) &lt;&gt; 0 And boardState(0, 2) = boardState(1, 1) And boardState(1, 1) = boardState(2, 0) Then
    winningLine = Array(0, 2, 1, 1, 2, 0)
    Return boardState(0, 2)
  End If
  
  winningLine.ResizeTo(-1)
  Return 0 // No winner yet
End Function</code></pre>



<ul class="wp-block-list">
<li>Scans board for winning combinations</li>



<li>Returns winning player or 0</li>



<li>Stores winning line coordinates</li>
</ul>



<h3 class="wp-block-heading">2. IsBoardFull</h3>



<pre class="wp-block-code"><code>Public Function IsBoardFull() As Boolean
  // This method checks if the board is completely filled with no empty cells.
  // It returns true if the board is full, otherwise false.
  
  // Check if the board is full (no empty cells)
  For i As Integer = 0 To kBoardSize - 1
    For j As Integer = 0 To kBoardSize - 1
      If boardState(i, j) = 0 Then
        Return False
      End If
    Next
  Next
  Return True
End Function</code></pre>



<ul class="wp-block-list">
<li>Checks if all cells are occupied</li>



<li>Determines if game is a draw</li>
</ul>



<h3 class="wp-block-heading">3. NewGame</h3>



<pre class="wp-block-code"><code>Public Sub NewGame()
  // This method resets the game state to start a new game.
  // It clears the board, resets the current player to Player 1, and updates the game status.
  
  // Reset the game state for a new game
  For i As Integer = 0 To 8
    boardState(i \ 3, i Mod 3) = 0
  Next
  currentPlayer = 1
  isGameOver = False
  animationProgress = 1
  If animationTimer &lt;&gt; Nil Then
    animationTimer.Enabled = False
  End If
  GameStatus("Player " + PlayerSymbol(currentPlayer) + "'s turn", scoreX, scoreO)
  winningLine.ResizeTo(-1)
  Refresh()
End Sub</code></pre>



<ul class="wp-block-list">
<li>Resets game to initial state</li>



<li>Clears board</li>



<li>Resets player turn</li>
</ul>



<h3 class="wp-block-heading">4. UpdateScore</h3>



<pre class="wp-block-code"><code>Public Sub UpdateScore(winner As Integer)
  // This method updates the score for the winning player by incrementing the respective score counter.
  
  // Update the score for the winning player
  If winner = 1 Then
    scoreX = scoreX + 1
  ElseIf winner = 2 Then
    scoreO = scoreO + 1
  End If
End Sub</code></pre>



<ul class="wp-block-list">
<li>Increments score for winning player</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Utility Methods</h2>



<h3 class="wp-block-heading">1. PlayerSymbol</h3>



<pre class="wp-block-code"><code>Public Function PlayerSymbol(player As Integer) As String
  // This method returns the symbol ('X' or 'O') corresponding to the player number (1 or 2).
  Return If(player = 1, "X", "O")
End Function</code></pre>



<ul class="wp-block-list">
<li>Converts player number to symbol</li>
</ul>



<h3 class="wp-block-heading">2. StartAnimation</h3>



<pre class="wp-block-code"><code>Public Sub StartAnimation(row As Integer, col As Integer)
  // This method initializes and starts the animation for a newly placed symbol,
  // by setting the target cell and resetting the animation progress.
  
  // Start the animation for a newly placed symbol
  lastPlayedRow = row
  lastPlayedCol = col
  animationProgress = 0
  
  If animationTimer = Nil Then
    animationTimer = New Timer
    animationTimer.Period = 16 // equivalent of 60 FPS
    AddHandler animationTimer.Action, AddressOf AnimationStep
  End If
  
  animationTimer.Enabled = True
  animationTimer.RunMode = Timer.RunModes.Multiple
End Sub</code></pre>



<ul class="wp-block-list">
<li>Initializes symbol animation</li>



<li>Sets up timer for smooth rendering</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Usage Instructions</h2>



<p>To use the <code>TicTacToeGame</code> control in your Xojo project:</p>



<ol class="wp-block-list">
<li><strong>Drag and Drop:</strong> Drag the <code>TicTacToeGame</code> icon (it should look like a small canvas) from the Navigator (the left side of the IDE) onto your <code>Window1</code> or any other window you want to use.
<ul class="wp-block-list">
<li>A <code>TicTacToeGame</code> instance will appear on the window. You can resize and position it as needed.</li>
</ul>
</li>



<li><strong>Initialize:</strong> While not strictly required, you might want to initialize the game in the <code>Window1.Open</code> event handler. This ensures the game is ready to play as soon as the window opens. You can do this by adding the following code to the <code>Window1.Open</code> event:<br><code>// Assuming 'TicTacToeGame1' is the name of your control instance on the window</code><br><code>TicTacToeGame1.NewGame</code><br>This will call the <code>NewGame</code> method of your custom control, setting up the board and starting a new game.</li>



<li><strong>Run the Project:</strong> Run your Xojo project. You should see the TicTacToe board on your window, ready to play!</li>
</ol>



<p>I highly suggest downloading the complete TicTacToe Xojo project for the game. If you find anything unclear, refer to this tutorial for explanations. <a href="https://files.xojo.com/BlogExamples/Tic-Tac-Toe Game.xojo_binary_project">Download the Xojo project</a>.</p>



<h2 class="wp-block-heading">Next Steps:</h2>



<p>Congratulations! You&#8217;ve successfully built a fully functional, modern tic-tac-toe game in Xojo. This project is a great foundation for exploring more advanced game development concepts, animations and how to make custom UI elements based on Xojo&#8217;s powerful <a href="https://documentation.xojo.com/api/user_interface/desktop/desktopcanvas.html#desktopcanvas" data-type="link" data-id="https://documentation.xojo.com/api/user_interface/desktop/desktopcanvas.html#desktopcanvas" target="_blank" rel="noreferrer noopener">DesktopCanvas</a> control.</p>



<p>Now that you understand the basics, here are some ideas to take your TicTacToe game to the next level:</p>



<ul class="wp-block-list">
<li><strong>Artificial Intelligence (AI):</strong> Implement a simple AI opponent so players can play against the computer. You could start with a random move generator and then explore more sophisticated algorithms like Minimax. Xojo already provides fully working AI integration examples.</li>



<li><strong>Different Game Modes:</strong> Add options for different board sizes (e.g., 4&#215;4, 5&#215;5) or variations of tic-tac-toe.</li>



<li><strong>Online Multiplayer:</strong> Enable players to challenge each other online using Xojo&#8217;s networking capabilities.</li>



<li><strong>Enhanced UI/UX:</strong> Improve the user interface with custom graphics, sound effects, and a more polished look and feel. Consider adding a timer.</li>



<li><strong>Go Multiplatform</strong>: Port the TicTacToeGame class to Mobile and Web projects.</li>
</ul>



<p>We encourage you to experiment, get creative, and explore these possibilities. Share your creations and connect with other Xojo developers in <a target="_blank" rel="noreferrer noopener" href="https://forum.xojo.com/">the forums</a> to learn and grow together. Happy coding!</p>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Beyond Static UI: Create Dynamic Loading Animations with Xojo Graphics</title>
		<link>https://blog.xojo.com/2024/11/05/beyond-static-ui-create-dynamic-loading-animations-with-xojo-graphics/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Tue, 05 Nov 2024 15:10:24 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Animation]]></category>
		<category><![CDATA[Custom Controls]]></category>
		<category><![CDATA[DesktopCanvas]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Phase-Animation]]></category>
		<category><![CDATA[UI-Design]]></category>
		<category><![CDATA[Visual Feedback]]></category>
		<category><![CDATA[Xojo-Graphics]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=13975</guid>

					<description><![CDATA[Ever wondered how to create those sleek, professional-looking loading animations you see in modern apps? In this blog post, we&#8217;re going to unlock the power&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Ever wondered how to create those sleek, professional-looking loading animations you see in modern apps?</p>



<p>In this blog post, we&#8217;re going to unlock the power of Xojo&#8217;s DesktopCanvas and its Graphics capabilities to recreate one of the popular <a href="https://tobiasahlin.com/spinkit/">SpinKit loading animations originally created by Tobias Ahlin</a>. We&#8217;ll be implementing the &#8220;Three Bounce&#8221; animation, transforming this beloved CSS animation into native Xojo code!</p>



<p>Not only will you learn how to create this specific animation, but you&#8217;ll also gain valuable insights into animation principles in Xojo that you can use to create your own custom animations. By the end of this tutorial, you&#8217;ll understand how to:</p>



<ul class="wp-block-list">
<li>Harness the power of DesktopCanvas for custom graphics</li>



<li>Create smooth animations using Timers</li>



<li>Implement phase-based animations</li>



<li>Use Graphics methods to draw dynamic shapes</li>



<li>Manage animation lifecycles properly</li>
</ul>



<h3 class="wp-block-heading">Why This Matters</h3>



<p>Loading animations are more than just eye candy – they&#8217;re essential UI elements that keep users engaged while your app processes data. Instead of using static images or third-party libraries, creating your own animations gives you complete control over the look and feel of your application while keeping your dependencies minimal.</p>



<h3 class="wp-block-heading">What We&#8217;re Building</h3>



<p>We&#8217;ll create a smooth, professional-looking three-dot loading animation where dots gracefully scale up and down in sequence. The result is a lightweight, customizable loading indicator that you can drop into any Xojo project. Here&#8217;s what makes this implementation special:</p>



<ul class="wp-block-list">
<li>Pure Xojo code &#8211; no external dependencies</li>



<li>Smooth, fluid animation</li>



<li>Easily customizable colors and timing</li>



<li>Efficient resource usage</li>



<li>Clean, object-oriented design</li>
</ul>



<figure class="wp-block-video"><video height="184" style="aspect-ratio: 360 / 184;" width="360" controls loop preload="auto" src="https://blog.xojo.com/wp-content/uploads/2024/10/spinkit_animation.mp4" playsinline></video></figure>



<p>Ok, let&#8217;s start building our awesome animation class.</p>



<h2 class="wp-block-heading">Step 1: Setting Up the Base Class</h2>



<p>First, create a new class (Insert &gt; Class) that inherits from DesktopCanvas:</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://storage.googleapis.com/co-writer/images/HRIwK4xjWLXvNRyAbzXbq5t8wJF3/-1730379923862.webp" alt="IMAGE"/></figure>
</div>


<pre class="wp-block-code"><code>Protected Class SpinKitClass Inherits DesktopCanvas</code></pre>



<h2 class="wp-block-heading">Step 2: Defining Core Properties</h2>



<p>Add these essential properties to control the animation:</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://storage.googleapis.com/co-writer/images/HRIwK4xjWLXvNRyAbzXbq5t8wJF3/-1730380034310.webp" alt="IMAGE"/></figure>
</div>


<pre class="wp-block-code"><code>Private animationPhases() As Double      // Stores phase offsets for each dot
Private animationSpeed As Integer = 60   // Animation refresh rate in milliseconds
Private animationTimer As Timer         // Timer to drive the animation
Private currentPhase As Double = 0.0    // Current animation phase
Private dotSize As Integer = 20         // Size of each dot
Private dotSpacing As Integer = 20      // Space between dots</code></pre>



<h2 class="wp-block-heading">Step 3: Setting Up Constants</h2>



<p>Define the constants that control the animation behavior:</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://storage.googleapis.com/co-writer/images/HRIwK4xjWLXvNRyAbzXbq5t8wJF3/-1730380079168.webp" alt="IMAGE"/></figure>
</div>


<pre class="wp-block-code"><code>Private Const kDefaultColor As Color = &amp;c333333    // Dot color
Private Const kPhaseIncrement As Double = 0.05    // Animation speed</code></pre>



<h2 class="wp-block-heading">Step 4: Implementing Animation Control Methods</h2>



<p>Add methods to start and stop the animation:</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Public Sub StartAnimation()
  /// Initializes and starts the animation
  /// Sets up initial phase offsets for the dots
  /// Creates and configures the animation timer
  
  If animationTimer = Nil Then
    animationPhases = Array(0.0, 0.16, 0.32)
    
    animationTimer = New Timer
    AddHandler animationTimer.Action, AddressOf UpdateAnimation
    animationTimer.Period = animationSpeed
    animationTimer.RunMode = Timer.RunModes.Multiple
  End If
End Sub</code></pre>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<pre class="wp-block-code"><code>Public Sub StopAnimation()
  /// Stops the running animation
  /// Removes the timer handler and cleans up
  /// Refreshes the canvas to clear the animation
  
  If animationTimer &lt;&gt; Nil Then
    RemoveHandler animationTimer.Action, AddressOf UpdateAnimation
    animationTimer = Nil
    Refresh
  End If
End Sub</code></pre>
</div>
</div>



<h2 class="wp-block-heading">Step 5: Creating the Animation Update Logic</h2>



<p>Implement the timer callback that drives the animation:</p>



<pre class="wp-block-code"><code>Private Sub updateAnimation(sender As Timer)
  /// Timer callback that updates the animation state
  /// Increments the phase and triggers a redraw
  /// @param sender The Timer that triggered this callback
  
  #Pragma Unused sender
  
  // Increment the current phase (spin08)
  currentPhase = currentPhase + kPhaseIncrement
  // Reset currentPhase if it exceeds 1.0
  If currentPhase &gt; 1.0 Then currentPhase = 0
  
  // Invalidate the canvas to trigger a repaint
  Refresh()
End Sub</code></pre>



<h2 class="wp-block-heading">Step 6: Implementing the Drawing Logic</h2>



<p>Add the core animation rendering code:</p>



<pre class="wp-block-code"><code>Private Sub animation08(g As Graphics)
  /// Renders a single frame of the three-dot loading animation
  /// Draws 3 dots that scale up and down in sequence
  /// @param g The graphics context to draw into
  
  Var centerX As Integer = Me.Width / 2
  Var centerY As Integer = Me.Height / 2
  
  For i As Integer = 0 To 2
    Var phase As Double = currentPhase + animationPhases(i)
    
    // Ensure phase is within 0 to 1
    If phase &gt; 1.0 Then phase = phase - 1.0
    
    Var scale As Double
    If phase &lt; 0.4 Then
      scale = phase / 0.4
    Else
      scale = (1.0 - phase) / 0.6
    End If
    
    Var size As Integer = dotSize * scale
    Var x As Integer = centerX + (i - 1) * dotSpacing - size / 2
    Var y As Integer = centerY - size / 2
    
    g.DrawingColor = kDefaultColor
    g.FillOval(x, y, size, size)
  Next
End Sub</code></pre>



<h2 class="wp-block-heading">Step 7: Handling the Paint Event</h2>



<p>Implement the Paint event to trigger the animation rendering:</p>



<pre class="wp-block-code"><code>Sub Paint(g As Graphics, areas() As Rect) Handles Paint
  /// Paint event handler that renders the animation
  /// Called automatically when the canvas needs to be redrawn
  /// If animation is running, calls animation08, otherwise clears the canvas
  
  If animationTimer &lt;&gt; Nil Then
    animation08(g)
  Else
    g.ClearRectangle(0, 0, Width, Height)
  End If
End Sub
</code></pre>



<h2 class="wp-block-heading">How It Works</h2>



<ol class="wp-block-list">
<li>The animation uses a phase-based system where each dot is offset in its animation cycle.</li>



<li>The dots scale up and down based on their current phase.</li>



<li>A timer drives the animation by incrementing the phase and triggering redraws.</li>



<li>The dots are positioned horizontally with equal spacing around the center.</li>



<li>Each dot&#8217;s size is calculated based on its current phase in the animation.</li>
</ol>



<h2 class="wp-block-heading">Using the Animation</h2>



<p>To use this animation in your project, all you have to do is simply drag and drop the class on a DesktopWindow and then start or stop the animation when needed.</p>



<pre class="wp-block-code"><code>// Start the animation
SpinKit1.StartAnimation()

// Stop the animation when done
SpinKit1.StopAnimation()</code></pre>



<h2 class="wp-block-heading">Customization</h2>



<p>You can customize the animation by adjusting:</p>



<ul class="wp-block-list">
<li><code>dotSize</code> for larger or smaller dots</li>



<li><code>dotSpacing</code> to change the spread of the dots</li>



<li><code>animationSpeed</code> to make it faster or slower</li>



<li><code>kDefaultColor</code> to change the dot color</li>



<li><code>kPhaseIncrement</code> to adjust the animation speed</li>
</ul>



<h2 class="wp-block-heading">Taking It Further</h2>



<p>The &#8220;Three Bounce&#8221; animation we&#8217;ve created is just one of many amazing animations in the <a href="https://tobiasahlin.com/spinkit/">SpinKit collection</a>. Now that you understand the fundamentals, why not try recreating other SpinKit animations? Here are some exciting challenges to tackle:</p>



<ul class="wp-block-list">
<li>Modify the current animation to use different shapes (squares, rectangles).</li>



<li>Experiment with different timing functions for varied animation effects.</li>



<li>Try implementing the &#8220;Rotating Plane&#8221; or &#8220;Chasing Dots&#8221; animations from SpinKit.</li>



<li>Create your own unique variations by combining what you&#8217;ve learned.</li>



<li>Optimize the class further.</li>
</ul>



<h2 class="wp-block-heading">Share Your Creations!</h2>



<p>Have you created an awesome variation or implemented another SpinKit animation in Xojo? Share it with the community! The <a target="_blank" rel="noreferrer noopener" href="https://forum.xojo.com/">Xojo Forums</a> and social media are great places to showcase your work and inspire others.</p>



<h2 class="wp-block-heading">Resources for Further Exploration</h2>



<ul class="wp-block-list">
<li>Original SpinKit animations by Tobias Ahlin: <a href="https://tobiasahlin.com/spinkit/" target="_blank" rel="noreferrer noopener">https://tobiasahlin.com/spinkit/</a></li>



<li>Xojo Graphics documentation: <a href="https://documentation.xojo.com/api/graphics/graphics.html" target="_blank" rel="noreferrer noopener">https://documentation.xojo.com/api/graphics/graphics.html</a></li>



<li>Xojo DesktopCanvas documentation: <a href="https://documentation.xojo.com/api/user_interface/desktop/desktopcanvas.html" target="_blank" rel="noreferrer noopener">https://documentation.xojo.com/api/user_interface/desktop/desktopcanvas.html</a></li>
</ul>



<p><strong>Remember</strong>: Every great application deserves great animations and now you have the tools to create them! Keep experimenting, keep creating, and most importantly, keep animating!</p>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		<enclosure url="https://blog.xojo.com/wp-content/uploads/2024/10/spinkit_animation.mp4" length="308355" type="video/mp4" />

			</item>
	</channel>
</rss>
