<?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>VB.NET Tutorials &amp; Examples in English</title>
	<atom:link href="https://robbelroot.de/blog/category/general/tutorials-general/vbnet-en/feed/" rel="self" type="application/rss+xml" />
	<link>https://robbelroot.de/blog/category/general/tutorials-general/vbnet-en/</link>
	<description>alias RobbelRoot – Freelance Full Stack Developer .NET</description>
	<lastBuildDate>Wed, 08 Apr 2026 22:31:47 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://robbelroot.de/wp-content/uploads/2020/12/cropped-favicon-32x32.png</url>
	<title>VB.NET Tutorials &amp; Examples in English</title>
	<link>https://robbelroot.de/blog/category/general/tutorials-general/vbnet-en/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>VB.NET Send WhatsApp Message via Cloud API (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-send-whatsapp-message/</link>
					<comments>https://robbelroot.de/blog/vbnet-send-whatsapp-message/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 22:30:55 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20495</guid>

					<description><![CDATA[<p>You can send WhatsApp messages from VB.NET using the official WhatsApp Cloud API and a simple HttpClient request. No third-party library required. This guide covers setup, sending text messages, template messages, handling responses, batch sending, and a Twilio alternative with complete code examples. What you need Before writing any code, &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-send-whatsapp-message/">VB.NET Send WhatsApp Message via Cloud API (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>You can <strong>send WhatsApp messages from VB.NET</strong> using the official WhatsApp Cloud API and a simple <code>HttpClient</code> request. No third-party library required. This guide covers setup, sending text messages, template messages, handling responses, batch sending, and a Twilio alternative with complete code examples.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a notification system?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I have been developing .NET applications professionally for over 17 years. From WhatsApp integrations to complete desktop solutions, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What you need</h2>



<p>Before writing any code, you need three things from Meta:</p>



<ol class="wp-block-list">
<li><strong>Meta Business Account</strong> with a registered app at <a href="https://developers.facebook.com/" target="_blank" rel="noopener">developers.facebook.com</a></li>



<li><strong>Phone Number ID</strong> from the WhatsApp section in your app dashboard</li>



<li><strong>Access Token</strong> (temporary for testing, permanent via System User for production)</li>
</ol>



<p>Meta provides a free test phone number and five test recipient numbers. You can send real messages during development without any cost.</p>



<h2 class="wp-block-heading">How the WhatsApp Cloud API works</h2>



<p>The WhatsApp Cloud API is a REST API hosted by Meta. Your VB.NET application sends an HTTP POST request with a JSON body to Meta&#8217;s servers. Meta then delivers the message to the recipient&#8217;s WhatsApp app. The flow is:</p>



<ol class="wp-block-list">
<li>Your app sends a POST request to <code>https://graph.facebook.com/v20.0/{phone_number_id}/messages</code></li>



<li>Meta validates your token, phone number, and message format</li>



<li>Meta delivers the message via WhatsApp servers</li>



<li>You receive a JSON response with the message ID or an error</li>
</ol>



<p>There is one important rule: you can only send <strong>template messages</strong> to users who have not messaged you in the last 24 hours. Free-form text messages are only allowed within a 24-hour conversation window. For most notification use cases (order confirmations, alerts, reminders), template messages are what you need.</p>



<h2 class="wp-block-heading">Sending a text message</h2>



<p>This is the simplest example. It sends a plain text message to a single recipient inside an active conversation window:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.Net.Http
Imports System.Text

Private Async Function SendWhatsAppMessage(recipientPhone As String, message As String) As Task(Of Boolean)
    Dim phoneNumberId As String = "YOUR_PHONE_NUMBER_ID"
    Dim accessToken As String = "YOUR_ACCESS_TOKEN"
    Dim url As String = $"https://graph.facebook.com/v20.0/{phoneNumberId}/messages"

    Dim json As String = "{" &amp;
        """messaging_product"": ""whatsapp""," &amp;
        """to"": """ &amp; recipientPhone &amp; """," &amp;
        """type"": ""text""," &amp;
        """text"": {""body"": """ &amp; message &amp; """}" &amp;
        "}"

    Using client As New HttpClient()
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " &amp; accessToken)
        Dim content As New StringContent(json, Encoding.UTF8, "application/json")
        Dim response = Await client.PostAsync(url, content)
        Return response.IsSuccessStatusCode
    End Using
End Function</pre>



<p>Call it from a button click or any event handler:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
    Dim success = Await SendWhatsAppMessage("491234567890", "Your order has been shipped.")

    If success Then
        lblStatus.Text = "Message sent."
    Else
        lblStatus.Text = "Failed to send message."
    End If
End Sub</pre>



<p>The phone number must include the country code without a plus sign or leading zeros. For a German number like +49 123 4567890, use <code>491234567890</code>.</p>



<h2 class="wp-block-heading">Sending a template message</h2>



<p>Template messages are pre-approved message formats that you create in your Meta Business dashboard. They are required for sending the first message to a user or for messages outside the 24-hour window:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Function SendTemplateMessage(recipientPhone As String, templateName As String) As Task(Of Boolean)
    Dim phoneNumberId As String = "YOUR_PHONE_NUMBER_ID"
    Dim accessToken As String = "YOUR_ACCESS_TOKEN"
    Dim url As String = $"https://graph.facebook.com/v20.0/{phoneNumberId}/messages"

    Dim json As String = "{" &amp;
        """messaging_product"": ""whatsapp""," &amp;
        """to"": """ &amp; recipientPhone &amp; """," &amp;
        """type"": ""template""," &amp;
        """template"": {" &amp;
            """name"": """ &amp; templateName &amp; """," &amp;
            """language"": {""code"": ""en_US""}" &amp;
        "}" &amp;
        "}"

    Using client As New HttpClient()
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " &amp; accessToken)
        Dim content As New StringContent(json, Encoding.UTF8, "application/json")
        Dim response = Await client.PostAsync(url, content)
        Return response.IsSuccessStatusCode
    End Using
End Function</pre>



<p>Meta provides a default template called <code>hello_world</code> for testing. Create your own templates in the WhatsApp Manager section of your Meta Business dashboard. Templates can include variables like <code>{{1}}</code> that you fill with dynamic content at send time.</p>



<h2 class="wp-block-heading">Template message with variables</h2>



<p>Most real templates include placeholders for customer names, order numbers, or dates. Here is how to send a template with variable parameters:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Function SendOrderConfirmation(phone As String, customerName As String, orderId As String) As Task(Of Boolean)
    Dim phoneNumberId As String = "YOUR_PHONE_NUMBER_ID"
    Dim accessToken As String = "YOUR_ACCESS_TOKEN"
    Dim url As String = $"https://graph.facebook.com/v20.0/{phoneNumberId}/messages"

    ' Template "order_confirmation" with two body parameters
    Dim json As String = "{" &amp;
        """messaging_product"": ""whatsapp""," &amp;
        """to"": """ &amp; phone &amp; """," &amp;
        """type"": ""template""," &amp;
        """template"": {" &amp;
            """name"": ""order_confirmation""," &amp;
            """language"": {""code"": ""en_US""}," &amp;
            """components"": [{" &amp;
                """type"": ""body""," &amp;
                """parameters"": [" &amp;
                    "{""type"": ""text"", ""text"": """ &amp; customerName &amp; """}," &amp;
                    "{""type"": ""text"", ""text"": """ &amp; orderId &amp; """}" &amp;
                "]" &amp;
            "}]" &amp;
        "}" &amp;
        "}"

    Using client As New HttpClient()
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " &amp; accessToken)
        Dim content As New StringContent(json, Encoding.UTF8, "application/json")
        Dim response = Await client.PostAsync(url, content)
        Return response.IsSuccessStatusCode
    End Using
End Function</pre>



<p>The parameters fill the <code>{{1}}</code>, <code>{{2}}</code> placeholders in your template in order. Make sure the number of parameters matches what the template expects.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Project in mind?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a complete notification system?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From WhatsApp alerts to full desktop applications with database integration. I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Reading the API response</h2>



<p>A successful response returns a JSON object with the message ID. An error returns a structured error object. Here is how to read both:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Function SendAndLog(recipientPhone As String, message As String) As Task
    Dim phoneNumberId As String = "YOUR_PHONE_NUMBER_ID"
    Dim accessToken As String = "YOUR_ACCESS_TOKEN"
    Dim url As String = $"https://graph.facebook.com/v20.0/{phoneNumberId}/messages"

    Dim json As String = "{" &amp;
        """messaging_product"": ""whatsapp""," &amp;
        """to"": """ &amp; recipientPhone &amp; """," &amp;
        """type"": ""text""," &amp;
        """text"": {""body"": """ &amp; message &amp; """}" &amp;
        "}"

    Using client As New HttpClient()
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " &amp; accessToken)
        Dim content As New StringContent(json, Encoding.UTF8, "application/json")
        Dim response = Await client.PostAsync(url, content)
        Dim body = Await response.Content.ReadAsStringAsync()

        If response.IsSuccessStatusCode Then
            ' body contains: {"messaging_product":"whatsapp","contacts":[...],"messages":[{"id":"wamid.xxx"}]}
            Console.WriteLine("Sent. Response: " &amp; body)
        Else
            ' body contains: {"error":{"message":"...","type":"...","code":100,...}}
            Console.WriteLine("Error " &amp; response.StatusCode.ToString() &amp; ": " &amp; body)
        End If
    End Using
End Function</pre>



<p>For production applications, log the response body to a file for debugging. See the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET write text file guide</strong></a> for file logging patterns.</p>



<h2 class="wp-block-heading">Sending to multiple recipients</h2>



<p>The WhatsApp API accepts one recipient per request. To send to multiple numbers, loop through a <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>VB.NET List</strong></a> of phone numbers:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Function SendToMultiple(phones As List(Of String), message As String) As Task
    Dim successCount As Integer = 0
    Dim failCount As Integer = 0

    For Each phone In phones
        Dim success = Await SendWhatsAppMessage(phone, message)

        If success Then
            successCount += 1
        Else
            failCount += 1
        End If

        ' Respect rate limits: max 80 messages per second for Business API
        Await Task.Delay(50)
    Next

    Console.WriteLine($"Sent: {successCount}, Failed: {failCount}")
End Function</pre>



<p>The <code>Task.Delay(50)</code> adds a small pause between requests to stay within the API rate limit. For large batches (thousands of messages), consider queuing messages and processing them in the background.</p>



<h2 class="wp-block-heading">Storing credentials safely</h2>



<p>Never hardcode your access token in source code. For desktop applications, store credentials in an external config file or environment variables:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Read from App.config or environment variable
Dim accessToken As String = Environment.GetEnvironmentVariable("WHATSAPP_TOKEN")
Dim phoneNumberId As String = Environment.GetEnvironmentVariable("WHATSAPP_PHONE_ID")

If String.IsNullOrEmpty(accessToken) OrElse String.IsNullOrEmpty(phoneNumberId) Then
    MessageBox.Show("WhatsApp credentials not configured.", "Configuration Error",
        MessageBoxButtons.OK, MessageBoxIcon.Error)
    Return
End If</pre>



<p>Set environment variables on the machine running your app, or use <code>My.Settings</code> with user-scoped settings for per-user configuration.</p>



<h2 class="wp-block-heading">Using Twilio as an alternative</h2>



<p>If you prefer a simpler SDK over raw HTTP requests, Twilio offers a WhatsApp integration with a .NET NuGet package. Install <code>Twilio</code> via NuGet, then:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Twilio
Imports Twilio.Rest.Api.V2010.Account

' Initialize once at app startup
TwilioClient.Init("YOUR_ACCOUNT_SID", "YOUR_AUTH_TOKEN")

' Send a message
Dim msg = MessageResource.Create(
    body:="Your order has been shipped.",
    from:=New Twilio.Types.PhoneNumber("whatsapp:+14155238886"),
    [to]:=New Twilio.Types.PhoneNumber("whatsapp:+491234567890")
)

Console.WriteLine("Message SID: " &amp; msg.Sid)</pre>



<p>Twilio costs money per message but handles rate limiting, retries, and delivery tracking for you. The direct Meta API is free (you only pay for conversations after the free tier), but you handle everything yourself.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Sending free-form text outside the 24-hour window</h3>



<p>The API returns error code 131047 if you try to send a text message to someone who has not messaged you in the last 24 hours. Use a template message instead. Template messages work anytime and are the standard approach for notifications.</p>



<h3 class="wp-block-heading">Wrong phone number format</h3>



<p>The API expects the full international number without a plus sign, spaces, or dashes. <code>491234567890</code> is correct. <code>+49 123 456 7890</code>, <code>01234567890</code>, or <code>0049-123-4567890</code> will fail. Strip all formatting before sending:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim cleanPhone = phone.Replace("+", "").Replace(" ", "").Replace("-", "")</pre>



<h3 class="wp-block-heading">Hardcoding the access token</h3>



<p>Temporary tokens expire after 24 hours. If you hardcode one and forget to update it, your app silently stops sending. Use a permanent System User token for production and store it in environment variables or a secure config file, never in source code.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Interested?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I take on your project, from API integrations to complete desktop applications. Just send me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-whatsapp-en-1"><strong class="schema-faq-question"><strong>Is the WhatsApp Business API free?</strong></strong> <p class="schema-faq-answer">Meta offers 1,000 free service conversations per month. Beyond that, you pay per conversation (not per message). Template messages for notifications cost a small fee per conversation, varying by country. Development and testing with the sandbox number is free.</p> </div> <div class="schema-faq-section" id="faq-whatsapp-en-2"><strong class="schema-faq-question"><strong>Can I send WhatsApp messages from VB.NET without the official API?</strong></strong> <p class="schema-faq-answer">Technically you could automate WhatsApp Web with Selenium, but this violates WhatsApp&#8217;s terms of service and can get your number banned. The official Cloud API or a provider like Twilio are the only reliable and compliant options.</p> </div> <div class="schema-faq-section" id="faq-whatsapp-en-3"><strong class="schema-faq-question"><strong>What is the difference between a text message and a template message?</strong></strong> <p class="schema-faq-answer">A text message is free-form content you can only send during an active 24-hour conversation window. A template message is a pre-approved format registered in your Meta dashboard that can be sent anytime, including to users who have not contacted you first.</p> </div> <div class="schema-faq-section" id="faq-whatsapp-en-4"><strong class="schema-faq-question"><strong>Do I need a special phone number for the WhatsApp API?</strong></strong> <p class="schema-faq-answer">You need a phone number that is not already registered with WhatsApp or WhatsApp Business App. Meta provides a free test number for development. For production, register a dedicated business number in the Meta Business dashboard.</p> </div> <div class="schema-faq-section" id="faq-whatsapp-en-5"><strong class="schema-faq-question"><strong>Can I receive WhatsApp messages in VB.NET too?</strong></strong> <p class="schema-faq-answer">Yes, but it requires a webhook. Meta sends incoming messages to a public HTTPS endpoint you configure. For a desktop VB.NET app, you would need a server component (ASP.NET or a cloud function) that receives the webhook and forwards messages to your app.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>Sending WhatsApp messages from VB.NET comes down to a single <code>HttpClient.PostAsync</code> call to Meta&#8217;s Cloud API. Use template messages for notifications, store your token securely, and clean phone numbers before sending. For simpler integration at higher cost, Twilio wraps the same functionality in a NuGet package. For logging API responses to a file, see the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET write text file guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-send-whatsapp-message/">VB.NET Send WhatsApp Message via Cloud API (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-send-whatsapp-message/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET MsgBox and MessageBox.Show – Complete Guide (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-msgbox-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-msgbox-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 22:15:00 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20488</guid>

					<description><![CDATA[<p>The VB.NET MsgBox function displays a message box and returns which button the user clicked. It is quick to use for alerts, confirmations, and yes/no prompts. This guide covers syntax, button combinations, icons, return values, the difference between MsgBox and MessageBox.Show, and when to use which. MsgBox vs. MessageBox.Show VB.NET &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-msgbox-en/">VB.NET MsgBox and MessageBox.Show – Complete Guide (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET MsgBox</strong> function displays a message box and returns which button the user clicked. It is quick to use for alerts, confirmations, and yes/no prompts. This guide covers syntax, button combinations, icons, return values, the difference between <code>MsgBox</code> and <code>MessageBox.Show</code>, and when to use which.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a desktop application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From message boxes to complete WinForms applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">MsgBox vs. MessageBox.Show</h2>



<p>VB.NET offers two ways to display a message box. Both open the same Windows dialog, but they differ in origin and API style:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' VB.NET-style (from Microsoft.VisualBasic namespace)
MsgBox("File saved.", MsgBoxStyle.Information, "Success")

' .NET Framework-style (from System.Windows.Forms)
MessageBox.Show("File saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)</pre>



<figure class="wp-block-table"><table><thead><tr><th>Aspect</th><th>MsgBox</th><th>MessageBox.Show</th></tr></thead><tbody><tr><td>Namespace</td><td><code>Microsoft.VisualBasic</code></td><td><code>System.Windows.Forms</code></td></tr><tr><td>Available in C#</td><td>No (VB.NET only)</td><td>Yes</td></tr><tr><td>Return type</td><td><code>MsgBoxResult</code></td><td><code>DialogResult</code></td></tr><tr><td>Button/icon enums</td><td><code>MsgBoxStyle</code> (combinable)</td><td>Separate enums for buttons and icons</td></tr><tr><td>Parameter order</td><td>Message, Style, Title</td><td>Message, Title, Buttons, Icon</td></tr></tbody></table></figure>



<p><code>MsgBox</code> is a VB.NET convenience wrapper around the Win32 <code>MessageBox</code> API. <code>MessageBox.Show</code> is the .NET Framework class that works across all .NET languages. Both produce the exact same dialog window. If your project is purely VB.NET, either works fine. If you share code with C# developers or follow .NET conventions, <code>MessageBox.Show</code> is the better choice.</p>



<h2 class="wp-block-heading">MsgBox syntax</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim result As MsgBoxResult = MsgBox(
    Prompt,          ' The message text (required)
    Buttons Or Icon, ' MsgBoxStyle flags (optional)
    Title            ' Window title (optional)
)</pre>



<p>The first parameter is the message. The second combines button and icon styles with <code>Or</code>. The third sets the window title. Only the first parameter is required.</p>



<h2 class="wp-block-heading">Simple message</h2>



<p>The simplest MsgBox with just a message and the default OK button:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">MsgBox("The file has been saved.")</pre>



<p>With a title and information icon:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">MsgBox("The file has been saved.", MsgBoxStyle.Information, "Success")</pre>



<h2 class="wp-block-heading">Yes/No confirmation</h2>



<p>Ask the user a yes/no question and react to their choice:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim result = MsgBox(
    "Do you want to delete this record?",
    MsgBoxStyle.YesNo Or MsgBoxStyle.Question,
    "Confirm deletion")

If result = MsgBoxResult.Yes Then
    DeleteRecord()
End If</pre>



<p>Combine button style and icon style with <code>Or</code>. The return value tells you which button was clicked.</p>



<h2 class="wp-block-heading">Yes/No/Cancel</h2>



<p>For unsaved changes, offer three options:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim result = MsgBox(
    "You have unsaved changes. Save before closing?",
    MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Exclamation,
    "Unsaved changes")

Select Case result
    Case MsgBoxResult.Yes
        SaveDocument()
        Me.Close()
    Case MsgBoxResult.No
        Me.Close()
    Case MsgBoxResult.Cancel
        ' Do nothing, stay on the form
End Select</pre>



<h2 class="wp-block-heading">All button styles</h2>



<figure class="wp-block-table"><table><thead><tr><th>MsgBoxStyle</th><th>Buttons shown</th><th>Possible results</th></tr></thead><tbody><tr><td><code>OKOnly</code> (default)</td><td>OK</td><td><code>Ok</code></td></tr><tr><td><code>OKCancel</code></td><td>OK, Cancel</td><td><code>Ok</code>, <code>Cancel</code></td></tr><tr><td><code>YesNo</code></td><td>Yes, No</td><td><code>Yes</code>, <code>No</code></td></tr><tr><td><code>YesNoCancel</code></td><td>Yes, No, Cancel</td><td><code>Yes</code>, <code>No</code>, <code>Cancel</code></td></tr><tr><td><code>RetryCancel</code></td><td>Retry, Cancel</td><td><code>Retry</code>, <code>Cancel</code></td></tr><tr><td><code>AbortRetryIgnore</code></td><td>Abort, Retry, Ignore</td><td><code>Abort</code>, <code>Retry</code>, <code>Ignore</code></td></tr></tbody></table></figure>



<h2 class="wp-block-heading">All icon styles</h2>



<figure class="wp-block-table"><table><thead><tr><th>MsgBoxStyle</th><th>Icon</th><th>Use case</th></tr></thead><tbody><tr><td><code>Information</code></td><td>Blue info circle</td><td>Status messages, confirmations</td></tr><tr><td><code>Question</code></td><td>Blue question mark</td><td>Yes/No prompts</td></tr><tr><td><code>Exclamation</code></td><td>Yellow warning triangle</td><td>Warnings, unsaved changes</td></tr><tr><td><code>Critical</code></td><td>Red error circle</td><td>Errors, critical failures</td></tr></tbody></table></figure>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a polished WinForms application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From message dialogs to complete data-driven desktop apps: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Setting the default button</h2>



<p>By default, the first button is focused. You can change this to prevent accidental clicks on a destructive action:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Focus "No" by default so the user doesn't accidentally delete
Dim result = MsgBox(
    "Delete all records permanently?",
    MsgBoxStyle.YesNo Or MsgBoxStyle.Critical Or MsgBoxStyle.DefaultButton2,
    "Warning")

If result = MsgBoxResult.Yes Then
    DeleteAllRecords()
End If</pre>



<p><code>DefaultButton2</code> focuses the second button (No). Use <code>DefaultButton3</code> for the third button in three-button dialogs.</p>



<h2 class="wp-block-heading">The same examples with MessageBox.Show</h2>



<p>Here are the same patterns using the .NET <code>MessageBox.Show</code> equivalent, so you can compare:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Simple message
MessageBox.Show("The file has been saved.", "Success",
    MessageBoxButtons.OK, MessageBoxIcon.Information)

' Yes/No confirmation
Dim result As DialogResult = MessageBox.Show(
    "Do you want to delete this record?", "Confirm deletion",
    MessageBoxButtons.YesNo, MessageBoxIcon.Question)

If result = DialogResult.Yes Then
    DeleteRecord()
End If

' Yes/No/Cancel with default button
Dim result2 As DialogResult = MessageBox.Show(
    "Save before closing?", "Unsaved changes",
    MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning,
    MessageBoxDefaultButton.Button2)

Select Case result2
    Case DialogResult.Yes
        SaveDocument()
        Me.Close()
    Case DialogResult.No
        Me.Close()
    Case DialogResult.Cancel
        ' Stay on the form
End Select</pre>



<p>The functionality is identical. <code>MessageBox.Show</code> uses separate enums (<code>MessageBoxButtons</code>, <code>MessageBoxIcon</code>, <code>MessageBoxDefaultButton</code>) instead of combining flags with <code>Or</code>. The return type is <code>DialogResult</code> instead of <code>MsgBoxResult</code>.</p>



<h2 class="wp-block-heading">When to use which</h2>



<p>Use <code>MsgBox</code> when:</p>



<ul class="wp-block-list">
<li>Your project is <strong>VB.NET only</strong> and you prefer shorter syntax</li>



<li>You are <strong>prototyping quickly</strong> and want minimal code</li>
</ul>



<p>Use <code>MessageBox.Show</code> when:</p>



<ul class="wp-block-list">
<li>Your codebase is <strong>shared with C# developers</strong></li>



<li>You want to follow <strong>.NET conventions</strong></li>



<li>You need the <strong>owner window parameter</strong> (to keep the dialog on top of a specific form)</li>



<li>You want your code to be <strong>portable</strong> to .NET 5+ / .NET 8+ projects</li>
</ul>



<h2 class="wp-block-heading">Complete example: save-before-close pattern</h2>



<p>A realistic example that asks to save before closing a form. This pattern works in any text editor, data entry form, or settings window:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private _hasChanges As Boolean = False

Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
    _hasChanges = True
End Sub

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    If Not _hasChanges Then Return

    Dim result = MsgBox(
        "You have unsaved changes. Save now?",
        MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Exclamation Or MsgBoxStyle.DefaultButton1,
        "Unsaved changes")

    Select Case result
        Case MsgBoxResult.Yes
            SaveToFile()
        Case MsgBoxResult.Cancel
            e.Cancel = True  ' Prevent closing
    End Select
End Sub

Private Sub SaveToFile()
    File.WriteAllText("data.txt", TextBox1.Text, Encoding.UTF8)
    _hasChanges = False
End Sub</pre>



<p>Setting <code>e.Cancel = True</code> in the <code>FormClosing</code> event prevents the form from closing. The user stays on the form and can continue editing. For more on writing files, see the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET Write Text File guide</strong></a>.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Ignoring the return value</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - asks Yes/No but ignores the answer
MsgBox("Delete?", MsgBoxStyle.YesNo)
DeleteRecord()

' GOOD - check which button was clicked
If MsgBox("Delete?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
    DeleteRecord()
End If</pre>



<h3 class="wp-block-heading">Confusing MsgBox with VBScript MsgBox</h3>



<p>The VB.NET <code>MsgBox</code> function lives in <code>Microsoft.VisualBasic.Interaction</code> and returns <code>MsgBoxResult</code>. The VBScript <code>MsgBox</code> returns integer values (1 for OK, 6 for Yes, etc.). They look similar but live in completely different environments. In VB.NET, always compare against the <code>MsgBoxResult</code> enum, never against magic numbers.</p>



<h3 class="wp-block-heading">Using MsgBox in a background thread</h3>



<p>Both <code>MsgBox</code> and <code>MessageBox.Show</code> must be called from the UI thread. If you call them from a background thread or <code>Task</code>, the dialog may appear behind other windows or cause cross-thread exceptions. Use <code>Me.Invoke()</code> to marshal the call to the UI thread if needed.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from user dialogs to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-msgbox-en-1"><strong class="schema-faq-question"><strong>What is the difference between MsgBox and MessageBox.Show?</strong></strong> <p class="schema-faq-answer"><code>MsgBox</code> is a VB.NET convenience function from the <code>Microsoft.VisualBasic</code> namespace. <code>MessageBox.Show</code> is the .NET Framework class from <code>System.Windows.Forms</code>. Both produce the same dialog. <code>MsgBox</code> uses <code>MsgBoxResult</code>, while <code>MessageBox.Show</code> uses <code>DialogResult</code>.</p> </div> <div class="schema-faq-section" id="faq-msgbox-en-2"><strong class="schema-faq-question"><strong>How do I show a Yes/No message box in VB.NET?</strong></strong> <p class="schema-faq-answer">Use <code>MsgBox("Your question", MsgBoxStyle.YesNo)</code> and compare the result to <code>MsgBoxResult.Yes</code> or <code>MsgBoxResult.No</code>. Add <code>Or MsgBoxStyle.Question</code> for a question mark icon.</p> </div> <div class="schema-faq-section" id="faq-msgbox-en-3"><strong class="schema-faq-question"><strong>How do I change the default button in MsgBox?</strong></strong> <p class="schema-faq-answer">Add <code>MsgBoxStyle.DefaultButton2</code> or <code>DefaultButton3</code> to the style parameter. This moves the initial focus to the second or third button, preventing accidental clicks on destructive actions.</p> </div> <div class="schema-faq-section" id="faq-msgbox-en-4"><strong class="schema-faq-question"><strong>Is VB.NET MsgBox the same as VBScript MsgBox?</strong></strong> <p class="schema-faq-answer">No. VB.NET <code>MsgBox</code> returns a typed <code>MsgBoxResult</code> enum. VBScript <code>MsgBox</code> returns integer constants (1 for OK, 6 for Yes, etc.). They look similar but run in completely different environments.</p> </div> <div class="schema-faq-section" id="faq-msgbox-en-5"><strong class="schema-faq-question"><strong>Can I use MsgBox in a background thread?</strong></strong> <p class="schema-faq-answer">Not directly. Both <code>MsgBox</code> and <code>MessageBox.Show</code> should be called from the UI thread. From a background thread, use <code>Me.Invoke()</code> to marshal the call to the UI thread first.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>MsgBox</code> function gives you quick message dialogs with one line of code. Combine button styles and icon styles with <code>Or</code>, always check the return value for Yes/No prompts, and use <code>DefaultButton2</code> before destructive actions. If you prefer .NET-standard code, use <code>MessageBox.Show</code> with separate enums for the same result. For handling button events dynamically, see the <a href="https://robbelroot.de/blog/vbnet-addhandler-en/"><strong>VB.NET AddHandler guide</strong></a>. For writing files after a save prompt, check the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET Write Text File guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-msgbox-en/">VB.NET MsgBox and MessageBox.Show – Complete Guide (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-msgbox-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET AddHandler – Wire Events at Runtime (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-addhandler-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-addhandler-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 21:53:35 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20482</guid>

					<description><![CDATA[<p>The VB.NET AddHandler statement connects events to methods at runtime instead of wiring them statically with the Handles keyword. This is essential when you create controls dynamically or need to assign event handlers based on conditions. This guide covers the syntax, the difference from Handles, dynamic controls, RemoveHandler, custom events, &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-addhandler-en/">VB.NET AddHandler – Wire Events at Runtime (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET AddHandler</strong> statement connects events to methods at runtime instead of wiring them statically with the <code>Handles</code> keyword. This is essential when you create controls dynamically or need to assign event handlers based on conditions. This guide covers the syntax, the difference from <code>Handles</code>, dynamic controls, <code>RemoveHandler</code>, custom events, and common mistakes.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a desktop application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From event handling to complete WinForms applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">Handles vs. AddHandler</h2>



<p>In WinForms there are two ways to connect an event to a method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Way 1: Handles clause (static, at design time)
Private Sub BtnSave_Click(sender As Object, e As EventArgs) Handles BtnSave.Click
    ' Automatically wired by the designer
End Sub

' Way 2: AddHandler (dynamic, at runtime)
AddHandler BtnSave.Click, AddressOf BtnSave_Click

Private Sub BtnSave_Click(sender As Object, e As EventArgs)
    ' No "Handles" needed
End Sub</pre>



<p>The <code>Handles</code> clause only works with controls that exist at design time. <code>AddHandler</code> works anywhere, including controls you create through code. As soon as you generate controls dynamically, <code>AddHandler</code> is the only option.</p>



<h2 class="wp-block-heading">AddHandler syntax</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">AddHandler Object.Event, AddressOf MethodName</pre>



<ul class="wp-block-list">
<li><code>Object.Event</code> is the event you want to subscribe to (e.g. <code>Button1.Click</code>)</li>



<li><code>AddressOf</code> returns a reference to the method that should be called</li>



<li>The method signature must match the event (e.g. <code>sender As Object, e As EventArgs</code>)</li>
</ul>



<h2 class="wp-block-heading">Basic example</h2>



<p>A button click using <code>AddHandler</code> instead of <code>Handles</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        AddHandler BtnGreet.Click, AddressOf BtnGreet_Click
    End Sub

    Private Sub BtnGreet_Click(sender As Object, e As EventArgs)
        MessageBox.Show("Hello!")
    End Sub
End Class</pre>



<p>Notice that <code>BtnGreet_Click</code> has no <code>Handles</code> clause at the end. The connection is made entirely through <code>AddHandler</code> in the <code>Load</code> event.</p>



<h2 class="wp-block-heading">Creating dynamic controls</h2>



<p>The most common use case for <code>AddHandler</code>: controls are created through code and need event handlers. Here is an example with dynamically created buttons:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim categories = New List(Of String) From {
        "Personal", "Business", "Archive"
    }

    For Each category In categories
        Dim btn As New Button()
        btn.Text = category
        btn.Size = New Size(120, 40)
        btn.Margin = New Padding(5)
        AddHandler btn.Click, AddressOf CategoryButton_Click
        FlowLayoutPanel1.Controls.Add(btn)
    Next
End Sub

Private Sub CategoryButton_Click(sender As Object, e As EventArgs)
    Dim btn = DirectCast(sender, Button)
    MessageBox.Show($"Category: {btn.Text}")
End Sub</pre>



<p>Every button gets the same click handler. Use <code>DirectCast(sender, Button)</code> to determine which button was clicked. This scales to any number of controls without needing a separate handler for each one.</p>



<h2 class="wp-block-heading">Complete example: numpad keyboard</h2>



<p>A realistic example that creates a virtual numpad keyboard at runtime. Each key button is connected to a shared handler via <code>AddHandler</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim keys = New List(Of String) From {
            "7", "8", "9",
            "4", "5", "6",
            "1", "2", "3",
            "0", "00", ","
        }

        For Each key In keys
            Dim btn As New Button()
            btn.Text = key
            btn.Size = New Size(50, 50)
            btn.FlatStyle = FlatStyle.Flat
            btn.FlatAppearance.BorderSize = 1
            btn.BackColor = Color.White
            btn.Font = New Font("Segoe UI", 12, FontStyle.Bold)
            AddHandler btn.Click, AddressOf NumpadButton_Click
            FlowLayoutPanel1.Controls.Add(btn)
        Next
    End Sub

    Private Sub NumpadButton_Click(sender As Object, e As EventArgs)
        Dim btn = DirectCast(sender, Button)
        TextBox1.Text &amp;= btn.Text
    End Sub
End Class</pre>



<p>You only need a <code>FlowLayoutPanel</code> (about 180 x 260 pixels) and a <code>TextBox</code> on the form. The buttons are created entirely through code, no designer work needed.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a polished WinForms application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From event handling to complete data-driven desktop apps: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">RemoveHandler: disconnecting events</h2>



<p>Use <code>RemoveHandler</code> to disconnect the event binding. This is important to avoid memory leaks when removing controls at runtime:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Assign handler
AddHandler btn.Click, AddressOf Button_Click

' Remove handler
RemoveHandler btn.Click, AddressOf Button_Click</pre>



<p>Use <code>RemoveHandler</code> whenever you:</p>



<ul class="wp-block-list">
<li><strong>Remove</strong> dynamic controls (e.g. when closing a tab)</li>



<li>Want to <strong>swap</strong> a handler (remove first, then add)</li>



<li>Want to prevent an event from <strong>firing multiple times</strong></li>
</ul>



<h2 class="wp-block-heading">Multiple events, one handler</h2>



<p>A single handler can process multiple events. This is especially useful for forms with many similar controls:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler TxtName.TextChanged, AddressOf ValidateField
    AddHandler TxtEmail.TextChanged, AddressOf ValidateField
    AddHandler TxtPhone.TextChanged, AddressOf ValidateField
End Sub

Private Sub ValidateField(sender As Object, e As EventArgs)
    Dim txt = DirectCast(sender, TextBox)
    If String.IsNullOrWhiteSpace(txt.Text) Then
        txt.BackColor = Color.MistyRose
    Else
        txt.BackColor = Color.White
    End If
End Sub</pre>



<p>All three text boxes share the <code>ValidateField</code> handler. The <code>sender</code> parameter identifies which text box changed.</p>



<h2 class="wp-block-heading">Custom events with AddHandler</h2>



<p>You can also define your own events and subscribe to them with <code>AddHandler</code>. This is useful for loosely coupled communication between classes:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class FileWatcher
    Public Event FileChanged(filePath As String)

    Public Sub CheckForChanges()
        ' Check logic...
        RaiseEvent FileChanged("C:\data\config.xml")
    End Sub
End Class

' In the form:
Private WithEvents watcher As New FileWatcher()

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler watcher.FileChanged, AddressOf OnFileChanged
End Sub

Private Sub OnFileChanged(filePath As String)
    MessageBox.Show($"File changed: {filePath}")
End Sub</pre>



<p>The class uses <code>RaiseEvent</code> to trigger the event. The subscriber reacts through <code>AddHandler</code>. This keeps the <code>FileWatcher</code> class independent from the form.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Handler registered multiple times</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - each click adds another handler
Private Sub BtnRefresh_Click(sender As Object, e As EventArgs) Handles BtnRefresh.Click
    AddHandler Timer1.Tick, AddressOf Timer1_Tick
End Sub

' GOOD - register handler once in Load
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler Timer1.Tick, AddressOf Timer1_Tick
End Sub</pre>



<p>Calling <code>AddHandler</code> multiple times for the same event and method causes the method to execute multiple times. Register handlers in <code>Load</code> or call <code>RemoveHandler</code> first. For more on timers, see the <a href="https://robbelroot.de/blog/vbnet-timer-en/"><strong>VB.NET Timer guide</strong></a>.</p>



<h3 class="wp-block-heading">Wrong method signature</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - parameters don't match the event
Private Sub Button_Click(text As String)
End Sub

' GOOD - signature matches the Click event
Private Sub Button_Click(sender As Object, e As EventArgs)
End Sub</pre>



<p>The method must have exactly the parameter types the event expects. For <code>Click</code>, that is <code>(sender As Object, e As EventArgs)</code>. The compiler will report an error if the signature does not match.</p>



<h3 class="wp-block-heading">Forgetting RemoveHandler for dynamic controls</h3>



<p>If you remove controls with <code>Controls.Remove()</code> but don&#8217;t disconnect the handler, the reference keeps the object in memory. Always call <code>RemoveHandler</code> before removing a dynamic control.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from event handling to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-addhandler-en-1"><strong class="schema-faq-question"><strong>What is the difference between Handles and AddHandler?</strong></strong> <p class="schema-faq-answer"><code>Handles</code> connects an event to a method statically at design time. <code>AddHandler</code> connects an event dynamically at runtime. For controls created through code, <code>AddHandler</code> is the only option.</p> </div> <div class="schema-faq-section" id="faq-addhandler-en-2"><strong class="schema-faq-question"><strong>Can I call AddHandler multiple times for the same event?</strong></strong> <p class="schema-faq-answer">Yes, but the handler will execute multiple times. If that is not what you want, call <code>RemoveHandler</code> first or register the handler only once in the <code>Load</code> event.</p> </div> <div class="schema-faq-section" id="faq-addhandler-en-3"><strong class="schema-faq-question"><strong>When do I need RemoveHandler?</strong></strong> <p class="schema-faq-answer">Whenever you remove dynamic controls, want to swap handlers, or need to prevent an event from firing multiple times. Without <code>RemoveHandler</code> you risk memory leaks.</p> </div> <div class="schema-faq-section" id="faq-addhandler-en-4"><strong class="schema-faq-question"><strong>Can one handler process multiple events?</strong></strong> <p class="schema-faq-answer">Yes. Use <code>AddHandler</code> multiple times with different controls but the same method. The <code>sender</code> parameter tells you which control triggered the event.</p> </div> <div class="schema-faq-section" id="faq-addhandler-en-5"><strong class="schema-faq-question"><strong>What does AddressOf mean in VB.NET?</strong></strong> <p class="schema-faq-answer"><code>AddressOf</code> creates a delegate, which is a typed reference to a method. It is used with <code>AddHandler</code> to tell the event which method to call when the event is raised.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>AddHandler</code> statement makes event handling in VB.NET flexible. Use it for dynamically created controls, when you need to assign or swap handlers at runtime, and for custom events in your classes. Remember to call <code>RemoveHandler</code> when removing controls, and register handlers only once to avoid multiple executions. For related topics, check out the <a href="https://robbelroot.de/blog/vbnet-timer-en/"><strong>VB.NET Timer guide</strong></a> and the <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>VB.NET List guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-addhandler-en/">VB.NET AddHandler – Wire Events at Runtime (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-addhandler-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET FolderBrowserDialog – Pick Folders Easily (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-folderbrowserdialog-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-folderbrowserdialog-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 21:28:21 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20476</guid>

					<description><![CDATA[<p>The VB.NET FolderBrowserDialog lets users pick a folder from the file system before your application processes its contents. It displays the standard Windows folder picker, returns the selected path, and optionally restricts the browsing root. This guide covers setup, root folder configuration, new-folder creation, batch processing, and common pitfalls. What &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-folderbrowserdialog-en/">VB.NET FolderBrowserDialog – Pick Folders Easily (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET FolderBrowserDialog</strong> lets users pick a folder from the file system before your application processes its contents. It displays the standard Windows folder picker, returns the selected path, and optionally restricts the browsing root. This guide covers setup, root folder configuration, new-folder creation, batch processing, and common pitfalls.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a desktop application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From folder dialogs to complete WinForms applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is the FolderBrowserDialog?</h2>



<p>The <code>FolderBrowserDialog</code> is a WinForms control from <code>System.Windows.Forms</code>. When you call <code>ShowDialog()</code>, it opens the Windows folder picker. The user navigates the folder tree, selects a folder, and clicks OK. Your code then reads the full folder path from the <code>SelectedPath</code> property.</p>



<p>Use it when:</p>



<ul class="wp-block-list">
<li>The user should <strong>choose an output folder</strong> for exports, backups, or reports</li>



<li>Your application needs to <strong>batch-process all files</strong> inside a directory</li>



<li>The user should <strong>pick an installation or working directory</strong></li>



<li>You want to let the user <strong>create a new folder</strong> on the fly</li>
</ul>



<p>If the user should pick a single file instead, use the <a href="https://robbelroot.de/blog/vbnet-openfiledialog-en/"><strong>OpenFileDialog</strong></a>. To save a file to a specific location, use the <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/"><strong>SaveFileDialog</strong></a>.</p>



<h2 class="wp-block-heading">Basic example</h2>



<p>The simplest FolderBrowserDialog that displays the selected path:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New FolderBrowserDialog()
    dlg.Description = "Select a folder"

    If dlg.ShowDialog() = DialogResult.OK Then
        MessageBox.Show($"Selected: {dlg.SelectedPath}")
    End If
End Using</pre>



<p>The <code>Using</code> block disposes the dialog after use. <code>ShowDialog()</code> returns <code>DialogResult.OK</code> when the user confirms, or <code>DialogResult.Cancel</code> when they close the dialog. Always check the result before using the path.</p>



<h2 class="wp-block-heading">Setting the root folder</h2>



<p>The <code>RootFolder</code> property restricts which top-level node the dialog shows. It accepts values from <code>Environment.SpecialFolder</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New FolderBrowserDialog()
    ' Only show folders inside Desktop
    dlg.RootFolder = Environment.SpecialFolder.Desktop
    dlg.Description = "Select a project folder"

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim folder As String = dlg.SelectedPath
    End If
End Using</pre>



<p>Common values for <code>RootFolder</code>:</p>



<ul class="wp-block-list">
<li><code>Environment.SpecialFolder.Desktop</code> (default) shows the full tree starting from Desktop</li>



<li><code>Environment.SpecialFolder.MyComputer</code> shows all drives</li>



<li><code>Environment.SpecialFolder.MyDocuments</code> limits browsing to the Documents folder</li>
</ul>



<h2 class="wp-block-heading">Pre-selecting a folder</h2>



<p>Use <code>SelectedPath</code> to pre-select a folder when the dialog opens. This is useful when the user already chose a folder earlier and you want to remember it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New FolderBrowserDialog()
    dlg.Description = "Select export folder"
    dlg.SelectedPath = "C:\Users\Public\Documents"

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim exportFolder As String = dlg.SelectedPath
    End If
End Using</pre>



<p>The dialog opens with that folder already highlighted. If the path does not exist, the dialog falls back to the root.</p>



<h2 class="wp-block-heading">Hiding the New Folder button</h2>



<p>By default, the dialog includes a &#8222;Make New Folder&#8220; button. If the user should only pick an existing folder, disable it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New FolderBrowserDialog()
    dlg.Description = "Select an existing folder"
    dlg.ShowNewFolderButton = False

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim folder As String = dlg.SelectedPath
    End If
End Using</pre>



<p>Set <code>ShowNewFolderButton = False</code> for read-only operations like importing files. Keep it <code>True</code> (the default) when the user might need to create a destination folder for exports.</p>



<h2 class="wp-block-heading">Complete example: batch-processing files</h2>



<p>A realistic example that lets the user pick a folder and then processes all text files inside it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO
Imports System.Text

Private Sub BtnImport_Click(sender As Object, e As EventArgs) Handles BtnImport.Click
    Using dlg As New FolderBrowserDialog()
        dlg.Description = "Select folder with text files"
        dlg.ShowNewFolderButton = False

        If dlg.ShowDialog() = DialogResult.OK Then
            Dim files() As String = Directory.GetFiles(
                dlg.SelectedPath, "*.txt")

            For Each filePath In files
                Dim content As String = File.ReadAllText(
                    filePath, Encoding.UTF8)
                ' Process each file...
            Next

            MessageBox.Show($"{files.Length} files processed.")
        End If
    End Using
End Sub</pre>



<p><code>Directory.GetFiles()</code> returns all matching files in the selected folder. Use the second parameter for pattern matching, e.g. <code>"*.csv"</code> or <code>"*.xml"</code>. For more on reading text files, see the <a href="https://robbelroot.de/blog/vbnet-read-text-file/"><strong>VB.NET Read Text File guide</strong></a>.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a polished WinForms application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From file dialogs to complete data-driven desktop apps: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Including subfolders</h2>



<p>To process files recursively, add <code>SearchOption.AllDirectories</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">If dlg.ShowDialog() = DialogResult.OK Then
    Dim files() As String = Directory.GetFiles(
        dlg.SelectedPath, "*.txt", SearchOption.AllDirectories)

    For Each filePath In files
        Dim relativePath As String = filePath.Replace(
            dlg.SelectedPath & "\", "")
        ' Process file with relative path info...
    Next
End If</pre>



<p>This searches the selected folder and all its subfolders. Be careful with large directory trees, as this can take a while if there are thousands of files.</p>



<h2 class="wp-block-heading">Using Environment.SpecialFolder</h2>



<p>You can combine the dialog with <code>Environment.GetFolderPath()</code> to pre-select common system folders. You can also use the <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>application path</strong></a> as the starting point:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Start in Documents
Using dlg As New FolderBrowserDialog()
    dlg.SelectedPath = Environment.GetFolderPath(
        Environment.SpecialFolder.MyDocuments)

    If dlg.ShowDialog() = DialogResult.OK Then
        ' Use dlg.SelectedPath...
    End If
End Using

' Start in the application directory
Using dlg As New FolderBrowserDialog()
    dlg.SelectedPath = Application.StartupPath

    If dlg.ShowDialog() = DialogResult.OK Then
        ' Use dlg.SelectedPath...
    End If
End Using</pre>



<h2 class="wp-block-heading">All important properties at a glance</h2>



<figure class="wp-block-table"><table><thead><tr><th>Property</th><th>Default</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Description</code></td><td>(empty)</td><td>Text shown above the folder tree</td></tr><tr><td><code>SelectedPath</code></td><td>(empty)</td><td>Pre-selected folder / selected folder after dialog</td></tr><tr><td><code>RootFolder</code></td><td>Desktop</td><td>Top-level node of the folder tree</td></tr><tr><td><code>ShowNewFolderButton</code></td><td>True</td><td>Show or hide the &#8222;Make New Folder&#8220; button</td></tr></tbody></table></figure>



<p>The FolderBrowserDialog has fewer properties than the <a href="https://robbelroot.de/blog/vbnet-openfiledialog-en/"><strong>OpenFileDialog</strong></a> because it only deals with folders, not files. There is no <code>Filter</code> or <code>Multiselect</code> property.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Using the path without checking DialogResult</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - SelectedPath is empty if user cancels
dlg.ShowDialog()
Dim files = Directory.GetFiles(dlg.SelectedPath)

' GOOD - check the result first
If dlg.ShowDialog() = DialogResult.OK Then
    Dim files = Directory.GetFiles(dlg.SelectedPath)
End If</pre>



<h3 class="wp-block-heading">Confusing RootFolder and SelectedPath</h3>



<p><code>RootFolder</code> limits which folders the user can see. <code>SelectedPath</code> pre-selects a folder within the visible tree. If you set <code>RootFolder = MyDocuments</code>, the user cannot navigate outside of Documents. If you only want to suggest a starting point but allow full navigation, use <code>SelectedPath</code> instead.</p>



<h3 class="wp-block-heading">Forgetting the Using block</h3>



<p>Like all WinForms dialogs, the FolderBrowserDialog holds unmanaged resources. Without <code>Using</code>, those resources stay in memory until the garbage collector runs. Always wrap it in a <code>Using</code> block.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from file operations to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-folderbrowser-en-1"><strong class="schema-faq-question"><strong>How do I open a FolderBrowserDialog in VB.NET?</strong></strong> <p class="schema-faq-answer">Create a <code>New FolderBrowserDialog()</code> inside a <code>Using</code> block, optionally set <code>Description</code>, then call <code>ShowDialog()</code>. Check if the result equals <code>DialogResult.OK</code> before reading <code>dlg.SelectedPath</code>.</p> </div> <div class="schema-faq-section" id="faq-folderbrowser-en-2"><strong class="schema-faq-question"><strong>How do I set the default folder in FolderBrowserDialog?</strong></strong> <p class="schema-faq-answer">Set <code>dlg.SelectedPath</code> to the desired folder path before calling <code>ShowDialog()</code>. The dialog opens with that folder highlighted. Do not confuse this with <code>RootFolder</code>, which restricts the visible tree.</p> </div> <div class="schema-faq-section" id="faq-folderbrowser-en-3"><strong class="schema-faq-question"><strong>Can I hide the New Folder button?</strong></strong> <p class="schema-faq-answer">Yes. Set <code>dlg.ShowNewFolderButton = False</code> before calling <code>ShowDialog()</code>. This is useful for read-only operations where the user should only select an existing folder.</p> </div> <div class="schema-faq-section" id="faq-folderbrowser-en-4"><strong class="schema-faq-question"><strong>What is the difference between RootFolder and SelectedPath?</strong></strong> <p class="schema-faq-answer"><code>RootFolder</code> limits the top-level node the user can browse, restricting navigation. <code>SelectedPath</code> pre-selects a folder within the visible tree without restricting navigation. Use <code>SelectedPath</code> when you want a starting point but full access.</p> </div> <div class="schema-faq-section" id="faq-folderbrowser-en-5"><strong class="schema-faq-question"><strong>Can the user select multiple folders?</strong></strong> <p class="schema-faq-answer">No. The built-in <code>FolderBrowserDialog</code> only supports selecting a single folder. If you need multiple folder selection, you would need to call the dialog multiple times or use a custom solution.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>FolderBrowserDialog</code> gives your VB.NET application a native folder picker with minimal code. Set <code>SelectedPath</code> to suggest a starting folder, use <code>RootFolder</code> to restrict navigation, and hide the New Folder button with <code>ShowNewFolderButton = False</code> when the user should only select existing folders. After the dialog closes, combine <code>SelectedPath</code> with <code>Directory.GetFiles()</code> to process folder contents. For picking individual files, use the <a href="https://robbelroot.de/blog/vbnet-openfiledialog-en/"><strong>OpenFileDialog</strong></a>. To save files, see the <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/"><strong>SaveFileDialog</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-folderbrowserdialog-en/">VB.NET FolderBrowserDialog – Pick Folders Easily (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-folderbrowserdialog-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET OpenFileDialog – Open Files with Dialog (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-openfiledialog-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-openfiledialog-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 21:16:14 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20470</guid>

					<description><![CDATA[<p>The VB.NET OpenFileDialog lets users pick one or more files to open before your application reads them. It handles path selection, file type filtering, and file-exists validation in a single control. This guide covers setup, filters, multi-select, common patterns, and integration with actual file reading. What is the OpenFileDialog? The &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-openfiledialog-en/">VB.NET OpenFileDialog – Open Files with Dialog (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET OpenFileDialog</strong> lets users pick one or more files to open before your application reads them. It handles path selection, file type filtering, and file-exists validation in a single control. This guide covers setup, filters, multi-select, common patterns, and integration with actual file reading.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a desktop application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From file dialogs to complete WinForms applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is the OpenFileDialog?</h2>



<p>The <code>OpenFileDialog</code> is a WinForms control from <code>System.Windows.Forms</code>. When you call <code>ShowDialog()</code>, it opens the standard Windows &#8222;Open&#8220; dialog. The user navigates to a folder, picks a file, and clicks Open. Your code then receives the full file path through the <code>FileName</code> property.</p>



<p>Use it when:</p>



<ul class="wp-block-list">
<li>The user should <strong>choose which file to open</strong> (imports, config files, images)</li>



<li>You need a <strong>file type filter</strong> (e.g. only .txt, .csv, or .png)</li>



<li>You want <strong>built-in validation</strong> that the selected file actually exists</li>



<li>You need to let the user <strong>select multiple files</strong> at once</li>
</ul>



<p>If you need the user to pick a save destination instead, use the <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/"><strong>SaveFileDialog</strong></a>.</p>



<h2 class="wp-block-heading">Basic example</h2>



<p>The simplest OpenFileDialog that reads a text file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New OpenFileDialog()
    dlg.Filter = "Text files (*.txt)|*.txt"
    dlg.Title = "Open text file"

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim content As String = File.ReadAllText(dlg.FileName)
        TextBox1.Text = content
    End If
End Using</pre>



<p>The <code>Using</code> block disposes the dialog after use. <code>ShowDialog()</code> returns <code>DialogResult.OK</code> when the user clicks Open, or <code>DialogResult.Cancel</code> when they close the dialog. Always check the result before reading.</p>



<h2 class="wp-block-heading">Setting up file type filters</h2>



<p>The <code>Filter</code> property controls which file types appear in the dropdown. The syntax is identical to the <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/"><strong>SaveFileDialog</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Single filter
dlg.Filter = "Text files (*.txt)|*.txt"

' Multiple filters
dlg.Filter = "Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*)|*.*"

' Pre-select the second filter (1-based index)
dlg.FilterIndex = 2

' Allow multiple extensions in one filter
dlg.Filter = "Images (*.png;*.jpg;*.gif)|*.png;*.jpg;*.gif"</pre>



<p>To combine multiple extensions in a single filter entry, separate them with semicolons inside the pattern part: <code>*.png;*.jpg;*.gif</code>. The user sees one &#8222;Images&#8220; dropdown entry that matches all three types.</p>



<h2 class="wp-block-heading">Setting the default folder</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New OpenFileDialog()
    ' Start in the user's Documents folder
    dlg.InitialDirectory = Environment.GetFolderPath(
        Environment.SpecialFolder.MyDocuments)

    dlg.Filter = "CSV files (*.csv)|*.csv"

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim content As String = File.ReadAllText(dlg.FileName)
    End If
End Using</pre>



<p>Common choices for <code>InitialDirectory</code>:</p>



<ul class="wp-block-list">
<li><code>Environment.SpecialFolder.MyDocuments</code> for user documents</li>



<li><code>Environment.SpecialFolder.Desktop</code> for the desktop</li>



<li>The <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>application path</strong></a> for files next to the executable</li>
</ul>



<p>If you don&#8217;t set <code>InitialDirectory</code>, the dialog remembers the last folder the user navigated to during the current session.</p>



<h2 class="wp-block-heading">Selecting multiple files</h2>



<p>Set <code>Multiselect = True</code> to let the user select more than one file. The selected paths are available through <code>FileNames</code> (plural):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New OpenFileDialog()
    dlg.Filter = "Images (*.png;*.jpg)|*.png;*.jpg"
    dlg.Multiselect = True
    dlg.Title = "Select images to import"

    If dlg.ShowDialog() = DialogResult.OK Then
        For Each filePath In dlg.FileNames
            Dim img As Image = Image.FromFile(filePath)
            ' Process each image...
        Next

        MessageBox.Show($"{dlg.FileNames.Length} files selected.")
    End If
End Using</pre>



<p>The user holds Ctrl to pick individual files, or Shift to select a range. <code>FileName</code> (singular) still returns the first selected file; <code>FileNames</code> returns all of them as a <code>String()</code> array.</p>



<h2 class="wp-block-heading">Complete example: reading a text file</h2>



<p>A realistic example that reads a text file with encoding support, using <a href="https://robbelroot.de/blog/vbnet-read-text-file/"><strong>ReadAllText</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO
Imports System.Text

Private Sub BtnOpen_Click(sender As Object, e As EventArgs) Handles BtnOpen.Click
    Using dlg As New OpenFileDialog()
        dlg.Title = "Open report"
        dlg.Filter = "Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*)|*.*"
        dlg.InitialDirectory = Environment.GetFolderPath(
            Environment.SpecialFolder.MyDocuments)

        If dlg.ShowDialog() = DialogResult.OK Then
            Dim content As String = File.ReadAllText(dlg.FileName, Encoding.UTF8)
            TextBox1.Text = content
            Me.Text = $"Editor - {Path.GetFileName(dlg.FileName)}"
        End If
    End Using
End Sub</pre>



<p>Specify <code>Encoding.UTF8</code> when reading text files to correctly handle special characters. <code>Path.GetFileName()</code> extracts just the file name from the full path, which is useful for setting the window title.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a polished WinForms application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From file dialogs to complete data-driven desktop apps: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Reading binary files (images, PDFs)</h2>



<p>OpenFileDialog works the same way for binary content. Only the read method changes:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Load an image into a PictureBox
Using dlg As New OpenFileDialog()
    dlg.Filter = "Images (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp"

    If dlg.ShowDialog() = DialogResult.OK Then
        PictureBox1.Image = Image.FromFile(dlg.FileName)
    End If
End Using

' Read a file as byte array
Using dlg As New OpenFileDialog()
    dlg.Filter = "All files (*.*)|*.*"

    If dlg.ShowDialog() = DialogResult.OK Then
        Dim bytes() As Byte = File.ReadAllBytes(dlg.FileName)
        ' Process bytes...
    End If
End Using</pre>



<h2 class="wp-block-heading">Reading large files with StreamReader</h2>



<p>For large files or line-by-line processing, use a <code>StreamReader</code> with the path from the dialog:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New OpenFileDialog()
    dlg.Filter = "CSV files (*.csv)|*.csv"

    If dlg.ShowDialog() = DialogResult.OK Then
        Using reader As New StreamReader(dlg.FileName, Encoding.UTF8)
            Dim lineNumber As Integer = 0
            While Not reader.EndOfStream
                Dim line As String = reader.ReadLine()
                lineNumber += 1
                ' Process each line...
            End While
        End Using
    End If
End Using</pre>



<p>The <code>StreamReader</code> approach is more memory-efficient for large files because it reads line by line instead of loading the entire file into memory. For more on reading text files, see the <a href="https://robbelroot.de/blog/vbnet-read-text-file/"><strong>VB.NET Read Text File guide</strong></a>.</p>



<h2 class="wp-block-heading">All important properties at a glance</h2>



<figure class="wp-block-table"><table><thead><tr><th>Property</th><th>Default</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Filter</code></td><td>(empty)</td><td>File type dropdown entries</td></tr><tr><td><code>FilterIndex</code></td><td>1</td><td>Pre-selected filter (1-based)</td></tr><tr><td><code>FileName</code></td><td>(empty)</td><td>Selected file path after dialog closes</td></tr><tr><td><code>FileNames</code></td><td>(empty)</td><td>All selected paths when Multiselect is True</td></tr><tr><td><code>InitialDirectory</code></td><td>(empty)</td><td>Starting folder</td></tr><tr><td><code>Multiselect</code></td><td>False</td><td>Allow selecting multiple files</td></tr><tr><td><code>CheckFileExists</code></td><td>True</td><td>Reject paths that don&#8217;t exist</td></tr><tr><td><code>CheckPathExists</code></td><td>True</td><td>Reject invalid folder paths</td></tr><tr><td><code>Title</code></td><td>&#8222;Open&#8220;</td><td>Dialog window title</td></tr><tr><td><code>ReadOnlyChecked</code></td><td>False</td><td>Pre-check the &#8222;Open as read-only&#8220; checkbox</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Reading without checking DialogResult</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - FileName is empty if user cancels
dlg.ShowDialog()
Dim content = File.ReadAllText(dlg.FileName)

' GOOD - check the result first
If dlg.ShowDialog() = DialogResult.OK Then
    Dim content = File.ReadAllText(dlg.FileName)
End If</pre>



<h3 class="wp-block-heading">Using FileName instead of FileNames with Multiselect</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - only gets the first file when Multiselect is True
dlg.Multiselect = True
If dlg.ShowDialog() = DialogResult.OK Then
    Dim content = File.ReadAllText(dlg.FileName)  ' only first file!
End If

' GOOD - iterate all selected files
If dlg.ShowDialog() = DialogResult.OK Then
    For Each path In dlg.FileNames
        Dim content = File.ReadAllText(path)
    Next
End If</pre>



<h3 class="wp-block-heading">Forgetting the Using block</h3>



<p>The OpenFileDialog holds unmanaged resources (a Windows COM object). Without <code>Using</code>, those resources stay in memory until the garbage collector runs. Always wrap it in a <code>Using</code> block.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from file operations to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-openfile-en-1"><strong class="schema-faq-question"><strong>How do I open an OpenFileDialog in VB.NET?</strong></strong> <p class="schema-faq-answer">Create a <code>New OpenFileDialog()</code> inside a <code>Using</code> block, set the <code>Filter</code> property, then call <code>ShowDialog()</code>. Check if the result equals <code>DialogResult.OK</code> before reading from <code>dlg.FileName</code>.</p> </div> <div class="schema-faq-section" id="faq-openfile-en-2"><strong class="schema-faq-question"><strong>How do I select multiple files with OpenFileDialog?</strong></strong> <p class="schema-faq-answer">Set <code>dlg.Multiselect = True</code> before calling <code>ShowDialog()</code>. The user can then hold Ctrl or Shift to select several files. Access all paths through <code>dlg.FileNames</code> (plural).</p> </div> <div class="schema-faq-section" id="faq-openfile-en-3"><strong class="schema-faq-question"><strong>How do I filter file types in OpenFileDialog?</strong></strong> <p class="schema-faq-answer">Set the <code>Filter</code> property with the pattern <code>Description|*.ext</code>. For multiple types, separate with pipes: <code>"Text (*.txt)|*.txt|CSV (*.csv)|*.csv"</code>. Combine extensions with semicolons: <code>"Images (*.png;*.jpg)|*.png;*.jpg"</code>.</p> </div> <div class="schema-faq-section" id="faq-openfile-en-4"><strong class="schema-faq-question"><strong>Does OpenFileDialog actually read the file?</strong></strong> <p class="schema-faq-answer">No. The dialog only lets the user pick a file path. You are responsible for reading the file yourself using methods like <code>File.ReadAllText()</code>, <code>File.ReadAllBytes()</code>, or a StreamReader with the path from <code>dlg.FileName</code>.</p> </div> <div class="schema-faq-section" id="faq-openfile-en-5"><strong class="schema-faq-question"><strong>What is the difference between OpenFileDialog and SaveFileDialog?</strong></strong> <p class="schema-faq-answer">OpenFileDialog lets the user pick an existing file to read, with file-exists validation built in. SaveFileDialog lets the user pick a destination for writing a new file, with overwrite confirmation. Both return the selected path through <code>FileName</code>.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>OpenFileDialog</code> gives your VB.NET application a native file-open experience with minimal code. Set a <code>Filter</code> for file types, enable <code>Multiselect</code> for batch operations, and always check <code>DialogResult.OK</code> before reading. For the actual file reading, use <code>File.ReadAllText()</code> for text or <code>File.ReadAllBytes()</code> for binary data. Need to save files instead? The <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/"><strong>SaveFileDialog</strong></a> follows the same pattern. For more on reading text files, see the <a href="https://robbelroot.de/blog/vbnet-read-text-file/"><strong>VB.NET Read Text File guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-openfiledialog-en/">VB.NET OpenFileDialog – Open Files with Dialog (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-openfiledialog-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET SaveFileDialog – Save Files with Dialog (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-savefiledialog-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-savefiledialog-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 20:36:05 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20465</guid>

					<description><![CDATA[<p>The VB.NET SaveFileDialog lets users pick a file name and location before your application writes data to disk. It handles path selection, file type filtering, and overwrite confirmation in a single control. This guide covers setup, filters, default paths, common patterns, and integration with actual file writing. What is the &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/">VB.NET SaveFileDialog – Save Files with Dialog (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET SaveFileDialog</strong> lets users pick a file name and location before your application writes data to disk. It handles path selection, file type filtering, and overwrite confirmation in a single control. This guide covers setup, filters, default paths, common patterns, and integration with actual file writing.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building a desktop application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From file dialogs to complete WinForms applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is the SaveFileDialog?</h2>



<p>The <code>SaveFileDialog</code> is a WinForms control from <code>System.Windows.Forms</code>. When you call <code>ShowDialog()</code>, it opens the standard Windows &#8222;Save As&#8220; dialog. The user picks a folder, enters a file name, and clicks Save. Your code then receives the full file path through the <code>FileName</code> property.</p>



<p>Use it when:</p>



<ul class="wp-block-list">
<li>The user should <strong>choose where to save</strong> a file (exports, reports, downloads)</li>



<li>You need a <strong>file type filter</strong> (e.g. only .txt, .csv, or .pdf)</li>



<li>You want <strong>built-in overwrite confirmation</strong> without writing your own</li>
</ul>



<h2 class="wp-block-heading">Basic example</h2>



<p>The simplest SaveFileDialog with a single file type:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New SaveFileDialog()
    dlg.Filter = "Text files (*.txt)|*.txt"
    dlg.Title = "Save text file"

    If dlg.ShowDialog() = DialogResult.OK Then
        File.WriteAllText(dlg.FileName, "Hello World")
    End If
End Using</pre>



<p>The <code>Using</code> block disposes the dialog after use. <code>ShowDialog()</code> returns <code>DialogResult.OK</code> when the user clicks Save, or <code>DialogResult.Cancel</code> when they close the dialog. Always check the result before writing.</p>



<h2 class="wp-block-heading">Setting up file type filters</h2>



<p>The <code>Filter</code> property controls which file types appear in the dropdown. The syntax is <code>Description|Pattern</code>, separated by pipes for multiple types:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Single filter
dlg.Filter = "Text files (*.txt)|*.txt"

' Multiple filters
dlg.Filter = "Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*)|*.*"

' Pre-select the second filter (1-based index)
dlg.FilterIndex = 2</pre>



<p>Each filter entry consists of two parts: the display text (e.g. &#8222;Text files (*.txt)&#8220;) and the actual pattern (e.g. &#8222;*.txt&#8220;). <code>FilterIndex</code> is 1-based, so <code>2</code> selects the second entry.</p>



<h3 class="wp-block-heading">Automatically adding the file extension</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Append extension if user doesn't type one (default: True)
dlg.AddExtension = True

' Set default extension (without the dot)
dlg.DefaultExt = "txt"</pre>



<p>With <code>AddExtension = True</code> (the default), if the user types &#8222;report&#8220; without an extension, the dialog appends &#8222;.txt&#8220; based on the selected filter or the <code>DefaultExt</code> value.</p>



<h2 class="wp-block-heading">Setting the default folder and file name</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New SaveFileDialog()
    ' Start in the user's Documents folder
    dlg.InitialDirectory = Environment.GetFolderPath(
        Environment.SpecialFolder.MyDocuments)

    ' Pre-fill the file name
    dlg.FileName = "report-2026.csv"

    dlg.Filter = "CSV files (*.csv)|*.csv"

    If dlg.ShowDialog() = DialogResult.OK Then
        File.WriteAllText(dlg.FileName, csvContent)
    End If
End Using</pre>



<p><code>InitialDirectory</code> sets where the dialog opens. Common choices:</p>



<ul class="wp-block-list">
<li><code>Environment.SpecialFolder.MyDocuments</code> for user documents</li>



<li><code>Environment.SpecialFolder.Desktop</code> for the desktop</li>



<li>The <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>application path</strong></a> for saving next to the executable</li>
</ul>



<p>If you don&#8217;t set <code>InitialDirectory</code>, the dialog remembers the last folder the user navigated to during the current session.</p>



<h2 class="wp-block-heading">Overwrite confirmation</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Show a warning when the file already exists (default: True)
dlg.OverwritePrompt = True</pre>



<p>This is enabled by default. When the user selects an existing file, Windows shows a confirmation dialog asking if they want to replace it. You don&#8217;t need to code this yourself.</p>



<h2 class="wp-block-heading">Complete example: saving a text file</h2>



<p>A realistic example that saves a multi-line text file with encoding support, using <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>WriteAllText</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO
Imports System.Text

Private Sub BtnSave_Click(sender As Object, e As EventArgs) Handles BtnSave.Click
    Using dlg As New SaveFileDialog()
        dlg.Title = "Save report"
        dlg.Filter = "Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv"
        dlg.InitialDirectory = Environment.GetFolderPath(
            Environment.SpecialFolder.MyDocuments)
        dlg.FileName = $"report-{DateTime.Now:yyyy-MM-dd}"

        If dlg.ShowDialog() = DialogResult.OK Then
            Dim content As String = BuildReport()
            File.WriteAllText(dlg.FileName, content, Encoding.UTF8)
            MessageBox.Show($"Saved to {dlg.FileName}",
                "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
    End Using
End Sub</pre>



<p>Always specify <code>Encoding.UTF8</code> when writing text files. Without it, .NET uses the system default encoding, which can cause issues with special characters on other machines.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a polished WinForms application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From file dialogs to complete data-driven desktop apps: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Saving binary files (images, PDFs)</h2>



<p>SaveFileDialog works the same way for binary content. The only difference is the write method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Save an image
Using dlg As New SaveFileDialog()
    dlg.Filter = "PNG images (*.png)|*.png|JPEG images (*.jpg)|*.jpg"
    dlg.FileName = "screenshot"

    If dlg.ShowDialog() = DialogResult.OK Then
        PictureBox1.Image.Save(dlg.FileName)
    End If
End Using

' Save a byte array (e.g. downloaded PDF)
Using dlg As New SaveFileDialog()
    dlg.Filter = "PDF files (*.pdf)|*.pdf"

    If dlg.ShowDialog() = DialogResult.OK Then
        File.WriteAllBytes(dlg.FileName, pdfBytes)
    End If
End Using</pre>



<h2 class="wp-block-heading">Saving with a StreamWriter (large files)</h2>



<p>For large files or line-by-line writing, use a <code>StreamWriter</code> with the path from the dialog:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using dlg As New SaveFileDialog()
    dlg.Filter = "CSV files (*.csv)|*.csv"

    If dlg.ShowDialog() = DialogResult.OK Then
        Using writer As New StreamWriter(dlg.FileName, False, Encoding.UTF8)
            writer.WriteLine("Name,Email,Age")
            For Each contact In contacts
                writer.WriteLine($"{contact.Name},{contact.Email},{contact.Age}")
            Next
        End Using
    End If
End Using</pre>



<p>The <code>StreamWriter</code> approach is more memory-efficient for large datasets because it writes line by line instead of building the entire string in memory first. For a deeper look at text file operations, see the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET Write Text File guide</strong></a>.</p>



<h2 class="wp-block-heading">All important properties at a glance</h2>



<figure class="wp-block-table"><table><thead><tr><th>Property</th><th>Default</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Filter</code></td><td>(empty)</td><td>File type dropdown entries</td></tr><tr><td><code>FilterIndex</code></td><td>1</td><td>Pre-selected filter (1-based)</td></tr><tr><td><code>FileName</code></td><td>(empty)</td><td>Pre-filled file name / selected path after dialog</td></tr><tr><td><code>InitialDirectory</code></td><td>(empty)</td><td>Starting folder</td></tr><tr><td><code>DefaultExt</code></td><td>(empty)</td><td>Extension added when user omits one</td></tr><tr><td><code>AddExtension</code></td><td>True</td><td>Auto-append extension from filter</td></tr><tr><td><code>OverwritePrompt</code></td><td>True</td><td>Warn before overwriting existing files</td></tr><tr><td><code>Title</code></td><td>&#8222;Save As&#8220;</td><td>Dialog window title</td></tr><tr><td><code>ValidateNames</code></td><td>True</td><td>Reject invalid file name characters</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Common mistakes</h2>



<h3 class="wp-block-heading">Writing without checking DialogResult</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - writes to empty path if user cancels
dlg.ShowDialog()
File.WriteAllText(dlg.FileName, content)

' GOOD - check the result first
If dlg.ShowDialog() = DialogResult.OK Then
    File.WriteAllText(dlg.FileName, content)
End If</pre>



<h3 class="wp-block-heading">Forgetting the Using block</h3>



<p>The SaveFileDialog holds unmanaged resources (a Windows COM object). Without <code>Using</code>, those resources stay in memory until the garbage collector runs. Always wrap it in a <code>Using</code> block.</p>



<h3 class="wp-block-heading">Broken filter syntax</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - missing pattern after pipe
dlg.Filter = "Text files|"

' BAD - using comma instead of pipe
dlg.Filter = "Text files (*.txt), *.txt"

' GOOD - correct description|pattern syntax
dlg.Filter = "Text files (*.txt)|*.txt"</pre>



<p>A broken <code>Filter</code> string throws an <code>ArgumentException</code> at runtime. The pattern is always <code>Display text|*.extension</code>, with a pipe separating each pair.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from file operations to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-savefile-en-1"><strong class="schema-faq-question"><strong>How do I open a SaveFileDialog in VB.NET?</strong></strong> <p class="schema-faq-answer">Create a <code>New SaveFileDialog()</code> inside a <code>Using</code> block, set the <code>Filter</code> property, then call <code>ShowDialog()</code>. Check if the result equals <code>DialogResult.OK</code> before writing to <code>dlg.FileName</code>.</p> </div> <div class="schema-faq-section" id="faq-savefile-en-2"><strong class="schema-faq-question"><strong>How do I set a default file name in SaveFileDialog?</strong></strong> <p class="schema-faq-answer">Set the <code>FileName</code> property before calling <code>ShowDialog()</code>. For example: <code>dlg.FileName = "report.csv"</code>. The user can still change it in the dialog.</p> </div> <div class="schema-faq-section" id="faq-savefile-en-3"><strong class="schema-faq-question"><strong>How do I filter file types in SaveFileDialog?</strong></strong> <p class="schema-faq-answer">Set the <code>Filter</code> property with the pattern <code>Description|*.ext</code>. For multiple types, separate them with pipes: <code>"Text (*.txt)|*.txt|CSV (*.csv)|*.csv"</code>.</p> </div> <div class="schema-faq-section" id="faq-savefile-en-4"><strong class="schema-faq-question"><strong>Does SaveFileDialog actually save the file?</strong></strong> <p class="schema-faq-answer">No. The dialog only lets the user pick a path and file name. You are responsible for writing the file yourself using methods like <code>File.WriteAllText()</code> or <code>File.WriteAllBytes()</code> with the path from <code>dlg.FileName</code>.</p> </div> <div class="schema-faq-section" id="faq-savefile-en-5"><strong class="schema-faq-question"><strong>What is the difference between SaveFileDialog and OpenFileDialog?</strong></strong> <p class="schema-faq-answer">SaveFileDialog lets the user pick a destination for writing a new file, with overwrite confirmation built in. OpenFileDialog lets the user pick an existing file to read. Both return the selected path through the <code>FileName</code> property.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>SaveFileDialog</code> gives your VB.NET application a native file-save experience with minimal code. Set a <code>Filter</code> for file types, use <code>InitialDirectory</code> for the starting folder, and always check <code>DialogResult.OK</code> before writing. For the actual file writing, use <code>File.WriteAllText()</code> for text or <code>File.WriteAllBytes()</code> for binary data. Need to read files instead? The OpenFileDialog follows the same pattern in reverse. For more on writing text files, see the <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>VB.NET Write Text File guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-savefiledialog-en/">VB.NET SaveFileDialog – Save Files with Dialog (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-savefiledialog-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET DataTable – Create, Fill, Filter with Examples (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-datatable-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-datatable-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 20:04:39 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20457</guid>

					<description><![CDATA[<p>The VB.NET DataTable stores tabular data in memory with typed columns, row-level access, and built-in filtering. It is the backbone of data binding in WinForms and the bridge between databases and UI controls like the DataGridView. This guide covers creation, filtering, sorting, database loading, and the most common mistakes. What &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-datatable-en/">VB.NET DataTable – Create, Fill, Filter with Examples (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET DataTable</strong> stores tabular data in memory with typed columns, row-level access, and built-in filtering. It is the backbone of data binding in WinForms and the bridge between databases and UI controls like the DataGridView. This guide covers creation, filtering, sorting, database loading, and the most common mistakes.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building more than a quick prototype?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From data layers to complete desktop applications, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is a DataTable and when to use it</h2>



<p>A <code>DataTable</code> lives in the <code>System.Data</code> namespace and represents a single table of in-memory data. Think of it as a spreadsheet: it has columns with names and types, and rows that hold the actual values.</p>



<p>Use a DataTable when:</p>



<ul class="wp-block-list">
<li>You need to <strong>bind tabular data to a DataGridView</strong> or other WinForms controls</li>



<li>You work with <strong>ADO.NET</strong> and want to fill a table from a database query</li>



<li>You need <strong>built-in filtering and sorting</strong> without writing your own logic</li>



<li>You process <strong>CSV or Excel imports</strong> where rows and columns map naturally</li>
</ul>



<p>If you only need an ordered list of objects, a <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>VB.NET List(Of T)</strong></a> is simpler and more type-safe. If you need key-value lookups, use a <a href="https://robbelroot.de/blog/the-vb-net-dictionary-a-complete-guide/"><strong>Dictionary</strong></a>. DataTable shines when you need a flexible, schema-based structure with filtering, sorting, and UI binding built in.</p>



<h2 class="wp-block-heading">Creating a DataTable with columns</h2>



<p>Every DataTable starts with a schema: the columns that define what data each row can hold.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim table As New DataTable("Contacts")

' Add columns with name and type
table.Columns.Add("Id", GetType(Integer))
table.Columns.Add("Name", GetType(String))
table.Columns.Add("Email", GetType(String))
table.Columns.Add("Age", GetType(Integer))

' Set a primary key
table.PrimaryKey = {table.Columns("Id")}</pre>



<p>Every column has a <code>DataType</code> (Integer, String, DateTime, Decimal, Boolean, etc.) and an optional <code>ColumnName</code>. The primary key enables fast lookups with <code>Rows.Find()</code> and prevents duplicate entries.</p>



<h3 class="wp-block-heading">Column properties</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Auto-increment column
Dim idCol = table.Columns("Id")
idCol.AutoIncrement = True
idCol.AutoIncrementSeed = 1
idCol.AutoIncrementStep = 1

' Default value
table.Columns("Age").DefaultValue = 0

' Disallow nulls
table.Columns("Name").AllowDBNull = False</pre>



<h2 class="wp-block-heading">Adding rows</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Method 1: NewRow + field assignment
Dim row As DataRow = table.NewRow()
row("Name") = "Alice"
row("Email") = "alice@example.com"
row("Age") = 30
table.Rows.Add(row)

' Method 2: Add values directly (column order)
table.Rows.Add(Nothing, "Bob", "bob@example.com", 25)

' Method 3: Add from an Object array
Dim values() As Object = {Nothing, "Clara", "clara@example.com", 35}
table.Rows.Add(values)</pre>



<p>When using <code>AutoIncrement</code> on the Id column, pass <code>Nothing</code> for that field. The DataTable assigns the next value automatically.</p>



<h2 class="wp-block-heading">Reading and accessing data</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Access a specific row by index
Dim firstRow As DataRow = table.Rows(0)
Dim name As String = CStr(firstRow("Name"))

' Iterate all rows
For Each row As DataRow In table.Rows
    Console.WriteLine($"{row("Id")}: {row("Name")} ({row("Email")})")
Next

' Find a row by primary key
Dim found As DataRow = table.Rows.Find(2)
If found IsNot Nothing Then
    Console.WriteLine($"Found: {found("Name")}")
End If

' Total row count
Dim count As Integer = table.Rows.Count</pre>



<p>Fields return <code>Object</code>, so you need to cast them (<code>CStr</code>, <code>CInt</code>, <code>CDec</code>, etc.) or use the typed <code>Field(Of T)</code> extension method: <code>row.Field(Of String)("Name")</code>. The <code>Field</code> method also handles <code>DBNull</code> safely for nullable types.</p>



<h2 class="wp-block-heading">Filtering with Select and DataView</h2>



<p>DataTable has two ways to filter data: the <code>Select()</code> method for quick queries, and <code>DataView</code> for persistent, bindable filters.</p>



<h3 class="wp-block-heading">DataTable.Select()</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Filter by condition
Dim olderThan25() As DataRow = table.Select("Age > 25")

' Filter + sort
Dim sorted() As DataRow = table.Select("Age > 25", "Name ASC")

' Multiple conditions
Dim specific() As DataRow = table.Select("Age > 20 AND Name LIKE 'A%'")

' Process results
For Each row In olderThan25
    Console.WriteLine(row("Name"))
Next</pre>



<p>The filter expression uses a SQL-like syntax: <code>=</code>, <code>&lt;&gt;</code>, <code>&gt;</code>, <code>&lt;</code>, <code>LIKE</code>, <code>AND</code>, <code>OR</code>, <code>IN</code>, <code>IS NULL</code>. The sort expression takes a column name followed by <code>ASC</code> or <code>DESC</code>.</p>



<h3 class="wp-block-heading">DataView for bindable filtering</h3>



<p>A <code>DataView</code> wraps a DataTable and provides a filtered, sorted view that you can bind directly to UI controls:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim view As New DataView(table)
view.RowFilter = "Age >= 30"
view.Sort = "Name ASC"

' Bind to DataGridView
DataGridView1.DataSource = view

' Change the filter at runtime (UI updates automatically)
view.RowFilter = "Name LIKE 'B%'"</pre>



<p>DataView is the better choice for <a href="https://robbelroot.de/blog/creating-a-vb-net-datagridview-filter-functionality/"><strong>DataGridView filter functionality</strong></a> because it updates the bound control automatically when you change <code>RowFilter</code> or <code>Sort</code>.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a scalable .NET data layer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From DataTables to complete application architecture: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Sorting rows</h2>



<p>DataTable itself has no <code>Sort()</code> method. You sort through <code>Select()</code> or <code>DataView</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Sort with Select (returns DataRow array)
Dim sortedRows() As DataRow = table.Select("", "Age DESC")

' Sort with DataView (bindable)
Dim view As New DataView(table)
view.Sort = "Name ASC, Age DESC"

' Sort with LINQ (returns new collection)
Dim linqSorted = From row In table.AsEnumerable()
                 Order By row.Field(Of String)("Name")
                 Select row</pre>



<p>For DataGridView binding, use DataView. For processing rows in code, the <code>Select()</code> method or LINQ both work well.</p>



<h2 class="wp-block-heading">Updating and deleting rows</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Update a field
table.Rows(0)("Email") = "alice.new@example.com"

' Update by primary key
Dim target As DataRow = table.Rows.Find(2)
If target IsNot Nothing Then
    target("Age") = 26
End If

' Delete a row (marks it for deletion)
table.Rows(0).Delete()

' Remove a row immediately
table.Rows.Remove(table.Rows(0))

' Accept all pending changes
table.AcceptChanges()</pre>



<p>There is an important difference: <code>Delete()</code> marks the row with <code>RowState = Deleted</code> but keeps it in the table until <code>AcceptChanges()</code> is called. <code>Remove()</code> deletes the row immediately. Use <code>Delete()</code> when you need change tracking (e.g. syncing back to a database with a DataAdapter).</p>



<h2 class="wp-block-heading">Binding a DataTable to a DataGridView</h2>



<p>One of the most common uses of DataTable is displaying data in a <a href="https://robbelroot.de/blog/creating-a-vb-net-datagridview-filter-functionality/"><strong>DataGridView</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Direct binding
DataGridView1.DataSource = table

' Binding via DataView (with filter + sort)
Dim view As New DataView(table)
view.RowFilter = "Age > 25"
view.Sort = "Name ASC"
DataGridView1.DataSource = view</pre>



<p>The DataGridView auto-generates columns from the DataTable schema. Changes in the grid are written back to the DataTable automatically. If you want a read-only grid, set <code>DataGridView1.ReadOnly = True</code>.</p>



<h3 class="wp-block-heading">Refreshing the grid after data changes</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Add a row programmatically
table.Rows.Add(Nothing, "David", "david@example.com", 28)

' Grid updates automatically when bound to the DataTable
' No manual refresh needed

' If you replace the entire DataTable, re-bind it
DataGridView1.DataSource = Nothing
DataGridView1.DataSource = newTable</pre>



<h2 class="wp-block-heading">Loading data from a database</h2>



<p>DataTable works with any ADO.NET provider. Here is an example using <a href="https://robbelroot.de/blog/vbnet-sqlite-en/"><strong>SQLite</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.Data.SQLite

Dim table As New DataTable()

Using connection As New SQLiteConnection("Data Source=app.db")
    connection.Open()
    Using command As New SQLiteCommand("SELECT * FROM Contacts", connection)
        Using reader As SQLiteDataReader = command.ExecuteReader()
            table.Load(reader)
        End Using
    End Using
End Using

' table now contains all rows from the Contacts table
DataGridView1.DataSource = table</pre>



<p>The <code>Load()</code> method reads all rows from a DataReader and creates the columns automatically based on the query result. This works with SQL Server, MySQL, PostgreSQL, or any other ADO.NET provider.</p>



<h3 class="wp-block-heading">Alternative: using a DataAdapter</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim table As New DataTable()

Using connection As New SQLiteConnection("Data Source=app.db")
    Using adapter As New SQLiteDataAdapter("SELECT * FROM Contacts", connection)
        adapter.Fill(table)
    End Using
End Using</pre>



<p>The DataAdapter approach is shorter and also supports writing changes back to the database with <code>adapter.Update(table)</code>. For simpler, strongly-typed database access without DataTable, consider <a href="https://robbelroot.de/blog/vbnet-dapper-en/"><strong>Dapper</strong></a> as an alternative.</p>



<h2 class="wp-block-heading">Converting between DataTable and List</h2>



<p>Sometimes you need to switch between a DataTable and a typed <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>List(Of T)</strong></a>:</p>



<h3 class="wp-block-heading">DataTable to List(Of T)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Contact
    Public Property Id As Integer
    Public Property Name As String
    Public Property Email As String
    Public Property Age As Integer
End Class

' Convert DataTable rows to a typed List
Dim contacts = table.AsEnumerable().Select(Function(row)
    Return New Contact With {
        .Id = row.Field(Of Integer)("Id"),
        .Name = row.Field(Of String)("Name"),
        .Email = row.Field(Of String)("Email"),
        .Age = row.Field(Of Integer)("Age")
    }
End Function).ToList()</pre>



<h3 class="wp-block-heading">List(Of T) to DataTable</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Function ToDataTable(contacts As List(Of Contact)) As DataTable
    Dim table As New DataTable()
    table.Columns.Add("Id", GetType(Integer))
    table.Columns.Add("Name", GetType(String))
    table.Columns.Add("Email", GetType(String))
    table.Columns.Add("Age", GetType(Integer))

    For Each c In contacts
        table.Rows.Add(c.Id, c.Name, c.Email, c.Age)
    Next

    Return table
End Function</pre>



<p>The LINQ approach with <code>AsEnumerable()</code> requires a reference to <code>System.Data.DataSetExtensions</code>. In modern .NET (5+), this is included by default.</p>



<h2 class="wp-block-heading">Common pitfalls</h2>



<h3 class="wp-block-heading">Ignoring DBNull</h3>



<p>DataTable fields can be <code>DBNull.Value</code> instead of <code>Nothing</code>. A direct cast will throw an <code>InvalidCastException</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - throws InvalidCastException if Email is DBNull
Dim email As String = CStr(row("Email"))

' GOOD - check for DBNull first
Dim email As String = If(row.IsNull("Email"), "", CStr(row("Email")))

' BEST - use the typed Field extension
Dim email As String = row.Field(Of String)("Email")  ' returns Nothing for DBNull</pre>



<h3 class="wp-block-heading">Deleting rows in a For Each loop</h3>



<p>Just like with a <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>List</strong></a>, modifying a DataTable while iterating throws an exception. Use a backward <code>For</code> loop or collect rows first:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - modifying during iteration
For Each row As DataRow In table.Rows
    If CInt(row("Age")) &lt; 18 Then
        row.Delete()
    End If
Next

' GOOD - iterate backwards
For i As Integer = table.Rows.Count - 1 To 0 Step -1
    If CInt(table.Rows(i)("Age")) &lt; 18 Then
        table.Rows(i).Delete()
    End If
Next
table.AcceptChanges()</pre>



<h3 class="wp-block-heading">Forgetting AcceptChanges</h3>



<p>When you call <code>Delete()</code> on a row, it is only marked as deleted. The row still shows up in <code>table.Rows.Count</code> and can cause unexpected behavior. Call <code>AcceptChanges()</code> after a batch of deletions, or use <code>Remove()</code> for immediate removal.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from clean data layers to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-datatable-en-1"><strong class="schema-faq-question"><strong>How do I create a DataTable in VB.NET?</strong></strong> <p class="schema-faq-answer">Create it with <code>Dim table As New DataTable()</code>, then add columns with <code>table.Columns.Add("Name", GetType(String))</code> and rows with <code>table.Rows.Add(values)</code>. Each column needs a name and a data type.</p> </div> <div class="schema-faq-section" id="faq-datatable-en-2"><strong class="schema-faq-question"><strong>How do I filter a DataTable in VB.NET?</strong></strong> <p class="schema-faq-answer">Use <code>table.Select("Age > 25")</code> for quick queries that return a DataRow array. For persistent, bindable filters, create a DataView with <code>view.RowFilter = "Age > 25"</code> and bind it to your DataGridView.</p> </div> <div class="schema-faq-section" id="faq-datatable-en-3"><strong class="schema-faq-question"><strong>How do I bind a DataTable to a DataGridView?</strong></strong> <p class="schema-faq-answer">Set <code>DataGridView1.DataSource = table</code>. The grid auto-generates columns from the table schema. For filtered views, bind a DataView instead: <code>DataGridView1.DataSource = New DataView(table)</code>.</p> </div> <div class="schema-faq-section" id="faq-datatable-en-4"><strong class="schema-faq-question"><strong>What is the difference between DataTable and List(Of T)?</strong></strong> <p class="schema-faq-answer">A DataTable stores untyped rows with columns defined at runtime, supports SQL-like filtering, and binds natively to DataGridView. A List(Of T) holds strongly-typed objects, is simpler to use, but requires manual filtering with LINQ.</p> </div> <div class="schema-faq-section" id="faq-datatable-en-5"><strong class="schema-faq-question"><strong>How do I load database data into a DataTable?</strong></strong> <p class="schema-faq-answer">Use <code>table.Load(reader)</code> with any ADO.NET DataReader, or use a DataAdapter with <code>adapter.Fill(table)</code>. Both approaches create columns automatically from the query result.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>DataTable</code> is the right choice when you need schema-based, filterable tabular data with native DataGridView binding. Use <code>Select()</code> for quick queries, <code>DataView</code> for live-filtered UI binding, and <code>Field(Of T)</code> to avoid DBNull issues. For simpler use cases with typed objects, a <a href="https://robbelroot.de/blog/vbnet-list-en/"><strong>List(Of T)</strong></a> is often the better fit. For database access without DataTable, take a look at <a href="https://robbelroot.de/blog/vbnet-dapper-en/"><strong>Dapper</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-datatable-en/">VB.NET DataTable – Create, Fill, Filter with Examples (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-datatable-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET List – Create, Sort, Filter with Examples (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-list-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-list-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 14:20:36 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20449</guid>

					<description><![CDATA[<p>The VB.NET List is one of the most used data structures in .NET development. It stores objects in an ordered, resizable collection and gives you methods to add, remove, sort, and search items with minimal code. This guide covers everything from basic creation to LINQ queries and performance considerations. What &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-list-en/">VB.NET List – Create, Sort, Filter with Examples (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The <strong>VB.NET List</strong> is one of the most used data structures in .NET development. It stores objects in an ordered, resizable collection and gives you methods to add, remove, sort, and search items with minimal code. This guide covers everything from basic creation to LINQ queries and performance considerations.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need .NET help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Building something bigger than a list?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. From data structures to complete application architecture, I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is a List(Of T) and when to use it</h2>



<p>A <code>List(Of T)</code> is a generic collection from the <code>System.Collections.Generic</code> namespace. The <code>T</code> stands for the type of objects you want to store: <code>List(Of String)</code> holds strings, <code>List(Of Integer)</code> holds numbers, <code>List(Of Customer)</code> holds your own objects.</p>



<p>Use a List when:</p>



<ul class="wp-block-list">
<li>You need an <strong>ordered</strong> collection where items have a position (index)</li>



<li>The <strong>number of items changes</strong> at runtime (unlike arrays, which have a fixed size)</li>



<li>You want built-in methods for <strong>sorting, searching, and filtering</strong></li>



<li>You access items <strong>by position</strong>, not by a key</li>
</ul>



<p>If you need key-value lookups instead (e.g. finding a customer by ID), use a <a href="https://robbelroot.de/blog/the-vb-net-dictionary-a-complete-guide/"><strong>VB.NET Dictionary</strong></a>. Lists search by iterating through every item, which gets slow with large datasets. Dictionaries find entries in constant time.</p>



<h2 class="wp-block-heading">Creating a List</h2>



<p>The most common ways to create a <code>List(Of T)</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Empty list
Dim names As New List(Of String)

' List with initial values
Dim numbers As New List(Of Integer) From {3, 5, 7, 9}

' List of custom objects
Dim customers As New List(Of Customer) From {
    New Customer With {.Name = "Alice", .Email = "alice@example.com"},
    New Customer With {.Name = "Bob", .Email = "bob@example.com"}
}</pre>



<p>The <code>From</code> keyword together with curly braces is a collection initializer. It calls <code>Add()</code> for each item behind the scenes.</p>



<h3 class="wp-block-heading">Setting an initial capacity</h3>



<p>If you know roughly how many items the list will hold, set the capacity upfront. This avoids repeated internal array resizing:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Pre-allocate space for ~500 items
Dim largeList As New List(Of String)(500)</pre>



<p>This doesn&#8217;t limit the list to 500 items. It just avoids unnecessary memory allocations until you exceed that number.</p>



<h2 class="wp-block-heading">Adding items</h2>



<h3 class="wp-block-heading">Add and AddRange</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim names As New List(Of String)

' Add single items
names.Add("Alice")
names.Add("Bob")

' Add multiple items at once
names.AddRange({"Clara", "David", "Eva"})

' Add items from another list
Dim moreNames As New List(Of String) From {"Frank", "Grace"}
names.AddRange(moreNames)</pre>



<h3 class="wp-block-heading">Insert at a specific position</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Insert "Zara" at position 0 (beginning)
names.Insert(0, "Zara")

' Insert at position 2
names.Insert(2, "Maria")</pre>



<p>Be aware that <code>Insert()</code> shifts all subsequent items, which costs O(n). For frequent insertions at the beginning, consider a <code>LinkedList(Of T)</code> instead.</p>



<h2 class="wp-block-heading">Accessing items</h2>



<p>Items are accessed by their zero-based index:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim names As New List(Of String) From {"Alice", "Bob", "Clara"}

' Access by index
Dim first As String = names(0)    ' "Alice"
Dim last As String = names(names.Count - 1)  ' "Clara"

' Iterate with For Each
For Each name In names
    Console.WriteLine(name)
Next

' Iterate with index
For i As Integer = 0 To names.Count - 1
    Console.WriteLine($"[{i}] {names(i)}")
Next</pre>



<p>Accessing an invalid index throws an <code>ArgumentOutOfRangeException</code>. Always check <code>Count</code> before using a specific index, or use methods like <code>FirstOrDefault()</code> from LINQ.</p>



<h2 class="wp-block-heading">Removing items</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim names As New List(Of String) From {"Alice", "Bob", "Clara", "David"}

' Remove by value (first occurrence)
names.Remove("Bob")   ' returns True if found

' Remove by index
names.RemoveAt(0)      ' removes "Alice"

' Remove last item
If names.Count > 0 Then
    names.RemoveAt(names.Count - 1)
End If

' Remove all items matching a condition
names.RemoveAll(Function(n) n.StartsWith("C"))

' Remove everything
names.Clear()</pre>



<p><code>Remove()</code> searches the list from the beginning and removes the first match. If the item doesn&#8217;t exist, it returns <code>False</code> without throwing an exception. <code>RemoveAt()</code> on an invalid index does throw.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a scalable .NET application?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From data structures to complete desktop applications: I design software that lasts. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Sorting a List</h2>



<h3 class="wp-block-heading">Simple sort</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim names As New List(Of String) From {"Clara", "Alice", "Bob"}

' Sort alphabetically (in-place)
names.Sort()
' Result: Alice, Bob, Clara

' Reverse the order
names.Reverse()
' Result: Clara, Bob, Alice</pre>



<h3 class="wp-block-heading">Sort with custom comparison</h3>



<p>For custom objects, pass a comparison lambda to <code>Sort()</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Contact
    Public Property Name As String
    Public Property Age As Integer
End Class

Dim contacts As New List(Of Contact) From {
    New Contact With {.Name = "Clara", .Age = 30},
    New Contact With {.Name = "Alice", .Age = 25},
    New Contact With {.Name = "Bob", .Age = 35}
}

' Sort by name
contacts.Sort(Function(a, b) String.Compare(a.Name, b.Name))

' Sort by age (descending)
contacts.Sort(Function(a, b) b.Age.CompareTo(a.Age))</pre>



<h3 class="wp-block-heading">Sorting with LINQ (non-destructive)</h3>



<p><code>Sort()</code> modifies the original list. If you want a new sorted list without changing the original, use LINQ:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Original stays unchanged
Dim sorted = contacts.OrderBy(Function(c) c.Name).ToList()
Dim sortedDesc = contacts.OrderByDescending(Function(c) c.Age).ToList()</pre>



<h2 class="wp-block-heading">Searching and filtering with LINQ</h2>



<p>LINQ is the most powerful way to query lists in VB.NET. Import <code>System.Linq</code> (usually imported by default) and you get a full set of query methods:</p>



<h3 class="wp-block-heading">Find items</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim contacts As New List(Of Contact) From {
    New Contact With {.Name = "Alice", .Age = 25},
    New Contact With {.Name = "Bob", .Age = 35},
    New Contact With {.Name = "Clara", .Age = 30}
}

' Find first match (returns Nothing if not found)
Dim alice = contacts.FirstOrDefault(Function(c) c.Name = "Alice")

' Check if any item matches
Dim hasOlderThan30 As Boolean = contacts.Any(Function(c) c.Age > 30)

' Check if all items match
Dim allAdults As Boolean = contacts.All(Function(c) c.Age >= 18)</pre>



<h3 class="wp-block-heading">Filter items</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Get all contacts older than 25
Dim olderContacts = contacts.Where(Function(c) c.Age > 25).ToList()

' Get just the names
Dim names = contacts.Select(Function(c) c.Name).ToList()

' Combine: filter + transform
Dim olderNames = contacts _
    .Where(Function(c) c.Age > 25) _
    .Select(Function(c) c.Name) _
    .ToList()</pre>



<h3 class="wp-block-heading">Aggregate queries</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Count, Min, Max, Average, Sum
Dim count As Integer = contacts.Count
Dim youngest As Integer = contacts.Min(Function(c) c.Age)
Dim oldest As Integer = contacts.Max(Function(c) c.Age)
Dim averageAge As Double = contacts.Average(Function(c) c.Age)

' Distinct values
Dim uniqueAges = contacts.Select(Function(c) c.Age).Distinct().ToList()</pre>



<h2 class="wp-block-heading">Converting lists</h2>



<p>Lists can be converted to and from other collection types:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' List to Array
Dim namesArray As String() = names.ToArray()

' Array to List
Dim backToList As List(Of String) = namesArray.ToList()

' List to Dictionary (key must be unique)
Dim contactDict = contacts.ToDictionary(
    Function(c) c.Name,
    Function(c) c)

' String from List (join)
Dim csv As String = String.Join(", ", names)
' Result: "Alice, Bob, Clara"</pre>



<p>The <code>ToDictionary()</code> conversion is especially useful when you later need fast lookups by key. Read more about that in the <a href="https://robbelroot.de/blog/the-vb-net-dictionary-a-complete-guide/"><strong>Dictionary guide</strong></a>.</p>



<h2 class="wp-block-heading">List vs. Array vs. Dictionary</h2>



<p>Choosing the right collection type matters for both readability and performance. Here&#8217;s a comparison of the most common choices:</p>



<figure class="wp-block-table"><table><thead><tr><th></th><th>List(Of T)</th><th>Array</th><th>Dictionary(Of TKey, TValue)</th></tr></thead><tbody><tr><td><strong>Size</strong></td><td>Dynamic</td><td>Fixed</td><td>Dynamic</td></tr><tr><td><strong>Access by index</strong></td><td>O(1)</td><td>O(1)</td><td>N/A</td></tr><tr><td><strong>Access by key</strong></td><td>O(n)</td><td>O(n)</td><td>O(1)</td></tr><tr><td><strong>Add item</strong></td><td>O(1) amortized</td><td>N/A (fixed size)</td><td>O(1) amortized</td></tr><tr><td><strong>Remove item</strong></td><td>O(n)</td><td>N/A</td><td>O(1)</td></tr><tr><td><strong>Best for</strong></td><td>Ordered collections, iteration</td><td>Fixed-size buffers, interop</td><td>Key-value lookups</td></tr></tbody></table></figure>



<p>A detailed performance comparison with real benchmarks is available in the <a href="https://robbelroot.de/blog/csharp-list-the-ultimate-guide/"><strong>C# List guide</strong></a>, which applies equally to VB.NET since both use the same .NET runtime.</p>



<h2 class="wp-block-heading">Common pitfalls</h2>



<h3 class="wp-block-heading">Modifying a list during iteration</h3>



<p>This is one of the most frequent beginner mistakes. Removing items inside a <code>For Each</code> loop throws an <code>InvalidOperationException</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' BAD - throws InvalidOperationException!
For Each name In names
    If name.StartsWith("A") Then
        names.Remove(name)
    End If
Next

' GOOD - use RemoveAll instead
names.RemoveAll(Function(n) n.StartsWith("A"))

' ALSO GOOD - iterate backwards with For
For i As Integer = names.Count - 1 To 0 Step -1
    If names(i).StartsWith("A") Then
        names.RemoveAt(i)
    End If
Next</pre>



<h3 class="wp-block-heading">Using a List for lookups</h3>



<p>If you frequently search for items by a unique property (name, ID, email), a <code>List</code> is the wrong choice. Every search iterates the entire list. A <code>Dictionary</code> finds the item instantly. This difference becomes massive with thousands of entries, as demonstrated in this <a href="https://robbelroot.de/blog/csharp-list-the-ultimate-guide/"><strong>benchmark comparison</strong></a>.</p>



<h3 class="wp-block-heading">Forgetting to call ToList()</h3>



<p>LINQ methods like <code>Where()</code> and <code>Select()</code> return lazy <code>IEnumerable</code> objects, not lists. Each time you iterate, the query runs again. Call <code>.ToList()</code> to materialize the result:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Lazy - runs the filter twice!
Dim filtered = names.Where(Function(n) n.Length > 3)
Console.WriteLine(filtered.Count())   ' filter runs here
Console.WriteLine(filtered.Count())   ' filter runs again

' Better - materialize once
Dim filteredList = names.Where(Function(n) n.Length > 3).ToList()
Console.WriteLine(filteredList.Count)  ' just reads the count
Console.WriteLine(filteredList.Count)  ' same, no re-evaluation</pre>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from clean data structures to finished desktop applications. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-list-en-1"><strong class="schema-faq-question"><strong>How do I create a List in VB.NET?</strong></strong> <p class="schema-faq-answer">Use <code>Dim myList As New List(Of String)</code> for an empty list, or <code>Dim myList As New List(Of String) From {"Alice", "Bob"}</code> with initial values. Replace <code>String</code> with whatever type you need.</p> </div> <div class="schema-faq-section" id="faq-list-en-2"><strong class="schema-faq-question"><strong>What is the difference between List and Array in VB.NET?</strong></strong> <p class="schema-faq-answer">Arrays have a fixed size set at creation. Lists grow and shrink dynamically with <code>Add()</code> and <code>Remove()</code> methods. Use arrays for fixed-size buffers, use lists whenever the number of items can change.</p> </div> <div class="schema-faq-section" id="faq-list-en-3"><strong class="schema-faq-question"><strong>How do I sort a List in VB.NET?</strong></strong> <p class="schema-faq-answer">Call <code>myList.Sort()</code> for default sorting. For custom objects, use a lambda: <code>myList.Sort(Function(a, b) a.Name.CompareTo(b.Name))</code>. Use LINQ <code>OrderBy()</code> if you want a new list without modifying the original.</p> </div> <div class="schema-faq-section" id="faq-list-en-4"><strong class="schema-faq-question"><strong>How do I remove items from a List in VB.NET?</strong></strong> <p class="schema-faq-answer">Use <code>Remove(item)</code> to remove by value, <code>RemoveAt(index)</code> by position, or <code>RemoveAll(Function(x) condition)</code> to remove all matching items. Never remove items inside a <code>For Each</code> loop.</p> </div> <div class="schema-faq-section" id="faq-list-en-5"><strong class="schema-faq-question"><strong>When should I use a Dictionary instead of a List?</strong></strong> <p class="schema-faq-answer">Use a Dictionary whenever you need to look up items by a unique key (name, ID, email). A List needs to iterate every entry to find a match (O(n)), while a Dictionary does it in constant time (O(1)).</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>The <code>List(Of T)</code> is the go-to collection for ordered, resizable data in VB.NET. Use <code>Add()</code> and <code>RemoveAll()</code> for basic operations, <code>Sort()</code> with a lambda for custom sorting, and LINQ for filtering and transforming. For key-based lookups, switch to a <a href="https://robbelroot.de/blog/the-vb-net-dictionary-a-complete-guide/"><strong>Dictionary</strong></a>. If you&#8217;re working with C# as well, the same concepts apply with slightly different syntax, as shown in the <a href="https://robbelroot.de/blog/csharp-list-the-ultimate-guide/"><strong>C# List guide</strong></a>.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-list-en/">VB.NET List – Create, Sort, Filter with Examples (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-list-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET Dapper – Micro ORM for Clean Data Access (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-dapper-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-dapper-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 08 Apr 2026 10:01:08 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20439</guid>

					<description><![CDATA[<p>Dapper is the fastest way to replace tedious SqliteCommand boilerplate in your VB.NET application with clean, strongly typed data access. In this guide I&#8217;ll show you how to set up Dapper via NuGet, map query results directly to classes, and perform all CRUD operations with minimal code. If you&#8217;ve been &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-dapper-en/">VB.NET Dapper – Micro ORM for Clean Data Access (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>Dapper</strong> is the fastest way to replace tedious <code>SqliteCommand</code> boilerplate in your <strong>VB.NET</strong> application with clean, strongly typed data access. In this guide I&#8217;ll show you how to set up Dapper via NuGet, map query results directly to classes, and perform all CRUD operations with minimal code. If you&#8217;ve been writing <code>AddWithValue</code> and <code>ExecuteReader</code> by hand, this is your next step.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need database help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Drowning in ADO.NET boilerplate?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. Whether it&#039;s Dapper, EF Core, or raw SQL &#8211; I can help you pick the right data access strategy.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">What is Dapper and why use it?</h2>



<p>Dapper is a lightweight &#8222;micro ORM&#8220; for .NET, originally built by the Stack Overflow team to solve their own performance problems. It sits on top of <code>IDbConnection</code> and extends it with convenience methods like <code>Query(Of T)</code> and <code>Execute()</code>. Here&#8217;s why it matters:</p>



<ul class="wp-block-list">
<li><strong>No boilerplate</strong> – no more manual <code>SqliteCommand</code>, <code>AddWithValue</code>, <code>ExecuteReader</code>, and column-by-column mapping</li>



<li><strong>Strongly typed</strong> – query results map directly to your VB.NET classes by matching property names to column names</li>



<li><strong>Fast</strong> – Dapper is nearly as fast as raw ADO.NET, much faster than Entity Framework for simple queries</li>



<li><strong>No magic</strong> – you write real SQL, so you always know exactly what hits the database</li>



<li><strong>Works with any database</strong> – SQLite, SQL Server, PostgreSQL, MySQL, anything that has an <code>IDbConnection</code></li>
</ul>



<p>If you&#8217;ve followed the <a href="https://robbelroot.de/blog/vbnet-sqlite-en/"><strong>VB.NET SQLite article</strong></a>, you already know how to create a database and run queries with <code>SqliteCommand</code>. Dapper replaces that manual work while keeping full control over your SQL.</p>



<h2 class="wp-block-heading">Installing Dapper via NuGet</h2>



<p>Open the Package Manager Console in Visual Studio (<em>Tools → NuGet Package Manager → Package Manager Console</em>) and run:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Install-Package Dapper
Install-Package Microsoft.Data.Sqlite</pre>



<p>You need both packages: <code>Dapper</code> provides the extension methods, and <code>Microsoft.Data.Sqlite</code> provides the database connection. If you already have <code>Microsoft.Data.Sqlite</code> installed from a previous project, just add <code>Dapper</code>.</p>



<p>Alternatively, right-click your project in Solution Explorer → <em>Manage NuGet Packages</em> → search for <code>Dapper</code> → Install. Then repeat for <code>Microsoft.Data.Sqlite</code> if you haven&#8217;t already.</p>



<h2 class="wp-block-heading">Setting up the model class</h2>



<p>Before writing any queries, define a class whose properties match the column names in your table. Dapper maps columns to properties by name, case-insensitive:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Contact
    Public Property Id As Long
    Public Property Name As String
    Public Property Email As String
    Public Property Phone As String
    Public Property CreatedAt As String
End Class</pre>



<p>That&#8217;s all Dapper needs. No attributes, no base classes, no configuration files. If a column name doesn&#8217;t match a property name, you can use a SQL alias: <code>SELECT first_name AS Name</code>.</p>



<h2 class="wp-block-heading">Connecting and creating tables</h2>



<p>Dapper works on top of any <code>IDbConnection</code>. You still create the connection yourself. Use <code>AppContext.BaseDirectory</code> to build a reliable path, as explained in the <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>Application Path article</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Dapper
Imports Microsoft.Data.Sqlite

Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "myapp.db")
Dim connectionString As String = $"Data Source={dbPath}"

Using connection As New SqliteConnection(connectionString)
    connection.Open()

    connection.Execute("
        CREATE TABLE IF NOT EXISTS Contacts (
            Id INTEGER PRIMARY KEY AUTOINCREMENT,
            Name TEXT NOT NULL,
            Email TEXT,
            Phone TEXT,
            CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
        )")
End Using</pre>



<p>Notice how <code>connection.Execute()</code> replaces the entire <code>SqliteCommand</code> / <code>ExecuteNonQuery()</code> pattern. Dapper opens the connection automatically if it&#8217;s closed, but I prefer opening explicitly for clarity.</p>



<h2 class="wp-block-heading">Inserting data</h2>



<p>With raw ADO.NET you&#8217;d create a command, add parameters one by one, and call <code>ExecuteNonQuery()</code>. With Dapper, you pass an anonymous object and it maps the properties to parameter names:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    connection.Execute(
        "INSERT INTO Contacts (Name, Email, Phone) VALUES (@Name, @Email, @Phone)",
        New With {
            .Name = "John Doe",
            .Email = "john@example.com",
            .Phone = "+49 123 456789"
        })
End Using</pre>



<p>Dapper automatically creates parameterized queries from the anonymous object. There is no risk of SQL injection because the values are never concatenated into the SQL string.</p>



<h3 class="wp-block-heading">Inserting multiple rows at once</h3>



<p>Pass a list instead of a single object and Dapper will execute the statement once per item:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim contacts As New List(Of Contact) From {
    New Contact With {.Name = "Alice", .Email = "alice@example.com"},
    New Contact With {.Name = "Bob", .Email = "bob@example.com"},
    New Contact With {.Name = "Clara", .Email = "clara@example.com"}
}

Using connection As New SqliteConnection(connectionString)
    connection.Open()

    connection.Execute(
        "INSERT INTO Contacts (Name, Email) VALUES (@Name, @Email)",
        contacts)
End Using</pre>



<p>For large datasets (hundreds or thousands of rows), wrap this in a transaction for significantly better performance. More on that in the transactions section below.</p>



<h2 class="wp-block-heading">Querying data</h2>



<p>This is where Dapper really shines. Instead of looping through a <code>DataReader</code> and mapping each column manually, you call <code>Query(Of T)</code> and get a typed collection back:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim contacts = connection.Query(Of Contact)(
        "SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name")

    For Each contact In contacts
        Console.WriteLine($"[{contact.Id}] {contact.Name} - {contact.Email}")
    Next
End Using</pre>



<p>The result is an <code>IEnumerable(Of Contact)</code> that you can iterate, filter with LINQ, or bind directly to a <a href="https://robbelroot.de/blog/creating-a-vb-net-datagridview-filter-functionality/"><strong>DataGridView</strong></a>.</p>



<h3 class="wp-block-heading">Querying with parameters</h3>



<p>Pass parameters as an anonymous object, just like with <code>Execute()</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim results = connection.Query(Of Contact)(
        "SELECT Id, Name, Email FROM Contacts WHERE Name LIKE @Search",
        New With {.Search = $"%{searchTerm}%"})

    For Each contact In results
        Console.WriteLine($"[{contact.Id}] {contact.Name}")
    Next
End Using</pre>



<h3 class="wp-block-heading">Getting a single row</h3>



<p>Use <code>QueryFirstOrDefault(Of T)</code> when you expect exactly one result (or nothing):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim contact = connection.QueryFirstOrDefault(Of Contact)(
        "SELECT Id, Name, Email, Phone FROM Contacts WHERE Id = @Id",
        New With {.Id = 1})

    If contact IsNot Nothing Then
        Console.WriteLine($"{contact.Name} ({contact.Email})")
    End If
End Using</pre>



<p>Other variants: <code>QueryFirst</code> (throws if no rows), <code>QuerySingle</code> (throws if not exactly one row), <code>QuerySingleOrDefault</code> (throws if more than one row).</p>



<h3 class="wp-block-heading">Scalar queries</h3>



<p>For queries that return a single value, use <code>ExecuteScalar(Of T)</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim count = connection.ExecuteScalar(Of Long)(
        "SELECT COUNT(*) FROM Contacts")

    Console.WriteLine($"{count} contacts in database")
End Using</pre>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a clean data access layer for your .NET app?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From SQLite to SQL Server, from raw ADO.NET to Dapper: I design data layers that scale. Let&#039;s talk about your project.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Updating and deleting data</h2>



<p>Updates and deletes follow the same pattern as inserts. <code>Execute()</code> returns the number of affected rows:</p>



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



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim rowsAffected = connection.Execute(
        "UPDATE Contacts SET Email = @Email WHERE Id = @Id",
        New With {.Email = "new-email@example.com", .Id = 1})

    Console.WriteLine($"{rowsAffected} row(s) updated.")
End Using</pre>



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



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Dim rowsAffected = connection.Execute(
        "DELETE FROM Contacts WHERE Id = @Id",
        New With {.Id = 5})

    Console.WriteLine($"{rowsAffected} row(s) deleted.")
End Using</pre>



<h2 class="wp-block-heading">Complete example: ContactRepository with Dapper</h2>



<p>Here&#8217;s a complete repository class that wraps all CRUD operations. Compare this to the <a href="https://robbelroot.de/blog/vbnet-sqlite-en/"><strong>manual ADO.NET version in the SQLite article</strong></a> to see much code Dapper saves:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Dapper
Imports Microsoft.Data.Sqlite

Public Class ContactRepository

    Private ReadOnly _connectionString As String

    Public Sub New(connectionString As String)
        _connectionString = connectionString
    End Sub

    Public Sub InitializeDatabase()
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()
            connection.Execute("
                CREATE TABLE IF NOT EXISTS Contacts (
                    Id INTEGER PRIMARY KEY AUTOINCREMENT,
                    Name TEXT NOT NULL,
                    Email TEXT,
                    Phone TEXT,
                    CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
                )")
        End Using
    End Sub

    Public Function AddContact(name As String, email As String, phone As String) As Long
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            connection.Execute(
                "INSERT INTO Contacts (Name, Email, Phone) VALUES (@Name, @Email, @Phone)",
                New With {.Name = name, .Email = email, .Phone = phone})

            Return connection.ExecuteScalar(Of Long)("SELECT last_insert_rowid()")
        End Using
    End Function

    Public Function GetAllContacts() As List(Of Contact)
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()
            Return connection.Query(Of Contact)(
                "SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name").AsList()
        End Using
    End Function

    Public Function GetContactById(id As Long) As Contact
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()
            Return connection.QueryFirstOrDefault(Of Contact)(
                "SELECT Id, Name, Email, Phone FROM Contacts WHERE Id = @Id",
                New With {.Id = id})
        End Using
    End Function

    Public Function UpdateContact(id As Long, name As String, email As String, phone As String) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()
            Dim rows = connection.Execute(
                "UPDATE Contacts SET Name = @Name, Email = @Email, Phone = @Phone WHERE Id = @Id",
                New With {.Name = name, .Email = email, .Phone = phone, .Id = id})
            Return rows > 0
        End Using
    End Function

    Public Function DeleteContact(id As Long) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()
            Dim rows = connection.Execute(
                "DELETE FROM Contacts WHERE Id = @Id",
                New With {.Id = id})
            Return rows > 0
        End Using
    End Function
End Class</pre>



<p>Usage is straightforward:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim repo As New ContactRepository($"Data Source={dbPath}")
repo.InitializeDatabase()

Dim newId = repo.AddContact("John Doe", "john@example.com", "+49 123 456789")
Console.WriteLine($"Created contact with ID {newId}")

Dim allContacts = repo.GetAllContacts()
For Each c In allContacts
    Console.WriteLine($"[{c.Id}] {c.Name} - {c.Email}")
Next</pre>



<h2 class="wp-block-heading">Transactions with Dapper</h2>



<p>For bulk operations, wrap them in a transaction. Without a transaction, SQLite commits after every single statement, which is extremely slow for hundreds of inserts:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection(connectionString)
    connection.Open()

    Using transaction = connection.BeginTransaction()
        Dim contacts As New List(Of Object)
        For i As Integer = 1 To 1000
            contacts.Add(New With {
                .Name = $"Contact {i}",
                .Email = $"contact{i}@example.com"
            })
        Next

        connection.Execute(
            "INSERT INTO Contacts (Name, Email) VALUES (@Name, @Email)",
            contacts,
            transaction)

        transaction.Commit()
    End Using
End Using</pre>



<p>The key difference to raw ADO.NET: you pass the <code>transaction</code> object as a parameter to <code>Execute()</code>. No need to set <code>command.Transaction</code> manually. And because Dapper loops through the list internally, you get transaction safety for all 1000 inserts with minimal code.</p>



<h2 class="wp-block-heading">Async queries: keeping the UI responsive</h2>



<p>Every Dapper method has an async counterpart. If you&#8217;re building a WinForms or WPF application, use them to keep the UI from freezing during database calls. For the fundamentals, check out the <a href="https://robbelroot.de/blog/vbnet-async-await-en/"><strong>VB.NET Async/Await guide</strong></a>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Function LoadContactsAsync() As Task(Of List(Of Contact))
    Using connection As New SqliteConnection(connectionString)
        Await connection.OpenAsync()

        Dim contacts = Await connection.QueryAsync(Of Contact)(
            "SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name")

        Return contacts.AsList()
    End Using
End Function</pre>



<p>Available async methods: <code>QueryAsync</code>, <code>QueryFirstOrDefaultAsync</code>, <code>ExecuteAsync</code>, <code>ExecuteScalarAsync</code>. They work exactly like their synchronous counterparts, just with <code>Await</code>.</p>



<h2 class="wp-block-heading">Dapper vs. raw ADO.NET: side-by-side comparison</h2>



<p>Here&#8217;s the same &#8222;get all contacts&#8220; operation written both ways. First, raw ADO.NET as shown in the <a href="https://robbelroot.de/blog/vbnet-sqlite-en/"><strong>SQLite article</strong></a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Raw ADO.NET - 13 lines
Using connection As New SqliteConnection(connectionString)
    connection.Open()
    Using command As New SqliteCommand("SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name", connection)
        Using reader = command.ExecuteReader()
            While reader.Read()
                Dim contact As New Contact With {
                    .Id = reader.GetInt64(0),
                    .Name = reader.GetString(1),
                    .Email = If(reader.IsDBNull(2), "", reader.GetString(2)),
                    .Phone = If(reader.IsDBNull(3), "", reader.GetString(3))
                }
                contacts.Add(contact)
            End While
        End Using
    End Using
End Using</pre>



<p>Now the same thing with Dapper:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' Dapper - 4 lines
Using connection As New SqliteConnection(connectionString)
    connection.Open()
    Dim contacts = connection.Query(Of Contact)(
        "SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name").AsList()
End Using</pre>



<p>Same result, same SQL, same performance. Dapper handles the reader loop, the null checks, and the property mapping automatically. The more complex your model, the bigger the difference.</p>



<h2 class="wp-block-heading">Common pitfalls and tips</h2>



<h3 class="wp-block-heading">Property names must match column names</h3>



<p>Dapper maps by name, case-insensitive. If your table has <code>first_name</code> but your class has <code>FirstName</code>, use a SQL alias:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim contacts = connection.Query(Of Contact)(
    "SELECT first_name AS FirstName, last_name AS LastName FROM Contacts")</pre>



<h3 class="wp-block-heading">NULL handling</h3>



<p>Dapper handles <code>NULL</code> columns automatically. If a column is <code>NULL</code> and the target property is a <code>String</code>, the property will be <code>Nothing</code>. If you want an empty string instead, initialize your properties with defaults:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Contact
    Public Property Id As Long
    Public Property Name As String = ""
    Public Property Email As String = ""
    Public Property Phone As String = ""
End Class</pre>



<h3 class="wp-block-heading">Don&#8217;t forget Using blocks</h3>



<p>Dapper does not manage connection lifetimes. You still need <code>Using</code> blocks for every connection, exactly like with raw ADO.NET. An open connection locks the SQLite file.</p>



<h3 class="wp-block-heading">Dapper and Dependency Injection</h3>



<p>In larger applications, inject the connection string instead of hardcoding it. The <code>ContactRepository</code> class above already follows this pattern. If you&#8217;re using a DI container like Autofac, check the <a href="https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/"><strong>Dependency Injection guide</strong></a>.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from database design to finished application. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-dapper-en-1"><strong class="schema-faq-question"><strong>What is Dapper in VB.NET?</strong></strong> <p class="schema-faq-answer">Dapper is a lightweight micro ORM that extends <code>IDbConnection</code> with methods like <code>Query(Of T)</code> and <code>Execute()</code>. It maps SQL results directly to VB.NET classes without the overhead of a full ORM like Entity Framework.</p> </div> <div class="schema-faq-section" id="faq-dapper-en-2"><strong class="schema-faq-question"><strong>Is Dapper faster than Entity Framework?</strong></strong> <p class="schema-faq-answer">Yes. Dapper is nearly as fast as raw ADO.NET because it does minimal processing. Entity Framework adds change tracking, proxy generation, and query translation, which makes it slower for simple operations. For CRUD-heavy applications, Dapper is often the better choice.</p> </div> <div class="schema-faq-section" id="faq-dapper-en-3"><strong class="schema-faq-question"><strong>Can I use Dapper with SQLite in VB.NET?</strong></strong> <p class="schema-faq-answer">Yes. Install both <code>Dapper</code> and <code>Microsoft.Data.Sqlite</code> via NuGet. Dapper works with any <code>IDbConnection</code>, so <code>SqliteConnection</code> works out of the box without additional configuration.</p> </div> <div class="schema-faq-section" id="faq-dapper-en-4"><strong class="schema-faq-question"><strong>How do I handle NULL values with Dapper?</strong></strong> <p class="schema-faq-answer">Dapper maps <code>NULL</code> columns to <code>Nothing</code> for reference types. If you prefer empty strings, initialize your class properties with default values like <code>Public Property Email As String = ""</code>.</p> </div> <div class="schema-faq-section" id="faq-dapper-en-5"><strong class="schema-faq-question"><strong>Does Dapper support async/await?</strong></strong> <p class="schema-faq-answer">Yes. Every Dapper method has an async counterpart: <code>QueryAsync</code>, <code>ExecuteAsync</code>, <code>ExecuteScalarAsync</code>, etc. Use them with <code>Await</code> in WinForms or WPF applications to keep the UI responsive.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>Dapper eliminates the repetitive ADO.NET boilerplate while keeping you in full control of your SQL. Install the NuGet package, define a model class, and replace your manual <code>SqliteCommand</code> / <code>DataReader</code> code with <code>Query(Of T)</code> and <code>Execute()</code>. If you haven&#8217;t set up your database yet, start with the <a href="https://robbelroot.de/blog/vbnet-sqlite-en/"><strong>VB.NET SQLite guide</strong></a> first, then come back here to simplify your data access layer.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-dapper-en/">VB.NET Dapper – Micro ORM for Clean Data Access (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-dapper-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET SQLite – Connect, Read &#038; Write Data (2026)</title>
		<link>https://robbelroot.de/blog/vbnet-sqlite-en/</link>
					<comments>https://robbelroot.de/blog/vbnet-sqlite-en/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Tue, 07 Apr 2026 20:32:59 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET (EN)]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=20405</guid>

					<description><![CDATA[<p>SQLite is the easiest way to add a local database to your VB.NET application, no server, no installation, no configuration. In this guide I&#8217;ll show you step by step how to set up SQLite via NuGet, create tables, and perform all CRUD operations with parameterized queries. Full working example included. &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-sqlite-en/">VB.NET SQLite – Connect, Read &amp; Write Data (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>SQLite</strong> is the easiest way to add a local database to your <strong>VB.NET</strong> application, no server, no installation, no configuration. In this guide I&#8217;ll show you step by step how to set up SQLite via NuGet, create tables, and perform all CRUD operations with parameterized queries. Full working example included. → <a href="#downloads" type="internal" id="#downloads"><strong>Go to downloads</strong></a>.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need database help?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Struggling with database integration in .NET?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ve been developing professionally in VB.NET and C# for over 17 years. Whether it&#039;s SQLite, SQL Server, or data architecture – I can help.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	





<h2 class="wp-block-heading">Why SQLite for your VB.NET application?</h2>



<p>There are many database engines available, but SQLite stands out for local VB.NET applications. Here&#8217;s why:</p>



<ul class="wp-block-list">
<li><strong>Zero setup</strong> – no server process, no installation, no admin rights needed</li>



<li><strong>Single file</strong> – the entire database lives in one <code>.db</code> file you can ship with your app</li>



<li><strong>Cross-platform</strong> – works on Windows, Linux, and macOS</li>



<li><strong>Lightweight</strong> – perfect for desktop apps, prototypes, embedded systems, and offline-first scenarios</li>



<li><strong>Reliable</strong> – used by Firefox, Chrome, Android, and millions of other applications</li>
</ul>



<p>If you just need to persist settings or small amounts of data, you could also <a href="https://robbelroot.de/blog/vbnet-write-text-file/"><strong>write a text file in VB.NET</strong></a>. But as soon as you need structured data, queries, or relationships, SQLite is the better choice.</p>



<h2 class="wp-block-heading">Installing the NuGet package</h2>



<p>There are two popular SQLite packages for .NET:</p>



<ol class="wp-block-list">
<li><code>Microsoft.Data.Sqlite</code> – Microsoft&#8217;s official, lightweight package (recommended for .NET 5+ / .NET Framework 4.6.1+)</li>



<li><code>System.Data.SQLite</code> – the older, full-featured package from the SQLite team (supports even .NET Framework 4.0)</li>
</ol>



<p>In this article I&#8217;ll use <code>Microsoft.Data.Sqlite</code> because it&#8217;s lighter and the recommended path going forward. The API is almost identical, so switching later is straightforward.</p>



<h3 class="wp-block-heading">Install via Package Manager Console</h3>



<p>Open the Package Manager Console in Visual Studio (<em>Tools → NuGet Package Manager → Package Manager Console</em>) and run:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Install-Package Microsoft.Data.Sqlite</pre>



<p>Alternatively, right-click your project in Solution Explorer → <em>Manage NuGet Packages</em> → search for <code>Microsoft.Data.Sqlite</code> → Install.</p>



<h3 class="wp-block-heading">Add the Imports statement</h3>



<p>At the top of your code file, add the following import:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Microsoft.Data.Sqlite</pre>



<h2 class="wp-block-heading">Creating a database and connecting</h2>



<p>A big advantage of SQLite: you don&#8217;t need to &#8222;create&#8220; a database manually. If the file doesn&#8217;t exist yet, SQLite creates it automatically when you open a connection.</p>



<h3 class="wp-block-heading">The connection string</h3>



<p>The SQLite connection string is minimal. All you need is the path to the database file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim connectionString As String = "Data Source=myapp.db"</pre>



<p>This creates the file <code>myapp.db</code> in the application&#8217;s working directory. If you want to place the database next to your executable, you can use the <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>VB.NET Application Path</strong></a> to build a reliable path:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "myapp.db")
Dim connectionString As String = $"Data Source={dbPath}"</pre>



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



<p>Always wrap your connection in a <code>Using</code> block. This ensures the connection is properly closed and disposed, even if an exception occurs:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    ' Your database operations here...

End Using ' connection is automatically closed</pre>



<p>This is the basic pattern you&#8217;ll use for every database operation. The <code>Using</code> block is essential to avoid file locks and &#8222;database is locked&#8220; errors (more on that later).</p>



<h2 class="wp-block-heading">Creating tables</h2>



<p>Before you can store data, you need at least one table. Use <code>CREATE TABLE IF NOT EXISTS</code> to safely run the command every time your app starts, without errors if the table already exists:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "
            CREATE TABLE IF NOT EXISTS Contacts (
                Id INTEGER PRIMARY KEY AUTOINCREMENT,
                Name TEXT NOT NULL,
                Email TEXT,
                Phone TEXT,
                CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
            )"
        command.ExecuteNonQuery()
    End Using
End Using</pre>



<h3 class="wp-block-heading">SQLite data types</h3>



<p>SQLite uses a dynamic type system that&#8217;s different from SQL Server. The most common types are:</p>



<ul class="wp-block-list">
<li><code>INTEGER</code> – whole numbers (equivalent to Integer / Long in VB.NET)</li>



<li><code>TEXT</code> – strings (equivalent to String)</li>



<li><code>REAL</code> – floating point numbers (equivalent to Double)</li>



<li><code>BLOB</code> – binary data (equivalent to Byte())</li>



<li><code>NULL</code> – no value</li>
</ul>



<p>A tip: SQLite doesn&#8217;t have a native <code>DATETIME</code> type. Store dates as <code>TEXT</code> in ISO 8601 format (e.g. <code>2026-04-07T14:30:00</code>) or as <code>INTEGER</code> (Unix timestamp).</p>



<h2 class="wp-block-heading">Inserting data</h2>



<p>Now let&#8217;s insert a record into our Contacts table. <strong>Always use parameterized queries</strong> instead of string concatenation. This protects your app from SQL injection and handles special characters correctly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "INSERT INTO Contacts (Name, Email, Phone) VALUES (@name, @email, @phone)"
        command.Parameters.AddWithValue("@name", "John Doe")
        command.Parameters.AddWithValue("@email", "john@example.com")
        command.Parameters.AddWithValue("@phone", "+49 123 456789")
        command.ExecuteNonQuery()
    End Using
End Using</pre>



<h3 class="wp-block-heading">Why parameterized queries matter</h3>



<p>Never build SQL strings by concatenating user input directly. This is the #1 security mistake that leads to SQL injection:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">' &#x274c; NEVER do this - SQL injection vulnerability!
command.CommandText = "INSERT INTO Contacts (Name) VALUES ('" &amp; txtName.Text &amp; "')"

' &#x2705; Always use parameters instead
command.CommandText = "INSERT INTO Contacts (Name) VALUES (@name)"
command.Parameters.AddWithValue("@name", txtName.Text)</pre>



<h3 class="wp-block-heading">Getting the last inserted ID</h3>



<p>After inserting a row, you often want to know the auto-generated ID. Use <code>last_insert_rowid()</code> for that:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">command.CommandText = "INSERT INTO Contacts (Name, Email) VALUES (@name, @email)"
command.Parameters.AddWithValue("@name", "Jane Doe")
command.Parameters.AddWithValue("@email", "jane@example.com")
command.ExecuteNonQuery()

command.CommandText = "SELECT last_insert_rowid()"
Dim newId As Long = CLng(command.ExecuteScalar())
Console.WriteLine($"New contact ID: {newId}")</pre>



<h2 class="wp-block-heading">Reading data (SELECT)</h2>



<p>Use <code>ExecuteReader()</code> to query data and iterate over the results. Again, wrap everything in <code>Using</code> blocks:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand("SELECT Id, Name, Email FROM Contacts", connection)
        Using reader As SqliteDataReader = command.ExecuteReader()
            While reader.Read()
                Dim id As Long = reader.GetInt64(0)
                Dim name As String = reader.GetString(1)
                Dim email As String = If(reader.IsDBNull(2), "", reader.GetString(2))

                Console.WriteLine($"[{id}] {name} - {email}")
            End While
        End Using
    End Using
End Using</pre>



<h3 class="wp-block-heading">Filtering with WHERE</h3>



<p>To search for specific records, use a parameterized <code>WHERE</code> clause:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using command As New SqliteCommand(
    "SELECT Id, Name, Email FROM Contacts WHERE Name LIKE @search",
    connection)
    command.Parameters.AddWithValue("@search", $"%{searchTerm}%")

    Using reader As SqliteDataReader = command.ExecuteReader()
        While reader.Read()
            ' Process results...
        End While
    End Using
End Using</pre>



<p>If you&#8217;re building a search-as-you-type feature, consider <a href="https://robbelroot.de/blog/how-to-debounce-function-calls-in-net-with-on-board-resources/"><strong>debouncing the input</strong></a> so you don&#8217;t fire a query on every keystroke.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Planning a project?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Need a .NET desktop application with database?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">From data models to finished WinForms or WPF app, I deliver your project. Let&#039;s talk about your requirements.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


<h2 class="wp-block-heading">Updating and deleting data</h2>



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



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "UPDATE Contacts SET Email = @email WHERE Id = @id"
        command.Parameters.AddWithValue("@email", "new-email@example.com")
        command.Parameters.AddWithValue("@id", 1)

        Dim rowsAffected As Integer = command.ExecuteNonQuery()
        Console.WriteLine($"{rowsAffected} row(s) updated.")
    End Using
End Using</pre>



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



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand("DELETE FROM Contacts WHERE Id = @id", connection)
        command.Parameters.AddWithValue("@id", 5)

        Dim rowsAffected As Integer = command.ExecuteNonQuery()
        Console.WriteLine($"{rowsAffected} row(s) deleted.")
    End Using
End Using</pre>



<h2 class="wp-block-heading">Complete example: Contact manager</h2>



<p>Let&#8217;s tie it all together. Here&#8217;s a complete helper class that wraps all CRUD operations for a Contacts table. You can use this as a starting point for your own projects:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Microsoft.Data.Sqlite

Public Class ContactRepository

    Private ReadOnly _connectionString As String

    Public Sub New(dbPath As String)
        _connectionString = $"Data Source={dbPath}"
    End Sub

    ''' Ensures the Contacts table exists.
    Public Sub InitializeDatabase()
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "
                    CREATE TABLE IF NOT EXISTS Contacts (
                        Id INTEGER PRIMARY KEY AUTOINCREMENT,
                        Name TEXT NOT NULL,
                        Email TEXT,
                        Phone TEXT,
                        CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
                    )"
                command.ExecuteNonQuery()
            End Using
        End Using
    End Sub

    ''' Adds a new contact and returns the generated Id.
    Public Function AddContact(name As String, email As String, phone As String) As Long
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "
                    INSERT INTO Contacts (Name, Email, Phone)
                    VALUES (@name, @email, @phone)"
                command.Parameters.AddWithValue("@name", name)
                command.Parameters.AddWithValue("@email", CObj(email) ?? DBNull.Value)
                command.Parameters.AddWithValue("@phone", CObj(phone) ?? DBNull.Value)
                command.ExecuteNonQuery()
            End Using

            Using command As New SqliteCommand("SELECT last_insert_rowid()", connection)
                Return CLng(command.ExecuteScalar())
            End Using
        End Using
    End Function

    ''' Returns all contacts as a list of dictionaries.
    Public Function GetAllContacts() As List(Of Dictionary(Of String, Object))
        Dim contacts As New List(Of Dictionary(Of String, Object))

        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand("SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name", connection)
                Using reader As SqliteDataReader = command.ExecuteReader()
                    While reader.Read()
                        Dim contact As New Dictionary(Of String, Object) From {
                            {"Id", reader.GetInt64(0)},
                            {"Name", reader.GetString(1)},
                            {"Email", If(reader.IsDBNull(2), "", reader.GetString(2))},
                            {"Phone", If(reader.IsDBNull(3), "", reader.GetString(3))}
                        }
                        contacts.Add(contact)
                    End While
                End Using
            End Using
        End Using

        Return contacts
    End Function

    ''' Updates a contact by Id.
    Public Function UpdateContact(id As Long, name As String, email As String, phone As String) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "UPDATE Contacts SET Name = @name, Email = @email, Phone = @phone WHERE Id = @id"
                command.Parameters.AddWithValue("@name", name)
                command.Parameters.AddWithValue("@email", CObj(email) ?? DBNull.Value)
                command.Parameters.AddWithValue("@phone", CObj(phone) ?? DBNull.Value)
                command.Parameters.AddWithValue("@id", id)

                Return command.ExecuteNonQuery() > 0
            End Using
        End Using
    End Function

    ''' Deletes a contact by Id.
    Public Function DeleteContact(id As Long) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand("DELETE FROM Contacts WHERE Id = @id", connection)
                command.Parameters.AddWithValue("@id", id)

                Return command.ExecuteNonQuery() > 0
            End Using
        End Using
    End Function

End Class</pre>



<h3 class="wp-block-heading">Using the ContactRepository</h3>



<p>Here&#8217;s how you&#8217;d use the class above, for example in a WinForms Form_Load event:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "contacts.db")
Dim repo As New ContactRepository(dbPath)

' Create the table (safe to call every time)
repo.InitializeDatabase()

' Add a contact
Dim newId As Long = repo.AddContact("Robert Skibbe", "mail@robbelroot.de", "+49 123 456")
Console.WriteLine($"Added contact with ID {newId}")

' Read all contacts
For Each contact In repo.GetAllContacts()
    Console.WriteLine($"[{contact("Id")}] {contact("Name")} - {contact("Email")}")
Next

' Update a contact
repo.UpdateContact(newId, "Robert S.", "new@robbelroot.de", "+49 999 999")

' Delete a contact
repo.DeleteContact(newId)</pre>



<p>If you want to display the results in a grid, have a look at the <a href="https://robbelroot.de/blog/creating-a-vb-net-datagridview-filter-functionality/"><strong>VB.NET DataGridView with filter functionality</strong></a>. For storing results by key, the <a href="https://robbelroot.de/blog/the-vb-net-dictionary-a-complete-guide/"><strong>VB.NET Dictionary</strong></a> is a great data structure.</p>



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



<p>When you need to insert or update multiple rows at once, wrap them in a transaction. This is both faster and safer: either all operations succeed or none of them do.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using transaction = connection.BeginTransaction()
        Using command As New SqliteCommand()
            command.Connection = connection
            command.Transaction = transaction
            command.CommandText = "INSERT INTO Contacts (Name, Email) VALUES (@name, @email)"

            For i As Integer = 1 To 1000
                command.Parameters.Clear()
                command.Parameters.AddWithValue("@name", $"Contact {i}")
                command.Parameters.AddWithValue("@email", $"contact{i}@example.com")
                command.ExecuteNonQuery()
            Next
        End Using

        transaction.Commit()
    End Using
End Using</pre>



<p>Without a transaction, inserting 1,000 rows can take several seconds because SQLite commits after each individual INSERT. With a transaction, the same operation finishes in milliseconds.</p>



<h2 class="wp-block-heading">Async/Await: keeping the UI responsive</h2>



<p>If a database query runs on the UI thread, the entire interface freezes until it completes. Using Async/Await moves the work to the background while the UI stays responsive. For a deeper dive, check out the article on <a href="https://robbelroot.de/blog/vbnet-async-await-en/"><strong>asynchronous programming in VB.NET</strong></a>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Async Function GetAllContactsAsync() As Task(Of List(Of Dictionary(Of String, Object)))
    Dim contacts As New List(Of Dictionary(Of String, Object))

    Using connection As New SqliteConnection(_connectionString)
        Await connection.OpenAsync()

        Using command As New SqliteCommand("SELECT Id, Name, Email FROM Contacts ORDER BY Name", connection)
            Using reader As SqliteDataReader = Await command.ExecuteReaderAsync()
                While Await reader.ReadAsync()
                    Dim contact As New Dictionary(Of String, Object) From {
                        {"Id", reader.GetInt64(0)},
                        {"Name", reader.GetString(1)},
                        {"Email", If(reader.IsDBNull(2), "", reader.GetString(2))}
                    }
                    contacts.Add(contact)
                End While
            End Using
        End Using
    End Using

    Return contacts
End Function</pre>



<p>The pattern applies to all operations: <code>OpenAsync()</code> instead of <code>Open()</code>, <code>ExecuteNonQueryAsync()</code> instead of <code>ExecuteNonQuery()</code>, <code>ReadAsync()</code> instead of <code>Read()</code>. The calling method must be declared as <code>Async Function</code>.</p>



<h2 class="wp-block-heading">Common pitfalls and solutions</h2>



<h3 class="wp-block-heading">&#8222;Database is locked&#8220;</h3>



<p>This is the most common SQLite error. It occurs when multiple threads or connections try to write to the database simultaneously. Here&#8217;s how to avoid it:</p>



<ul class="wp-block-list">
<li><strong>Always dispose connections</strong> – use <code>Using</code> blocks consistently. A forgotten open connection locks the file.</li>



<li><strong>Enable WAL mode</strong> – Write-Ahead Logging allows readers and one writer to work concurrently:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using walCommand As New SqliteCommand("PRAGMA journal_mode=WAL;", connection)
        walCommand.ExecuteNonQuery()
    End Using
End Using</pre>



<ul class="wp-block-list">
<li><strong>Set a busy timeout</strong> – instead of failing immediately, let SQLite retry for a few seconds:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Using walCommand As New SqliteCommand("PRAGMA busy_timeout=5000;", connection)
    walCommand.ExecuteNonQuery()
End Using</pre>



<ul class="wp-block-list">
<li><strong>Use transactions</strong> for bulk operations instead of opening/closing connections in a tight loop.</li>



<li><strong>Avoid long-running reads</strong> while writing. If you need complex background work, look into <a href="https://robbelroot.de/blog/vbnet-async-await-en/"><strong>asynchronous programming in VB.NET</strong></a>.</li>
</ul>



<h3 class="wp-block-heading">File path issues</h3>



<p>A relative path like <code>Data Source=myapp.db</code> resolves against the current working directory, which may differ depending on how your application was started (e.g. double-click vs. command line). Use an absolute path built from <code>AppContext.BaseDirectory</code> to be safe, as shown in the <a href="https://robbelroot.de/blog/vbnet-application-path-en/"><strong>Application Path article</strong></a>.</p>



<h3 class="wp-block-heading">NULL handling</h3>



<p>When reading nullable columns, always check with <code>reader.IsDBNull()</code> before calling <code>GetString()</code> or <code>GetInt64()</code>. Otherwise you&#8217;ll get an <code>InvalidCastException</code> at runtime. When inserting NULL values, pass <code>DBNull.Value</code> instead of <code>Nothing</code>.</p>


<div style="box-sizing:border-box;margin:36px 0;border-radius:12px;overflow:hidden;background:#1a1a2e;position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.03) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;"></div>
  <div style="position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:radial-gradient(circle,rgba(230,126,34,0.18) 0%,transparent 70%);pointer-events:none;"></div>
  <div style="position:relative;padding:32px 28px;">

        <div style="display:inline-block;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#e67e22;margin-bottom:14px;border:1px solid rgba(230,126,34,0.35);border-radius:4px;padding:3px 10px;">Need expert support?</div>
    
        <p style="margin:0 0 10px 0;font-size:clamp(17px,4vw,21px);font-weight:700;color:#ffffff;line-height:1.35;">Looking for an experienced .NET developer?</p>
    
        <p style="margin:0 0 24px 0;font-size:14px;color:rgba(255,255,255,0.65);line-height:1.7;max-width:540px;">I&#039;ll take on your project, from database design to finished application. Just drop me a message.</p>
    
    <div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">

            <a href="https://robbelroot.de/contact/"
         style="display:inline-block;padding:12px 24px;background:#e67e22;color:#ffffff;font-size:14px;font-weight:700;text-decoration:none;border-radius:7px;letter-spacing:0.3px;transition:background .2s ease;white-space:nowrap;"
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Request a project</a>
      
      
    </div>
  </div>
</div>
	


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



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-sqlite-en-1"><strong class="schema-faq-question"><strong>How do I create an SQLite database in VB.NET?</strong></strong> <p class="schema-faq-answer">Just open a connection with a file path that doesn&#8217;t exist yet: <code>New SqliteConnection("Data Source=myapp.db")</code>. SQLite creates the file automatically when you call <code>Open()</code>.</p> </div> <div class="schema-faq-section" id="faq-sqlite-en-2"><strong class="schema-faq-question"><strong>What is the SQLite connection string for VB.NET?</strong></strong> <p class="schema-faq-answer">The minimal connection string is <code>Data Source=path\to\database.db</code>. You can add options like <code>Mode=ReadWrite</code> or <code>Cache=Shared</code> depending on your needs.</p> </div> <div class="schema-faq-section" id="faq-sqlite-en-3"><strong class="schema-faq-question"><strong>Why do I get &#8222;database is locked&#8220; in SQLite?</strong></strong> <p class="schema-faq-answer">This happens when multiple connections try to write simultaneously, or a connection wasn&#8217;t properly closed. Use <code>Using</code> blocks to dispose connections, enable WAL mode with <code>PRAGMA journal_mode=WAL</code>, and set a busy timeout.</p> </div> <div class="schema-faq-section" id="faq-sqlite-en-4"><strong class="schema-faq-question"><strong>Should I use Microsoft.Data.Sqlite or System.Data.SQLite?</strong></strong> <p class="schema-faq-answer"><code>Microsoft.Data.Sqlite</code> is lighter, modern, and maintained by Microsoft. <code>System.Data.SQLite</code> is older and supports .NET Framework 4.0+. For new projects on .NET 5+ or .NET Framework 4.6.1+, <code>Microsoft.Data.Sqlite</code> is the recommended choice.</p> </div> <div class="schema-faq-section" id="faq-sqlite-en-5"><strong class="schema-faq-question"><strong>Can I use SQLite with async/await in VB.NET?</strong></strong> <p class="schema-faq-answer">Yes. <code>Microsoft.Data.Sqlite</code> supports async methods like <code>ExecuteNonQueryAsync()</code>, <code>ExecuteReaderAsync()</code>, and <code>ExecuteScalarAsync()</code>. Use them with <code>Await</code> to keep the UI responsive during database operations.</p> </div> </div>



<h2 class="wp-block-heading">Wrapping up</h2>



<p>SQLite is the fastest way to add a local database to your VB.NET application. Install <code>Microsoft.Data.Sqlite</code> via NuGet, use <code>Using</code> blocks for every connection, and always write parameterized queries. For multi-threaded scenarios, enable WAL mode and set a busy timeout to avoid &#8222;database is locked&#8220; errors.</p>



<h2 class="wp-block-heading" id="downloads">Downloads</h2>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/downloads/vbnet/VbNetSqliteExample.zip" target="_blank" rel="noreferrer noopener"><strong>VbNetSqliteExample.zip</strong></a></div>
</div>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2026/04/vbnet-sqlite-winforms-example.png"><img fetchpriority="high" decoding="async" width="536" height="484" src="https://robbelroot.de/wp-content/uploads/2026/04/vbnet-sqlite-winforms-example.png" alt="VB.NET SQLite example project – WinForms app with DataGridView and CRUD buttons" class="wp-image-20434"/></a></figure>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vbnet-sqlite-en/">VB.NET SQLite – Connect, Read &amp; Write Data (2026)</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vbnet-sqlite-en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
