TechnicalUnits.WinForms 1.3.1.277

dotnet add package TechnicalUnits.WinForms --version 1.3.1.277
                    
NuGet\Install-Package TechnicalUnits.WinForms -Version 1.3.1.277
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TechnicalUnits.WinForms" Version="1.3.1.277" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TechnicalUnits.WinForms" Version="1.3.1.277" />
                    
Directory.Packages.props
<PackageReference Include="TechnicalUnits.WinForms" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TechnicalUnits.WinForms --version 1.3.1.277
                    
#r "nuget: TechnicalUnits.WinForms, 1.3.1.277"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TechnicalUnits.WinForms@1.3.1.277
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TechnicalUnits.WinForms&version=1.3.1.277
                    
Install as a Cake Addin
#tool nuget:?package=TechnicalUnits.WinForms&version=1.3.1.277
                    
Install as a Cake Tool

TechnicalUnits

Nuget

A .NET library for working with SI units and engineering notation - format numeric values with SI prefixes, parse user input back to base values, and define/convert between units.

Motivation

Engineering and scientific applications frequently need to:

  • Display values like 1.50 MHz, 3.30 mV, or 10.0 mT.
  • Accept user input in engineering notation (e.g., "3u3" for 3.3 µ or "1k5" for 1500).
  • Convert between units (e.g., inches to meters, Celsius to Kelvin).

TechnicalUnits provides a small, focused library that handles all of this - with UI controls for Avalonia and WinForms.

Features

  • Unit system with SI base units, derived units, and dimensional analysis.
  • Formatting of double values with SI prefixes (k, M, µ, n, etc.).
  • Parsing of strings back to numeric values, including unit conversion.
  • Math expression evaluation (optional) - evaluate expressions like "2 * pi * 1k".
  • UI controls for Avalonia and WinForms with validation and increment/decrement logic.

Architecture

The library is organized into the following layers:

1. Unit Definitions (TechnicalUnits.UnitDefinition)

Defines the unit system:

Type Description
Dimension Represents SI base dimensions (time, length, mass, current, temperature, amount of substance, luminous intensity, angle) as a set of exponents. Supports dimensional arithmetic (*, /, ^).
Unit A named unit with a symbol and Dimension (e.g., Volt, Hertz). Supports operator overloads to combine dimensions.
DerivedUnit A unit derived from a base Unit via a linear conversion factor or arbitrary from-base/to-base functions (e.g., temperature offsets).
UnitsLocator Central registry of all known Unit instances with lookup by name or symbol.

Built-in units:

SIUnits provides the seven SI base units and all named SI derived units:

Category Units
SI Base Second, Meter, Kilogram, Ampere, Kelvin, Mol, Candela
SI Derived Radian, Degree, Steradian, Hertz, Newton, Pascal, Joule, Watt, Coulomb, Volt, Farad, Ohm, Siemens, Weber, Tesla, Henry, Lumen, Lux, Becquerel, Gray, Sievert, CatalyticActivity
Compound Area, Volume, Speed, Acceleration, VolumetricFlow, AngularVelocity

Units provides commonly used derived and non-SI units organized by category:

Category Units
Length Millimeter, Centimeter, Kilometer, Micrometer, Nanometer, Inch, Feet, Yard, Mile, NauticalMile, Mil
Mass Milligram, Gram, Ounce, Pound, MetricTon, ShortTon, LongTon
Temperature Celsius, Fahrenheit (with non-linear conversion functions)
Time Minute, Hour, Day, Week
Force KiloNewton, Dyne
Pressure KiloPascal, Bar, Millibar, Atmosphere, Torr, PSI
Energy Calorie, Kilocalorie, KilowattHour, Electronvolt, BTU
Speed Knot, Mach
Volumetric Liter, Pint, Quart, Gallon
Flow LiterPerMinute
Angular Velocity RevolutionsPerMinute
Electrical ElectricField, CurrentDensity
Magnetic MagneticField, MagneticMoment, MagneticGradient
Chemical MassDensity, MassConcentration, MolarConcentration
Physical DynamicViscosity, MomentOfForce, SurfaceTension, GyromagneticRatio, EnergyDensity

