Back to Devexpress

Semantic Search AI-powered Extension

wpf-405431-ai-powered-extensions-semantic-search.md

latest18.6 KB
Original Source

Semantic Search AI-powered Extension

  • Dec 17, 2025
  • 10 minutes to read

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

Applies To

Data Grid

How It Works

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 SearchPanelAllowFilter property to specify filter or search mode.

To activate Semantic Search you should install DevExpress AI NuGet packages and register the AI client.

Install DevExpress NuGet Packages

  1. DevExpress.AIIntegration.Wpf
  2. DevExpress.Wpf.Grid

See the following help topics for information on how to obtain the DevExpress NuGet Feed and install DevExpress NuGet packages:

Register Embedding Generator and Vector Store

Note

Refer to the following help topic for information on required NuGet packages and system requirements: How to Register an AI Client.

csharp
using Azure.AI.OpenAI;
using DevExpress.AIIntegration;
using DevExpress.Xpf.Core;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel.Connectors.InMemory;
using System;
using System.ClientModel;
using System.Windows;

namespace WPFSemanticSearch {
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application {
        static App() {
            CompatibilitySettings.UseLightweightThemes = true;
        }

        protected override void OnStartup(StartupEventArgs e) {
            base.OnStartup(e);
            ApplicationThemeHelper.ApplicationThemeName = "Win11Light";

            var container = AIExtensionsContainerDesktop.Default;

            /* The following code uses an Azure OpenAI embedding model.
            * The 'text-embedding-3-small' model produces 1536-dimensional vectors. */
            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);
        }
    }
}
vb
Imports Azure.AI.OpenAI
Imports DevExpress.AIIntegration
Imports DevExpress.Xpf.Core
Imports Microsoft.Extensions.AI
Imports Microsoft.SemanticKernel.Connectors.InMemory
Imports System
Imports System.ClientModel
Imports System.Windows

Namespace WPFSemanticSearch
    ''' <summary>
    ''' Interaction logic for App.xaml
    ''' </summary>
    Public Partial Class App
        Inherits Application

        Shared Sub New()
            CompatibilitySettings.UseLightweightThemes = True
        End Sub

        Protected Overrides Sub OnStartup(e As StartupEventArgs)
            MyBase.OnStartup(e)
            ApplicationThemeHelper.ApplicationThemeName = "Win11Light"

            Dim container = AIExtensionsContainerDesktop.Default

            ' The following code uses an Azure OpenAI embedding model.
            ' The 'text-embedding-3-small' model produces 1536-dimensional vectors.
            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 vector collection in the AI container.
            container.AddVectorCollection(vectorCollection)

            ' Register the vector store in the AI container
            container.AddVectorStore(vectorStore)
        End Sub
    End Class
End Namespace

Initialize Vector Store

Supply a vector store or database to hold and retrieve vectorized data. The following code snippet does the following:

  • Defines a record stored in the vector store for semantic search (VectorStoreRecord).
  • Initializes an in-memory vector store (for demo purposes).

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:

csharp
using DevExpress.AIIntegration;
using DevExpress.Xpf.Core;
using Microsoft.Extensions.VectorData;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Windows;

namespace WPFSemanticSearch {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : ThemedWindow {
        public MainWindow() {
            InitializeComponent();
            // Bind the Grid Control to data.
            DataHelper.Load();
            gridControl.ItemsSource = DataHelper.Items;
            // 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.
                DXMessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.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; }
    }

    public class DataHelper {
        public static List<DataItem> Items { get; private set; }
        public static void Load() {
            if (Items != null)
                return;

            Items = new List<DataItem> {
                // Populate data items.
            };
        }
    }
}
vb
Imports DevExpress.AIIntegration
Imports DevExpress.Xpf.Core
Imports Microsoft.Extensions.VectorData
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Windows

Namespace WPFSemanticSearch
    ''' <summary>
    ''' Interaction logic for MainWindow.xaml
    ''' </summary>
    Public Partial Class MainWindow
        Inherits ThemedWindow

        Public Sub New()
            InitializeComponent()
            ' Bind the Grid Control to data.
            DataHelper.Load()
            gridControl.ItemsSource = DataHelper.Items
            ' 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.
                DXMessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.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

    Public Class DataHelper
        Public Shared Property Items As List(Of DataItem)

        Public Shared Sub Load()
            If Items IsNot Nothing Then
                Return
            End If

            Items = New List(Of DataItem) {
                ' Populate data items.
            }
        End Sub
    End Class
