windowsforms-405400-ai-powered-extensions-semantic-search.md
Semantic search enables users to locate relevant data quickly and accurately within large datasets. Unlike standard keyword-based search, semantic search leverages Natural Language Processing (NLP) to analyze search queries beyond exact keyword matching.
Run Demo: Semantic Search - Grid Control
Semantic search uses an embedding generator to convert text into numerical vector representations. Vector embeddings are stored in a vector store (for example, a database, in-memory collection, or a custom implementation that stores vector values in files). When a user enters a search query, the search engine computes similarity scores between the query vector and stored data vectors to return the most relevant results.
Semantic search can operate in two modes:
Filter ModeDisplays only matching records.Search ModeHighlights relevant data rows for more intuitive data discovery.
Tip
Use the GridView.OptionsFind.Behavior property to specify filter or search mode.
DevExpress.AIIntegration.WinForms.SemanticSearchDevExpress.Win.Design (enables design-time features for DevExpress UI controls)See the following help topics for information on how to obtain the DevExpress NuGet Feed and install DevExpress NuGet packages:
Note
Install NuGet packages required by an AI Client used in your project (for example, OpenAI, Azure OpenAI, Ollama, etc.). See the following help topic for additional information: Prerequisites.
The following code snippet does the following:
Note
Requires the following Microsoft NuGet Packages:
Azure.AI.OpenAI (2.2.0-beta.5)
Microsoft.Extensions.AI.OpenAI (9.7.1-preview.1.25365.4)
Microsoft.SemanticKernel.Connectors.InMemory (1.62.0-preview)
using Azure.AI.OpenAI;
using DevExpress.AIIntegration;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel.Connectors.InMemory;
using System;
using System.Windows.Forms;
internal static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var container = AIExtensionsContainerDesktop.Default;
// The following code uses an Azure OpenAI embedding model.
var embeddingGenerator = new AzureOpenAIClient(AzureOpenAIEndpoint, AzureOpenAIKey)
.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();
// Create an in-memory vector collection to store vectorized records.
var vectorCollection = new InMemoryCollection<string, VectorStoreRecord>("Cars", new InMemoryCollectionOptions{ EmbeddingGenerator = embeddingGenerator});
// Register the vector collection in the AI container.
container.RegisterVectorCollection(vectorCollection);
Application.Run(new Form1());
}
}
Imports Azure.AI.OpenAI
Imports DevExpress.AIIntegration
Imports Microsoft.Extensions.AI
Imports Microsoft.SemanticKernel.Connectors.InMemory
Imports System
Imports System.Windows.Forms
Friend Module Program
<STAThread>
Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Dim container = AIExtensionsContainerDesktop.Default
' The following code uses an Azure OpenAI embedding model.
Dim embeddingGenerator = New AzureOpenAIClient(AzureOpenAIEndpoint, AzureOpenAIKey) _
.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator()
' Create an in-memory vector collection to store vectorized records.
Dim vectorCollection As New InMemoryCollection(Of String, VectorStoreRecord)("Cars", New InMemoryCollectionOptions With {.EmbeddingGenerator = embeddingGenerator})
' Register the embedding generator in the AI container.
container.AddEmbeddingGenerator(embeddingGenerator)
' Register the vector collection in the AI container.
container.AddVectorCollection(vectorCollection)
Application.Run(New Form1())
End Sub
End Module
Supply a vector store or database to hold and retrieve vectorized data. The following code snippet does the following:
VectorStoreRecord).Tip
Production AI applications use vector databases and services to improve relevancy. You can use any vector store that implements the IVectorStore interface to store embeddings. See the following topics for additional information:
using DevExpress.AIIntegration;
using DevExpress.AIIntegration.SemanticSearch;
using DevExpress.AIIntegration.WinForms;
using Microsoft.Extensions.VectorData;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class Form1 : DevExpress.XtraBars.Ribbon.RibbonForm {
public Form1() {
InitializeComponent();
// Bind the Grid Control to data.
gridControl1.DataSource = DataHelper.Items;
// Enable search mode.
gridView1.OptionsFind.Behavior = DevExpress.XtraEditors.FindPanelBehavior.Search;
// Initialize the vector store.
Task.Run(InitializeVectorStore);
}
async Task InitializeVectorStore() {
// Get the default AI extensions container.
var container = AIExtensionsContainerDesktop.Default;
try {
// Create a vector store collection named "Cars" with DataHelper.Items data.
var collection = container.GetVectorCollection<string, VectorStoreRecord>("Cars");
if (await collection.CollectionExistsAsync())
return;
var records = DataHelper.Items.Select((item, index) => new VectorStoreRecord{
Key = item.Id.ToString(), // Use the item ID as the key.
Vector = string.Join(". ", item.Trademark, item.Model, item.Description) // Store the generated vector.
});
await collection.EnsureCollectionExistsAsync();
await collection.UpsertAsync(records);
}
catch (Exception ex) {
// Display an error message if the operation fails.
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// Define a record stored in the vector store for semantic search.
public class VectorStoreRecord {
[VectorStoreKey]
// A unique identifier for the record in a vector store.
public string Key { get; set; }
// Azure OpenAI (embedding model) produces 1536-dimensional vectors.
// Specify how vector data is stored and compared in a vector store.
// Use cosine similarity as the distance function for comparisons.
[VectorStoreVector(1536, DistanceFunction = DistanceFunction.CosineDistance)]
public string Vector { get; set; }
}
public class DataItem {
public Guid Id { get; set; }
public string Trademark { get; set; }
public string Model { get; set; }
public string Description { get; set; }
}
Imports DevExpress.AIIntegration
Imports DevExpress.AIIntegration.SemanticSearch
Imports DevExpress.AIIntegration.WinForms
Imports Microsoft.Extensions.VectorData
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Windows.Forms
Public Partial Class Form1
Inherits DevExpress.XtraBars.Ribbon.RibbonForm
Public Sub New()
InitializeComponent()
' Bind the Grid Control to data.
gridControl1.DataSource = DataHelper.Items
' Enable search mode.
gridView1.OptionsFind.Behavior = DevExpress.XtraEditors.FindPanelBehavior.Search
' Initialize the vector store.
Task.Run(AddressOf InitializeVectorStore)
End Sub
Private Async Function InitializeVectorStore() As Task
' Get the default AI extensions container.
Dim container = AIExtensionsContainerDesktop.Default
Try
' Create a vector store collection named "Cars" with DataHelper.Items data.
Dim collection = container.GetVectorCollection(Of String, VectorStoreRecord)("Cars")
If Await collection.CollectionExistsAsync() Then
Return
End If
Dim records = DataHelper.Items.Select(Function(item, index) New VectorStoreRecord() With {
.Key = item.Id.ToString(), ' Use the item ID as the key.
.Vector = String.Join(". ", item.Trademark, item.Model, item.Description) ' Store the generated vector.
})
Await collection.EnsureCollectionExistsAsync()
Await collection.UpsertAsync(records)
Catch ex As Exception
' Display an error message if the operation fails.
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Function
End Class
' Define a record stored in the vector store for semantic search.
Public Class VectorStoreRecord
<VectorStoreKey>
' A unique identifier for the record in a vector store.
Public Property Key As String
' Azure OpenAI (embedding model) produces 1536-dimensional vectors.
' Specify how vector data is stored and compared in a vector store.
' Use cosine similarity as the distance function for comparisons.
<VectorStoreVector(1536, DistanceFunction:=DistanceFunction.CosineDistance)>
Public Property Vector As String
End Class
Public Class DataItem
Public Property Id As Guid
Public Property Trademark As String
Public Property Model As String
Public Property Description As String
End Class
BehaviorManager component from the Toolbox onto a Form.The following code snippet activates semantic search for a GridLookUpEdit:
public Form1() {
InitializeComponent();
gridLookUpEdit1.Properties.DataSource = DataHelper.Items;
gridLookUpEdit1.Properties.DisplayMember = "Model";
gridLookUpEdit1.Properties.ValueMember = "Id";
behaviorManager1.Attach<SemanticSearchBehavior>(gridLookUpEdit1.Properties.View, behavior => {
behavior.Properties.VectorCollectionName = "Cars";
behavior.Properties.DataSourceKeyField = "Id";
behavior.Properties.SearchResultCount = 10;
behavior.Properties.SearchMode = ControlSearchMode.Semantic;
// It is implied, that Cosine Distance filtering is used for the vector store.
// Lower = more similar
behavior.Properties.ScoreThreshold = 0.5D;
behavior.Properties.ScoreThresholdFilter = ScoreThresholdFilter.LessOrEqual;
});
}
Note
Call the BehaviorInitializer.Initialize() method at application startup if your project targets the .NET Framework and you create AI-powered behaviors in code. Otherwise, an exception is thrown.
internal static class Program {
[STAThread]
static void Main() {
//...
// The Initialize() method forcibly initializes the behavior manager in .NET Framework apps.
DevExpress.AIIntegration.WinForms.BehaviorInitializer.Initialize();
Application.Run(new Form1());
}
}
In your vectorized data model, you must annotate a vector field with the VectorStoreVector attribute and specify the desired similarity metric using the DistanceFunction property:
using Microsoft.Extensions.VectorData;
// Define a record stored in the vector store for semantic search.
public class VectorStoreRecord {
// A unique identifier for the record in a vector store.
[VectorStoreKey]
public string Key { get; set; }
// Azure OpenAI (embedding model) produces 1536-dimensional vectors.
// Specify how vector data is stored and compared in a vector store.
// This example uses cosine similarity as the distance function for comparisons.
[VectorStoreVector(1536, DistanceFunction = DistanceFunction.CosineDistance)]
public string Vector { get; set; }
}
Warning
The VectorStoreVector attribute indicates the preferred distance function, but the vector store ultimately determines which similarity metric is supported. If your vector store does not support the specified function, it will throw a runtime error.
Use the following properties to configure similarity filtering:
The SemanticSearchBehavior.Properties.ScoreThreshold property specifies the minimum or maximum score at which search results are considered relevant. The actual meaning of the score depends on the selected similarity metric/distance function. For example:
Cosine Similarity
| Value | Description |
|---|---|
1.0 | Vectors are identical (maximum similarity) |
0.0 | Vectors are orthogonal (no similarity) |
-1.0 | Vectors are opposite |
Cosine Distance
| Value | Description |
|---|---|
0.0 | Vectors are identical (maximum similarity) |
1.0 | Vectors are orthogonal (no similarity) |
2.0 | Vectors are opposite |
The SemanticSearchBehavior.Properties.ScoreThresholdFilter property specifies how the ScoreThreshold is applied during filtering:
| Value | Description |
|---|---|
None | No filtering is applied. All results are returned regardless of score. |
GreaterOrEqual | Include results with a score greater than or equal to ScoreThreshold. Used with Cosine Similarity , where higher scores mean greater similarity. |
LessOrEqual | Include results with a score less than or equal to ScoreThreshold. Used with Cosine Distance , where lower scores mean greater similarity. |
Cosine Similarity Filtering
Returns results with similarity scores from 0.5 to 1.0:
// Higher = more similar
semanticSearchBehavior.ScoreThreshold = 0.5;
semanticSearchBehavior.ScoreThresholdFilter = ScoreThresholdFilter.GreaterOrEqual;
Cosine Distance Filtering
Returns results with distance scores from 0.0 to 0.5:
// Lower = more similar
semanticSearchBehavior.ScoreThreshold = 0.5;
semanticSearchBehavior.ScoreThresholdFilter = ScoreThresholdFilter.LessOrEqual;
Once AI-driven semantic search is enabled, a drop-down button appears within the Grid control’s search field. This button opens a popup menu that allows users to specify search mode:
Note
In GridLookUpEdit, users cannot specify search mode. Use the SemanticSearchBehavior.Properties.SearchMode property to specify search mode.
Semantic search does not support Server Mode.