WPF Components

Desktop query building controls with Material Design styling, MVVM architecture, and multi-framework targeting.

Multi-framework targeting

NetQueryBuilder.WPF targets .NET 6, .NET 8, and .NET 9 (Windows), so you can use it across all currently supported .NET versions.

Installation#

Install the WPF package via the .NET CLI. You will also need the core library and, optionally, the Entity Framework integration.

shell
dotnet add package NetQueryBuilder.WPF

For Entity Framework Core support, add the EF integration package as well:

shell
dotnet add package NetQueryBuilder
dotnet add package NetQueryBuilder.EntityFramework
dotnet add package NetQueryBuilder.WPF

Theme Resources#

NetQueryBuilder.WPF ships with a complete set of styles and templates in Generic.xaml. You must merge this resource dictionary into your application or window resources before using any controls.

App-wide (recommended)

Add the resource dictionary to your App.xaml so all windows can use the controls:

xml
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/NetQueryBuilder.WPF;component/Themes/Generic.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Per-window

Alternatively, scope the theme to a single window:

xml
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/NetQueryBuilder.WPF;component/Themes/Generic.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

Quick Start#

The fastest way to get a query builder on screen is to drop a QueryBuilderContainer into your XAML and assign a configurator in the code-behind.

XAML

xml
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpf="clr-namespace:NetQueryBuilder.WPF.Controls;assembly=NetQueryBuilder.WPF"
        Title="Query Builder" Height="600" Width="900">

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/NetQueryBuilder.WPF;component/Themes/Generic.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <ScrollViewer>
        <wpf:QueryBuilderContainer x:Name="QueryContainer"/>
    </ScrollViewer>
</Window>

Code-behind

csharp
using NetQueryBuilder.Configurations;

public partial class MainWindow : Window
{
    private readonly AppDbContext _dbContext;

    public MainWindow()
    {
        InitializeComponent();

        // Create DbContext (in-memory for demos)
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase("SampleDb")
            .UseLazyLoadingProxies()
            .Options;
        _dbContext = new AppDbContext(options);

        // Seed data and initialize
        InitializeAsync().ConfigureAwait(false).GetAwaiter().GetResult();
    }

    private async Task InitializeAsync()
    {
        await _dbContext.SeedAsync();

        // Create the EF Core configurator
        var configurator = new EfQueryConfigurator<AppDbContext>(_dbContext);

        // Assign to the container control
        QueryContainer.Configurator = configurator;
    }
}

Component Hierarchy#

The WPF controls form a visual tree. The container manages the overall lifecycle while nested controls handle specific parts of the query.

text
QueryBuilderContainer
├── Entity Selector (ComboBox)
├── QueryBuilder<T>
│   ├── SELECT Section (Property Checkboxes)
│   ├── WHERE Section (ConditionEditor)
│   ├── Expression Preview
│   └── Execute Button
└── QueryResultGrid (Paginated DataGrid)

QueryBuilderContainer

The top-level control that orchestrates the entire query building experience. It provides an entity type selector (ComboBox) populated from the configurator, manages IQuery instance lifecycle, and offers "New Query" functionality to reset the builder.

csharp
public class QueryBuilderContainer : UserControl
{
    /// <summary>The configurator that provides entity types and builds queries.</summary>
    public IQueryConfigurator? Configurator { get; set; }
}

QueryBuilder<T>

The generic query builder control with three main sections. SELECT displays checkboxes for each available property so the user can choose which columns appear in results. WHERE hosts the hierarchical condition editor for filtering. An Expression Preview panel shows the generated LINQ expression in real time, and the Execute button runs the compiled query.

csharp
public class QueryBuilder<T> : UserControl
{
    /// <summary>The query instance to build conditions for.</summary>
    public IQuery? Query { get; set; }
}

ConditionEditor

A recursive router control that renders the correct editor based on the condition type. It delegates to SimpleConditionEditor for leaf conditions (Field + Operator + Value) and BlockConditionEditor for grouped conditions with AND/OR logic. Blocks can be nested to arbitrary depth, enabling complex filter expressions.

QueryResultGrid

Displays paginated query results in a DataGrid with dynamically generated columns matching the selected properties. Pagination controls (First, Previous, Next, Last) and a total-items counter are built in.

MVVM Architecture#

