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
<PackageReference Include="TechnicalUnits.WinForms" Version="1.3.1.277" />
<PackageVersion Include="TechnicalUnits.WinForms" Version="1.3.1.277" />
<PackageReference Include="TechnicalUnits.WinForms" />
paket add TechnicalUnits.WinForms --version 1.3.1.277
#r "nuget: TechnicalUnits.WinForms, 1.3.1.277"
#:package TechnicalUnits.WinForms@1.3.1.277
#addin nuget:?package=TechnicalUnits.WinForms&version=1.3.1.277
#tool nuget:?package=TechnicalUnits.WinForms&version=1.3.1.277
TechnicalUnits
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, or10.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
doublevalues 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. TheSI()wrapper enables the:SIspecifier by accepting aFormattableString.
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.Defaultis 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 | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows7.0 is compatible. |
-
net10.0-windows7.0
- TechnicalUnits (>= 1.3.1.277)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.