Introduction

Every journey begins with the first step. But, sometimes, the first step is the hardest to give. Once you started, things fly and everything becomes easier. Starting a new project is always difficult: you don’t have a clear view of the parts and don’t know how to add them to your project in a way they interact seamlessly.

This is worse when you are using a new technology like UWP: what do I add to my project, what are the best practices and so on. To help us on this task and give us a starting point, the Windows team has created the Windows Template Studio, a new Visual Studio template that will help us to create a full UWP project, using only the parts we want.

In this article, we will create a UWP project to manipulate customer data with a master/detail view and use the MVVM pattern with the MVVM Light Framework.

Installing the template

The installation of the template is very simple: in Visual Studio 2017, go to Tools/Extensions and Updates and search for Windows Template Studio:

image

Once you install the template, a new option appears when you select File/New Project under Windows Universal:

image

Creating the project

Once you select this option and give the project name, a new screen will open:

image

In this screen, you can select the kind of the project you want. We will check the Navigation Pane and the MVVM Light Framework and then click in the Next button. Then we select the pages and features.

Select the Master/Detail page and name it Customer. Also select the Settings page:

image

Under the Features, select the Settings Storage, the Live Tile and the Toast Notifications, then click on Create. The new project will be created.

image

As you can see, a full project has been created, separated by folders, with all the needed files for the selected options. This project also is localized, if you want to translate it, you just need to add the localized resources. If you run the project, you will have a page with a hamburger menu and two options:

image

The project has some sample data and all the code to handle navigation, settings (including setting a dark theme), toast notifications and live tiles.

Now it’s time to customize the project for our needs.

Customizing the project

The first step in customizing our app is to set its description in the settings page. If you open SettingsPage.xaml you will see it uses the x:Uid tag to localize strings.

<TextBlock
    x:Uid="Settings_AboutDescription"
    Style="{ThemeResource BodyTextBlockStyle}"/>

To modify the description we need to open the Resources.resw file under Strings\en-US and edit the description:

image

I’ve also changed some margins in the text StackPanel in the page:

<StackPanel Grid.Row="2" Margin="30,16,30,0">
    <TextBlock
        x:Uid="Settings_About"
        Style="{ThemeResource TitleTextBlockStyle}"/>
    <TextBlock
        Text="{x:Bind ViewModel.AppDescription, Mode=OneWay}"
        Style="{ThemeResource SubtitleTextBlockStyle}"
        Margin="0,10"/>
    <TextBlock Margin="0,10"
        x:Uid="Settings_AboutDescription"
        Style="{ThemeResource BodyTextBlockStyle}"/>
    <HyperlinkButton 
        x:Uid="Settings_PrivacyTermsLink"
        Margin="0,10"/>
</StackPanel>

After changing that and running the application again, you can see the new description in the settings page:

image

Now, let’s customize the app for our needs. We don’t need the main page, so let’s remove it. You can also remove MainViewModel.cs. Removing these files, you must go to ViewModelLocator.cs and remove the references to the main viewmodel:

public class ViewModelLocator
{
    NavigationServiceEx _navigationService = new NavigationServiceEx();

    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        SimpleIoc.Default.Register(() => _navigationService);
        SimpleIoc.Default.Register<ShellViewModel>();
        Register<CustomerViewModel, CustomerPage>();
        Register<CustomerDetailViewModel, CustomerDetailPage>();
        Register<SettingsViewModel, SettingsPage>();
    }

    public SettingsViewModel SettingsViewModel => 
        ServiceLocator.Current.GetInstance<SettingsViewModel>();

    public CustomerDetailViewModel CustomerDetailViewModel => 
        ServiceLocator.Current.GetInstance<CustomerDetailViewModel>();

    public CustomerViewModel CustomerViewModel => 
        ServiceLocator.Current.GetInstance<CustomerViewModel>();

    public ShellViewModel ShellViewModel => 
        ServiceLocator.Current.GetInstance<ShellViewModel>();

    public void Register<VM, V>() where VM : class
    {
        SimpleIoc.Default.Register<VM>();
        _navigationService.Configure(typeof(VM).FullName, typeof(V));
    }
}

If you build the project now you will get many errors due to the removal of the main page. Let’s fix them:

The first one is in App.xaml.cs, for the activation of the window:

private ActivationService CreateActivationService()
{
    return new ActivationService(this, typeof(ViewModels.MainViewModel), new Views.ShellPage());
}

We have to change the reference to CustomerViewModel:

private ActivationService CreateActivationService()
{
    return new ActivationService(this, typeof(ViewModels.CustomerViewModel), new Views.ShellPage());
}

The second one is in ShellViewModel.cs, where the items in the NavBar are populated:

private void PopulateNavItems()
{
    _primaryItems.Clear();
    _secondaryItems.Clear();

    // More on Segoe UI Symbol icons: 
    //      https://docs.microsoft.com/windows/uwp/style/segoe-ui-symbol-font
    // Edit String/en-US/Resources.resw: Add a menu item title for each page
    _primaryItems.Add(new ShellNavigationItem("Shell_Main".GetLocalized(), 
        Symbol.Document, typeof(MainViewModel).FullName));
    _primaryItems.Add(new ShellNavigationItem("Shell_Customer".GetLocalized(), 
        Symbol.Document, typeof(CustomerViewModel).FullName));
    _secondaryItems.Add(new ShellNavigationItem("Shell_Settings".GetLocalized(), 
        Symbol.Setting, typeof(SettingsViewModel).FullName));
}

We remove that reference and, while changing that, we also change the Customer’s icon (you can see the reference in the comments):

private void PopulateNavItems()
{
    _primaryItems.Clear();
    _secondaryItems.Clear();

    // More on Segoe UI Symbol icons: 
    //    https://docs.microsoft.com/windows/uwp/style/segoe-ui-symbol-font
    // Edit String/en-US/Resources.resw: Add a menu item title for each page
    _primaryItems.Add(new ShellNavigationItem("Shell_Customer".GetLocalized(), 
        Symbol.People, typeof(CustomerViewModel).FullName));
    _secondaryItems.Add(new ShellNavigationItem("Shell_Settings".GetLocalized(), 
        Symbol.Setting, typeof(SettingsViewModel).FullName));
}

Now, when we run the app, the main page is not there anymore and the customer page has a new symbol in the NavBar:

image

Adding data to the app

We need some customer data, so I added a json file with the customers (this file should be added to the project as Content and Copy if newer):