Every control is backed by a dedicated ViewModel that inherits from ViewModelBase. This base class implements INotifyPropertyChanged with a SetProperty helper for clean property change notification. Commands use RelayCommand (synchronous) and AsyncRelayCommand (asynchronous) with CommandManager.RequerySuggested for automatic re-evaluation.

ViewModel Responsibility
QueryBuilderContainerViewModel Entity selection, query creation, new-query command
QueryBuilderViewModel Query configuration, execution, debounced expression updates (300ms)
SimpleConditionViewModel Single condition management: property, operator, value
BlockConditionViewModel Grouped conditions with AND/OR logic, child condition management
QueryResultViewModel Pagination, result display, dynamic column generation

ViewModelBase

All ViewModels inherit from ViewModelBase, which provides SetProperty for property changes and implements INotifyPropertyChanged:

csharp
public class MyViewModel : ViewModelBase
{
    private string _name = "";

    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
}

RelayCommand

Commands follow the ICommand pattern. RelayCommand handles synchronous operations while AsyncRelayCommand tracks an _isExecuting flag with try-finally to prevent double-execution.

csharp
// Synchronous command
public ICommand ResetCommand => new RelayCommand(() =>
{
    // Reset logic
});

// Async command with busy tracking
public ICommand ExecuteCommand => new AsyncRelayCommand(async () =>
{
    await RunQueryAsync();
});

Debouncing

The QueryBuilderViewModel uses a DispatcherTimer with a 300ms interval to debounce expression compilation. Every time a condition changes, the timer resets. The expression only compiles after 300ms of inactivity, preventing performance issues during rapid user input.

Styling & Theming#

The library ships with a professional Material Design-inspired color palette and a full set of control styles.

Color Scheme

Role Hex Usage
Primary #1976D2 Primary buttons, selected state, active accents
Secondary #424242 Secondary buttons, text, borders
Success #4CAF50 Execute button, positive actions
Error #F44336 Remove/delete buttons, error states
Background #FAFAFA Control backgrounds, panels

Button Styles

Four named styles are provided for consistent button appearance across the query builder:

Custom Overrides

Override default styles by redefining them after importing Generic.xaml. Use BasedOn to extend existing styles:

xml
<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="pack://application:,,,/NetQueryBuilder.WPF;component/Themes/Generic.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <!-- Override the primary button color -->
    <Style x:Key="PrimaryButtonStyle" TargetType="Button"
           BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="Background" Value="#6200EA"/>
        <Setter Property="Foreground" Value="White"/>
    </Style>
</ResourceDictionary>

Standalone Usage#

While QueryBuilderContainer is the simplest way to get started, you can use the individual controls independently for more control over layout and behavior.

QueryBuilder without container

Use the QueryBuilder control directly when you manage entity selection yourself:

xml
<wpf:QueryBuilder Query="{Binding MyQuery}"/>
csharp
var configurator = new QueryableQueryConfigurator<Person>(people.AsQueryable());
var query = configurator.BuildFor<Person>();
MyQuery = query;

QueryResultGrid independently

Display query results in a standalone paginated grid, useful when results come from a different source or you want to position the grid separately:

xml
<wpf:QueryResultGrid Results="{Binding QueryResults}"
                      DisplayProperties="{Binding SelectedProperties}"/>

Custom Operators#

Add custom operators through the core library's ConfigureConditions API. The WPF controls automatically pick up any operators registered on the configurator and display them in the operator dropdown.

csharp
configurator.ConfigureConditions(config =>
{
    config.AddOperator(new MyCustomOperator());
});

The custom operator will appear alongside the built-in operators (Equals, Contains, GreaterThan, etc.) for any property type it supports. See the Core Library documentation for details on implementing IOperator.

Framework Comparison#

Both the WPF and Blazor packages share the same core NetQueryBuilder engine, ensuring feature parity and consistent query behavior. The differences are in the UI layer:

Feature WPF Blazor
UI Framework WPF (XAML + Code-behind) Blazor (Razor Components)
Architecture MVVM with ViewModels Component-based
Styling ResourceDictionaries, Styles CSS, Custom Components
Data Binding DependencyProperty, TwoWay Parameter binding, EventCallback
Rendering Native WPF controls HTML + CSS (MudBlazor)