Units can be combined using operators to create new dimensions:

using TechnicalUnits.UnitDefinition;

// Newton = kg·m/s²
var newton = SIUnits.Kilogram * SIUnits.Meter / (SIUnits.Second * SIUnits.Second);

2. Formatting + Parsing (TechnicalUnits.Formatting)

Class Description
Formatter Formats double values with SI prefixes, unit symbols, and configurable padding/separators. Uses multiples-of-three exponents (k, M, G, …, m, µ, n, …).
Parser Parses strings (e.g., "1.5 kHz", "3u3") back to numeric values with strict validation for data import and best-effort recovery for interactive input.
FormattingOptions Controls output style, precision, separators, padding, and more.
UnitOptions Specifies the base Unit and optional AlternateUnits (as DerivedUnit) for parsing.
SIStyles Enum controlling SI prefix rendering (Unicode, ASCII, English names, or plain float).
TechnicalUnitsFormatProvider Custom IFormatProvider and ICustomFormatter enabling :SI format specifiers in interpolated strings.
FormattableStringExtensions Extension methods (SI()) for formatting FormattableString with SI notation.

Formatting styles (SIStyles):

Style Description Example output
SIStyleSI Unicode SI prefix symbols 3.30 mV (uses µ for micro)
SIStyleSIAz ASCII-safe SI prefix symbols 3.30 mV (uses u for micro)
SIStyleSINamesEN English SI prefix names 3.30 milliV
SIStyleFloat Plain floating-point, no SI prefix 0.0033 V

3. Extension Methods (TechnicalUnits.Extensions)

Class Description
UnitUtility Extension methods on Unit for convenient Format, Parse, and FormatSimple operations.

FormatSimple is a lightweight formatting path with fewer options - useful when you just need quick SI-prefixed output.

4. Math Expression Evaluation (TechnicalUnits.Math)

Class Description
MathEvaluator Evaluates infix math expressions containing SI-prefixed numbers, unit symbols, built-in functions, named constants, and unit conversion brackets. Uses the Shunting-Yard algorithm.
ExpressionBase Abstract base class for expression nodes (NumberExpression, OperatorExpression, FunctionExpression, ConvertExpression).

5. UI Control Infrastructure

Type Description
ITechnicalUnitsControl Interface for a control with Value, Text, UnitOptions, FormattingOptions, EnableMath, min/max, increment, and read-only properties.
ITechnicalUnitsControlImpl Extended interface adding SetValue and UpdateText for control implementations.
TechnicalUnitsControlExtensions Extension methods for ConvertValueToText, ConvertTextToValue, ValidateLimits.
TechnicalUnitsControlMixin Reusable logic for increment/decrement with keyboard modifiers (None, Shift, Alt, Shift+Alt).

Quickstart

Install

dotnet add package TechnicalUnits

Formatting

Examples below use a decimal point (invariant culture). Default formatting follows CurrentUICulture; pass cultureInfo: CultureInfo.InvariantCulture for fixed output.

using TechnicalUnits.Extensions;
using TechnicalUnits.UnitDefinition;

// Full formatting with SI prefixes
string text = SIUnits.Volt.Format(0.0033);       // "3.3 mV"
string text2 = SIUnits.Hertz.Format(1500000);    // "1.5 MHz"

// Control precision
string text3 = SIUnits.Meter.Format(1.23e3, precision: 2);  // "1.23 km"

Formatting rounds to the configured significant and fractional precision. Formatting a value and parsing it back therefore preserves its displayed precision, not necessarily every bit of the original double.

Lightweight Formatting (FormatSimple)

A simpler API with fewer options - useful when you just need quick SI-prefixed output:

using TechnicalUnits.Extensions;
using TechnicalUnits.UnitDefinition;

string text = SIUnits.Tesla.FormatSimple(0.010);                 // "10.0 mT"
string text2 = SIUnits.Tesla.FormatSimple(0.010, precision: 1);  // "10.0 mT"

FormatSimple shortens trailing zeros to at least one decimal digit when precision is positive. Use precision: 0 for no fractional digits, or shortenTrailingZeros: false to retain the requested number.

