Category Archives: Programming Patterns
Google Authenticator
As promised a while ago I am going to describe the Google Authenticator today. I do not use my own example source code this time. There is a really nice one here that I will explain. Download the source code and get some extra information from this post. This should be enough know-how to use the Google Authenticator in your own application afterwards.
What is the Google Authenticator?
The Google Authenticator is a program that allows time based passwords. It makes the online identification process much safer. You enter your Id, your password and an extra time based password. A third-party can hardly know the time based password. Even if there is a spy program on your PC that reads all your key inputs, then the hacker only has less than 30 seconds to exploit the opportunity. Otherwise he cannot use your credentials. The time component changes constantly. This is a pretty safe approach.
To steal your identity, the hacker needs to get access to the server database and decrypt your information. This is much more difficult than reading your password by using a key Trojan.
Once again our example program is using WPF.
The XAML is pretty much straight forward. Each element (TextBlock or Image) is using Binding to the underlying object. Just the Hmac TextBlock is less intuitive. The text consists of 3 TextBlocks, which in fact look like one. The green text is the second TextBlock. There is no method to write all text into one TextBlock and then color it. This would be far more complex than this 3 TextBlock solution.
label
|
property binding
|
type
|
Identity | Identity | string |
Secret | Secret | string |
QR code | QRCodeUrl | Image |
Timestamp | Timestamp | string |
Hmac | HmacPart1, HmacPart2, HmacPart3 | string |
One-time password | OneTimePassword | string |
Seconds to go | SecondsToGo | string |
The C# source code has some hyperlinks. You should follow them in case you want to learn some detailed background information. You don’t have to do this. To apply the code in your program it should be enough to study and replicate the example source code. This code is really short and I appreciate this a lot. Many programmers make things far more complicated than needed. Are they trying to show off? I don’t know.
Let’s start from the top now.
The DispatcherTimer is the corresponding class for the good old System.Windows.Forms.Timer class. We are not using WinForms anymore. The idea behind the DispatcherTimer is that the timer fires on the Dispatcher thread. This thread is the same that your window is using. Any other thread could freeze your application.
Do not use the System.Threading.Timer class. This class is neither WPF nor WinForms related. It fires on any thread. You must not access WPF elements on arbitrary threads. On the other hand you should use the System.Threading.Timer class in case you do not want to access WPF. Don’t waste precious time of the Dispatcher thread.
The DispatcherTimer fires each 500 ms (=half second) and assigns the actual value of the 30 second countdown to the property SecondsToGo.
The properties Secret and Identity are initialized with arbitrary example data.
Then the DataContext is set. This enables the data binding between the (XAML) window and the (C#) code.
public MainWindow() { InitializeComponent(); var timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(500); timer.Tick += (s, e) => SecondsToGo = 30 - Convert.ToInt32(GetUnixTimestamp() % 30); timer.IsEnabled = true; Secret = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0xDE, 0xAD, 0xBE, 0xEF }; Identity = "user@host.com"; DataContext = this; }
You may have noticed already that the MainWindow class implements the interface INotifyPropertyChanged. This exposes a WPF event called PropertyChanged, which is used to notify clients via binding that a property value has changed.
namespace System.ComponentModel { public interface INotifyPropertyChanged { // Occurs when a property value changes. event PropertyChangedEventHandler PropertyChanged; } }
You notify a client (UI) with the property name (string) rather than a delegate.
The standard WPF pattern looks like this:
public event PropertyChangedEventHandler PropertyChanged; // INotifyPropertyChanged implementation protected void OnPropertyChanged(string xPropertyName) { PropertyChangedEventHandler h = PropertyChanged; if (h == null) return; h(this, new PropertyChangedEventArgs(xPropertyName)); } // private string _MyProperty; public string MyProperty { get{ return _MyProperty; } set { _MyProperty = value; OnPropertyChanged(“MyProperty”); } } //
And here is the source code from the GoogleAuthenticator example:
public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } private int _secondsToGo; public int SecondsToGo { get { return _secondsToGo; } private set { _secondsToGo = value; OnPropertyChanged("SecondsToGo"); if (SecondsToGo == 30) CalculateOneTimePassword(); } }
You can find that pattern many times in today’s example source code. Be aware that all properties use the same event, which is PropertyChanged. This makes your code look neat, but on the other hand you unfortunately start working with strings instead of delegates. It is part of the MVVM concept to separate code and GUI. For sure it makes debugging much more difficult and code execution slower.
Just to highlight it again. The Hmac consists of three elements. Therefore the property looks like this:
public byte[] Hmac { get { return _hmac; } private set { _hmac = value; OnPropertyChanged("Hmac"); OnPropertyChanged("HmacPart1"); OnPropertyChanged("HmacPart2"); OnPropertyChanged("HmacPart3"); } }
What is an Hmac? To make complex interrelations easier, let’s reduce the Hmac description to “a long calculated key”, which is used to determine our time based password. Therefore to generate the key you need some kind of time input. The other component is a static password (=Secret). I added comments in the following code excerpt:
private void CalculateOneTimePassword() { // Get the number of seconds since 1/1/1970 and devide them by 30 seconds. // Thus one Timestamp unit is 30 seconds. Timestamp = Convert.ToInt64(GetUnixTimestamp() / 30); // Convert the 64 bit integer Timestamp to a byte array (8 bytes). // eg. ba d9 c7 02 00 00 00 00 // Then reverse them (=> 00 00 00 00 02 c7 d9 ba) and write the result to the byte array "data". var data = BitConverter.GetBytes(Timestamp).Reverse().ToArray(); // Generate the Hmac key from your password (byte array) and time (byte array). Hmac = new HMACSHA1(Secret).ComputeHash(data); // Bit-operation: Get the last 4 bits of the Hmac. The results are always equal to or between 0 and 15. // The offset determines the area of the Hmac that is used to generate the time based password. Offset = Hmac.Last() & 0x0F; // The Hmac is 20 bytes long. A block of 4 bytes is used for the OneTimePassword, which changes each 30 seconds. // 15 is the highest Offset. Therefore the last used byte is number 18 (first byte is zero based). // The 19th (=last) byte is the Offset. More precisely the <a href="http://en.wikipedia.org/wiki/Nibble" title="Wiki Nibble Byte" target="_blank">right nibble</a> of the 19th byte is the Offset value. // Bit masks are applied on the selected Hmac block to limit the number. The resulting bits are rotated to the left and added together. // Basically we are looking at a manual "bit to integer" conversion. // the result is then devided by 1,000,000 and only the remainder is taken. Consequently all results are less than 1,000,000. // (The bit mask 0xff is useless. I guess it was used to emphasize the technique for readability purposes. 0x7f does make sense.) OneTimePassword = ( ((Hmac[Offset + 0] & 0x7f) << 24) | ((Hmac[Offset + 1] & 0xff) << 16) | ((Hmac[Offset + 2] & 0xff) << 8) | (Hmac[Offset + 3] & 0xff)) % 1000000; }
When I looked at the program I was trying to find the bitmap for the QR code. You would expect a method somewhere to convert bytes to an image. But this is not the case.
The GetQRCodeUrl method generates a Url. Follow this Url and you will see that it opens an image in your browser. Google does the work for you.
The programmer of that example code has also added the Url to the description on how to generate such QR code Url. Well done! My advice is to have a quick look at it.
private string GetQRCodeUrl() { // https://code.google.com/p/google-authenticator/wiki/KeyUriFormat var base32Secret = Base32.Encode(Secret); return String.Format("https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/{0}%3Fsecret%3D{1}", Identity, base32Secret); }
Now we have all bits and pieces to use the Google Authenticator. But how?
1) Ask for a user identification and a password. For instance user “super@man.com” and password “Strong5rThan$trong!”.
2) Convert the password to a byte array.
3) Generate the QR code.
4) The user can scan this QR code with his android cell phone. Press “Set up account” followed by “Scan a barcode” in the Google Authenticator app.
5) The new account appears and updates in the Authenticator app. The Identity and Secret were encoded in the QR code. This is why the app knows your name already.
6) From now on you can ask for the Id, the password and the time based password.
7) Make sure to have a plan B in place. The user might lose access to his Google Authenticator app. You have to provide a new login then.
Btw. I do know some people, who do not lock their tablet PC or cell phone. The Google Authenticator obviously is not for these kind of people. The problem is not behind the screen … it is in front of the screen.
WPF Datagrid formatting (part 2, advanced)
We stick to the previous DataGrid example and enhance it now.
The improvements/additions are:
- Cells are vertically centered now.
- Copy/paste includes the header text.
ClipboardCopyMode="IncludeHeader"
- Templates cannot be copied/pasted. The DataGrid does not know what property it has to read. Therefore a ClipboardContentBinding was added.
ClipboardContentBinding="{Binding Birthday}
- A yellow smiley is drawn on a Canvas with ellipses and a Bézier curve.
- The birthday string is formatted.
- The DataGrid rows use alternating colors.
- CheckBoxes are centered in the cells.
- A bit closer to hardcore: DatePicker
The method to remove all borders requires slightly more know-how. The required information was taken from my earlier post WPF Control Templates (part 1). Also the background color of the DatePickerTextBox is made transparent. This is done without defining a new template for the DatePicker.The XAML definition
<DatePicker SelectedDate="{Binding Birthday, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" BorderThickness="0" Loaded="DataGrid_DatePicker_Loaded" />
calls:
private void DataGrid_DatePicker_Loaded(object sender, RoutedEventArgs e) {...}
which in turn calls:
private static void RemoveBorders(DependencyObject xDependencyObject) {...}
- RowHeaders were added. On the internet you can find a lot of ToggleButton examples. You face the same code roots over and over again. To avoid coming up with a similar example I used a button with a +/- sign instead. This way you can easily change the code and replace the text by custom images.
- My advice here is: Play with the FrozenColumnCount property. I am sure you will need it someday.
- This example uses more templates than the last one.
- RowDetailsTemplate was added. This enables expanding DataGrid rows to eg. show or enter additional information.
- UpdateSourceTrigger makes sure you can see DataGridCell changes immediately on the RowDetailsTemplate.
To achieve this, the class Person needs to inherit INotifyPropertyChanged.
<Window x:Class="WpfDatagrid.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Language="en-GB" Loaded="Window_Loaded" Closed="Window_Closed" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <!-- DataGrid: header style --> <Style TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="SeparatorBrush" Value="WhiteSmoke" /> <Setter Property="FontWeight" Value="Bold" /> </Style> <!--DataGrid: vertical/horizontal text alignment --> <Style x:Key="AlignRight" TargetType="{x:Type TextBlock}"> <Setter Property="HorizontalAlignment" Value="Right" /> <Setter Property="VerticalAlignment" Value="Center" /> </Style> <Style x:Key="AlignLeft" TargetType="{x:Type TextBlock}"> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="VerticalAlignment" Value="Center" /> </Style> <!--DataGrid: center the CheckBox --> <Style x:Key="AlignCheckBox" TargetType="{x:Type DataGridCell}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridCell}"> <Grid Background="{TemplateBinding Background}"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- DataGrid: template for expandable area --> <DataTemplate x:Key="TemplateRowDetails"> <DockPanel> <Canvas DockPanel.Dock="Left" Width="60"> <Canvas.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FF061246" Offset="0.497"/> <GradientStop Color="#FF7F7FC9" Offset="1"/> </LinearGradientBrush> </Canvas.Background> <Ellipse Fill="Yellow" Height="50" Width="50" StrokeThickness="2" Stroke="Black" Canvas.Left="5" Canvas.Top="5" /> <Ellipse Fill="Black" Height="12" Width="8" Canvas.Left="17" Canvas.Top="20" /> <Ellipse Fill="Black" Height="12" Width="8" Canvas.Left="37" Canvas.Top="20" /> <Path Stroke="Black" StrokeThickness="3"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure StartPoint="15,37"> <PathFigure.Segments> <PathSegmentCollection> <QuadraticBezierSegment Point1="30,52" Point2="45,37" /> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> </Canvas> <TextBlock DockPanel.Dock="Top" FontSize="12" FontWeight="Bold" Text="{Binding FirstName}" /> <TextBlock DockPanel.Dock="Top" FontSize="12" FontWeight="Bold" Text="{Binding LastName}" /> <TextBlock DockPanel.Dock="Top" FontSize="12" FontWeight="Bold" Text="{Binding Birthday, StringFormat={}{0:dd MMMM yyyy}}" /> <TextBlock DockPanel.Dock="Top" FontSize="12" FontWeight="Bold" Text="{Binding Homepage}" /> </DockPanel> </DataTemplate> </Window.Resources> <Grid> <!-- advice: play with the property FrozenColumnCount --> <DataGrid AlternatingRowBackground="PeachPuff" AutoGenerateColumns="False" ItemsSource="{Binding}" CanUserAddRows="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserResizeRows="False" SelectionUnit="Cell" SelectionMode="Extended" ClipboardCopyMode="IncludeHeader" RowDetailsTemplate="{StaticResource TemplateRowDetails}" ColumnHeaderHeight="{Binding RowHeight, RelativeSource={RelativeSource Self}}"> <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="DetailsVisibility" Value="Collapsed" /> </Style> </DataGrid.RowStyle> <!-- DataGrid: RowHeader --> <DataGrid.RowHeaderTemplate> <DataTemplate> <Button Click="DataGridRowHeader_Button_Click" Cursor="Hand" HorizontalAlignment="Center" > <Button.Style> <Style TargetType="Button"> <Style.Setters> <Setter Property="VerticalAlignment" Value="Top" /> <Setter Property="Content" Value="+" /> <Setter Property="FontStretch" Value="UltraExpanded" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="Height" Value="20" /> <Setter Property= "Width" Value="20" /> <!-- <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}" /> --> </Style.Setters> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow}},Path=DetailsVisibility}" Value="Visible"> <Setter Property="Background" Value="Salmon" /> <Setter Property="Content" Value="-" /> <Setter Property="Height" Value="86" /> </DataTrigger> </Style.Triggers> </Style> </Button.Style> </Button> </DataTemplate> </DataGrid.RowHeaderTemplate> <!-- DataGrid: Row color when selected --> <DataGrid.CellStyle> <Style> <Style.Setters> <Setter Property="DataGridCell.VerticalContentAlignment" Value="Center" /> </Style.Setters> <Style.Triggers> <Trigger Property="DataGridCell.IsSelected" Value="True"> <Setter Property="DataGridCell.Background" Value="SteelBlue" /> </Trigger> </Style.Triggers> </Style> </DataGrid.CellStyle> <DataGrid.Columns> <!-- Column: Alive --> <DataGridCheckBoxColumn Header="Alive" Binding="{Binding Alive, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CellStyle="{StaticResource AlignCheckBox}" /> <!-- Column: Name --> <DataGridTextColumn Header="Name" Binding="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ElementStyle="{StaticResource AlignLeft}" /> <!-- Column: LastName --> <DataGridTextColumn Header="LastName" Binding="{Binding LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ElementStyle="{StaticResource AlignLeft}" /> <!-- Column: Birthday --> <DataGridTemplateColumn Header="Birthday" SortMemberPath="Birthday.Day" ClipboardContentBinding="{Binding Birthday}"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DatePicker SelectedDate="{Binding Birthday, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" BorderThickness="0" Loaded="DataGrid_DatePicker_Loaded" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <!-- Column: Age --> <DataGridTextColumn Header="Age" Binding="{Binding Age, StringFormat=N2}" ElementStyle="{StaticResource AlignRight}" IsReadOnly="True" /> <!-- Column: Homepage --> <DataGridHyperlinkColumn Header="Homepage" Binding="{Binding Homepage}" IsReadOnly="True"> <DataGridHyperlinkColumn.ElementStyle> <Style> <EventSetter Event="Hyperlink.Click" Handler="Hyperlink_Clicked"/> <Setter Property="TextBlock.HorizontalAlignment" Value="Left" /> <Setter Property="TextBlock.VerticalAlignment" Value="Center" /> </Style> </DataGridHyperlinkColumn.ElementStyle> </DataGridHyperlinkColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Media; namespace WpfDatagrid { public partial class MainWindow : Window { public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string xName) { PropertyChangedEventHandler h = PropertyChanged; if (h == null) return; h(this, new PropertyChangedEventArgs(xName)); } // private bool _Alive; public bool Alive { get { return _Alive; } set { _Alive = value; OnPropertyChanged("Alive"); } } private string _FirstName; public string FirstName { get { return _FirstName; } set { _FirstName = value; OnPropertyChanged("FirstName"); } } private string _LastName; public string LastName { get { return _LastName; } set { _LastName = value; OnPropertyChanged("LastName"); } } public double Age { get { return DateTime.Now.Subtract(Birthday).TotalDays / 365; } } public string Homepage { get; set; } private DateTime _Birthday; public DateTime Birthday { get { return _Birthday; } set { _Birthday = value; OnPropertyChanged("Birthday"); } } } // class public MainWindow() { InitializeComponent(); } // // set the window DataContext private void Window_Loaded(object sender, RoutedEventArgs e) { List<Person> lPersons = new List<Person>(); lPersons.Add(new Person() { FirstName = "Liza", LastName = "Minnelli", Birthday = new DateTime(1946, 03, 12), Alive = true, Homepage = "www.officiallizaminnelli.com" }); lPersons.Add(new Person() { FirstName = "Bastian", LastName = "Ohta", Birthday = new DateTime(1975, 03, 13), Alive = true, Homepage = "www.ohta.de" }); lPersons.Add(new Person() { FirstName = "Albert", LastName = "Einstein", Birthday = new DateTime(1879, 03, 14), Alive = false, Homepage = "www.alberteinsteinsite.com" }); lPersons.Add(new Person() { FirstName = "Coenraad", LastName = "van Houten", Birthday = new DateTime(1801, 03, 15), Alive = false, Homepage = "www.vanhoutendrinks.com" }); lPersons.Add(new Person() { FirstName = "Andrew", LastName = "Miller-Jones", Birthday = new DateTime(1910, 03, 16), Alive = false, Homepage = "dead as a Dodo" }); lPersons.Add(new Person() { FirstName = "Gottlieb", LastName = "Daimler", Birthday = new DateTime(1834, 03, 17), Alive = false, Homepage = "www.daimler.com" }); lPersons.Add(new Person() { FirstName = "Rudolf", LastName = "Diesel", Birthday = new DateTime(1858, 03, 18), Alive = false, Homepage = "http://en.wikipedia.org/wiki/Rudolf_Diesel" }); DataContext = lPersons; } // // exit the application private void Window_Closed(object sender, EventArgs e) { Application.Current.Shutdown(0); } // // open the hyperlink in a browser private void Hyperlink_Clicked(object sender, RoutedEventArgs e) { try { Hyperlink lHyperlink = e.OriginalSource as Hyperlink; string lUri = lHyperlink.NavigateUri.OriginalString; Process.Start(lUri); } catch (Exception ex) { MessageBox.Show(ex.Message); } } // // find the correct DataGridRow and set the DetailsVisibility private void DataGridRowHeader_Button_Click(object sender, RoutedEventArgs e) { DependencyObject lDependencyObject = e.OriginalSource as DependencyObject; //Button lButton = lDependencyObject as Button; //if (lButton == null) return; while (!(lDependencyObject is DataGridRow) && lDependencyObject != null) lDependencyObject = VisualTreeHelper.GetParent(lDependencyObject); DataGridRow lRow = lDependencyObject as DataGridRow; if (lRow == null) return; //lRow.IsSelected = (lRow.DetailsVisibility != Visibility.Visible); lRow.DetailsVisibility = lRow.DetailsVisibility == System.Windows.Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed; Console.WriteLine(lRow.ActualHeight); } // private void DataGrid_DatePicker_Loaded(object sender, RoutedEventArgs e) { // get the DatePicker control DatePicker lDatePicker = sender as DatePicker; lDatePicker.VerticalContentAlignment = System.Windows.VerticalAlignment.Center; // find the inner textbox and adjust the Background colour DatePickerTextBox lInnerTextBox = lDatePicker.Template.FindName("PART_TextBox", lDatePicker) as DatePickerTextBox; lInnerTextBox.Background = Brushes.Transparent; lInnerTextBox.VerticalContentAlignment = System.Windows.VerticalAlignment.Center; lInnerTextBox.Height = lDatePicker.ActualHeight - 2; // remove watermark ContentControl lWatermark = lInnerTextBox.Template.FindName("PART_Watermark", lInnerTextBox) as ContentControl; lWatermark.IsHitTestVisible = false; lWatermark.Focusable = false; lWatermark.Visibility = System.Windows.Visibility.Collapsed; lWatermark.Opacity = 0; // just as demo ContentControl lContentHost = lInnerTextBox.Template.FindName("PART_ContentHost", lInnerTextBox) as ContentControl; // remove ugly borders RemoveBorders(lInnerTextBox); // hardcore 🙂 } // private static void RemoveBorders(DependencyObject xDependencyObject) { for (int i = 0, n = VisualTreeHelper.GetChildrenCount(xDependencyObject); i < n; i++) { DependencyObject lDependencyObject = VisualTreeHelper.GetChild(xDependencyObject, i); RemoveBorders(lDependencyObject); Border lBorder = lDependencyObject as Border; if (lBorder == null) continue; lBorder.BorderBrush = Brushes.Transparent; } } // } // class } // namespace
WPF Datagrid formatting (part 1)
This source code demonstrates the use of a simple DataGrid. You can sort the rows by any column. The Age column is horizontally aligned to the right. The column headers are using a bold font.
The DataGrid element itself is very flexible. You can add all kinds of columns. The most interesting one is DataGridTemplateColumn, which allows you to add any template. The example template only uses a DatePicker, but you could add far more complexity to it.
Set the SortMemberPath to enable sorting, otherwise the DataGrid sorting algorithm cannot know what data to look at. Remember, we are using a template and not a clearly identifiable data type. In today’s example SortMemberPath is set to “Birthday.Day”, which sorts by the day of the month. In case you prefer to sort by date in general, use SortMemberPath=”Birthday” instead.
I changed the selection color, because the dark blue had a low contrast compared to the web-links. This is dealt with by style triggers. The advantage of triggers is that they only override properties temporarily. As soon as the trigger becomes invalid the control element returns to its previous formatting.
<Window x:Class="WpfDatagrid.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Language="en-GB" Loaded="Window_Loaded" Closed="Window_Closed" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="SeparatorBrush" Value="WhiteSmoke" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="Height" Value="30" /> </Style> <Style x:Key="AlignRight" TargetType="{x:Type TextBlock}"> <Setter Property="HorizontalAlignment" Value="Right" /> </Style> </Window.Resources> <Grid> <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" CanUserAddRows="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserResizeRows="False" SelectionUnit="Cell" SelectionMode="Extended"> <DataGrid.CellStyle> <Style> <Style.Triggers> <Trigger Property="DataGridCell.IsSelected" Value="True"> <Setter Property="DataGridCell.Background" Value="SteelBlue" /> <Setter Property="DataGridCell.BorderBrush" Value="GreenYellow" /> </Trigger> </Style.Triggers> </Style> </DataGrid.CellStyle> <DataGrid.Columns> <DataGridCheckBoxColumn Header="Alive" Binding="{Binding Alive}" /> <DataGridTextColumn Header="Name" Binding="{Binding FirstName}" /> <DataGridTextColumn Header="LastName" Binding="{Binding LastName}" /> <!--<DataGridTemplateColumn Header="Birthday" SortMemberPath="Birthday">--> <DataGridTemplateColumn Header="Birthday" SortMemberPath="Birthday.Day"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DatePicker SelectedDate="{Binding Birthday}" BorderThickness="0" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="Age" Binding="{Binding Age, StringFormat=N2}" ElementStyle="{StaticResource AlignRight}" IsReadOnly="True" /> <DataGridHyperlinkColumn Header="Homepage" Binding="{Binding Homepage}" IsReadOnly="True"> <DataGridHyperlinkColumn.ElementStyle> <Style> <EventSetter Event="Hyperlink.Click" Handler="Hyperlink_Clicked"/> </Style> </DataGridHyperlinkColumn.ElementStyle> </DataGridHyperlinkColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>
using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Documents; namespace WpfDatagrid { public partial class MainWindow : Window { public class Person { public bool Alive { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime Birthday { get; set; } public double Age { get { return DateTime.Now.Subtract(Birthday).TotalDays / 365; } } public string Homepage { get; set; } } // public MainWindow() { InitializeComponent(); } // private void Window_Loaded(object sender, RoutedEventArgs e) { List<Person> lPersons = new List<Person>(); lPersons.Add(new Person() { FirstName = "Liza", LastName = "Minnelli", Birthday = new DateTime(1946, 03, 12), Alive = true, Homepage = "www.officiallizaminnelli.com" }); lPersons.Add(new Person() { FirstName = "Bastian", LastName = "Ohta", Birthday = new DateTime(1975, 03, 13), Alive = true, Homepage = "www.ohta.de" }); lPersons.Add(new Person() { FirstName = "Albert", LastName = "Einstein", Birthday = new DateTime(1879, 03, 14), Alive = false, Homepage = "www.alberteinsteinsite.com" }); lPersons.Add(new Person() { FirstName = "Coenraad", LastName = "van Houten", Birthday = new DateTime(1801, 03, 15), Alive = false, Homepage = "www.vanhoutendrinks.com" }); lPersons.Add(new Person() { FirstName = "Andrew", LastName = "Miller-Jones", Birthday = new DateTime(1910, 03, 16), Alive = false, Homepage = "dead as a Dodo" }); lPersons.Add(new Person() { FirstName = "Gottlieb", LastName = "Daimler", Birthday = new DateTime(1834, 03, 17), Alive = false, Homepage = "www.daimler.com" }); lPersons.Add(new Person() { FirstName = "Rudolf", LastName = "Diesel", Birthday = new DateTime(1858, 03, 18), Alive = false, Homepage = "http://en.wikipedia.org/wiki/Rudolf_Diesel" }); DataContext = lPersons; } // private void Window_Closed(object sender, EventArgs e) { Application.Current.Shutdown(0); } // private void Hyperlink_Clicked(object sender, RoutedEventArgs e) { try { Hyperlink lHyperlink = e.OriginalSource as Hyperlink; string lUri = lHyperlink.NavigateUri.OriginalString; Process.Start(lUri); } catch (Exception ex) { MessageBox.Show(ex.Message); } } // } // class } // namespace
Inversion of Control and Dependency Injection (advanced, Part 1), Programming Patterns
I finally had some time and spent some hours on Inversion of Control (IoC) and Dependency Injection (DI). When I did the same a few weeks ago I did not understand a lot. I got lost on the concept of inversion. I tried to figure out, what the inversion was. This blocked all and I ended up pretty much blank despite nearly an hour of pumping information into my head. When you get lost then you should do it at least properly and at full steam 😉 Well, I finally understood the headline … and the world started making sense again.
Why the name “Inversion of Control”? (IoC)
An entirely hardcoded class controls the program execution path. All branches are predetermined. By using interfaces you can decouple theses flows, so that at the time of creation the class does not exactly know what instructions to call next.
Let’s say you are using events. The class, which is raising an event, hands the control over to another class. The subscribing class is in control of the program flow, not the class that raises the event. The subscribing class can subscribe or unsubscribe. The class, which is providing the event, should not have any active control over subscriptions. Hence it is an inversion of control.
By using IoC classes become more encapsulated. Let’s say a perfect class is blind and urgently needs a guide dog. The control can now be taken over by external factors. This inverts the control entirely.
Unit testing uses that mechanism. A container can be used to control classes and change the bindings.
What is a container?
The expression “container” is rarely used in C#. The C++ world calls containers what C# calls collections. We have come across containers in my C++ posts. Follow the link for a quick refresher.
To simplify the matter we can call a UnityContainer a dictionary of objects with some additional methods.
This container performs binding between components. For instance it replaces all specific interface declarations by fully instantiated classes without the need to explicitly call any initialization.
What is dependency?
The structure of a program is: Input, Calculations, Output. The same generally applies to classes. Let’s say you want to run an analysis of a text file. That analysis class can only function properly if the required text file does exist. The calculations depend on the input. The same applies to the output, which can only run if the calculation was successful.
The input class calls the calculation class, which in turn calls the output class. As we are discussing inversion, let’s decouple the classes and implement events. In this case the output class subscribes to the calculation result event and the calculator subscribes to the input event.
What Dependency Injection? (DI)
“Dependency Injection” is a subset of “Inversion of Control”. IoC is a principle, whereas DI is an actual implementation of the principle.
DI is a software design pattern that allows removing hard-coded dependencies. First of all you do not create objects directly; you just describe how they look like. To accomplish this, the pattern uses interfaces or base classes. As the dependency is not hardcoded, the actual object can vary each time. It just needs to fulfill the inheritable pattern of an interface or base class.
public class Car { } // base class public class Audi : Car { } public class BMW : Car { } public class Exhibition { Car StageObject { set; get; } // assign Audi or BMW as the dependency object of class Exhibition }
Interfaces
What are the benefits of interfaces besides multiple inheritance? Several people can work on different problems simultaneously by using interfaces. One person for instance writes the code for logging to text files and another person writes the code for logging to databases. If both use the same interface definition, then the underlying classes can be easily replaced by each other. Interfaces are like promises to provide predefined class patterns at runtime.
Thus we end up with component separation, which is very useful in unit testing. Interfaces can eliminate unfathomable dependencies if used wisely.
Dependency Injection and Unit Testing
When you run unit tests, then you will need input data. But it could be too complex to run the entire program just to test some methods. You would try to only instantiate the minimum requirements. Think of a syringe and inject the data into the test case to create an acceptable environment. This can be difficult in case the program was not structured well. You need to examine the code to find dependencies.
I will cover some IoC/DI basics today and will follow-up on this after some other posts, which were queuing up in the last weeks:
- Calling Java from C#
- Calling C# from Java
- Implement all Java source code examples of the 15 day series C# to C++
- WPF Datagrid formatting
- Google Authenticator
- create a post index for my blog
Dependency Injection
There are several ways to implement dependencies in a class. The easiest way is to have a field that holds a reference to the dependency, which is probably the worst approach you can have in terms of flexibility.
public class Report1 { private IDataBase _DB = new DataBase(); } Report1 r1 = new Report1(); // dependency DataBase must be figured out by examining the code
You can use methods or properties to tell your classes what dependency objects they should use. The best approach though is via constructors. You can hardly miss parameters when trying to call the constructor. Of course bad constructor overloading can jeopardize this concept.
public class Car { } // base class (instead of an interface) public class Audi : Car { } public class BMW : Car { } public class Exhibition { Car StageObject { set; get; } // assign Audi or BMW as the dependency object of class Exhibition } public class Report2 { public IDataBase DB { get; private set; } public void SetDB(IDataBase xDB) { DB = xDB; } } public class Report3 { public IDataBase DB { get; set; } } public class Report4 { private IDataBase _DB; public Report4(IDataBase xDB) { _DB = xDB; } } DataBase lDB = new DataBase(); Report2 r2 = new Report2(); r2.SetDB(lDB); Report3 r3 = new Report3(); r3.DB = lDB; Report4 r4 = new Report4(lDB);
If the world was just like class Report4, then we could more or less end the post here. Unfortunately dependencies are often not that obvious. They are well hidden and you need to read the code thoroughly to build unit tests.
Dependency Injection goes further and the real take off takes place with Moq, which I will explain in the follow-up post.
The following code example was didactically compiled. You don’t need any further information, it should be self-explanatory. You can download unity by following this link or using NuGet.
using System; using Microsoft.Practices.Unity; public interface IDataBase { void QuerySomething(); } // interface public interface ITextFile { void LoadSomething(); } // interface public interface INetwork { string Text { set; get; } void ReceiveSomething(); } // interface public class Network : INetwork { public void ReceiveSomething() { Console.WriteLine("Receiving TCP data ..."); } public string Text { set; get; } } // class public class DataBase : IDataBase { private string _Dummy = "I am doing something."; public void QuerySomething() { Console.WriteLine(_Dummy); } } // class public class TextFile1 : ITextFile { public void LoadSomething() { Console.WriteLine("TF1: Loading something..."); } } // class public class TextFile2 : ITextFile { public void LoadSomething() { Console.WriteLine("TF2: Loading something..."); } } // class public class TextFile3 : ITextFile { public void LoadSomething() { Console.WriteLine("TF3: Loading something..."); } } // class public class Report5 { public string Text = "#N/A"; private IDataBase _DB; public readonly ITextFile TF; public IDataBase getDB() { return _DB; } public Report5(IDataBase xDB, ITextFile xTextFile) { _DB = xDB; TF = xTextFile; } } // class public class Report6 { public readonly string Text1; public readonly string Text2; private readonly ITextFile _TextFile; public readonly INetwork Network; public Report6(ITextFile xTextFile, INetwork xNetwork, string xText1, string xText2) { _TextFile = xTextFile; Network = xNetwork; Text1 = xText1; Text2 = xText2; } // constructor } // class class Program { static void Main(string[] args) { UnityContainer lContainer = new UnityContainer(); // using Microsoft.Practices.Unity; Report5 r; // insufficient data Console.WriteLine("test: insufficient data"); Console.WriteLine("Registering IDataBase"); lContainer.RegisterType(typeof(IDataBase), typeof(DataBase)); // whenever someone asks for an IDataBase, then return a new DataBase instance try { r = lContainer.Resolve<Report5>(); // throws an exception, because ITextFile is undefined } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine(); // full data Console.WriteLine("test: sufficient data"); Console.WriteLine("Registering ITextFile TF1"); Console.WriteLine("IDataBase is still registered"); lContainer.RegisterType(typeof(ITextFile), typeof(TextFile1)); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile1 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile1 Console.WriteLine(); // override a previous type registration with another type Console.WriteLine("test: override a previous type registration with another type"); Console.WriteLine("Registering ITextFile TF2"); lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); // override the first type registration //lContainer.RegisterType<ITextFile, TextFile2>(); // same as lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile2 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile2 Console.WriteLine(); // override a previous type registration with an instance Console.WriteLine("test: override a previous type registration with an instance"); Console.WriteLine("Registering an instance of TextFile3"); ITextFile lTextFile = new TextFile3(); lContainer.RegisterInstance(lTextFile); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile3 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile3 Console.WriteLine(); // using names to register instances Console.WriteLine("test: using names to register instances"); lContainer.RegisterType<Report5, Report5>(); // creates a default class without any name Report5 a = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("A", a); a.Text = "Report A"; Report5 b = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("B", b); b.Text = "Report B"; r = lContainer.Resolve<Report5>("A"); Console.WriteLine("got " + r.Text); r = lContainer.Resolve<Report5>("B"); Console.WriteLine("got " + r.Text); r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?"; r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text); // new instance r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // new instance Console.WriteLine(); // => HAVE A LOOK AT THE BELOW CONTAINER SNAPSHOT => there are 3 instances for Report5 // using names to register instances Console.WriteLine("test: revision, using the same instance"); Console.WriteLine("ContainerControlledLifetimeManager: re-use instances (singleton behaviour for objects)"); lContainer.RegisterType<Report5, Report5>(new ContainerControlledLifetimeManager()); r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?"; r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text); // new instance r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // same instance !!!!! Console.WriteLine(); // constructors with parameters lContainer.RegisterType<INetwork, Network>(); lContainer.RegisterType<ITextFile, TextFile2>(); Console.WriteLine("test: constructors with parameters"); ResolverOverride[] lParameters = new ResolverOverride[] { new ParameterOverride("xText1", "Hello "), new ParameterOverride("xText2", "world") }; Report6 lReport6 = lContainer.Resolve<Report6>(lParameters); Console.WriteLine("Report6 text field values are: " + lReport6.Text1 + lReport6.Text2); Console.ReadLine(); } // } // class
example output:
test: insufficient data
Registering IDataBase
Resolution of the dependency failed, type = “Report5”, name = “(none)”.
Exception occurred while: while resolving.
Exception is: InvalidOperationException – The type ITextFile does not have an ac
cessible constructor.
———————————————–
At the time of the exception, the container was:
Resolving Report5,(none)
Resolving parameter “xTextFile” of constructor Report5(IDataBase xDB, ITextFil
e xTextFile)
Resolving ITextFile,(none)
test: sufficient data
Registering ITextFile TF1
IDataBase is still registered
type of r.TF is TextFile1
I am doing something.
TF1: Loading something…
test: override a previous type registration with another type
Registering ITextFile TF2
type of r.TF is TextFile2
I am doing something.
TF2: Loading something…
test: override a previous type registration with an instance
Registering an instance of TextFile3
type of r.TF is TextFile3
I am doing something.
TF3: Loading something…
test: using names to register instances
got Report A
got Report B
got #N/A
got #N/A
got #N/A
test: revision, using the same instance
ContainerControlledLifetimeManager: re-use instances (singleton behaviour for ob
jects)
got #N/A
got #N/A
got same instance?
test: constructors with parameters
Report6 text field values are: Hello world