Skip to content

Commit 82eed32

Browse files
authored
Add unit converter WPF sample app (#380)
* Add unit converter WPF sample app To illustrate how to enumerate quantities, units and to convert between them without hard coding any quantities. This sample also illustrates that there is currently need for reflection in order to enumerate units for a selected quantity, as well as getting the abbreviation for a unit, since the library does not provide the means to retrieve these generically. * Update Samples.sln * Add README
1 parent d40fb8e commit 82eed32

30 files changed

+2041
-45
lines changed

Docs/Images/logo-512.ico

109 KB
Binary file not shown.

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,33 @@ RotationalSpeedUnit rpm2 == RotationalSpeed.ParseUnit("r/min"); // RotationalSp
123123
string abbrevKg = Mass.GetAbbreviation(MassUnit.Kilogram); // "kg"
124124
```
125125

126-
### <a name="example-app"></a>Example: Creating a unit converter app
126+
### <a name="example-app"></a>Example: Creating a dynamic unit converter app
127127

128-
*TODO: Add actual sample app and link to it here with screenshot. See [#274](https://github.com/angularsen/UnitsNet/issues/274) for details.*
128+
![image](https://user-images.githubusercontent.com/787816/34920961-9b697004-f97b-11e7-9e9a-51ff7142969b.png)
129+
130+
See [Samples/UnitConverter.Wpf](https://github.com/angularsen/UnitsNet/tree/master/Samples/UnitConverter.Wpf) for source code.
129131

130132
This example shows how you can create a dynamic unit converter, where the user selects the quantity to convert, such as `Length` or `Mass`, then selects to convert from `Meter` to `Centimeter` and types in a value for how many meters.
131133

134+
NOTE: There are still some limitations in the library that requires reflection to enumerate units for quantity and getting the abbreviation for a unit, when we want to dynamically enumerate and convert between units.
135+
136+
### <a name="example-app-hardcoded"></a>Example: Creating a unit converter app with hard coded quantities
137+
138+
If you can live with hard coding what quantities to convert between, then the following code snippet shows you one way to go about it:
139+
132140
```C#
133141
// Get quantities for populating quantity UI selector
134142
QuantityType[] quantityTypes = Enum.GetValues(typeof(QuantityType)).Cast<QuantityType>().ToArray();
135143

136144
// If Length is selected, get length units for populating from/to UI selectors
137145
LengthUnit[] lengthUnits = Length.Units;
138146

139-
// Perform conversion by using .ToString() on the selected units
147+
// Perform conversion using input value and selected from/to units
148+
double inputValue; // Obtain from textbox
149+
LengthUnit fromUnit, toUnit; // Obtain from ListBox selections
150+
double resultValue = Length.From(inputValue, fromUnit).As(toUnit);
151+
152+
// Alternatively, you can also convert using string representations of units
140153
double centimeters = UnitConverter.ConvertByName(5, "Length", "Meter", "Centimeter"); // 500
141154
double centimeters2 = UnitConverter.ConvertByAbbreviation(5, "Length", "m", "cm"); // 500
142155
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp1.0</TargetFramework>
5+
<AssemblyName>ConsoleApp-NetCore</AssemblyName>
6+
<OutputType>Exe</OutputType>
7+
<PackageId>ConsoleApp-NetCore</PackageId>
8+
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
9+
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
10+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
11+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
12+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="UnitsNet" Version="3.45.0" />
17+
</ItemGroup>
18+
19+
</Project>

Samples/ConsoleApp-NetCore/ConsoleApp-NetCore.xproj

Lines changed: 0 additions & 19 deletions
This file was deleted.

Samples/ConsoleApp-NetCore/project.json

Lines changed: 0 additions & 20 deletions
This file was deleted.

Samples/Samples.sln

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25420.1
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27130.2020
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ConsoleApp-NetCore", "ConsoleApp-NetCore\ConsoleApp-NetCore.xproj", "{955A6CBF-C4E3-4C55-85C8-613E2CA4CCD7}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp-NetCore", "ConsoleApp-NetCore\ConsoleApp-NetCore.csproj", "{955A6CBF-C4E3-4C55-85C8-613E2CA4CCD7}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitConverter.Wpf", "UnitConverter.Wpf\UnitConverter.Wpf\UnitConverter.Wpf.csproj", "{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfMVVMSample", "WpfMVVMSample\WpfMVVMSample\WpfMVVMSample.csproj", "{B72F9215-70FF-4155-89BC-9A02CC550447}"
711
EndProject
812
Global
913
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,8 +19,19 @@ Global
1519
{955A6CBF-C4E3-4C55-85C8-613E2CA4CCD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
1620
{955A6CBF-C4E3-4C55-85C8-613E2CA4CCD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
1721
{955A6CBF-C4E3-4C55-85C8-613E2CA4CCD7}.Release|Any CPU.Build.0 = Release|Any CPU
22+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{B72F9215-70FF-4155-89BC-9A02CC550447}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{B72F9215-70FF-4155-89BC-9A02CC550447}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{B72F9215-70FF-4155-89BC-9A02CC550447}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{B72F9215-70FF-4155-89BC-9A02CC550447}.Release|Any CPU.Build.0 = Release|Any CPU
1830
EndGlobalSection
1931
GlobalSection(SolutionProperties) = preSolution
2032
HideSolutionNode = FALSE
2133
EndGlobalSection
34+
GlobalSection(ExtensibilityGlobals) = postSolution
35+
SolutionGuid = {A78FD611-C7A6-472D-95CA-4B474F775CCA}
36+
EndGlobalSection
2237
EndGlobal

Samples/UnitConverter.Wpf/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Unit Converter WPF Sample App
2+
3+
This is a simple sample showing how UnitsNet can be used to create a generic unit converter, using all the quantities and units available in the UnitsNet library.
4+
5+
![image](https://user-images.githubusercontent.com/787816/34920961-9b697004-f97b-11e7-9e9a-51ff7142969b.png)
6+
7+
It allows you to convert a value from one unit to another, by first selecting the quantity (`Length`), then selecting the from/to units (`Meter` to `Centimeter`) and finally typing the numeric value (`15.3`).
8+
The resulting value (`1530`) is computed instantly and reacts to whenever the numeric value or the from/to unit selection changes.
9+
10+
This sample also highlights a limitation in the library that requires reflection in order to enumerate units for a selected quantity and to get the abbreviation for a unit, since the library does not provide a generic means to retrieve these without referencing types like `Length` and `LengthUnit` directly. There is definitely room for improvement in the library here.
11+
12+
### Dependencies
13+
http://mahapps.com/ - UI toolkit for WPF
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27130.2020
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitConverter.Wpf", "UnitConverter.Wpf\UnitConverter.Wpf.csproj", "{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{D04EE35D-496A-4C83-A369-09B9B2BEAEEC}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {1A5C77D4-9765-41D3-9C68-AC307DED35E2}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Wpf_005FGenericUnitConverter_002EAnnotations/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
5+
</startup>
6+
</configuration>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Application x:Class="UnitsNet.Samples.UnitConverter.Wpf.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
StartupUri="MainWindow.xaml">
5+
<Application.Resources>
6+
<ResourceDictionary>
7+
<ResourceDictionary.MergedDictionaries>
8+
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
9+
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
10+
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
11+
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
12+
<!-- Accent and AppTheme setting -->
13+
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
14+
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
15+
</ResourceDictionary.MergedDictionaries>
16+
</ResourceDictionary>
17+
</Application.Resources>
18+
</Application>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Windows;
2+
3+
namespace UnitsNet.Samples.UnitConverter.Wpf
4+
{
5+
/// <summary>
6+
/// Interaction logic for App.xaml
7+
/// </summary>
8+
public partial class App : Application
9+
{
10+
protected override void OnStartup(StartupEventArgs e)
11+
{
12+
// BEGIN WORKAROUND
13+
// HACK to fix issue trying to type "1.5" in the Value textbox when bound to a decimal, since "1." is not a valid decimal value
14+
// it will prevent you from typing that.
15+
// We could use Delay=500 in the binding, but it feels weird to only see the updated result value some time after typing
16+
// in a new value.
17+
System.Windows.FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
18+
// END WORKAROUND
19+
20+
base.OnStartup(e);
21+
}
22+
}
23+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Windows.Input;
3+
4+
namespace UnitsNet.Samples.UnitConverter.Wpf
5+
{
6+
/// <summary>
7+
/// Simple <see cref="ICommand" /> implementation, that executes a callback upon executing the command, such as
8+
/// when clicking a button.
9+
/// </summary>
10+
public sealed class DelegateCommand : ICommand
11+
{
12+
private readonly Action _commandDelegate;
13+
14+
public DelegateCommand(Action commandDelegate)
15+
{
16+
_commandDelegate = commandDelegate;
17+
}
18+
19+
public bool CanExecute(object parameter)
20+
{
21+
return true;
22+
}
23+
24+
public void Execute(object parameter)
25+
{
26+
_commandDelegate.Invoke();
27+
}
28+
29+
public event EventHandler CanExecuteChanged;
30+
}
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.ObjectModel;
2+
using System.ComponentModel;
3+
using System.Windows.Input;
4+
using UnitsNet.Samples.UnitConverter.Wpf.Properties;
5+
6+
namespace UnitsNet.Samples.UnitConverter.Wpf
7+
{
8+
/// <summary>
9+
/// This interface ensures that the view model and the design-time view model are consistent, since the XAML
10+
/// intellisense only knows about the design-time type.
11+
/// </summary>
12+
public interface IMainWindowVm : INotifyPropertyChanged
13+
{
14+
ReadOnlyObservableCollection<QuantityType> Quantities { get; }
15+
ReadOnlyObservableCollection<UnitListItem> Units { get; }
16+
QuantityType SelectedQuantity { get; set; }
17+
18+
[CanBeNull]
19+
UnitListItem SelectedFromUnit { get; set; }
20+
21+
[CanBeNull]
22+
UnitListItem SelectedToUnit { get; set; }
23+
24+
string FromHeader { get; }
25+
string ToHeader { get; }
26+
decimal FromValue { get; set; }
27+
decimal ToValue { get; }
28+
ICommand SwapCommand { get; }
29+
}
30+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<controls:MetroWindow
2+
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
3+
x:Class="UnitsNet.Samples.UnitConverter.Wpf.MainWindow"
4+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
xmlns:wpf="clr-namespace:UnitsNet.Samples.UnitConverter.Wpf"
9+
mc:Ignorable="d"
10+
Title="UnitsNet - WPF unit converter sample app" Height="350" Width="525"
11+
d:DataContext="{d:DesignInstance wpf:MainWindowDesignVm, IsDesignTimeCreatable=True}">
12+
13+
<Grid Margin="10">
14+
<Grid.ColumnDefinitions>
15+
<ColumnDefinition Width="1*" />
16+
<ColumnDefinition Width="1*" />
17+
<ColumnDefinition Width="50" />
18+
<ColumnDefinition Width="1*" />
19+
</Grid.ColumnDefinitions>
20+
<Grid.RowDefinitions>
21+
<RowDefinition Height="*" />
22+
<RowDefinition Height="80" />
23+
</Grid.RowDefinitions>
24+
25+
<GroupBox Header="Quantity" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.RowSpan="2">
26+
<ListBox
27+
ItemsSource="{Binding Path=Quantities}"
28+
SelectedItem="{Binding SelectedQuantity, Mode=TwoWay}"
29+
SelectionChanged="Selector_OnSelectionChanged" />
30+
</GroupBox>
31+
32+
<GroupBox Header="From" Grid.Row="0" Grid.Column="1">
33+
<Grid>
34+
<ListBox
35+
ItemsSource="{Binding Path=Units}"
36+
DisplayMemberPath="Text"
37+
SelectedItem="{Binding SelectedFromUnit, Mode=TwoWay}" />
38+
</Grid>
39+
</GroupBox>
40+
41+
<GroupBox Header="To" Grid.Row="0" Grid.Column="3">
42+
<Grid>
43+
<ListBox
44+
ItemsSource="{Binding Path=Units}"
45+
DisplayMemberPath="Text"
46+
SelectedItem="{Binding SelectedToUnit, Mode=TwoWay}" />
47+
</Grid>
48+
</GroupBox>
49+
50+
51+
<WrapPanel Grid.Row="1" Grid.Column="0" Margin="10">
52+
<!--Placeholder-->
53+
</WrapPanel>
54+
55+
<GroupBox Header="{Binding FromHeader}" Grid.Row="1" Grid.Column="1">
56+
<TextBox FontSize="16" FontWeight="DemiBold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
57+
Text="{Binding FromValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
58+
</GroupBox>
59+
60+
<Grid Grid.Row="1" Grid.Column="2">
61+
<Button Content="" Style="{StaticResource MetroCircleButtonStyle}"
62+
Width="50" Height="50"
63+
FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="10"
64+
Command="{Binding SwapCommand}"/>
65+
</Grid>
66+
67+
<GroupBox Header="{Binding ToHeader}" Grid.Row="1" Grid.Column="3">
68+
<TextBox FontSize="16" FontWeight="DemiBold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
69+
Text="{Binding ToValue, Mode=OneWay}" IsReadOnly="true" />
70+
</GroupBox>
71+
72+
</Grid>
73+
</controls:MetroWindow>
74+
75+
76+
77+
78+
79+
80+

0 commit comments

Comments
 (0)