Outside the supported SI-prefix range, shorthand formatting uses scientific notation. Exa shorthand uses exa (for example 1exa25V) to distinguish it from scientific notation such as 1E25V.

String Interpolation with SI Notation

Use the SI() extension method to format interpolated strings with SI prefixes using the :SI format specifier:

using TechnicalUnits.UnitDefinition;
using static TechnicalUnits.Formatting.FormattableStringExtensions;

double thickness = 25e-9;
double frequency = 1.5e6;

// Basic usage with unit symbol
string text = SI($"Thickness = {thickness:SI}", "m");   // "Thickness = 25 nm"
string text2 = SI($"Frequency = {frequency:SI}", "Hz"); // "Frequency = 1.5 MHz"

// With Unit object
string text3 = SI($"Thickness = {thickness:SI}", SIUnits.Meter);

// With maximum fractional precision (SI0-SI15; trailing zeros are trimmed)
string text4 = SI($"Value = {thickness:SI3}", "m");     // "Value = 25 nm"

// Multiple values in one string
string text5 = SI($"d={thickness:SI}, f={frequency:SI}", "");

Note: C# doesn't allow custom format specifiers directly in $"" strings. The SI() wrapper enables the :SI specifier by accepting a FormattableString.

For full control over formatting, create a custom TechnicalUnitsFormatProvider:

using TechnicalUnits.Formatting;
using TechnicalUnits.UnitDefinition;
using static TechnicalUnits.Formatting.FormattableStringExtensions;

var provider = new TechnicalUnitsFormatProvider
{
    UnitOptions = new UnitOptions(SIUnits.Volt),
    FormattingOptions = FormattingOptions.Default.Clone()
};
provider.FormattingOptions.FractionalPrecision = 2;
// Set ZeroPostpad = 2 as well if you want "3.30 mV" instead of "3.3 mV".

string text = SI($"Voltage = {0.0033:SI}", provider);  // "Voltage = 3.3 mV"

Parsing

The Unit.Parse extension methods return values in the configured base unit. They use best-effort parsing, which can recover usable values from incomplete or malformed input. Use Parser.TryParse or Parser.ParseStrict when the entire input must be valid.

using TechnicalUnits.Extensions;
using TechnicalUnits.UnitDefinition;

double value = SIUnits.Hertz.Parse("1.5 kHz");   // 1500.0
double value2 = SIUnits.Volt.Parse("3u3");        // 0.0000033 (SI prefix as decimal separator)

Strict Parsing

Use TryParse for validation or data import, and ParseStrict when you need details about invalid input:

using TechnicalUnits.Formatting;
using TechnicalUnits.UnitDefinition;

bool valid = Parser.TryParse("1.5 kHz", SIUnits.Hertz, out double frequency);
// valid == true, frequency == 1500

ParseResult result = Parser.ParseStrict("1.5 kHz extra", SIUnits.Hertz);
// result.Success == false; result.UnconsumedText == "extra"
// result.ErrorPosition gives the zero-based position of the invalid suffix

Strict parsing rejects empty input, malformed numbers, incomplete exponents, unknown units, numeric overflow, and trailing text. TryParse returns false and sets the output value to zero on failure. ParseStrict also provides ErrorMessage, ErrorPosition, CharactersConsumed, and UnconsumedText.

Decimal points and commas are accepted, but thousands grouping is not. A unit or SI prefix can replace the decimal separator (1V5, 1k5). Unit symbols are case-sensitive, and the longest configured matching unit wins. Explicit NaN and infinity literals are accepted; check for finite values separately if your application requires them.

For interactive input, Parser.ParseString accepts an optional warnings list describing recovery from invalid text. Both Unit.Parse and the UI controls use this best-effort behavior.

Parsing with Alternate Units

using TechnicalUnits.Extensions;
using TechnicalUnits.UnitDefinition;

// Parse "10.0in" as meters, using Inch and Mil as alternate units
double meters = SIUnits.Meter.Parse("10.0in", [Units.Inch, Units.Mil]);  // 0.254