End Namespace

Create and Configure Semantic Search Behavior

  1. Attach the SemanticSearchBehavior to the Grid Control.

  2. Configure behavior settings

The following code snippet does the following:

  • Attaches the SemanticSearchBehavior to the Grid Control.

  • Configures behavior settings.

  • Enables the grid’s Search mode.

  • Displays scrollbar annotations.

  • XAML

xaml
<dx:ThemedWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" 
    xmlns:dxai="http://schemas.devexpress.com/winfx/2008/xaml/ai"
    xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
    xmlns:local="clr-namespace:WPFSemanticSearch"
    x:Class="WPFSemanticSearch.MainWindow"
    Title="MainWindow" Height="800" Width="1000">
    <Grid>
        <dxg:GridControl x:Name="gridControl" AutoGenerateColumns="AddNew" EnableSmartColumnsGeneration="True">
            <dxmvvm:Interaction.Behaviors>
                <dxai:SemanticSearchBehavior
                    VectorCollectionName="Cars"
                    DataSourceKeyField="Id"
                    SearchMode="Semantic"
                    ScoreThreshold="0.5"
                    ScoreThresholdFilter="LessOrEqual"
                    SearchResultCount="10"/>
            </dxmvvm:Interaction.Behaviors>
            <dxg:GridControl.View>
                <dxg:TableView SearchPanelAllowFilter="False" ScrollBarAnnotationMode="SearchResult"/>
            </dxg:GridControl.View>
        </dxg:GridControl>
    </Grid>
</dx:ThemedWindow>

Similarity Filtering

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:

csharp
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:

Score Threshold

The SemanticSearchBehaviorScoreThreshold 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

ValueDescription
1.0Vectors are identical (maximum similarity)
0.0Vectors are orthogonal (no similarity)
-1.0Vectors are opposite

Cosine Distance

ValueDescription
0.0Vectors are identical (maximum similarity)
1.0Vectors are orthogonal (no similarity)
2.0Vectors are opposite

Score Threshold Filter

The SemanticSearchBehavior.ScoreThresholdFilter property specifies how the ScoreThreshold is applied during filtering:

ValueDescription
NoneNo filtering is applied. All results are returned regardless of score.
GreaterOrEqualInclude results with a score greater than or equal to ScoreThreshold. Used with Cosine Similarity , where higher scores mean greater similarity.
LessOrEqualInclude results with a score less than or equal to ScoreThreshold. Used with Cosine Distance , where lower scores mean greater similarity.

Examples

Cosine Similarity Filtering

Returns results with similarity scores from 0.5 to 1.0 (Higher = more similar):

xaml
<dxmvvm:Interaction.Behaviors>
    <dxai:SemanticSearchBehavior
        VectorCollectionName="Cars"
        DataSourceKeyField="Id"
        SearchMode="Semantic"
        ScoreThreshold="0.5"
        ScoreThresholdFilter="GreaterOrEqual"
        SearchResultCount="10"/>
</dxmvvm:Interaction.Behaviors>

Cosine Distance Filtering

Returns results with distance scores from 0.0 to 0.5 (Lower = more similar):

xaml
<dxmvvm:Interaction.Behaviors>
    <dxai:SemanticSearchBehavior
        VectorCollectionName="Cars"
        DataSourceKeyField="Id"
        SearchMode="Semantic"
        ScoreThreshold="0.5"
        ScoreThresholdFilter="LessOrEqual"
        SearchResultCount="10"/>
</dxmvvm:Interaction.Behaviors>

Search Modes

Once AI-powered 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:

  • Standard (traditional keyword-based search)
  • Semantic
  • Hybrid (a combination of standard and semantic search)

Use the SemanticSearchBehavior.SearchMode to specify search mode.

See Also