{
  "Customers": [
    {
      "Id": "ALFKI",
      "CompanyName": "Alfreds Futterkiste",
      "ContactName": "Maria Anders",
      "ContactTitle": "Sales Representative",
      "Address": "Obere Str. 57",
      "City": "Berlin",
      "PostalCode": "12209",
      "Country": "Germany",
      "Phone": "030-0074321",
      "Fax": "030-0076545"
    },
...

If you take a look at the Models folder you will see that the template has added a sample model:

public class SampleModel
{
    public string Title { get; set; }
    public string Description { get; set; }
    public Symbol Symbol { get; set; }

    public char SymbolAsChar
    {
        get { return (char)Symbol; }
    }
}

We don’t want that, so we can remove it and add our model, Customer.cs:

public class Customer
{
    public string Id { get; set; }
    public string CompanyName { get; set; }
    public string ContactName { get; set; }
    public string ContactTitle { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
    public string Phone { get; set; }
    public string Fax { get; set; }
}

When we do that, a lot of code will break, as it is dependent of SampleModel. The first place is in CustomerDetailViewModel, where there is an Item property:

private SampleModel _item;
public SampleModel Item
{
    get { return _item; }
    set { Set(ref _item, value); }
}

We change that to:

private Customer _item;
public Customer Item
{
    get { return _item; }
    set { Set(ref _item, value); }
}

The second place to change is in CustomerViewModel, where we change all references from SampleModel to Customer. There is also a property SampleItems that we will use the Rename refactoring (CTRL+R+R) to rename it to Customers.

In CustomerViewModel, there is a reference to SampleModelService, the service used to serve data to the ViewModel, we have to change it to read data from our json file and rename it to CustomerService (by now, we will make it a read-only service, we will change that later):

public class CustomerService
{
    public async Task<IEnumerable<Customer>> GetDataAsync()
    {
        StorageFile customerFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Customers.json"));
        var customerJson = await FileIO.ReadTextAsync(customerFile);
        return await Json.ToObjectAsync<List<Customer>>(customerJson);
    }
}

We are using the Json.cs helper class to read the data and return the list of customers.

The next step is to change the references in the views. Change all references of SampleModel in CustomerDetailControl.xaml.cs and CustomerDetailPage.xaml.cs. Then, we must change the views to point to the Customer properties. The first change is in the MasterViewItemTemplate in CustomerPage.xaml:

<DataTemplate x:Key="MasterListViewItemTemplate" x:DataType="model:Customer">
    <Grid Margin="12,12,12,12">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock 
            Text="{x:Bind CompanyName}" 
            FontSize="16" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap"/>
        <TextBlock
            Grid.Row="1"
            Opacity="0.6"
            Text="{x:Bind ContactName}"
            FontSize="16" FontWeight="Normal" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap"/>
    </Grid>
</DataTemplate>

Then we must replace the data in CustomDetailControl.xaml to show the customer data (there is even a comment in the file for that):

<TextBlock
    x:Name="TitlePage"
    Text="{x:Bind MasterMenuItem.CompanyName, Mode=OneWay}"
    FontSize="28" FontWeight="SemiLight" TextTrimming="CharacterEllipsis" 
    TextWrapping="NoWrap" VerticalAlignment="Center"
    Margin="0,0,12,7"/>

<ScrollViewer
    Grid.Row="1"
    ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    ScrollViewer.VerticalScrollBarVisibility="Auto"
    ScrollViewer.VerticalScrollMode="Auto">

    <!--The SystemControlPageBackgroundChromeLowBrush background represents where you should place your detail content.-->
    <Grid Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">

        <!--Replate FontIcon and TextBlock with your detail content.-->
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Contact Name" VerticalAlignment="Center" Margin="10,0" Grid.Row="0" Grid.Column="0"/>
        <TextBlock Text="Contact Title" VerticalAlignment="Center" Margin="10,0" Grid.Row="1" Grid.Column="0"/>
        <TextBlock Text="Address" VerticalAlignment="Center" Margin="10,0" Grid.Row="2" Grid.Column="0"/>
        <TextBlock Text="City" VerticalAlignment="Center" Margin="10,0" Grid.Row="3" Grid.Column="0"/>
        <TextBlock Text="Country" VerticalAlignment="Center" Margin="10,0" Grid.Row="4" Grid.Column="0"/>
        <TextBox Text="{x:Bind MasterMenuItem.ContactName, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="0" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.ContactTitle, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="1" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.Address, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="2" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.City, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="3" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.Country, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="4" Grid.Column="1"/>
    </Grid>
</ScrollViewer>
<TextBlock
    x:Name="TitlePage"
    Text="{x:Bind MasterMenuItem.CompanyName, Mode=OneWay}"
    FontSize="28" FontWeight="SemiLight" TextTrimming="CharacterEllipsis" 
    TextWrapping="NoWrap" VerticalAlignment="Center"
    Margin="0,0,12,7"/>

<ScrollViewer
    Grid.Row="1"
    ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    ScrollViewer.VerticalScrollBarVisibility="Auto"
    ScrollViewer.VerticalScrollMode="Auto">

    <!--The SystemControlPageBackgroundChromeLowBrush background represents where you should place your detail content.-->
    <Grid Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">

        <!--Replate FontIcon and TextBlock with your detail content.-->
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Id" VerticalAlignment="Center" Margin="10,0" Grid.Row="0" Grid.Column="0"/>
        <TextBlock Text="Company Name" VerticalAlignment="Center" Margin="10,0" Grid.Row="1" Grid.Column="0"/>
        <TextBlock Text="Contact Name" VerticalAlignment="Center" Margin="10,0" Grid.Row="2" Grid.Column="0"/>
        <TextBlock Text="Contact Title" VerticalAlignment="Center" Margin="10,0" Grid.Row="3" Grid.Column="0"/>
        <TextBlock Text="Address" VerticalAlignment="Center" Margin="10,0" Grid.Row="4" Grid.Column="0"/>
        <TextBlock Text="City" VerticalAlignment="Center" Margin="10,0" Grid.Row="5" Grid.Column="0"/>
        <TextBlock Text="Country" VerticalAlignment="Center" Margin="10,0" Grid.Row="6" Grid.Column="0"/>
        <TextBox Text="{x:Bind MasterMenuItem.Id, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="0" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.CompanyName, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="1" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.ContactName, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="2" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.ContactTitle, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="3" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.Address, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="4" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.City, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="5" Grid.Column="1"/>
        <TextBox Text="{x:Bind MasterMenuItem.Country, Mode=TwoWay}" 
                 VerticalAlignment="Center" Margin="10,5" Grid.Row="6" Grid.Column="1"/>
    </Grid>
</ScrollViewer>

Now, if you run the app, you will see the customer data:

image

Adding, updating and deleting customers

Until now, the data is read-only, we can’t update it. To allow updating the data, we need to save the file in the isolated storage; the actual file is located in the installation folder and it’s read-only. To copy the file to the isolated storage, we must change the Customer Service:

public class CustomerService
{
    public async Task<IEnumerable<Customer>> GetDataAsync()
    {
        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        var customerData = await localFolder.ReadAsync<List<Customer>>("Customers");
        if (customerData == null)
        {
            customerData = await LoadInitialCustomerDataAsync();
            await localFolder.SaveAsync("Customers", customerData);
        }
        return customerData;
    }

    private static async Task<List<Customer>> LoadInitialCustomerDataAsync()
    {
        StorageFile customerFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Customers.json"));
        var customerJson = await FileIO.ReadTextAsync(customerFile);
        return await Json.ToObjectAsync<List<Customer>>(customerJson);
    }
}
 public class CustomerService
 {
     public async Task<IEnumerable<Customer>> GetDataAsync()
     {
         StorageFolder localFolder = ApplicationData.Current.LocalFolder;
         var customerData = await localFolder.ReadAsync<List<Customer>>("Customers");
         if (customerData == null)
         {
             customerData = await LoadInitialCustomerDataAsync();
             await localFolder.SaveAsync("Customers", customerData);
         }
         return customerData;
     }

     private static async Task<List<Customer>> LoadInitialCustomerDataAsync()
     {
         StorageFile customerFile = await StorageFile.GetFileFromApplicationUriAsync(
             new Uri("ms-appx:///Customers.json"));
         var customerJson = await FileIO.ReadTextAsync(customerFile);
         return await Json.ToObjectAsync<List<Customer>>(customerJson);
     }

     public async Task SaveDataAsync(IEnumerable<Customer> customerData)
     {
         StorageFolder localFolder = ApplicationData.Current.LocalFolder;
         await localFolder.SaveAsync("Customers", customerData);
     }
 }

We are using the SettingsStorageExtensions helper class to load the data from the isolated storage. If there is no data, we load the customers from the installation file. We also created a SaveDataAsync method to save the data.

Now we must create some buttons to allow adding, deleting and saving customers. In CustomerPage.xaml we will add the buttons for these actions:

<!--The SystemControlPageBackgroundChromeLowBrush background represents where you should place your master content.-->
<StackPanel Orientation="Horizontal" Grid.Row="1" Margin="0,4">
    <Button Width="48" Height="48" BorderThickness="0" Background="Transparent" 
            Command="{x:Bind ViewModel.AddCustomerCommand}">
        <Grid>
            <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="16"/>
            <Grid Background="White" Margin="16,16,0,0" Width="8" Height="8">
                <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="8" Foreground="Red" />
            </Grid>
        </Grid>
    </Button>
    <Button Width="48" Height="48" BorderThickness="0" Background="Transparent" 
            Command="{x:Bind ViewModel.DeleteCustomerCommand}">
        <Grid>
            <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="16"/>
            <Grid Background="White" Margin="16,16,0,0" Width="8" Height="8">
                <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="8" Foreground="Red" />
            </Grid>
        </Grid>
    </Button>
    <Button Width="48" Height="48" BorderThickness="0" Background="Transparent" 
            Command="{x:Bind ViewModel.SaveCustomersCommand}">
        <Grid>
            <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="16"/>
            <Grid Background="White" Margin="16,16,0,0" Width="8" Height="8">
                <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" FontSize="8" Foreground="Red" />
            </Grid>
        </Grid>
    </Button>
</StackPanel>

We must add the three commands in CustomerViewModel:

public ICommand AddCustomerCommand { get; }
public ICommand DeleteCustomerCommand { get; }
public ICommand SaveCustomersCommand { get; }

public CustomerViewModel()
{
    ItemClickCommand = new RelayCommand<ItemClickEventArgs>(OnItemClick);
    StateChangedCommand = new RelayCommand<VisualStateChangedEventArgs>(OnStateChanged);
    AddCustomerCommand = new RelayCommand(DoAddCustomer);
    DeleteCustomerCommand = new RelayCommand(DoDeleteCustomer);
    SaveCustomersCommand = new RelayCommand(DoSaveCustomers);
}

private async void DoSaveCustomers()
{
    var customerService = new CustomerService();
    await customerService.SaveDataAsync(Customers);
}

private void DoDeleteCustomer()
{
    if (Selected != null)
        Customers.Remove(Selected);
}

private void DoAddCustomer()
{
    var customer = new Customer();
    Customers.Add(customer);
    Selected = customer;
}
public ICommand AddCustomerCommand { get; }
public ICommand DeleteCustomerCommand { get; }
public ICommand SaveCustomersCommand { get; }

public CustomerViewModel()
{
    ItemClickCommand = new RelayCommand<ItemClickEventArgs>(OnItemClick);
    StateChangedCommand = new RelayCommand<VisualStateChangedEventArgs>(OnStateChanged);
    AddCustomerCommand = new RelayCommand(DoAddCustomer);
    DeleteCustomerCommand = new RelayCommand(DoDeleteCustomer);
    SaveCustomersCommand = new RelayCommand(DoSaveCustomers);
}

private async void DoSaveCustomers()
{
    var customerService = new CustomerService();
    await customerService.SaveDataAsync(Customers);
}

private void DoDeleteCustomer()
{
    if (Selected != null)
        Customers.Remove(Selected);
    Selected = Customers.FirstOrDefault();
}

private void DoAddCustomer()
{
    var customer = new Customer();
    Customers.Add(customer);
    Selected = customer;
}

Now, when you run the program, you will see three icons at the top of the customer list and you are able to create update and delete customers. If you click the Save button, the data will be saved and will be available in the next run.

Saving and Loading the current state

One thing that is nice in an application is that the current state is saved when the app is closed. We want to save the selected customer when the app is closed and restore it when it is reopened. To do that, we will save the id of the selected customer to the application settings, using the SettingsStorageExtensions class:

public async Task LoadDataAsync(VisualState currentState)
{
    _currentState = currentState;
    Customers.Clear();

    var service = new CustomerService();
    var data = await service.GetDataAsync();

    foreach (var item in data)
    {
        Customers.Add(item);
    }
    await LoadSettingsAsync();
}

public async void SaveSettings()
{
    if (Selected != null)
    {
        var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

        var container =
            localSettings.CreateContainer("CustSettings",
                Windows.Storage.ApplicationDataCreateDisposition.Always);
        await container.SaveAsync("LastCust", Selected.Id);
    }
}

public async Task LoadSettingsAsync()
{
    if (Selected != null)
    {
        var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

        var container =
            localSettings.CreateContainer("CustSettings",
                Windows.Storage.ApplicationDataCreateDisposition.Always);
        var lastCust = await container.ReadAsync<string>("LastCust");
        if (!string.IsNullOrEmpty(lastCust))
        Selected = Customers.FirstOrDefault(c => c.Id == lastCust);
    }
}
public async Task LoadDataAsync(VisualState currentState)
{
    _currentState = currentState;
    Customers.Clear();

    var service = new CustomerService();
    var data = await service.GetDataAsync();

    foreach (var item in data)
    {
        Customers.Add(item);
    }
    await LoadSettingsAsync();
}

private async void SaveSettings()
{
    if (Selected != null)
    {
        var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

        var container =
            localSettings.CreateContainer("CustSettings",
                Windows.Storage.ApplicationDataCreateDisposition.Always);
        await container.SaveAsync("LastCust", Selected.Id);
    }
}

private async Task LoadSettingsAsync()
{
    var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

    var container =
        localSettings.CreateContainer("CustSettings",
            Windows.Storage.ApplicationDataCreateDisposition.Always);
    var lastCust = await container.ReadAsync<string>("LastCust");
    Selected = !string.IsNullOrEmpty(lastCust) ? 
        Customers.FirstOrDefault(c => c.Id == lastCust) : 
        Customers.FirstOrDefault();
}
public Customer Selected
{
    get { return _selected; }
    set
    {
        Set(ref _selected, value);
        SaveSettings();
    }
}

public async Task LoadDataAsync(VisualState currentState)
{
    _currentState = currentState;
    Customers.Clear();

    var service = new CustomerService();
    var data = await service.GetDataAsync();

    foreach (var item in data)
    {
        Customers.Add(item);
    }
    await LoadSettingsAsync();
}

private async void SaveSettings()
{
    if (Selected != null)
    {
        var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

        var container =
            localSettings.CreateContainer("CustSettings",
                Windows.Storage.ApplicationDataCreateDisposition.Always);
        await container.SaveAsync("LastCust", Selected.Id);
    }
}

private async Task LoadSettingsAsync()
{
    var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

    var container =
        localSettings.CreateContainer("CustSettings",
            Windows.Storage.ApplicationDataCreateDisposition.Always);
    var lastCust = await container.ReadAsync<string>("LastCust");
    Selected = !string.IsNullOrEmpty(lastCust) ? 
        Customers.FirstOrDefault(c => c.Id == lastCust) : 
        Customers.FirstOrDefault();
}

When the data is loaded, the id of the last selected customer is retrieved from the storage settings. This information is saved every time the selected customer changes. That way, when the user opens the app, the last selected customer is shown in the screen.

Working with toasts and tiles

You should have noticed that every time you run the project, a toast notification is shown. As we selected to add the Toast Notifications to the project, these were added and we can customize them. The sample toast is called from DefaultLaunchActivationHandler.cs:

protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args)
{
    // When the navigation stack isn't restored navigate to the first page,
    // configuring the new page by passing required information as a navigation
    // parameter
    NavigationService.Navigate(_navElement, args.Arguments);

    // TODO UWPTemplates: This is a sample on how to show a toast notification.
    // You can use this sample to create toast notifications where needed in your app.
    Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample();
    await Task.CompletedTask;
}

We will remove this call and add it at the end of the load process, to show how many customers were loaded and after saving the customer file, to show how many customers were saved. Before that, we must change the sample toast (in ToastNotificationsService.Samples.cs) to show a custom message:

public void ShowToastNotificationSample(string message)
{
    var customerService = new CustomerService();
    // Create the toast content
    var content = new ToastContent()
    {
        // TODO UWPTemplates: Check this documentation to know more about the Launch property
        // Documentation: https://developer.microsoft.com/en-us/windows/uwp-community-toolkit/api/microsoft_toolkit_uwp_notifications_toastcontent
        Launch = "ToastContentActivationParams",

        Visual = new ToastVisual()
        {
            BindingGeneric = new ToastBindingGeneric()
            {
                Children =
                {
                    new AdaptiveText()
                    {
                        Text = "Customer CRUD"
                    },

                    new AdaptiveText()
                    {
                         Text = message
                    }
                }
            }
        },

        Actions = new ToastActionsCustom()
        {
            Buttons =
            {
                // TODO UWPTemplates: Check this documentation to know more about Toast Buttons
                // Documentation: https://developer.microsoft.com/en-us/windows/uwp-community-toolkit/api/microsoft_toolkit_uwp_notifications_toastbutton
                new ToastButton("OK", "ToastButtonActivationArguments")
                {
                    ActivationType = ToastActivationType.Foreground
                },

                new ToastButtonDismiss("Cancel")
            }
        }
    };

    // Create the toast
    var toast = new ToastNotification(content.GetXml())
    {
        // TODO UWPTemplates: Gets or sets the unique identifier of this notification within the notification Group. Max length 16 characters.
        // Documentation: https://docs.microsoft.com/uwp/api/windows.ui.notifications.toastnotification
        Tag = "ToastTag"
    };

    // And show the toast
    ShowToastNotification(toast);
}

Then we can change the methods to load and save data in CustomerViewModel.cs to show the toast:

 private async void DoSaveCustomers()
 {
     var customerService = new CustomerService();
     await customerService.SaveDataAsync(Customers);
     Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample($"Saved {Customers.Count} customers");
 }

 public async Task LoadDataAsync(VisualState currentState)
 {
     _currentState = currentState;
     Customers.Clear();

     var service = new CustomerService();
     var data = await service.GetDataAsync();

     foreach (var item in data)
     {
         Customers.Add(item);
     }
     await LoadSettingsAsync();
     
     Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample($"Loaded {Customers.Count} customers");
 }

Now, when you run the app, every time the data is loaded or saved, a toast is shown with the number of customers in the file. You can even capture the click of the OK button in the HandleInternalAsync method in ToastNotificationsService.

One final change should be made in our app: it is still showing a default tile.We will change that to reflect the number of customers in the file. The sample tile is shown in the StartupAsync method in ActivationService.cs.

private async Task StartupAsync()
{
    Singleton<LiveTileService>.Instance.SampleUpdate();
    Services.ThemeSelectorService.SetRequestedTheme();
    await Task.CompletedTask;
}

We will change the SampleUpdate in LiveTileService.Samples.cs to show a custom message:

public void SampleUpdate(string message)
{
    // See more information about Live Tiles Notifications
    // Documentation: https://docs.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-sending-a-local-tile-notification

    // These would be initialized with actual data
    string title = "Customer CRUD";
   
    // Construct the tile content
    TileContent content = new TileContent()
    {
        Visual = new TileVisual()
        {
            Arguments = "Customer CRUD",
            TileMedium = new TileBinding()
            {
                Content = new TileBindingContentAdaptive()
                {
                    Children =
                    {
                        new AdaptiveText()
                        {
                            Text = title
                        },
                        new AdaptiveText()
                        {
                            Text = message,
                            HintStyle = AdaptiveTextStyle.CaptionSubtle
                        }
                    }
                }
            },

            TileWide = new TileBinding()
            {
                Content = new TileBindingContentAdaptive()
                {
                    Children =
                    {
                        new AdaptiveText()
                        {
                            Text = title,
                            HintStyle = AdaptiveTextStyle.Subtitle
                        },
                        new AdaptiveText()
                        {
                            Text = message,
                            HintStyle = AdaptiveTextStyle.CaptionSubtle
                        }
                    }
                }
            }
        }
    };

    // Then create the tile notification            
    var notification = new TileNotification(content.GetXml());
    UpdateTile(notification);
}

With that change, we can change CustomerViewModel to update the live tile every time the customer file is loaded or saved:

private async void DoSaveCustomers()
{
    var customerService = new CustomerService();
    await customerService.SaveDataAsync(Customers);
    Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample($"Saved {Customers.Count} customers");
    Singleton<LiveTileService>.Instance.SampleUpdate($"{Customers.Count} customers in the database");
}

public async Task LoadDataAsync(VisualState currentState)
{
    _currentState = currentState;
    Customers.Clear();

    var service = new CustomerService();
    var data = await service.GetDataAsync();

    foreach (var item in data)
    {
        Customers.Add(item);
    }
    await LoadSettingsAsync();
    
    Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample($"Loaded {Customers.Count} customers");
    Singleton<LiveTileService>.Instance.SampleUpdate($"{Customers.Count} customers in the database");
}

Conclusions

As you can see, we went from 0 to a full UWP app, using the MVVM pattern, toast and live tiles, using best practices. The Windows Template Studio gives us a good starting point for it: just select what you want and a sample project is created, where you can fully customize it. In this case, I used a json file, but CustomerService could get its data from an external service with minimal changes. There is a lot of room to improve this app, but the basis is there, it’s not impossible to create a LOB app using UWP.

The full project source is in https://github.com/bsonnino/CustomerCrud

protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args)
{
    // When the navigation stack isn't restored navigate to the first page,
    // configuring the new page by passing required information as a navigation
    // parameter
    NavigationService.Navigate(_navElement, args.Arguments);

    // TODO UWPTemplates: This is a sample on how to show a toast notification.
    // You can use this sample to create toast notifications where needed in your app.
    Singleton<ToastNotificationsService>.Instance.ShowToastNotificationSample();
    await Task.CompletedTask;
}

Some time ago, I’ve written a post about Surface Dial programming. With the introduction of the Creator’s Update, some things have improved. In this post, I will show some changes and will revisit the program that was developed and add these to the app. To use these changes, you need to have the Windows 10 Creator’s Update installed (you will have it if you are using the Windows Insider builds), Visual Studio 2017 and the Creator’s Update SDK (build 15063) installed. Once you have these pre requisites installed, go to the project properties in Visual Studio and change the target version to 15063:

image

With that, the new APIs will be opened.

Menu icons from font glyphs

A welcome change was the possibility to use font glyphs for the menu items. In the previous version, we had to add PNG files to the project, create the icon and then create the menu item, with some code like this:

var iconResize = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Resize.png"));
var itemResize = RadialControllerMenuItem.CreateFromIcon("Resize", iconResize);

This is not needed anymore: now, we can use the CreateFromFontGlyph method, that creates the menu item from a font glyph, that can come both from an installed font or for a custom font for the app. This code creates the menu icons for the app:

// Create the items for the menu
var itemResize = RadialControllerMenuItem.CreateFromFontGlyph("Resize", "\xE8B9", "Segoe MDL2 Assets");
var itemRotate = RadialControllerMenuItem.CreateFromFontGlyph("Rotate", "\xE7AD", "Segoe MDL2 Assets");
var itemMoveX = RadialControllerMenuItem.CreateFromFontGlyph("MoveX", "\xE8AB", "Segoe MDL2 Assets");
var itemMoveY = RadialControllerMenuItem.CreateFromFontGlyph("MoveY", "\xE8CB", "Segoe MDL2 Assets");
var itemColor = RadialControllerMenuItem.CreateFromFontGlyph("Color", "\xE7E6", "Segoe MDL2 Assets"); 

We only have to add the Unicode character and the font name and that’s all. The new Surface Dial menu is like this:

image

Once we have the new menu, we can add new features to the app.

Changing behavior when the button is pressed

One change that we can make to the project is to detect when the button is pressed and change the behavior when the dial is rotated. This is done by using the new IsButtonPressed property in the RadialControllerRotationChangedEventArgs class. We can use it in the RotationChanged event to move diagonally when the button is pressed. We only need to do a small change to the code:

private void ControllerRotationChanged(RadialController sender, 
    RadialControllerRotationChangedEventArgs args)
{
    switch (_currentTool)
    {
        case CurrentTool.Resize:
            Scale.ScaleX += args.RotationDeltaInDegrees / 10;
            Scale.ScaleY += args.RotationDeltaInDegrees / 10;
            break;
        case CurrentTool.Rotate:
            Rotate.Angle += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.MoveX:
            Translate.X += args.RotationDeltaInDegrees;
            if (args.IsButtonPressed)
                Translate.Y += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.MoveY:
            Translate.Y += args.RotationDeltaInDegrees;
            if (args.IsButtonPressed)
                Translate.X += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.Color:
            _selBrush += (int)(args.RotationDeltaInDegrees / 10);
            if (_selBrush >= _namedBrushes.Count)
                _selBrush = 0;
            if (_selBrush < 0)
                _selBrush = _namedBrushes.Count-1;
            Rectangle.Fill = _namedBrushes[(int)_selBrush];
            break;
        default:
            break;
    }
}

 

When the tool is MoveX or MoveY, we check for IsButtonPressed and, if it’s on, we move both in the X and Y directions at the same time. Now, if you run the code and press the button while rotating the dial, you will see that the rectangle moves in the diagonal.

Hiding the menu

The menu is a great tool, but some times, it’s too intrusive and you would like to hide it. Until now, there was no way to hide it, but the Creator’s Update introduced the possibility to do that. You just have to set the IsMenuSuppressed property of the RadialControllerConfiguration class to true and the menu will not appear anymore.

In this case, there will be an extra problem – the controller won’t send messages to the app anymore. You will need some extra steps to regain control:

  • Set the ActiveControllerWhenMenuIsSuppressed property of RadialControllerConfiguration to point to the controller
  • Capture the ButtonHolding event of the controller to set the actions when the user is holding the button
  • Give feedback to the user, as he won’t see the menu anymore

With these steps, you’re ready and don’t need to show the menu.

We will add a new TextBlock to the window to show the tool the user is using. In MainPage.xaml, add this code:

 

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle ...
    </Rectangle>
    <TextBlock x:Name="ToolText" HorizontalAlignment="Right" VerticalAlignment="Bottom" 
               Margin="5" Foreground="Red" FontSize="20" FontWeight="Bold"/>
</Grid>

Then, in the constructor of MainPage, add this code:

// Leave only the Volume default item - Zoom and Undo won't be used
RadialControllerConfiguration config = RadialControllerConfiguration.GetForCurrentView();
config.SetDefaultMenuItems(new[] { RadialControllerSystemMenuItemKind.Volume });
config.ActiveControllerWhenMenuIsSuppressed = controller;
config.IsMenuSuppressed = true;
controller.ButtonHolding += (s, e) => _isButtonHolding = true;
controller.ButtonReleased += (s, e) => _isButtonHolding = false;
ToolText.Text = _currentTool.ToString();

It will suppress the menu, set the controller as the active controller when the menu is suppressed and set the _isButtonHolding field to true when the button is pressed. The next step is to change tools when the button is pressed. This is done in the RotationChanged event:

private void ControllerRotationChanged(RadialController sender,
    RadialControllerRotationChangedEventArgs args)
{
    if (_isButtonHolding)
    {
        _currentTool = args.RotationDeltaInDegrees > 0 ? 
            MoveNext(_currentTool) : MovePrevious(_currentTool); 
        ToolText.Text = _currentTool.ToString();
        return;
    }
    switch (_currentTool)
    ...

When the button is pressed, we change the current tool by calling the MoveNext and MovePrevious (depending of the direction of the rotation) methods:

private CurrentTool MoveNext(CurrentTool currentTool)
{
    return Enum.GetValues(typeof(CurrentTool)).Cast()
        .FirstOrDefault(t => (int)t > (int)currentTool);
}

private CurrentTool MovePrevious(CurrentTool currentTool)
{
    return currentTool == CurrentTool.Resize ? CurrentTool.Color :
        Enum.GetValues(typeof(CurrentTool)).Cast()
        .OrderByDescending(t => t)
        .FirstOrDefault(t => (int)t < (int)currentTool);
}

These methods use LINQ to get the next tool and, if there isn’t one, reset to the first (or last). With this code in place, you can run the app and see that the menu isn’t shown, but the tool is changed when the dial is pressed and rotated, and the TextBlock reflects the current tool.

image

Haptic Feedback

Another improvement in the SDK is the ability to control the haptic feedback, the vibration the dial sends for every change in the rotation. You can disable this feedback by setting the UseAutomaticHapticFeedback property of the controller to false. You can then send your feedback using the methods SendHapticFeedback, SendHapticFeedbackForDuration and SendHapticFeedbackForPlayCount of the SimpleHapticsController class. You can get an instance of this class using the SimpleHapticsController property of the RadialControllerRotationChangedEventArgs class in the RotationChanged event.

We will add the feedback when the user tries to move the rectangle beyond the window’s limits. The first step is to remove the feedback, in the constructor of MainPage:

controller.UseAutomaticHapticFeedback = false;

The next step is to check if the movement is beyond the window’s limits and send the feedback if it is:

case CurrentTool.MoveX:
    if (CanMove(Translate, Scale, args.RotationDeltaInDegrees))
    {
        Translate.X += args.RotationDeltaInDegrees;
        if (args.IsButtonPressed)
            Translate.Y += args.RotationDeltaInDegrees;
    }
    else
        SendHapticFeedback(args.SimpleHapticsController,3);
    break;
case CurrentTool.MoveY:
    if (CanMove(Translate, Scale, args.RotationDeltaInDegrees))
    {
        Translate.Y += args.RotationDeltaInDegrees;
        if (args.IsButtonPressed)
            Translate.X += args.RotationDeltaInDegrees;
    }
    else
        SendHapticFeedback(args.SimpleHapticsController,3);
    break;

The code uses the CanMove method to check if the user is trying to move outside the limits and then, if that’s the case, it calls the SendHapticFeedback method to send the feedback. The CanMove method uses the translation, scale and rotation delta to check it the rectangle can be moved:

private bool CanMove(TranslateTransform translate, ScaleTransform scale,
    double delta)
{
    var canMove = delta > 0 ?
        translate.X + 60 * scale.ScaleX + delta < ActualWidth / 2 &&
        translate.Y + 60 * scale.ScaleY + delta < -ActualWidth / 2 &&
        translate.X - 60 * scale.ScaleX + delta > -ActualWidth / 2 &&
        translate.Y - 60 * scale.ScaleY + delta > -ActualHeight / 2;
    return canMove;
}

SendHapticFeedback receives the SimpleHapticsController as a parameter and sends the feedback:

private void SendHapticFeedback(SimpleHapticsController simpleHapticsController, int count)
{
    var feedback = simpleHapticsController.SupportedFeedback.FirstOrDefault(f =>; 
        f.Waveform == KnownSimpleHapticsControllerWaveforms.Click);
    if (feedback != null)
        simpleHapticsController.SendHapticFeedbackForPlayCount(feedback, 1, count, 
            TimeSpan.FromMilliseconds(100));
}

This method uses LINQ to get the feedback of type Click and, if it finds it, it uses the SendHapticFeedbackForPlayCount to send the feedback three times with an interval of 100ms. We just need to make an extra change: as we removed completely the feedback, there will be no feedback when the user changes tools. One way to restore it is to send the feedback every time we change tools:

if (_isButtonHolding)
{
    _currentTool = args.RotationDeltaInDegrees > 0 ?
        MoveNext(_currentTool) : MovePrevious(_currentTool);
    ToolText.Text = _currentTool.ToString();
    SendHapticFeedback(args.SimpleHapticsController, 1);
    return;
}

Now, if you run the app, you will see that there is no more feedback for every rotation, but you still get feedback when you change tools or when you try to move beyond window’s limits.

Conclusions

We could see a lot of improvements in the Surface Dial API, now we have a lot of control on the menu and on the feedback sent to the user. The Surface Dial brings a new way to input data into your apps, and using it can improve the user experience for your users and make your app stand above the competition.

The full source code for this project is at https://github.com/bsonnino/SurfaceDial2

Recently, Microsoft introduced the new version of its test framework, MS-Test 2. With this new version, they introduced a new feature that I was waiting for a long time: parametrized tests (yes, NUnit and XUnit have had this for a long time, I know).

And what are parametrized tests? Let me show you with an example. Let’s say we have this routine to return Fibonacci numbers (source: https://www.dotnetperls.com/fibonacci)

 

public static int Fibonacci(int n)
{
    int a = 0;
    int b = 1;
    // In N steps compute Fibonacci sequence iteratively.
    for (int i = 0; i &lt; n; i++)
    {
        int temp = a;
        a = b;
        b = temp + b;
    }
    return a;
}

And we want to test it. We would like to test it with the numbers 0, 1, 2 and 80 (the first two are special cases, the third is a normal case and 80 is a large number to be sure that the routine works with large numbers). We should create a test like this:

[TestMethod]
public void Given0FibonacciReturns0()
{
   var fib = new Fib();
    var actual = fib.Fibonacci(0);
    Assert.AreEqual(0,actual);
}

This is not a bad test, but we must copy and paste to test the other results. You may argue that we could create a test like this one:

[TestMethod]
public void GivenDataFibonacciReturnsResultsOk()
{
    var numbers = new[] { 0, 1, 2, 80 };
    var results = new[] { 0L, 1L, 1L, 23416728348467685L };
    var fib = new Fib();
    for (int i = 0; i &lt; numbers.Length; i++)
    {
        var actual = fib.Fibonacci(numbers[i]);
        Assert.AreEqual(results[i], actual);
    }
}

But this has some problems:

  • If a test fails, it’s difficult to know which number failed
  • If one number fails, the next ones are not tested
  • You don’t have a clear view of what is being tested

MS-Test has had for a long time Data Driven tests (https://msdn.microsoft.com/en-us/library/ms182527.aspx), but this is very cumbersome. You must create a data file, assign it to the test and run the test using the TestContext. It’s too much work for just four tests, no?

Then it comes MS-Test 2. With it, you can create a DataTestMethod, with DataRows for each test. Let’s see how do you create a test with this new feature.

Creating Parametrized tests with MS-Test 2

In Visual Studio, create a new Console Project. In this project, create a new class and name it Fib.cs. Add this code to the class:

 public class Fib
 {
     public int Fibonacci(int n)
     {
         int a = 0;
         int b = 1;
         // In N steps compute Fibonacci sequence iteratively.
         for (int i = 0; i &lt; n; i++)
         {
             int temp = a;
             a = b;
             b = temp + b;
         }
         return a;
     }
 }

Then, in the solution, add a new Class Library project. Right click the References node in the Solution Explorer and add a reference to the console project. Then right click in the References node again and select “Manage NuGet packages”. Add the packages MsTest.TestAdapter and MsTest.TestFramework.

image

With that, you have a test project with MS-Test 2. If you are using Visual Studio 2017, the Test Project template already includes these two packages, but you must update them to the latest version, as the parametrized tests didn’t run well with the default packages.

Then, we can create our test:

[TestClass]
public class FibonacciTests
{
    [DataRow(0, 0)]
    [DataRow(1, 1)]
    [DataRow(2, 1)]
    [DataRow(80, 23416728348467685)]
    [DataTestMethod]
    public void GivenDataFibonacciReturnsResultsOk(int number, Int64 result)
    {
        var fib = new Fib();
        var actual = fib.Fibonacci(number);
        Assert.AreEqual(result, actual);
    }
}

The test is very similar to the ones we are used to create, it just has some differences:

 

  • Instead of the TestMethod attribute, it is decorated with the DataTestMethod attribute
  • The method receives two parameters
  • Each test has a DataRow attribute associated to it.

 

 

If we run this test, we get these results:

image

As you can see, we have three tests that passed and one that failed. We didn’t take in account in our routine that the results could be very large and overflow. So, we must change the routine to take this in account:

public Int64 Fibonacci(int n)
{
    Int64 a = 0;
    Int64 b = 1;
    // In N steps compute Fibonacci sequence iteratively.
    for (int i = 0; i &lt; n; i++)
    {
        Int64 temp = a;
        a = b;
        b = temp + b;
    }
    return a;
}

Now, when you run the tests, you get this:

image

All tests are passing, and we can have a clear view of which tests were run, without the need of extra files or any other tricks. Cool, no? This was a very welcome addition to MS-Test and can improve a lot our testing.

The source code for this article is in https://github.com/bsonnino/Fibonacci

Recently, Microsoft introduced the new version of its test framework, MS-Test 2. With this new version, they introduced a new feature that I was waiting for a long time: parametrized tests (yes, NUnit and XUnit have had this for a long time, I know).

And what are parametrized tests? Let me show you with an example. Let’s say we have this routine to return Fibonacci numbers (source: https://www.dotnetperls.com/fibonacci)

 

public static int Fibonacci(int n)
{
    int a = 0;
    int b = 1;
    // In N steps compute Fibonacci sequence iteratively.
    for (int i = 0; i &lt; n; i++)
    {
        int temp = a;
        a = b;
        b = temp + b;
    }
    return a;
}

And we want to test it. We would like to test it with the numbers 0, 1, 2 and 80 (the first two are special cases, the third is a normal case and 80 is a large number to be sure that the routine works with large numbers). We should create a test like this:

[TestMethod]
public void Given0FibonacciReturns0()
{
   var fib = new Fib();
    var actual = fib.Fibonacci(0);
    Assert.AreEqual(0,actual);
}

This is not a bad test, but we must copy and paste to test the other results. You may argue that we could create a test like this one:

[TestMethod]
public void GivenDataFibonacciReturnsResultsOk()
{
    var numbers = new[] { 0, 1, 2, 80 };
    var results = new[] { 0L, 1L, 1L, 23416728348467685L };
    var fib = new Fib();
    for (int i = 0; i &lt; numbers.Length; i++)
    {
        var actual = fib.Fibonacci(numbers[i]);
        Assert.AreEqual(results[i], actual);
    }
}

But this has some problems:

  • If a test fails, it’s difficult to know which number failed
  • If one number fails, the next ones are not tested
  • You don’t have a clear view of what is being tested

MS-Test has had for a long time Data Driven tests (https://msdn.microsoft.com/en-us/library/ms182527.aspx), but this is very cumbersome. You must create a data file, assign it to the test and run the test using the TestContext. It’s too much work for just four tests, no?

Then it comes MS-Test 2. With it, you can create a DataTestMethod, with DataRows for each test. Let’s see how do you create a test with this new feature.

Creating Parametrized tests with MS-Test 2

In Visual Studio, create a new Console Project. In this project, create a new class and name it Fib.cs. Add this code to the class:

 public class Fib
 {
     public int Fibonacci(int n)
     {
         int a = 0;
         int b = 1;
         // In N steps compute Fibonacci sequence iteratively.
         for (int i = 0; i &lt; n; i++)
         {
             int temp = a;
             a = b;
             b = temp + b;
         }
         return a;
     }
 }

Then, in the solution, add a new Class Library project. Right click the References node in the Solution Explorer and add a reference to the console project. Then right click in the References node again and select “Manage NuGet packages”. Add the packages MsTest.TestAdapter and MsTest.TestFramework.

image

With that, you have a test project with MS-Test 2. If you are using Visual Studio 2017, the Test Project template already includes these two packages, but you must update them to the latest version, as the parametrized tests didn’t run well with the default packages.

Then, we can create our test:

[TestClass]
public class FibonacciTests
{
    [DataRow(0, 0)]
    [DataRow(1, 1)]
    [DataRow(2, 1)]
    [DataRow(80, 23416728348467685)]
    [DataTestMethod]
    public void GivenDataFibonacciReturnsResultsOk(int number, Int64 result)
    {
        var fib = new Fib();
        var actual = fib.Fibonacci(number);
        Assert.AreEqual(result, actual);
    }
}

The test is very similar to the ones we are used to create, it just has some differences:

 

  • Instead of the TestMethod attribute, it is decorated with the DataTestMethod attribute
  • The method receives two parameters
  • Each test has a DataRow attribute associated to it.

 

 

If we run this test, we get these results:

image

As you can see, we have three tests that passed and one that failed. We didn’t take in account in our routine that the results could be very large and overflow. So, we must change the routine to take this in account:

public Int64 Fibonacci(int n)
{
    Int64 a = 0;
    Int64 b = 1;
    // In N steps compute Fibonacci sequence iteratively.
    for (int i = 0; i &lt; n; i++)
    {
        Int64 temp = a;
        a = b;
        b = temp + b;
    }
    return a;
}

Now, when you run the tests, you get this:

image

All tests are passing, and we can have a clear view of which tests were run, without the need of extra files or any other tricks. Cool, no? This was a very welcome addition to MS-Test and can improve a lot our testing.

The source code for this article is in https://github.com/bsonnino/Fibonacci

After using the program developed in the last post, I was thinking about some ways to optimize it. Then I went to the FileFinder class and saw this:

class FileFinder
{
    public async Task<ConcurrentDictionary<string, List>> GetFiles(string[] paths, 
        Regex excludeFilesRegex, Regex excludePathsRegex, bool incremental)
    {
        var files = new ConcurrentDictionary<string, List>();
        var tasks = paths.Select(path =>
            Task.Factory.StartNew(() =>
            {
                var rootDir = "";
                var drive = Path.GetPathRoot(path);
                if (!string.IsNullOrWhiteSpace(drive))
                {
                    rootDir = drive[0] + "_drive";
                    rootDir = rootDir + path.Substring(2);
                }
                else
                    rootDir = path;
                var selectedFiles = GetFilesInDirectory(path, excludeFilesRegex, excludePathsRegex, incremental);
                files.AddOrUpdate(rootDir, selectedFiles.ToList(), (a, b) => b);
            }));
        await Task.WhenAll(tasks);
        return files;
    }

    private List GetFilesInDirectory(string directory, Regex excludeFilesRegex, 
        Regex excludePathsRegex,bool incremental)
    {
        var files = new List();
        try
        {
            var directories = Directory.GetDirectories(directory);
            try
            {
                var selectedFiles = Directory.EnumerateFiles(directory).Where(f => !excludeFilesRegex.IsMatch(f.ToLower()));
                if (incremental)
                    selectedFiles = selectedFiles.Where(f => (File.GetAttributes(f) & FileAttributes.Archive) != 0);
                files.AddRange(selectedFiles);
            }
            catch
            {
            }
            foreach (var dir in directories.Where(d => !excludePathsRegex.IsMatch(d.ToLower())))
            {
                files.AddRange(GetFilesInDirectory(Path.Combine(directory, dir), excludeFilesRegex, excludePathsRegex, incremental));
            }
        }
        catch
        {
        }

        return files;
    }
}

I pass the filters to the GetFilesInDirectory method and do my filter there. That way, the folders I don’t want aren’t enumerated. For that, I had to make a change in the Config class, adding a new property for the path Regex and initializing it:

public class Config
{
    public Config(string fileName)
    {
        if (!File.Exists(fileName))
            return;
        var doc = XDocument.Load(fileName);
        if (doc.Root == null)
            return;
        IncludePaths = doc.Root.Element("IncludePaths")?.Value.Split(';');
        ExcludeFiles = doc.Root.Element("ExcludeFiles")?.Value.Split(';') ?? new string[0] ;
        ExcludePaths = doc.Root.Element("ExcludePaths")?.Value.Split(';') ?? new string[0];
        BackupFile = $"{doc.Root.Element("BackupFile")?.Value}{DateTime.Now:yyyyMMddhhmmss}.zip";
        ExcludeFilesRegex = new Regex(string.Join("|", ExcludeFiles));
        ExcludePathRegex = new Regex(string.Join("|", ExcludePaths));
    }

    public Regex ExcludeFilesRegex { get; }
    public Regex ExcludePathRegex { get; }
    public IEnumerable IncludePaths { get; }
    public IEnumerable ExcludeFiles { get; }
    public IEnumerable ExcludePaths { get; }
    public string BackupFile { get; }
}

With this changes, I could run the program again and measure the differences. That made a great difference. Before the change, the program was taking 160s to enumerate the files and give me 470000 files. After the change, enumerating the files took only 14.5s to give me the same files (I ran the programs three times each to avoid distortions). That’s a huge difference, no?

Then I started to think a little bit more and thought that the Regex could be compiled. So, I made two simple changes in the Config class:

ExcludeFilesRegex = new Regex(string.Join("|", ExcludeFiles),RegexOptions.Compiled);
ExcludePathRegex = new Regex(string.Join("|", ExcludePaths), RegexOptions.Compiled);

When I ran again the program, it took me 11.5s to enumerate the files. It doesn’ t seem much, but it’s a 25% improvement with just a simple change. That was really good. That way, I have a backup program that enumerates files way faster than before.

All the source code for the project is at https://github.com/bsonnino/BackupData

Introduction

When you have something digital, having backups is something fundamental to keep your data safe. There are many threats over there that can destroy your data: hardware failures, viruses, natural disasters are some of the ways to make all your data vanish from one moment to the other.

I use to keep my data in several places (you can never be 100% sure Smile), making cloud and local backups, and I can say that they have saved me more than once. For cloud backups, there are several services out there and I won’t discuss them, but for local backups, my needs are very specific, and I don’t need the fancy stuff out there (disk images, copying blocked data, and so on). I just need a backup that has these features:

  • Copies data in a compressed way – it would be better that it’s a standard format, like zip files, so I can open the backups with normal tools and don’t need to use the tool to restore data.
  • Allows the copy of selected folders in different drives. I don’t want to copy my entire disk (why keep a copy of the Windows installation, or the installed programs, if I can reinstall them when I need).
  • Allows the exclusion of files and folders in the copy (I just want to copy my source code, there is no need to copy the executables and dlls).
  • Allows incremental (only the files changed) or full backup (all files in a set)
  • Can use backup configurations (I want to backup only my documents or only my source code, and sometimes both)
  • Can be scheduled and run at specified times without the need of manually starting it.

With these requirements, I started to look for backup programs out there and I have found some free ones that didn’t do everything I wanted and some paid ones that did everything, but I didn’t want to pay what they were asking for. So, being a developer, I decided to make my own backup with the free tools out there.

The first requirement is a compressed backup, with a standard format. For zip files, I need zip64, as the backup files can be very large and the normal zip files won’t handle large files. So, I decided to use the DotNetZip library (https://dotnetzip.codeplex.com/), an open source library that is very simple to use and supports Zip64 files. Now I can go to the next requirements. That can be done with a normal .NET console program.

Creating the backup program

In Visual Studio, create a new Console Program and, in the Solution Explorer, right-click the References node and select “Manage NuGet packages” and add the DotNetZip package. I don’t want to add specific code for working with the command line options, so I added a second package, CommandLineParser (https://github.com/gsscoder/commandline), that does this for me. I just have to create a new class with the options I want and it does all the parsing for me:

class Options
{
    [Option(DefaultValue = "config.xml", 
      HelpText = "Configuration file for the backup.")]
    public string ConfigFile { get; set; }

    [Option('i', "incremental", DefaultValue= false,
      HelpText = "Does an increamental backap.")]
    public bool Incremental { get; set; }

    [HelpOption]
    public string GetUsage()
    {
        return HelpText.AutoBuild(this,
          (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
    }
}

To use it, I just have to pass the command line arguments and have it parsed:

var options = new Options();
CommandLine.Parser.Default.ParseArguments(args, options);

It will even give me a –help command line for help:

image

The next step is to process the configuration file. Create a new class and name it Config.cs:

public class Config
{
    public Config(string fileName)
    {
        if (!File.Exists(fileName))
            return;
        var doc = XDocument.Load(fileName);
        if (doc.Root == null)
            return;
        IncludePaths = doc.Root.Element("IncludePaths")?.Value.Split(';');
        ExcludeFiles = doc.Root.Element("ExcludeFiles")?.Value.Split(';') ?? new string[0] ;
        ExcludePaths = doc.Root.Element("ExcludePaths")?.Value.Split(';') ?? new string[0];
        BackupFile = $"{doc.Root.Element("BackupFile")?.Value}{DateTime.Now:yyyyMMddhhmmss}.zip";
        ExcludeFilesRegex =
            new Regex(string.Join("|", string.Join("|", ExcludeFiles), string.Join("|", ExcludePaths)));
    }

    public Regex ExcludeFilesRegex { get; }
    public IEnumerable IncludePaths { get; }
    public IEnumerable ExcludeFiles { get; }
    public IEnumerable ExcludePaths { get; }
    public string BackupFile { get; }
}

To make it easy to select the paths and files to be excluded, I decided to give it a Regex style and create a Regex that will match all files. For example, if you want to remove all mp3 files, you would add something like “\.mp3$” (starts with a “.”, then mp3 and then the end of the string). If you want to remove mp3 and mp4 files, you can add this: “\.mp[34]$”. For the paths, you get the same thing, but they start and end with a slash (double slash, for the regex).

With this in place, we can start our backup. Create a new class and call it Backup.cs. Add this code to it:

class Backup
{
    private readonly FileFinder _fileFinder = new FileFinder();

    public async Task DoBackup(Config config, bool incremental)
    {
        var files = await _fileFinder.GetFiles(config.IncludePaths.ToArray(), 
               config.ExcludeFilesRegex, incremental);
        using (ZipFile zip = new ZipFile())
        {
            zip.UseZip64WhenSaving = Zip64Option.AsNecessary;
            foreach (var path in files)
                zip.AddFiles(path.Value, false, path.Key);
            zip.Save(config.BackupFile);
        }
        foreach (var file in files.SelectMany(f => f.Value))
            ResetArchiveAttribute(file);
        return 0;
    }

    public void ResetArchiveAttribute(string fileName)
    {
        var attributes = File.GetAttributes(fileName);
        File.SetAttributes(fileName, attributes & ~FileAttributes.Archive);
    }
}

This class uses a FileFinder class to find all files that match the pattern we want and creates a zip file. The GetFiles method from FileFinder returns a dictionary structured like this:

  • The key is a path related to the search path. As the paths can be on any disk of your system and they can have the same names (ex C:\Temp and D:\Temp), and that would not be ok in the zip file, the paths are changed to reflect the same structure, but their names are changed to allow to be added to the zip files. That way, if I am searching in C:\Temp and in D:\Temp, the keys for this dictionary would be C_Drive\Temp and D_Drive\Temp. That way, both paths will be stored in the zip and they wouldn’t clash. These keys are used to change the paths when adding the files to the zip
  • The value is a list of files found in that path

The files are added to the zip and, after that, their Archive bit is reset. This must be done, so the incremental backup can work in the next time: incremental backups are based on the Archive bit: if it’s set, the file was modified and it should be backed up. If not, the file was untouched. This is not a foolproof method, but it works fine for most cases. A more foolproof way to do this would be to keep a log file every full backup with the last modified dates of the files and compare them with the current file dates. This log should be updated every backup. For my case, I think that this is too much and the archive bit is enough.

The FileFinder class is like this one:

class FileFinder
{
    public async Task<ConcurrentDictionary<string, List>> GetFiles(string[] paths, 
        Regex regex, bool incremental)
    {
        var files = new ConcurrentDictionary<string, List>();
        var tasks = paths.Select(path =>
            Task.Factory.StartNew(() =>
            {
                var rootDir = "";
                var drive = Path.GetPathRoot(path);
                if (!string.IsNullOrWhiteSpace(drive))
                {
                    rootDir = drive[0] + "_drive";
                    rootDir = rootDir + path.Substring(2);
                }
                else
                    rootDir = path;
                var selectedFiles = Enumerable.Where(GetFilesInDirectory(path), f => 
                     !regex.IsMatch(f.ToLower()));
                if (incremental)
                    selectedFiles = selectedFiles.Where(f => (File.GetAttributes(f) & FileAttributes.Archive) != 0);
                files.AddOrUpdate(rootDir, selectedFiles.ToList(), (a, b) => b);
            }));
        await Task.WhenAll(tasks);
        return files;
    }

    private List GetFilesInDirectory(string directory)
    {
        var files = new List();
        try
        {
            var directories = Directory.GetDirectories(directory);
            try
            {
                files.AddRange(Directory.EnumerateFiles(directory));
            }
            catch
            {
            }
            foreach (var dir in directories)
            {
                files.AddRange(GetFilesInDirectory(Path.Combine(directory, dir)));
            }
        }
        catch
        {
        }

        return files;
    }
}

The main method of this class is GetFiles. It is an asynchronous method, I will create a new task for every search path. The result is a ConcurrentDictionary, and it has to be so, because there are many threads updating it at once and we could have concurrency issues. The ConcurrentDictionary handles locking when adding data from different threads.

The GetFilesInDirectory finds all files in one directory and, after all files are found, the data is filtered according to the Regex and, if the user asks for an incremental backup, the files are checked for their archive bit set. With this set of files, I can add them to the zip and have a backup file that can be read with standard programs.

Just one requirement remains: to have a scheduled backup. I could make the program stay in the system tray and fire the backup at the scheduled times, but there is an easier way to do it: use the Windows task scheduler. You just need to open a command prompt and type the command:

schtasks /create /sc daily /st "08:15" /tn "Incremental Backup" /t
r "D:\Projetos\Utils\BackupData\BackupData\bin\Debug\Backupdata.exe -i"

That will create a scheduled task that will run the incremental backup every day at 8:15. The main program for this backup is very simple:

static void Main(string[] args)
{
    var options = new Options();
    CommandLine.Parser.Default.ParseArguments(args, options);
    if (string.IsNullOrWhiteSpace(options.ConfigFile))
        return;
    if (string.IsNullOrWhiteSpace(Path.GetDirectoryName(options.ConfigFile)))
    {
        var currentDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        if (!string.IsNullOrWhiteSpace(currentDir))
            options.ConfigFile = Path.Combine(currentDir, options.ConfigFile);
    }
    var config = new Config(options.ConfigFile);
    var backup = new Backup();
    var result = backup.DoBackup(config, options.Incremental).Result;

}

I will parse the arguments, read and parse the config file, create the backup and exit. As you can see, the last line calls DoBackup.Result. This is because the Main method cannot be async and, if I just run it without calling async, it would not wait and would exit without running the backup. Calling result, the program will wait for the task completion.

Just one issue, here – if you wait for the task schedule to fire, you will see that a console window appears, and we don’t want that this happens while we are doing something else. One way to hide the console window is to go to the app properties and set the output type as a Windows application. That will be enough to hide the console window:

image

Conclusions

As you can see, it’s not too difficult to make a backup program using open source tools we have available. This program is very flexible, small and not intrusive. It can run anytime you want and have different configuration files. Not bad, huh?

The full source code for the project is in  https://github.com/bsonnino/BackupData

Acabo de concluir uma série de vídeos sobre o uso de sensores em UWP, que publiquei no Channel 9. São vídeos curtos, com até 15 minutos cada. Vale a pena dar uma conferida e ver como usar os sensores disponíveis no Windows 10 (os programas funcionam tanto no desktop, em tablets, como no smartphone Windows 10):

https://channel9.msdn.com/Series/Windows-Development/Trabalhando-com-Sensores-em-UWP-Parte-1-Sensor-de-Luz – Sensor de Luz – Mostra como usar o sensor de luz para mudar a visualização conforme a luminosidade do ambiente

https://channel9.msdn.com/Series/Windows-Development/Trabalhando-com-Sensores-em-UWP-Parte-2-Bssola – Bússola – Mostra como usar a bússola para dizer ao usuário a sua orientação

https://channel9.msdn.com/Series/Windows-Development/Trabalhando-com-Sensores-em-UWP-Parte-3-Inclinmetro – Inclinômetro – Usa o inclinômetro para mover uma bola na tela conforme o usuário inclina seu dispositivo para a direita ou para a esquerda

https://channel9.msdn.com/Series/Windows-Development/Trabalhando-com-Sensores-em-UWP-Parte-4-Acelermetro – Acelerômetro – Usa o acelerômetro para fazer uma bola pular na tela quando se chacoalha o dispositivo

https://channel9.msdn.com/Series/Windows-Development/Trabalhando-com-Sensores-em-UWP-Parte-5-Geolocalizao – Geolocalização – Usa o sensor de localização para obter o local atual, consultar um serviço de meteorologia e saber se irá chover no seu local atual

When you have a multi-monitor device, you usually want to write code in one monitor and debug the program in another one. This is especially helpful when you want to debug some visual interface that is rendered by the code (or debug the Paint event, for WinForms apps).

But the program you’re debugging insists to open in the same monitor you are writing code. If you try to find some setting in Visual Studio to set the monitor to open the program, you won’t find any. So, what can be done in this case?

I’ve found two options, the code one, and the Windows one. Let’s start with the code option:

Add some code in the closing of the main form to save the window position and in the constructor of the form to restore the window position. This code could be used as a feature for your program: that way, the user can reposition and resize the window and the next time he opens it, it will be in the same position. If you don’t want this feature for the released version, enclose the code in the conditional compiler directive #if DEBUG ..#endif. You can check something like that for WPF in this CodeProject article: https://www.codeproject.com/Articles/50761/Save-and-Restore-WPF-Window-Size-Position-and-or-S (things should be similar for WinForms). With this code, you can move the window to the other monitor and start debugging there. Just remember to close the app normally, to save the current position. The next time you will debug the program, the window it will be in the same place.

For UWP, saving the last window position is the default behavior, so you don’t have to do anything in the code: just move the window to the new position and close it normally and everything is set.

The Windows option is very simple, but not quite at sight: when you have a multi-monitor disposition, there is a checkbox in the display settings that says “Make this my main display”. All you have to do in this case is to right click the desktop of any monitor, select “ Display settings”, select the monitor you want to open your programs, check this box and voilà, all programs will open by default on the selected monitor. The only side effect in this case is that the search bar and the system tray will move to this monitor, but I think that this is minimal and does not affect my daily use. Easy, no?

image

That way, you can edit code in one screen and run the program in the other. So, happy debugging!

Not long ago, Microsoft released its new device, the Surface Studio, a powerhorse with a 28” touch screen, with characteristics that make it a go in the wish list for every geek (https://www.microsoft.com/en-us/surface/devices/surface-studio/overview).

With it, Microsoft released the Surface Dial, a rotary wheel that redefines the way you work when you are doing some creative design (https://www.microsoft.com/en-us/surface/accessories/surface-dial). Although it was created to interact directly with the Surface Studio (you can put it on the screen and it will open new ways to work, you can also use it even if you don’t have the Surface Studio.

In fact, you can use it with any device that has bluetooth connection and Windows 10 Anniversary edition. This article will show you how to use the Surface Dial in a UWP application and enhance the ways your user can interact with your app.

Working with the Surface Dial

Work with the Surface Dial is very easy. Just open the “Manage Bluetooth Devices” window and, in the Surface Dial, open the bottom cover to show the batteries and press the button there until the led starts blinking. The Surface Dial will appear in the window:

image

Just click on it and select “Connect”. Voilà, you are ready to go. And what can you do, now? Just out of the box, many things:

  • Go to a browser window and turn the wheel, and you’ll start scrolling the page content
  • Click on the dial and a menu opens:

image

If you turn the wheel, you can change the function the wheel does:

  • Volume, to increase or decrease the sound volume of you machine
  • Scroll, to scroll up and down the content (the default setting)
  • Zoom, to zoom in and out
  • Undo, to undo or redo the last thing

The best thing here is that it works in the program you are in, no matter if it’s a Windows 10 program or no (for example, you can use it for Undo/Redo in Word or to scroll in the Chrome browser). All of this comes for free, you don’t have to do anything.

If you want to give an extra usability for your Surface Dial, you can go to Windows Settings/Devices/Wheel and set new tools for your apps. For example, if you have a data entry form and you want to move between the text boxes, you can create a Custom Tool like this:

image

That way, the user will be able to move between the boxes using the Surface Dial. Nice, no?

But what if you want to create a special experience for your user? We’ll do that now, creating a UWP program that will be able to change properties of a rectangle: change its size, position, angle or color, with just one tool: the Surface Dial.

Creating a UWP program for the Surface Dial

Initially, let’s create a UWP program in Visual Studio. You must target the Anniversary Edition. If you don’t have it, you must install Visual Studio Update 3 on your system.

Then, add the rectangle to MainPage.xaml:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle Width="100" Height="100" Fill =" Red" x:Name="Rectangle" RenderTransformOrigin="0.5,0.5">
        <Rectangle.RenderTransform>
            <TransformGroup>
                <ScaleTransform x:Name="Scale" ScaleX="1"/>
                <RotateTransform x:Name="Rotate" Angle="0"/>
                <TranslateTransform x:Name="Translate" X="0" Y="0"/>
            </TransformGroup>
        </Rectangle.RenderTransform>
    </Rectangle>
</Grid>

We have added a RenderTransform to the rectangle, so we can scale, rotate or translate it later with the Surface Dial.

The next step is add some png files for the Surface Dial icons. Get some Pngs that represent Rotate, Resize, Move in X Axis, Move in Y Axis and Change Colors (I’ve got mine from https://www.materialui.co) and add them to the Assets folder in the project.

In MainPage.xaml.cs, get the Dial controller, initialize the Surface Dial menu and set its RotationChanged event handler:

public enum CurrentTool
{
    Resize,
    Rotate,
    MoveX,
    MoveY,
    Color
}

private CurrentTool _currentTool;
private readonly List<SolidColorBrush> _namedBrushes;
private int _selBrush;

public MainPage()
{
    this.InitializeComponent();
    // Create a reference to the RadialController.
    var controller = RadialController.CreateForCurrentView();

    // Create the icons for the dial menu
    var iconResize = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Resize.png"));
    var iconRotate = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Rotate.png"));
    var iconMoveX= RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/MoveX.png"));
    var iconMoveY = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/MoveY.png"));
    var iconColor = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Color.png"));

    // Create the items for the menu
    var itemResize = RadialControllerMenuItem.CreateFromIcon("Resize", iconResize);
    var itemRotate = RadialControllerMenuItem.CreateFromIcon("Rotate", iconRotate);
    var itemMoveX = RadialControllerMenuItem.CreateFromIcon("MoveX", iconMoveX);
    var itemMoveY = RadialControllerMenuItem.CreateFromIcon("MoveY", iconMoveY);
    var itemColor = RadialControllerMenuItem.CreateFromIcon("Color", iconColor);

    // Add the items to the menu
    controller.Menu.Items.Add(itemResize);
    controller.Menu.Items.Add(itemRotate);
    controller.Menu.Items.Add(itemMoveX);
    controller.Menu.Items.Add(itemMoveY);
    controller.Menu.Items.Add(itemColor);

    // Select the correct tool when the item is selected
    itemResize.Invoked += (s,e) =>_currentTool = CurrentTool.Resize;
    itemRotate.Invoked += (s,e) =>_currentTool = CurrentTool.Rotate;
    itemMoveX.Invoked += (s,e) =>_currentTool = CurrentTool.MoveX;
    itemMoveY.Invoked += (s,e) =>_currentTool = CurrentTool.MoveY;
    itemColor.Invoked += (s,e) =>_currentTool = CurrentTool.Color;

    // Get all named colors and create brushes from them
    _namedBrushes = typeof(Colors).GetRuntimeProperties().Select(c => new SolidColorBrush((Color)c.GetValue(null))).ToList();
    
    controller.RotationChanged += ControllerRotationChanged;
    
    // Leave only the Volume default item - Zoom and Undo won't be used
    RadialControllerConfiguration config = RadialControllerConfiguration.GetForCurrentView();
    config.SetDefaultMenuItems(new[] { RadialControllerSystemMenuItemKind.Volume });

}

The first step is to get the current controller with RadialController.CreateForCurrentView(). Then, create the icons, initialize the items and add them to the menu. For each item, there will be an Invoked event that will be called when the menu item is invoked. At that time, we select the tool we want. The last steps are to set up the RotationChanged event handler and to remove the default menu items we don’t want.

The RotationChanged event handler is

private void ControllerRotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args)
{
    switch (_currentTool)
    {
        case CurrentTool.Resize:
            Scale.ScaleX += args.RotationDeltaInDegrees / 10;
            Scale.ScaleY += args.RotationDeltaInDegrees / 10;
            break;
        case CurrentTool.Rotate:
            Rotate.Angle += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.MoveX:
            Translate.X += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.MoveY:
            Translate.Y += args.RotationDeltaInDegrees;
            break;
        case CurrentTool.Color:
            _selBrush += (int)(args.RotationDeltaInDegrees / 10);
            if (_selBrush >= _namedBrushes.Count)
                _selBrush = 0;
            if (_selBrush < 0)
                _selBrush = _namedBrushes.Count-1;
            Rectangle.Fill = _namedBrushes[(int)_selBrush];
            break;
        default:
            break;
    }
    
}

Depending on the tool we have, we work with the desired transform. For changing colors, I’ve used an array of brushes, created from the named colors in the system. When the user rotates the dial, I select a new color in the array.

Now, when you run the program, you have something like this:

image

Conclusions

As you can see, we’ve created a completely new interaction for the user. Now, he can change size, angle, position or even color with the dial, without having to select things with the mouse. That’s really a completely new experience and that can be achieved just with a few lines of code. With the Surface Dial you can give to your users a different experience, even if they are not using the Surface Studio.

The full source code for the project is at https://github.com/bsonnino/SurfaceDial.

2016 is coming to an end (What an year here in Brazil!), and we can see retrospectives everywhere. I don’t want to do a “What happened”  or “What did I do”  retrospective. Instead, I want to write about the open source projects I used along the year.

I use open source projects a lot (I don’t like to reinvent the wheel) and they help me a lot. Although many open source projects have a lot of collaborators, most of the time, they are an effort of a single developer (or a very small group of developers) that spend their own spare time to release these gems to the public, without asking anything in return (yes – they are a case of free as “free beer” and free as “freedom”). Nothing more fair than write some lines about them as a way to say “thank you”. So let’s go!

Microsoft open source projects

In the latest years, Microsoft released a lot of open source projects: Roslyn, .NET Core, Asp.NET core, Reactive Extensions, VSCode, TypeScrypt, SignalR and PowerShell are just some of the projects that can be cloned and modified as you will (do you know that Microsoft has the largest number of open source contributors to GitHub: http://www.networkworld.com/article/3120774/open-source-tools/microsoft-s-the-top-open-source-contributor-on-github.html ?).

Although releasing the core projects is something new, I’ve been using Microsoft open source code for a long time – when I started developing, their sample projects were the best way to learn how to use the technology, and this is already the case. If you haven’t checked, go to the Microsoft samples (https://github.com/Microsoft/Windows-universal-samples) to check how to use the newest technology for Windows 10.

So, the first in this retrospective is Microsoft – you can check its open source projects in https://opensource.microsoft.com/ (there were 193 projects there at the time) and in their GitHub: https://github.com/Microsoft (there were 42 pages of repositories there!).

MVVM Light

If you develop for any of the XAML technologies and don’t use the MVVM pattern, shame on you! You should be using this pattern, as it eases the separation between business rules and UI,  facilitates testing and enhances maintainability of your code.

The pattern is very easy to implement and you don’t need any special framework to use it, but using a framework will make things easier and enforce some good rules for your code. There are several MVVM frameworks out there (MVVM Light, Caliburn Micro, Calcium, MVVM Cross, Prism are some that come to my mind, now – maybe I can add to write an article about them – this can be on my resolutions for 2017 Smile), but the one I use the most is MVVM Light Toolkit (http://www.mvvmlight.net/ , source code at http://mvvmlight.codeplex.com/), a nice work from Laurent Bugnion, an old time MVP from Switzerland.

This framework is very easy to use, you must create your ViewModels derived from ViewModelBase and, when you need a command binding, you can use the RelayCommand class. For communication between ViewModels and Views, you can use the Messenger class for sending messages between them. There is a simple IoC container, so you can use dependency injection in your code (another nice feature to remove coupling).

The simplicity and ease of use is definitely a big plus in this framework and the only thing I can say now is “Thanks, Laurent” !

Json.NET

If you are adding Nuget packages to your project, this is the first project that appears in the list. If you need to process Json data (and most developers need it), you must use this project. It’s a high performance Json package and it’s even included in the template for a new Asp.Net project!

It is mainly developed by James Newton-King and you can find it in http://www.newtonsoft.com/json (source code at https://github.com/JamesNK/Newtonsoft.Json). It has a very good Json serializer/deserializer and you can even use Linq to process the Json data. If you want to see it in use, take a look at my article http://blogs.msmvps.com/bsonnino/2016/05/26/minifying-and-formatting-json-data-with-c/ or check my Json Minifier project at https://github.com/bsonnino/ProcessJson.

LightInject

There are many open source IoC containers frameworks out there, and I like very much SimpleInjector, Unity and Ninject, but the one I use most is LightInject (http://www.lightinject.net , source code at https://github.com/seesharper/LightInject), a framework developed by Bernhard Richter. It’s very fast and lightweight and it works on most platforms I use to develop. It works fine with the IoC container available in MVVM Light, and you have several ways to resolve your dependencies.

I really recommend using an IoC container to break your dependencies and make your code more robust. Although it takes some time to get used to this kind of programming, it certainly pays it off when it comes to reusing code and debugging it.

FakeItEasy

Testing, oh, Testing! Testing is like flossing – everybody knows that they should do it, but very few (I would say nobody, but that would not be fair to some poor souls) really do it. When it comes to testing, I really think that one of the worst parts is to mock objects for the tests: create fake classes that do some fake behavior seems to me like some sin I am doing – I should not be doing that or I will go to hell.

FakeItEasy (https://fakeiteasy.github.io/ , source code at https://github.com/FakeItEasy/FakeItEasy) relieves me from that sin. With it, I am able to mock objects and create behaviors that simulate what I want in my tests. The quantity of source code this library saves me is really huge! Oh, and besides all the documentation you have for it, you can also have a free book, published by Syncfusion (one other thing I really recommend – their Succinctly series, now with 103 free ebooks, have fast documentation about a number of subjects: https://www.syncfusion.com/resources/techportal/ebooks): https://www.syncfusion.com/resources/techportal/details/ebooks/fakeiteasy. The only thing I regret in this library is the lack of support for faking under Windows 8 or Windows 10 (In this case, I have to try the LightMock library and see if it fits – another resolution for 2017).

HtmlAgilityPack

By the name, you can think that this pack is for use in an ASP.NET program, but this is not the case. This framework parses HTML files, and you can use it in any .NET application. It’s wonderful when you want to do some web scraping: just load your HTML file into an HtmlDocument and you can easily get the data in it – you can parse the HTML and even change the data in it.

You can get this package at https://htmlagilitypack.codeplex.com/

DotNetZip

If you need to manipulate Zip files in a .NET program, you can use DotNetZip (https://dotnetzip.codeplex.com/). With it, you can open zip files and add new files to them, extract or list the contents of a zip. You can even use Linq to query for entries you want. The files can have password encryption, and it also supports Zip64.

MetroLog

Logging is something that I always need for my programs. When it comes to UWP, there are not many options out there. One of them is Metrolog (https://github.com/onovotny/MetroLog), a very lightweight logging framework developed by Oren Novotny, that works fine with Windows 8/8.1 or UWP.

Although lightweight, it has many features, you can configure the destination of the logs, the logging format and it’s very easy to use: just configure the logging and get a logger with LogManagerFactory.GetDefaultLogger and you can start logging. 

UWPCommunityToolkit

Although this project is under the Microsoft Github (https://github.com/Microsoft/UWPCommunityToolkit, website at https://developer.microsoft.com/en-us/windows/uwp-community-toolkit), it’s not entirely developed by Microsoft. This is a set of components developed by community developers and Microsoft employees, and  features a set of components that are not in the UWP SDK.

You get there a set of controls, like  the Hamburger menu, a Radial gauge or a listview with the “pull to refresh”, many new animations, some code helpers, like new converters or storage helpers, or even access to services like Bing, Facebook or Twitter.

This set of components can improve your UWP apps with minimal code, it’s highly recommended.

SQLite.Net

If you need some kind of structured storage in a UWP application, the best way to do it is using a Sqlite database. In this case, you should use Sqlite.NET (https://github.com/praeclarum/sqlite-net), a small ORM for SQLite databases.

It’s very easy to use and you can have a very good database to store your data in a UWP app, without having to resort to database commands. I really like ORMs, as they abstract the use of a database and use normal C# classes to store data. When you are using a local SQLite database, this ORM is really nice!

Trinet.Core.IO.Ntfs

You won’t be using this library very often, but if you want to handle Alternate Data Streams, like I did in my article here, the Trinet.Core.IO.Ntfs (https://www.codeproject.com/Articles/2670/Accessing-alternative-data-streams-of-files-on-an , source code at https://github.com/hubkey/Trinet.Core.IO.Ntfs) will help you, without the need of adding P/Invokes and working with the Win32 API.

You can check its use in my article and take a look at the ADSs in your system (it surely worth to see what’s hidden in your files).

WPFFolderBrowser

One missing component in WPF is the Folder Browser. Usual answers to this item is to add System.Windows.Forms as a reference to your project and use the Windows Forms component in the program. That’s something I really don’t like: why should I add Windows Forms to my WPF project if I don’t want to use it?

With the Vista Bridge project (now the Windows API Code Pack – https://blogs.msdn.microsoft.com/windowssdk/2009/06/12/windows-api-code-pack-for-microsoft-net-framework/), there were a lot of components that made the bridge between the Windows 32 API released by Vista and WPF.

WPFFolderBrowser (http://wpffolderbrowser.codeplex.com/) takes the work in The Code Pack and creates a Folder Browser for WPF without having to use Windows Forms.

WriteableBitmapEx

Did you ever had the need to create a bitmap from scratch? Or to take a bitmap and change it? WriteableBitmapEx (https://github.com/teichgraf/WriteableBitmapEx/) is a library to manipulate bitmaps. It extends WriteableBitmaps and adds more processing to the bitmap manipulation.

It’s really fast and easy to use, and if you need to do something with a bitmap, you should really use it.

PCLCrypto

Cryptography is something that is needed in your apps from time to time. Windows 8/8.1 and UWP have cryptographic functions that work very well, but, if you need to have portable functions between UWP and full .NET (WPF, Windows Forms or ASP.NET), you should try PCLCrypto (https://github.com/AArnott/PCLCrypto), that uses façade assemblies to the real implementations in every platform.

This way, you use the native cryptographic assemblies for every platform without having to resort to conditional compilation.

Callisto

Callisto (https://timheuer.github.io/callisto/ , source at https://github.com/timheuer/callisto) is a framework developed by Tim Heuer, a Microsoft Program Manager, and was very useful for Windows 8 apps, especially when there was no Flyout control – at that time, the best way to implement it was to use Callisto.

Now, that a Flyout control is in the SDK, there are still a lot to use in Callisto: the rating control, the numeric up/down and the custom dialog are just some I use normally. If you haven’t checked it, it’s worth giving a look at it.

Conclusions

There was a lot covered here. As you can see, I use open source a lot, and I know it’s a big effort to maintain these libraries up-to date, so I’d like to thank all the developers that maintain these libraries and release them as open source, in a way that anybody can analyze them and use them as source code examples, use them to improve their apps without asking anything in return.

And you, do you have any other library that you think it should be listed? Do you want to know more about any of these (maybe I can write an extra article about it)? If you have anything to comment, please place it in the comments section.