// Parse "10.0mil" as meters
double meters2 = SIUnits.Meter.Parse("10.0mil", [Units.Inch, Units.Mil]); // 0.000254

Defining Custom Units

using TechnicalUnits.UnitDefinition;

// Derived unit with a linear conversion factor
var centimeter = new DerivedUnit("centimeter", "cm", 0.01, SIUnits.Meter);

// Derived unit with conversion functions (for non-linear conversions)
var celsius = new DerivedUnit(
    "celsius", "°C",
    fromBaseUnitFunc: k => k - 273.15,
    toBaseUnitFunc: c => c + 273.15,
    baseUnit: SIUnits.Kelvin);

// Convert between base and derived
double kelvin = celsius.ToBase(100.0);   // 373.15
double degC = celsius.FromBase(373.15);  // 100.0

Linear conversion factors express base units per derived unit: ToBase(value) = value * BaseConversionFactor and FromBase(value) = value / BaseConversionFactor. For example, one inch is 0.0254 meters. Alternate-unit parsing applies these factors or custom conversion functions after evaluating the numeric prefix or exponent, including temperature offsets.

Looking Up Units by Name or Symbol

using TechnicalUnits.UnitDefinition;

Unit? volt = UnitsLocator.FindBySymbol("V");     // case-sensitive symbol lookup
Unit? meter = UnitsLocator.FindByName("length");  // case-insensitive name lookup

// Enumerate all registered units
IReadOnlyList<Unit> all = UnitsLocator.AllUnits;

Math Expression Evaluation

The MathEvaluator class can parse and evaluate infix mathematical expressions containing SI-prefixed numbers, functions, constants, and unit conversions:

using TechnicalUnits.Math;
using TechnicalUnits.Formatting;
using TechnicalUnits.UnitDefinition;

var evaluator = new MathEvaluator();
var unitOpts = new UnitOptions(SIUnits.Volt);
var fmtOpts = FormattingOptions.Default;

double result = evaluator.Evaluate("2 * pi * 1k", unitOpts, fmtOpts, warnings: null);
// result ≈ 6283.185...

double result2 = evaluator.Evaluate("sqrt(2) * 100m", unitOpts, fmtOpts, warnings: null);
// result2 ≈ 0.14142... (100m = 0.1 in base units)

When used via ITechnicalUnitsControl (with EnableMath = true), expression evaluation is enabled automatically in the UI controls.

Unit conversions use bracket notation inside expressions:

100 [°C>K]          → 373.15

Built-in Constants

Constant Value Description
pi 3.14159… Pi
e 2.71828… Euler's number
sqrt2 1.41421… Square root of 2
c 299 792 458 Speed of light (m/s)
g 9.80665 Standard gravity (m/s²)
h 6.626 070 15 × 10⁻³⁴ Planck's constant (J·s)
hbar 1.054 571 817 × 10⁻³⁴ Reduced Planck's constant (J·s)
kB 1.380 649 × 10⁻²³ Boltzmann constant (J/K)
NA 6.022 140 76 × 10²³ Avogadro's number
Gc 6.674 30 × 10⁻¹¹ Gravitational constant
qe 1.602 176 634 × 10⁻¹⁹ Elementary charge (C)
me 9.109 383 70 × 10⁻³¹ Electron mass (kg)
mp 1.672 621 92 × 10⁻²⁷ Proton mass (kg)
mn 1.674 927 50 × 10⁻²⁷ Neutron mass (kg)
mu0 1.256 637 06 × 10⁻⁶ Vacuum permeability
eps0 8.854 187 81 × 10⁻¹² Vacuum permittivity (F/m)
R 8.314 462 618 Gas constant (J/(mol·K))
Ry 2.179 872 36 × 10⁻¹⁸ Rydberg constant (J)
F 96 485.332 12 Faraday constant (C/mol)
Vm 22.413 962 Molar volume at STP (L/mol)
atm 101 325 Standard atmosphere (Pa)

Constants are stored in MathEvaluator.Constants and can be added or removed at runtime.

Built-in Functions

Single-argument: abs, acos, asin, atan, ceiling, cos, cosh, exp, floor, log, log10, sin, sinh, sqrt, tan, tanh

