Nextended.UI
WPF and Windows Forms utilities for desktop application development.
Overview
Nextended.UI provides comprehensive utilities, behaviors, and helpers for building Windows desktop applications with WPF and Windows Forms.
Installation
dotnet add package Nextended.UI
Note: This package is Windows-only and targets net8.0-windows and net9.0-windows.
Key Features
1. ViewUtility
Comprehensive utility class for UI operations and manipulations.
using Nextended.UI;
// UI helper operations
var element = ViewUtility.FindVisualChild<Button>(parent);
var parent = ViewUtility.FindVisualParent<Grid>(child);
2. WPF Behaviors
Custom behaviors for enhanced WPF functionality.
<TextBox>
<i:Interaction.Behaviors>
<behaviors:WatermarkBehavior Watermark="Enter text here..." />
</i:Interaction.Behaviors>
</TextBox>
3. ViewModel Base Classes
Base classes for implementing MVVM pattern.
using Nextended.UI.ViewModels;
public class MainViewModel : ViewModelBase
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
}
4. Theming Support
Built-in theming capabilities for WPF applications.
using Nextended.UI.Theming;
// Apply theme
ThemeManager.ApplyTheme(ThemeType.Dark);
Usage Examples
MVVM ViewModel
using Nextended.UI.ViewModels;
using System.Windows.Input;
using Nextended.Core.Extensions;
public class UserViewModel : ViewModelBase
{
private string _name;
private string _email;
private bool _isLoading;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
public bool IsLoading
{
get => _isLoading;
set => SetProperty(ref _isLoading, value);
}
public ICommand SaveCommand { get; }
public ICommand CancelCommand { get; }
public UserViewModel()
{
SaveCommand = new RelayCommand(SaveUser, CanSaveUser);
CancelCommand = new RelayCommand(Cancel);
}
private bool CanSaveUser()
{
return !string.IsNullOrWhiteSpace(Name) &&
!string.IsNullOrWhiteSpace(Email);
}
private async void SaveUser()
{
IsLoading = true;
try
{
await SaveUserToDatabase();
MessageBox.Show("User saved successfully!");
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}");
}
finally
{
IsLoading = false;
}
}
private void Cancel()
{
// Reset or close
Name = string.Empty;
Email = string.Empty;
}
}
Visual Tree Helper
using Nextended.UI;
using System.Windows;
using System.Windows.Controls;
public class ControlFinder
{
public T FindChild<T>(DependencyObject parent, string childName = null)
where T : DependencyObject
{
return ViewUtility.FindVisualChild<T>(parent, childName);
}
public void FindAllButtons(DependencyObject parent)
{
var buttons = ViewUtility.FindVisualChildren<Button>(parent);
foreach (var button in buttons)
{
Console.WriteLine($"Found button: {button.Name}");
}
}
public T FindParent<T>(DependencyObject child)
where T : DependencyObject
{
return ViewUtility.FindVisualParent<T>(child);
}
}
Custom Behavior Example
using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
public class NumericOnlyBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += OnPreviewTextInput;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
}
private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsTextNumeric(e.Text);
}
private static bool IsTextNumeric(string text)
{
return int.TryParse(text, out _);
}
}
<TextBox>
<i:Interaction.Behaviors>
<local:NumericOnlyBehavior />
</i:Interaction.Behaviors>
</TextBox>
Dialog Service
using System.Windows;
public class DialogService
{
public bool? ShowDialog(Window dialog, Window owner = null)
{
if (owner != null)
{
dialog.Owner = owner;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else
{
dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
return dialog.ShowDialog();
}
public MessageBoxResult ShowMessage(
string message,
string title = "Information",
MessageBoxButton buttons = MessageBoxButton.OK,
MessageBoxImage icon = MessageBoxImage.Information)
{
return MessageBox.Show(message, title, buttons, icon);
}
public bool ShowConfirmation(string message, string title = "Confirm")
{
var result = MessageBox.Show(
message,
title,
MessageBoxButton.YesNo,
MessageBoxImage.Question
);
return result == MessageBoxResult.Yes;
}
}
Property Grid Searcher
using Nextended.UI.Helper;
using System.Windows.Controls;
public class PropertySearchExample
{
private readonly PropertyGridSearcher _searcher;
public PropertySearchExample()
{
_searcher = new PropertyGridSearcher();
}
public void SearchProperties(PropertyGrid propertyGrid, string searchText)
{
_searcher.Search(propertyGrid, searchText);
}
}
Best Practices
1. Use MVVM Pattern
// ViewModel
public class MainViewModel : ViewModelBase
{
private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get => _users;
set => SetProperty(ref _users, value);
}
}
<!-- View -->
<Window DataContext="{Binding Source={StaticResource MainViewModel}}">
<ListBox ItemsSource="{Binding Users}" />
</Window>
2. Implement INotifyPropertyChanged
public class User : NotificationObject
{
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
}
3. Use Commands for User Actions
public ICommand DeleteUserCommand { get; }
public MainViewModel()
{
DeleteUserCommand = new RelayCommand<User>(
user => DeleteUser(user),
user => user != null
);
}
4. Handle UI Thread Properly
public async Task LoadDataAsync()
{
IsLoading = true;
var data = await Task.Run(() => LoadFromDatabase());
// Update UI on UI thread
Application.Current.Dispatcher.Invoke(() =>
{
Users.Clear();
foreach (var user in data)
{
Users.Add(user);
}
});
IsLoading = false;
}
WPF-Specific Features
Resource Dictionaries
<ResourceDictionary>
<Style x:Key="ModernButtonStyle" TargetType="Button">
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<!-- Custom template -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Data Templates
<DataTemplate x:Key="UserItemTemplate">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Email}" Foreground="Gray"/>
</StackPanel>
</DataTemplate>
Configuration
App.xaml.cs
using System.Windows;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Initialize services
InitializeServices();
// Set up exception handling
DispatcherUnhandledException += OnDispatcherUnhandledException;
// Show main window
var mainWindow = new MainWindow();
mainWindow.Show();
}
private void InitializeServices()
{
// Register services, set up DI, etc.
}
private void OnDispatcherUnhandledException(
object sender,
DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show(
$"An error occurred: {e.Exception.Message}",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
e.Handled = true;
}
}
Supported Frameworks
- .NET 8.0 (Windows)
- .NET 9.0 (Windows)
Platform Support
Windows Only - This package requires Windows and will not build or run on Linux or macOS.
Dependencies
Nextended.Core- Core utilities and extensionsMicrosoft.Xaml.Behaviors.Wpf- WPF behaviors support
Related Projects
- Nextended.Core - Foundation library with NotificationObject base classes