Two-argument: max, min, pow

Use semicolons between function arguments, for example max(1+2;4), to avoid ambiguity with decimal commas. Custom functions can be registered via MathEvaluator.RegisterFunction.

When sharing a standalone MathEvaluator between threads, synchronize access to evaluation, constants, and function registration.

UI Controls

The core library defines the ITechnicalUnitsControl interface and shared TechnicalUnitsControlMixin logic. Framework-specific packages provide ready-to-use controls:

TechnicalUnits.Avalonia

dotnet add package TechnicalUnits.Avalonia

Provides TechnicalUnitsUpDown - an Avalonia control that integrates with Avalonia's data binding and styling. It includes a Spinner and TextBox template part.

TechnicalUnits.WinForms

dotnet add package TechnicalUnits.WinForms

Provides:

Type Description
TechnicalUnitsUpDown A WinForms UpDownBase control with engineering notation support. Also implements IDataGridViewEditingControl.
TechnicalUnitsUpDownColumn A DataGridView column that hosts TechnicalUnitsUpDown cells.
TechnicalUnitsUpDownCell A DataGridView cell for inline editing with TechnicalUnitsUpDown.

Input and Validation

Controls accept best-effort numeric input and report validation errors. With EnableMath = true, they also recover usable numeric literals within math expressions. If an expression cannot be evaluated, the current value remains unchanged. Each control has its own evaluator; access controls from their UI thread.

Increment/Decrement Behavior

The TechnicalUnitsControlMixin provides smart up/down behavior based on keyboard modifiers:

Modifier Up Action Down Action
None value += Increment value -= Increment
Shift value *= IncrementMult value /= IncrementMult
Alt Increment at the position before the relative decimal separator Decrement at the position before the relative decimal separator
Shift+Alt Increment at the highest significant digit Decrement at the highest significant digit

Configuration

FormattingOptions

Property Default Description
SIStyle SIStyleSI SI prefix style (Unicode, ASCII, English names, or plain float).
FractionalPrecision 3 Maximum fractional digits after SI scaling (0–15); trailing zeros are trimmed.
SignificantDigits 6 Maximum significant digits for rounding, clamped to 1–15.
PrefixOrUnitAsDecimalSeparator false Enable 1k5 syntax (SI prefix used as decimal separator).
ForceSign false Always show + for positive values.
ForceDecimalSeparator false Show decimal separator even when no fractional digits follow.
NumberFormat Current UI culture Culture-specific decimal/group separators (NumberFormatInfo).
SISeparator " " Characters inserted before the SI prefix.
UnitSeparator "" Characters inserted before the unit symbol.

Note: FormattingOptions.Default is a shared singleton instance. Use .Clone() if you need to modify options without side effects.

UnitOptions

Property Description
Unit The base Unit for formatting and parsing.
AlternateUnits An AlternateUnitCollection of DerivedUnit instances accepted during parsing (e.g., cm, in for a Meter base).

UnitOptions supports implicit conversion from Unit, so you can pass a Unit directly where a UnitOptions is expected.

Add or remove alternate units through the collection API, including Add, AddRange, Insert, indexed replacement, Remove, and Clear. The collection updates parsing lookups when its contents change. Do not modify unit or formatting options concurrently with parsing.

Building from Source

The projects target .NET 10. Build the solution or create NuGet packages with:

dotnet build TechnicalUnits.slnx -c Release
dotnet pack TechnicalUnits.slnx -c Release

Run the test projects with:

dotnet run --project Tests/TechnicalUnits.Tests
dotnet run --project Tests/TechnicalUnits.Avalonia.Tests
dotnet run --project Tests/TechnicalUnits.WinForms.Tests

Avalonia tests run headlessly. WinForms projects and tests require Windows.

License

TechnicalUnits is licensed under the terms of the MIT license (http://opensource.org/licenses/MIT, see LICENSE.txt).

Product Compatible and additional computed target framework versions.
.NET net10.0-windows7.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.1.277 38 10/4/2026
1.2.1.265 88 9/23/2026
1.2.0.98 145 6/16/2026
1.1.0.57 140 2/26/2026