When you are using SQL Server stored procedures, you get a lot of flexibility in your code – changing the code doesn’t need a recompile, deploying the new or updated procedures is as easy as sending and running text scripts, and so on. I won’t enter on the pros and cons of keeping code in the database, as this will open a very long discussion, with no winners.

In this article, I will focus on one aspect of the SQL Server code, the lack of rigid rules for the code. It all starts with being case insensitive, something like “SELECT * from customer” is the same of “select * FROM customer”. It’s not a great deal, but when you have a lot of stored procedures and many people maintaining it, you may need to enforce some standards. In this article I will show you how to use C# to parse SQL Server code and enforce some coding standards in your SQL Server procedures.

Retrieving the stored procedures from the database

The first step for processing the stored procedures is to retrieve them from the database. For this article, we will be using the WorldWideImporters sample database, that can be downloaded from https://github.com/Microsoft/sql-server-samples/releases/tag/wide-world-importers-v1.0.

To get the names of all procedures in the database, you must use a command like this one:

select s.name+'.'+p.name as name from sys.procedures p 
inner join sys.schemas s on p.schema_id = s.schema_id 
order by name

Once you have the names of all the procedures in the database, you can get their code with the procedure sys.sp_helpText, passing the procedure name as a parameter. This code will get all procedures from the database and store them in a list:

static void Main()
{
	using (var con = new SqlConnection("Server=.;Database=WideWorldImporters;Trusted_Connection=True;"))
	{
		con.Open();
		var procTexts = GetStoredProcedures(con).Select(n => new {ProcName = n, ProcText = GetProcText(con, n)}).ToList();
	}
}

private static List<string> GetStoredProcedures(SqlConnection con)
{
	using (SqlCommand sqlCommand = new SqlCommand("select s.name+'.'+p.name as name from sys.procedures p " +
	  "inner join sys.schemas s on p.schema_id = s.schema_id order by name", con))
	{
		using (DataTable procs = new DataTable())
		{
			procs.Load(sqlCommand.ExecuteReader());
			return procs.Rows.OfType<DataRow>().Select(r => r.Field<String>("name")).ToList();
		}
	}
}

private static string GetProcText(SqlConnection con, string procName)
{
	using (SqlCommand sqlCommand = new SqlCommand("sys.sp_helpText", con)
	{
		CommandType = CommandType.StoredProcedure
	})
	{
		sqlCommand.Parameters.AddWithValue("@objname", procName);
		using (var proc = new DataTable())
		{
			try
			{
				proc.Load(sqlCommand.ExecuteReader());
				return string.Join("", proc.Rows.OfType<DataRow>().Select(r => r.Field<string>("Text")));
			}
			catch (SqlException)
			{
				return null;
			}
		}
	}
}

This code will open a connection to the database, get all procedure names and then get their text, returning a list of an anonymous class with two members: ProcName and ProcText. You can then process all the procedures in the database.

Parsing the procedures

The next step is to parse the procedures to split into the containing parts. This is not an easy task to make from scratch. The SQL Server language is not easy and evolves with each version of SQL Server. There are many ways to do the same thing, so writing a parser is not something I would recommend. Fortunately, Microsoft has already written a parser for us, and it’s made available via NuGet package. To use it, just add the Microsoft.SqlServer.TransactSql.ScriptDom package to your app. When you add the NuGet package to the app you will see it’s not an official package: this dll is part of a larger package, Microsoft.SqlServer.DacFx.x64 and it’s there only to make the number of dlls added smaller.

Once you add the package, you can use this code to parse the code of the procedure:

private static (TSqlFragment sqlTree, IList<ParseError> errors) ParseSql(string procText)
{
	var parser = new TSql150Parser(true);
	using (var textReader = new StringReader(procText))
	{
		var sqlTree = parser.Parse(textReader, out var errors);

		return (sqlTree, errors);
	}
}

This method uses a new feature of C# 7.0, ValueTuples. If you are using .NET version 4.7 or higher, you don’t need to add anything to use it. If you are using an older version, you must add the System.ValueTuple NuGet package to your project. At the beginning, we instantiate a TSql150Parser class. Depending on the version of SQL server that you want to parse, you can choose a different parser class. Then, we call the Parse method, that will parse the code, return any errors in the errors variable and return a TSqlFragment with the parsed data. Just with this simple code, you already have many benefits: you can parse the procedure for many versions of SQL Server, check if the procedure has errors in it and parse the procedure into its components. The returned tree is of type TSqlScript, that has some interesting properties:

  • Batches – gets the batches in the procedure, so you can process them individually
  • FirstTokenIndex – shows where the fragment begins – for the script, it usually starts at 0, but when you are working with other kinds of fragments, that may not be the case
  • FragmentLength – size of the fragment, in bytes
  • LastTokenIndex – shows where the fragment ends
  • ScriptTokenStream – the list of parsed tokens
  • StartColumn – start column of the fragment
  • StartLine – start line of the fragment
  • Offset – offset in bytes from the start

As you can see, there is already a lot of information there. When you take a look at the ScriptTokenStream, you can see a lot more there:

The SQL code is parsed into tokens and every token is categorized. With this code, you can group the tokens and have brief statistics of your code:

static void Main()
{
	using (var con = new SqlConnection("Server=.;Database=WideWorldImporters;Trusted_Connection=True;"))
	{
		con.Open();
		var procTexts = GetStoredProcedures(con).Select(n => 
		  new { ProcName = n, ProcText = GetProcText(con, n) }).ToList();
		var procTrees = procTexts.Select(p =>
		{
			var processed = ParseSql(p.ProcText);
			return new { p.ProcName, processed.sqlTree, processed.errors };
		});
		var procStats = procTrees
		  .SelectMany(p => p.sqlTree.ScriptTokenStream)
		  .GroupBy(t => t.TokenType)
		  .Select(g => new { Key = g.Key, Count = g.Count()})
		  .OrderByDescending(g => g.Count);
	}
}

With this code, we can see something like this in the database:

WhiteSpace              15693
Identifier               3571
Variable                 1976
Plus                     1667
UnicodeStringLiteral     1192
Dot                      1085
Semicolon                1051
Comma                     957
LeftParenthesis           937
RightParenthesis          937

We can see the variable names used with this code:

static void Main()
{
	using (var con = new SqlConnection("Server=.;Database=WideWorldImporters;Trusted_Connection=True;"))
	{
		con.Open();
		var procTexts = GetStoredProcedures(con).Select(n => 
		  new { ProcName = n, ProcText = GetProcText(con, n) }).ToList();
		var procTrees = procTexts.Select(p =>
		{
			var processed = ParseSql(p.ProcText);
			return new { p.ProcName, processed.sqlTree, processed.errors };
		});
		var procVariables = procTrees
		  .SelectMany(p => p.sqlTree.ScriptTokenStream)
		  .Where(t => t.TokenType == TSqlTokenType.Variable)
		  .GroupBy(t => t.Text)
		  .Select(g => g.Key)
		  .OrderBy(t => t);
	}
}

If you run this code, you will see that, although there are 1976 tokens of type Variable, there are only 76 different names used.One thing that called me my attention is that there are two similar variables used, @Crlf and @CrLf. We can use this parsing to know in which procedures these variables are used. Note that this is different than a text search: a text search could find the variables inside a comment or in a string. In this case, we will only find the real SQL variables:

static void Main()
{
	using (var con = new SqlConnection("Server=.;Database=WideWorldImporters;Trusted_Connection=True;"))
	{
		con.Open();
		var procTexts = GetStoredProcedures(con).Select(n => new { ProcName = n, ProcText = GetProcText(con, n) }).ToList();
		var procTrees = procTexts.Select(p =>
		{
			var processed = ParseSql(p.ProcText);
			return new { p.ProcName, processed.sqlTree, processed.errors };
		});
		Func<TSqlParserToken, bool> queryVariables = t => t.TokenType == TSqlTokenType.Variable
			  && (t.Text == "@CrLf" || t.Text == "@Crlf");
		var variablesFound = procTrees
		  .Where(p => p.sqlTree.ScriptTokenStream.Any(queryVariables))
		   .Select(t => new { t.ProcName, FoundVariables = t.sqlTree.ScriptTokenStream.Where(queryVariables)});
	}
}

If you run this code, you will see that most of the variables is written as @CrLf, while some are written as @Crlf. We will change all variables to @CrLf with this code:

static void Main()
{
	using (var con = new SqlConnection("Server=.;Database=WideWorldImporters;Trusted_Connection=True;"))
	{
		con.Open();
		var procTexts = GetStoredProcedures(con).Select(n => new { ProcName = n, ProcText = GetProcText(con, n) }).ToList();
		var procTrees = procTexts.Select(p =>
		{
			var processed = ParseSql(p.ProcText);
			return new { p.ProcName, processed.sqlTree, processed.errors };
		});
		var variablesFound = procTrees
		  .Where(p => p.sqlTree.ScriptTokenStream.Any(queryVariables))
		   .Select(t => new { t.ProcName, FoundVariables = t.sqlTree.ScriptTokenStream.Where(queryVariables)});
		foreach (var procName in variablesFound.Select(p => p.ProcName))
		{
			var sqlTree = procTrees.First(p => p.ProcName == procName).sqlTree;
			foreach (var token in sqlTree.ScriptTokenStream.Where(t => t.Text == "@Crlf"))
			{
				token.Text = "@CrLf";
			}
			var changedSql = string.Join("", sqlTree.ScriptTokenStream.Select(t => t.Text).ToArray());
		}

	}
}

ChangedSql has the changed SQL, that can be applied to the database, making the variable names uniform.

Conclusions

As you can see, you can do a lot with this parser, like have statistics, change variable names and so on. But still we haven’t scratched the surface of what can be done with the parser. We will see more in the next article. See you then!

I was developing an app in UWP where I needed to do some cleanup when the window was closed, and the only way that I had found until now was to used the Application event OnSuspending, but it was somewhat cumbersome. I wanted something that was fired when the user clicked the “X” button in the window.

With WinForms or WPF you have some events that fire when the window was closed, but in UWP, there weren’t such events.  Then I found the Restricted Capabilities. These are not normal capabilities, that you can edit using the Package.AppXManifest editor in Visual Studio. To edit them, you must open the XML file directly and add them. One other problem with them is that you will need special approval when submitting the app to the store, but that’s better than the alternative.

To use this capability, you must edit the package.appxmanifest manually. Right click on it in the Solution Explorer and select View Code. Then, in the xml, add the Restricted Capabilities namespace to the Package tag:

<Package ... 
         xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
         IgnorableNamespaces="uap mp uap6 rescap">

Then, add the confirmAppClose capability to the Capabilities node:

<Capabilities>
  <Capability Name="internetClient" />
  <uap6:Capability Name="graphicsCapture" />
  <rescap:Capability Name="confirmAppClose"/>
</Capabilities>

With that, you can use the SystemNavigationManagerPreview.GetForCurrentView().CloseRequested
event to check when the window is being closed, like this:

public MainPage()
{
    this.InitializeComponent();
    SystemNavigationManagerPreview.GetForCurrentView().CloseRequested+= (s, e) =>
    {
        // Code run when the window is closing
    };
}

Nice, no?

One thing that went unnoticed in the last post is that the console app is multi-instance: if you click in the live tile or in the start menu, instead of bringing the same app to the front, you are opening a new window and running a new instance of it. This is something new for UWP apps: until now, you could only have one instance of the app running in your machine. While this works for many apps, when you are developing an LOB app, for example, you may need to open more than one instance and that can be limiting. The SDK released in the Aril 2018 update allow you to create multi instanced apps.

Single instance apps

The default UWP apps have a single instance. When you activate it for the first time, it opens the main window and runs the app. After the second call, the same app is activated and you can handle the activation in the OnLaunched method, like this:

private int _activationNo;
/// <summary>
/// Invoked when the application is launched normally by the end user.  Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (e.PrelaunchActivated == false)
    {
        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter
            rootFrame.Navigate(typeof(MainPage), e.Arguments);
        }

        if (rootFrame.Content is MainPage page)
            page.MainText = 
                $"Activation# {++_activationNo}";
        // Ensure the current window is active
        Window.Current.Activate();
    }
}

This way, you will see the activation number in the main page for the app. The app continues to have a single page, but you can control the number of times it’s activated. One other way to handle this is to open multiple windows, one for each activation. It is done like this:

private int _activationNo;
/// <summary>
/// Invoked when the application is launched normally by the end user.  Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (e.PrelaunchActivated == false)
    {
        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter
            rootFrame.Navigate(typeof(MainPage), e.Arguments);


            if (rootFrame.Content is MainPage page)
                page.MainText =
                    $"Activation# {++_activationNo}";
            // Ensure the current window is active
            Window.Current.Activate();
        }
        else
        {
            CreateNewView();
        }
    }
}

private async void CreateNewView()
{
    CoreApplicationView newView = CoreApplication.CreateNewView();
    int newViewId = 0;
    await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        Frame frame = new Frame();
        frame.Navigate(typeof(MainPage), null);
        Window.Current.Content = frame;
        if (frame.Content is MainPage page)
            page.MainText =
                $"Activation# {++_activationNo}";
        // You have to activate the window in order to show it later.
        Window.Current.Activate();

        newViewId = ApplicationView.GetForCurrentView().Id;
    });
    await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
}

The process is similar to the previous ones, but there is a difference: for the first activation, it fills the main window, and if not, it creates a new window with the CreateNewView method, then fills its content in the same way it did for the main window. After the window and the view are created and activated, we call ApplicationViewSwitcher.TryShowAsStandaloneAsync to show it. This will show the new window for the app.

MultiWnd

This arrangement works fine, but there is a problem, here: all windows are in the same process and, if you have a problem in one of them, all will crash.  This may not be what you want, and it’s changed in the April 2018 update – you can have multi-instance UWP apps.

Multi-Instance apps

The main difference is the SupportsMultipleInstances attribute in the package.appxmanifest file, in the Application node:

<Application 
  Id="App"
  Executable="$targetnametoken$.exe"
  EntryPoint="MultiInstance.Program"
  desktop4:SupportsMultipleInstances="true" 
  iot2:SupportsMultipleInstances="true">

The desktop4 and iot2 namespaces are defined in the Package node:

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" 
  xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" 
  xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2" 
  IgnorableNamespaces="uap mp uap5 iot2 desktop4">

Once you put these in your package.appxmanifest, your application will be multi-instanced:

 

MultiInstance

If you don’t want to add this data manually, you can install the Multi-Instance templates, created by Microsoft. In Visual Studio, go to Tools/Extensions and Updates and install the Multi-Instance template:

MultiInstanceTemplate

Once you do that, you will get two new UWP templates, the Multi-Instance UWP App and Multi-Instance UWP Redirection App:

NewUwpTemplates

The Multi-Instance UWP App template does what we did before: creates a blank app with the new attribute. The Multi-Instance Redirection UWP App is a bit more complex. Sometimes you want to handle how multi instancing works and call a specific instance. In this case, the template generates a Program.cs file with a Main method, where you can handle the redirection.

public static class Program
{
    // This project includes DISABLE_XAML_GENERATED_MAIN in the build properties,
    // which prevents the build system from generating the default Main method:
    //static void Main(string[] args)
    //{
    //    global::Windows.UI.Xaml.Application.Start((p) => new App());
    //}

    // This example code shows how you could implement the required Main method to
    // support multi-instance redirection. The minimum requirement is to call
    // Application.Start with a new App object. Beyond that, you may delete the
    // rest of the example code and replace it with your custom code if you wish.

    static void Main(string[] args)
    {
        // First, we'll get our activation event args, which are typically richer
        // than the incoming command-line args. We can use these in our app-defined
        // logic for generating the key for this instance.
        IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();

        // In some scenarios, the platform might indicate a recommended instance.
        // If so, we can redirect this activation to that instance instead, if we wish.
        if (AppInstance.RecommendedInstance != null)
        {
            AppInstance.RecommendedInstance.RedirectActivationTo();
        }
        else
        {
            // Define a key for this instance, based on some app-specific logic.
            // If the key is always unique, then the app will never redirect.
            // If the key is always non-unique, then the app will always redirect
            // to the first instance. In practice, the app should produce a key
            // that is sometimes unique and sometimes not, depending on its own needs.
            uint number = CryptographicBuffer.GenerateRandomNumber();
            string key = (number % 2 == 0) ? "even" : "odd";
            var instance = AppInstance.FindOrRegisterInstanceForKey(key);
            if (instance.IsCurrentInstance)
            {
                // If we successfully registered this instance, we can now just
                // go ahead and do normal XAML initialization.
                global::Windows.UI.Xaml.Application.Start((p) => new App());
            }
            else
            {
                // Some other instance has registered for this key, so we'll 
                // redirect this activation to that instance instead.
                instance.RedirectActivationTo();
            }
        }
    }
}

From the comments in the file, you can see that you can get the default behavior just by using this code in the Main method:

static void Main(string[] args)
{
    global::Windows.UI.Xaml.Application.Start((p) => new App());
}

The key will be linked to the instance you want. For example, if you generate only different keys, new instances will be opened, and if you generate only one key, the first instance will be called. You can control how many instances you want and which ones you want to activate by using the keys you generate. For example, this code will allow you to have three instances open and will call them in a round-robin scheme:

static void Main(string[] args)
{
    // First, we'll get our activation event args, which are typically richer
    // than the incoming command-line args. We can use these in our app-defined
    // logic for generating the key for this instance.
    IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();

    // In some scenarios, the platform might indicate a recommended instance.
    // If so, we can redirect this activation to that instance instead, if we wish.
    if (AppInstance.RecommendedInstance != null)
    {
        AppInstance.RecommendedInstance.RedirectActivationTo();
    }
    else
    {
        // Define a key for this instance, based on some app-specific logic.
        // If the key is always unique, then the app will never redirect.
        // If the key is always non-unique, then the app will always redirect
        // to the first instance. In practice, the app should produce a key
        // that is sometimes unique and sometimes not, depending on its own needs.
        var instanceNo = GetInstanceNo();
        string key = instanceNo.ToString();
        var instance = AppInstance.FindOrRegisterInstanceForKey(key);
        if (instance.IsCurrentInstance)
        {
            // If we successfully registered this instance, we can now just
            // go ahead and do normal XAML initialization.
            global::Windows.UI.Xaml.Application.Start(p => new App());
        }
        else
        {
            // Some other instance has registered for this key, so we'll 
            // redirect this activation to that instance instead.
            instance.RedirectActivationTo();
        }
    }
}

private static int GetInstanceNo()
{
    ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
    var instanceNo = 0;
    object data = localSettings.Values["instanceNo"];
    if (data != null)
    {
        instanceNo = ((int)localSettings.Values["instanceNo"] + 1) % 3;
    }

    localSettings.Values["instanceNo"] = instanceNo;
    return instanceNo;
}

In this case, we are keeping it simple, but we can make something more complex here. For example, we can activate some instance depending on the parameters passed to the application:

static void Main(string[] args)
{
    // First, we'll get our activation event args, which are typically richer
    // than the incoming command-line args. We can use these in our app-defined
    // logic for generating the key for this instance.
    IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();
    // In some scenarios, the platform might indicate a recommended instance.
    // If so, we can redirect this activation to that instance instead, if we wish.
    if (AppInstance.RecommendedInstance != null)
    {
        AppInstance.RecommendedInstance.RedirectActivationTo();
    }
    else
    {
        // Define a key for this instance, based on some app-specific logic.
        // If the key is always unique, then the app will never redirect.
        // If the key is always non-unique, then the app will always redirect
        // to the first instance. In practice, the app should produce a key
        // that is sometimes unique and sometimes not, depending on its own needs.
        string key = "MainInstance";
        if (args.Length > 0)
            key = args[0];
        var instance = AppInstance.FindOrRegisterInstanceForKey(key);
        if (instance.IsCurrentInstance)
        {
            // If we successfully registered this instance, we can now just
            // go ahead and do normal XAML initialization.
            global::Windows.UI.Xaml.Application.Start(p => new App());
        }
        else
        {
            // Some other instance has registered for this key, so we'll 
            // redirect this activation to that instance instead.
            instance.RedirectActivationTo();
        }
    }
}

The key for the instance depends on the first parameter passed to the app. If we change our package.appxmanifest to add an ExecutionAlias, we can open a console window and call the app with a parameter:

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
  xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" 
  xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2" 
  IgnorableNamespaces="uap mp desktop4 iot2 uap5">

    ....

   <Extensions>
     <uap5:Extension
       Category="windows.appExecutionAlias"
       Executable="MultiInstance.exe"
       EntryPoint="MultiInstance.App">
       <uap5:AppExecutionAlias>
         <uap5:ExecutionAlias Alias="MultiInstance.exe" />
       </uap5:AppExecutionAlias>
     </uap5:Extension>
   </Extensions>
 </Application>

Now, we can handle different instances of our app. If we want to process the arguments (for example, to open a different file depending on the passed parameter), we can do it in the App.xaml.cs. Please note that the OnLaunched method is not called when the app is activated from the command line. In this case, you must override the OnActivated  method and process the arguments from there:

protected override void OnActivated(IActivatedEventArgs args)
{
    var arguments = (args as CommandLineActivatedEventArgs)?.Operation.Arguments;
    Frame rootFrame = Window.Current.Content as Frame;
    if (rootFrame == null)
    {
        rootFrame = new Frame();
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        rootFrame.Navigate(typeof(MainPage), 
            $"Activated Kind {args.Kind}\nArguments: {arguments}");
    }
    Window.Current.Activate();
}

The processing of the arguments in the main page are done in the OnNavigatedTo method in MainPage.xaml.cs:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var param = e?.Parameter;
    MainTxt.Text = param?.ToString() ?? "";
    base.OnNavigatedTo(e);
}

Now, if we open a command line window and type something like MultiInstance “this is a test”, the program will open a new instance with the arguments:

MultiInstanceArgs

Conclusions

As you can see, this new feature brings a lot of flexibility to your app. You already had the possibility to create many windows and now you can even create new instances and redirect the app to the right instance, depending on how the app is launched.

The full source code is available in http://github.com/bsonnino/MultiInstanceUWP

One new feature introduced in the April 2018 Windows 10 update is the possibility of writing console apps using UWP. You could ask “why build an UWP console app if we have console apps that work fine now?”.

There are many advantages in using a UWP console app:

  • You can add them to the store and get all the benefits of them: easy discovery and deploy, virus verifying and monetizing possibilities
  • You have fast install and uninstall
  • You can use the new OS features with no hacks
  • You have a global access in the machine without having to add them to the path, like normal console apps

Writing the app

To start writing the app, you should have the April 2018 update and its SDK installed. Once you have that set up, you can open Visual Studio and, in Tools/Extensions and Updates install the Console Project template:

ConsoleTemplate

When the template is installed, you can create a new project and select the console app under the Universal tab:

NewConsoleApp

When you select it, you should set the minimum and target version to 1803, so it can run ok. Then, Visual Studio creates a console app similar to the ones you are used to, but with some twists:

  • There is an Assets folder with the assets for the tiles
  • There is a package.appxmanifest file, like any other UWP program

When you run it, it will open a console and write the data to it:

ConsoleApp

Until now, there’s nothing special. Things start to happen when you open the Start menu and see the icon for the app and you can add the tile to the start menu by right clicking the icon and selecting Pin to Start:

StartMenu

The other change you can see is that you can open a Command Prompt window and type ToastConsole param1 param2 (the name I have given to the app is ToastConsole) and the app will run, no matter in which folder you are. Pretty cool, no?

This app is an UWP app and it can be uninstalled like any other app. Once you uninstall it, it will be completely removed from your machine.

Adding OS features to the app

The cool feature for UWP Console apps is the possibility to add OS features to it and use them as we would do in a normal UWP app. We will add a toast notification to the app, it will take the parameters typed to the app and send a toast notification with the first parameter as the title and the second parameter as the content.

To create a toast notification, you should create the content as XML and use it to send the toast, but there are better ways to do it: you can install the Windows UWP Toolkit Notifications package and use its helper functions to create the toast content. Right click in the References node in the Solution Explorer and select Manage NuGet packages and install the Microsoft.Toolkit.Uwp.Notifications.

With it installed, you can create the ShowToast method:

static void ShowToast(string title, string content)
{
    ToastVisual visual = new ToastVisual
    {
        BindingGeneric = new ToastBindingGeneric
        {
            Children =
            {
                new AdaptiveText
                {
                    Text = title
                },

                new AdaptiveText
                {
                    Text = content
                }
            }
        }
    };
    ToastContent toastContent = new ToastContent
    {
        Visual = visual,
    };

    var toast = new ToastNotification(toastContent.GetXml());
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

You create a ToastVisual, then a ToastContent and a ToastNotification with the ToastContent as XML, then show the toast. The main program is similar to this:

static void Main(string[] args)
{
    var title = "Hello UWP Console";
    var content = "Sample toast launched from UWP Console app";
    if (args.Length >= 2)
    {
        title = args[0];
        content = args[1];
    }
    else if (args.Length == 1)
    {
        content = args[0];
    }
    ShowToast(title,content);
    Console.WriteLine("Press a key to continue: ");
    Console.ReadLine();
}

This code will take the two parameters passed to the command line and will create a new toast with them and will show the toast. Simple, no?

Toast

Until now, we’ve accessed the program with the name of the app, but we can change this. Just right click the package.appxmanifest file in the Solution Explorer and select View Code. There, you can edit the execution alias and change the Alias attribute:

<Extensions>
  <uap5:Extension 
    Category="windows.appExecutionAlias" 
    Executable="ToastConsole.exe" 
    EntryPoint="ToastConsole.Program">
    <uap5:AppExecutionAlias desktop4:Subsystem="console" iot2:Subsystem="console">
      <uap5:ExecutionAlias Alias="ToastSender.exe" />
    </uap5:AppExecutionAlias>
  </uap5:Extension>
</Extensions>

With this change, you will access the program with the ToastSender alias and ToastConsole won’t be acessible anymore.

This new resource opens a new kind of applications, now you have this option to create apps that interact with the OS with no need of an UI.

The source code for the application is in https://github.com/bsonnino/ToastConsole

Sending emails in your app is a normal thing – after some operation, you must send confirmation data to the user, so he can keep the record of it. All the infrastructure for that is available in  .NET, in the System.Net.Mail namespace, there is not much secret in that.

But sometimes things can be more difficult than that – there may be many templates for the email, depending on the kind of email you want to send, they are HTML based and the data in it must come from a different source, like a database.

Sending HTML emails

Sending an HTML email in .NET is fairly easy: you just have to create a MailMessage object, set its From, To, Subject and Body fields, set the IsBodyHtml property to true and send the message using an SmtpClient object:

static void Main(string[] args)
{
    var mail = new MailMessage
    {
        From = new MailAddress("sender@gmail.com"),
        Subject = "Test Mail",
        Body = @" ... ",
        IsBodyHtml = true
    };
    mail.To.Add("email@server.com");
    var client = new SmtpClient("smtp.gmail.com")
    {
        Port = 587,
        Credentials = new System.Net.NetworkCredential(
             username, password),
                EnableSsl = true
    };
 
    client.Send(mail);
 }

Templated emails

The previous code sends a fixed email, but sometimes we have to send a templated email, where we have a template and must fill it with some variables that come from a different source. The variable parts can come embedded in many ways, like between special characters, but I’ll choose the Mustache (http://mustache.github.io/) way: using double braces before and after every variable, like this {{variable}}.

We now need to read the template, find the variables and replace them with the data, creating an HTML string that will be assigned to the Body property of the email:

static void Main(string[] args)
{
    var template = File.ReadAllText("EmailTemplate.html");
    var data = JsonConvert.DeserializeObject<Dictionary>(
        File.ReadAllText("EmailData.json"));
    data.Add("date",DateTime.Now.ToShortDateString());
    var emailBody = ProcessTemplate(template, data);
    var mail = new MailMessage
    {
        From = new MailAddress("sender@gmail.com"),
        Subject = "Test Mail",
        Body = emailBody,
        IsBodyHtml = true
    };
    mail.To.Add("email@server.com");
    var client = new SmtpClient("smtp.gmail.com")
    {
        Port = 587,
        Credentials = new System.Net.NetworkCredential(
           username, password),         
        EnableSsl = true
    };

    client.Send(mail);
}

As we are using a dictionary with the variables, I’ve added a new variable that doesn’t come from the file: the current date. This method is very flexible and you can add data that comes from many sources.

The ProcessTemplate method uses a simple Regex replace to replace the found variables with the data in the dictionary:

private static string ProcessTemplate(string template, 
    Dictionary data)
{
    return Regex.Replace(template, "\\{\\{(.*?)\\}\\}", m =>
        m.Groups.Count > 1 && data.ContainsKey(m.Groups[1].Value) ? 
            data[m.Groups[1].Value] : m.Value);
}

Improving the processing

The processing we’ve chosen is very simple and we can improve it. We can use a library that processes the Mustache variables, like Nustache (https://github.com/jdiamond/Nustache). To use it, we just have to add the Nustache Nuget package. With it installed, the ProcessTemplate method becomes:

private static string ProcessTemplate(string template, 
    Dictionary data)
{
    return Render.StringToString(template, data);
}

This is easier and doesn’t rely in the Regex processing. You can also use some helper functions in the processing. For example, if your data file has the first and last name and you want the full name in the replaced template, you can define a function like this:

private static void FullName(RenderContext context, 
    IList arguments, IDictionary options, 
    RenderBlock fn, RenderBlock inverse)
{
    if (arguments?.Count >= 2) 
        context.Write($"{arguments[0]} {arguments[1]}");
}

And register it like this:

Helpers.Register("FullName", FullName);

Easy, no? Now we have a simple way to create a templated email with data coming from different sources. We’ve used Nustache to process the template, but you could use something else like Handlebars.net (https://github.com/rexm/Handlebars.Net), which would be very similar to what we did here.

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

On last post, I’ve shown how to publish a .NET app to the store. This is quite easy with Visual Studio, but the app must be a .NET app and it must be ported to .NET 4.6.1 or later. Many times, this is not possible – you have a .NET app that targets an older version of the framework and cannot be updated or you have a Win32 app (have I heard VB6 or Delphi?) that you want to publish to the store. Sometimes, even the source code is lost and all you have is the installer or executable. Even with this case there is no problem: you can still publish it to the store, the only thing is that you won’t be able to use Visual Studio.

To demonstrate it, I will use an old Delphi game sample, Swat!, which source code is in the Delphi samples since the earlier versions of Delphi (I think it’s there since Delphi 1, in 1994).

In this game, you must kill the ants with a hammer, this is an old game and I used it as an example of what you can convert to the Store.

The first step is to download the Desktop app converter from the store

Once you have it installed, you should run it with admin rights: in the start menu, right click in its icon and select “More/Run as administrator”. That will open a console window where you will run the converter app.

This window shows some examples of command line for packaging your app, but the first example must always be run once to setup the environment. You must download a base image for your system and run the setup. The base image can be downloaded from here, it must be compatible with your system version. You can check the system version by pressing Win+R and running winver:

My Windows version is 16299. so I have to donwload the base image for that version. It will be downloaded in my downloads folder, with the name “Windows_InsiderPreview_DAC_16299.wim”. So, the first step is to run the command line:

DesktopAppConverter.exe -Setup -BaseImage "E:\Downloads\Windows_InsiderPreview_DAC_16299.wim"

You may have an error after running this command line. This is because the Containers feature must be enabled in your system. Note: you must have Windows 10 Pro or Enterprise to enable this feature – if you have Windows 10 Home, you won’t be able to use the packager:

The converter will add this feature, but you must restart your machine. When you restart it, the Desktop App Converter will reopen and re-run the setup. You are then ready to package your app. The program we are running doesn’t have an installer, it is a single file. If you have an installer, you can point DesktopAppConverter to it, but in this case, we just need to create a folder and put all the files we need there (in this case, just the executable) and another folder for the output. Note: don’t create an output folder as a subfolder from the install folder – I did that and got an error “Path too long” – this is because the output folder is added as an install folder and this is recursive – newbie mistake. Then we must run this command line:

DesktopAppConverter.exe -Installer D:\Temp\Swat\ -AppExecutable Swat.exe -Destination D:\Temp\SwatUWP -PackageName "SwatUWP" -Publisher "CN=RevolutionSoft" -Version 0.0.0.1 -MakeAppx -Sign -Verbose -Verify

This will add all the files in D:\Temp\Swat and create the output file in D:\Temp\SwatUWP, creating and signing an appx file. You can go to the output folder and double click the Appx file. It will open the installer for the app:

If it’s the first time you are installing the app, you will get an error, because the certificate is not installed. You can install it by going to the output folder and double clicking the cer file. That will open the install certificate window:

You must click on the Install Certificate button and select Local Machine as the store location. Then click on Next and then in Place all certificates in the following store, clicking on Browse and selecting Trusted People. The certificate will be installed and you can click again in the Appx file to install it. Now you have your app running as a UWP app.

If you have an installer (it can be an MSI or an EXE installer) that can be run unattended, you can use it to convert your app. In this case, all the files in the installer will be added to the package.

Now it’s time to customize the package, but this is something for another post.

 

 

You have an old app (maybe a WPF or even a VB6 app), it still works fine, but the plans to rewrite it to UWP and get all the features for Windows 10 are always postponed (yes, there is always something more urgent than rewrite an app that is working fine). But, at the same time, you would like to publish it to the Store to get the benefits of being there: easy install and uninstall, worldwide discovery (you always knew there was somebody in the other side of the world that would benefit of the app), easy deploy, checking against viruses and other threats, and even monetization of the app – it would be nice to sell the app and receive the revenues at the end of the month without worrying with that.

Until some time ago, that would be impossible to do, unless you did a complete rewrite of the app, but with the Desktop Bridge, this is possible. And you can do it in many ways: you can just convert your app without any changes (you don’t even need the source code for the app), you can add some Windows 10 features to it, rewrite some parts of the app to UWP and so on. That way, you can start immediately and convert the app at your own pace. That will give you all the benefits of the store at once and you don’t need to worry with the conversion until you are ready for it.

Can your app be converted?

Before converting your app, you must be sure it can be converted and, if not, you must make some changes to make it compliant with the conversion. These are good practices that should be done even before thinking to convert:

  • Running in elevated mode – converted apps cannot run in elevated mode. Do you really need elevated mode? If not, you must change your app before converting.
  • Write in the install folder – this is one of the most common mistakes done by old apps. This is not allowed in UWP apps and, unless you change this to write in another folder, your app won’t be approved.
  • Write in system folders – yes, Microsoft has something to do with this issue. When you were using ini files to save your configuration, the default folder for them was in the Windows folder
  • Write in the HKLM registry hive. If you have this in your app, you must change it to HKCU instead

There are some other issues that may prevent your app to be submitted to the Store. You can check them here.

Starting the conversion process

Once you are sure that your app can be converted, you can start the conversion. The first step for the conversion is filling a form here. This is needed because the submission process will be different from normal UWP apps. The converted apps will have full thrust in the machine they are installed and they must be double-checked. One other issue is that you don’t need the source code, so you must be the legal owner of the app to publish it (and you must have some kind of proof of that). Once you have that in place, you can convert your app. You have three ways to convert it:

  • using Visual Studio (you must have the source code for that and the project must be opened in Visual Studio (in this case, you cannot convert your VB6 projects or any project that cannot be opened in Visual Studio)
  • using the Desktop App Converter – this is a Store app that must be run to convert your app. In this case, you have more flexibility than using Visual Studio: you can convert any app, including Win32 ones. You just need to have an installer that runs silently (with no user interaction)
  • Doing a manual conversion – this is the most flexible way, you can choose the files that will be packaged and how they will be, but this way will require more work and knowledge.

Using Visual Studio to convert a WPF app

We will use Visual Studio to convert my Data Structures app, discussed in the posts from 2016 (the last one in the series is here). You can get the full project in https://github.com/bsonnino/DataStructures. Once you download it and open it in Visual Studio, you are ready to convert it to the Store.

The first step here is to convert it to use the .NET Framework version 4.6.1 or later. You need that to convert the app to the store, as this is the minimum version supported there. You can do that by going to Project/Options/Application:

The next step is to add a new project to the solution, of type Windows Application Packaging Project (under Windows Universal):

Name it DataStructuresUWP. That will be the name of your UWP app. You must select the Windows version 14393 as the minimum version:

When the project is created, you must right click the Applications node and add the application as a reference

Once the app is added, you can right-click in the node under Applications and select “Set as Entry Point”. You are now ready to create the UWP app, by selecting the conversion project in the Solution Explorer, right-clicking on it and selecting “Store/Create app packages”.

When you select to build the package for the App Store, you need to have an account there (you can create an account at http://dev.windows.com) and select a name for your app:

You can also create packages that won’t go to the store and will be installed in the local machine. When you create the package, a link where the files will be located is shown and you must right click the Add-AppDevPackage.ps1 file and select “Run With Powershell”.

By distributing these files, you can sideload the app in any machine that has the developer mode enabled (in the Windows Settings). In our case, we have a faster way to install and run the app: just right click the conversion project in the Solution Explorer and select “Set as Startup Project”. Now, when you run the project, you will install and run the converted app, which is an UWP app. If you see the icon for the app in the taskbar, you will see the default UWP icon (the square with an X) and opening the start menu, you will see the installed app there.

That was easy, no? Now, you can customize your app to make it better for the store.

Customizing the app for the store

Now that the app is converted, you can customize it for the store by setting its name and assets (icons, tiles, etc). This is done by double clicking the Package.appxmanifest file in the Solution Explorer:

There, you can change its display name, icons and tiles and make it ready for sending to the store. With this, your app is ready for the store and you already have all the benefits of being there. As you can see, the changes were minimal and your app still runs the same. If you still want to use your WPF app, the only change that was made is to retarget for the 4.6.1 .NET Framework. In this case, we had the source code to build the app, but in the next article I will show how to convert a Win32 app with no source code, so you can get the same benefits. See you there!

Sometimes you need to enumerate the files in a folder and its subfolders and that can be a very long task, especially with large folders. This article will show a fast and easy way to enumerate all files in a folder when using UWP.

Enumerating files the traditional way

UWP runs in a sandbox and you don’t have access to all files in the system. One way to get access to a folder and its subfolders is to use the FolderPicker. You can’t access the folders directly (unless for some libraries when you ask for their use in the app manifest), and using the folder picker gives you access to other folders: you open it, the user selects a folder and you have access to it and its subfolders. That can be done with a code like this:

private static async Task<StorageFolder> SelectFolderAsync()
{
    var folderPicker = new Windows.Storage.Pickers.FolderPicker
    {
        SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop
    };
    folderPicker.FileTypeFilter.Add("*");
    StorageFolder folder = await folderPicker.PickSingleFolderAsync();
    return folder;
}

Once you get the folder reference, you can enumerate all files using a code like this:

private List<StorageFile> _allFiles;

private async Task GetFilesInFolder(StorageFolder folder)
{
    var items = await folder.GetItemsAsync();
    foreach (var item in items)
    {
        if (item is StorageFile)
            _allFiles.Add(item as StorageFile);
        else
            await GetFilesInFolder(item as StorageFolder);
    }
}

This recursive function will get all files and folders in the folder and will add the files to the files list and will call the function for all subfolders. Notice that you must use the StorageFolder and StorageFile classes to enumerate the files. While you can use .NET Standard 2.0 FileIO classes, this will be blocked by access permissions to UWP programs (for example, you will have access denied on the Documents library if you try to enumerate any file there with these classes even if you got the folder through the FolderPicker).

Using this function, I can get all files in a folder and fill a GridView in the main page with this function:

 

private async void GetFolderSlow(object sender, RoutedEventArgs e)
{
    StorageFolder folder = await SelectFolderAsync();
    if (folder != null)
    {
        DocsGrid.ItemsSource = null;
        StatusTxt.Text = "";
        var sw = new Stopwatch();
        sw.Start();
        _allFiles = new List<StorageFile>();
        await GetFilesInFolder(folder);
        DocsGrid.ItemsSource = _allFiles;
        sw.Stop();
        StatusTxt.Text = $"Ellapsed time: {sw.ElapsedMilliseconds}  {_allFiles.Count} files";
    }
}

In my machine, it takes about 2 minutes to enumerate the Documents Library (about 38,000 files). That is way more than using a .NET console program (in my machine, it took about 5 seconds to get all file names and process them to get their sizes), but there is no better way (until now – maybe there will be some kind of Full Thrust UWP app in the future :-)) to enumerate the files in UWP. Or there is?

Enumerating files the fast way

Yes, there is a faster way. You can use the Windows indexing service and query the files faster, when they are indexed. For that, you just need to create a QueryOptions instance and tell that you want to use the indexer. This code makes all the work:

 

private async void GetFolder(object sender, RoutedEventArgs e)
{
    var folder = await SelectFolderAsync();
    if (folder != null)
    {
        DocsGrid.ItemsSource = null;
        StatusTxt.Text = "";
        var sw = new Stopwatch();
        sw.Start();
        var queryOptions = new QueryOptions
        {
            FolderDepth = FolderDepth.Deep,
            IndexerOption = IndexerOption.UseIndexerWhenAvailable
        };
        var query = folder.CreateFileQueryWithOptions(queryOptions);
        var allFiles = await query.GetFilesAsync();
        DocsGrid.ItemsSource = allFiles;
        sw.Stop();
        StatusTxt.Text = $"Ellapsed time: {sw.ElapsedMilliseconds}  {allFiles.Count} files";
    }
}

As you can see, there is no need to have a recursive function. Setting the FolderDepth property to FolderDepth.Deep does all the recursive work for you. An you can use the index if it’s available and query for some files (like – only pictures or files created by an author). While this is not as fast as the console enumeration, it’s still twice as fast as the previous version.

Conclusions

Enumerating files in UWP is not very fast, but when you need it, you have a faster alternative to the default, using the indexer. That can be a handy alternative, especially when you need to filter the files.

All the source code for the article is in https://github.com/bsonnino/DocumentsAccess

 

After finishing my last post, I saw I could improve it a little bit. The UI was too raw (my goal at the time was not to show the UI, but show how to add logging to a UWP app) and could be improved: instead of listviews, why not use a DataGrid to show the client’s data?

Although there is no native DataGrid in the SDK, Telerik open sourced its UWP components, so you can use them in your UWP apps. If you want to know more,  you can go to the Telerik’s GitHub page (https://github.com/telerik/UI-For-UWP) and take a look at their components for UWP.

So, let’s start where we left: you can go to my GitHub page (https://github.com/bsonnino/LoggingSerilog) and download the project developed in the last post.  Then, add the NuGet package Telerik.UI.for.UniversalWindowsPlatform.

The first step in our change is to change the ListView in the Customers view to a RadDataGrid:

<grid:RadDataGrid ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer}"/>

You should add the namespace Telerik.UI.Xaml.Controls.Grid in the beginning pf the XAML file:

<UserControl
    x:Class="TelerikGrid.View.Customers"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:grid="using:Telerik.UI.Xaml.Controls.Grid"
    mc:Ignorable="d"
    d:DesignHeight="500"
    d:DesignWidth="700"
    DataContext="{Binding Customers, Source={StaticResource Locator}}">

With this simple change, you get a full featured Data Grid, that allows sorting, filtering or grouping. You can group by any column by dragging it to the group box, at the left:

Really nice, no? If you take a look at the grid, you will see that all the columns are being shown, including InDesignMode, a property introduced in ViewModelBase, but we don’t want that. To set the columns, we have to set the property AutoGenerateColumns to False and set the columns we want in the Columns property. If you want, you can also set the CanUserChooseColumns to True, so the user can choose the columns he wants to display:

<grid:RadDataGrid ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer}" 
                  AutoGenerateColumns="False" CanUserChooseColumns="True">
    <grid:RadDataGrid.Columns>
        <grid:DataGridTextColumn PropertyName="Id"/>
        <grid:DataGridTextColumn PropertyName="Name"/>
        <grid:DataGridTextColumn PropertyName="City"/>
        <grid:DataGridTextColumn PropertyName="Country" />
    </grid:RadDataGrid.Columns>
</grid:RadDataGrid>

One extra twist is to add alternating columns to the grid. This is very easy, just set the AlternationStep property to a value greater than 1.

Now that we have the grid in place, let’s go to the second step: use a DataForm for the detail view, That way you can have a single control for easy editing of objects.

Adding a DataForm to edit the selected item

The DataForm is an easy way to edit objects in UWP. With it, you don’t need to add the editors for each field, you just need to add it and set the Item property to the item you want to edit:

<data:RadDataForm Item="{Binding SelectedCustomer}" />

As you can see, it works the same way as it did with all the TextBoxes, but the labels are not there. To fix this, we must add an attribute to the ViewModel properties to display the header:

public class CustomerViewModel : ViewModelBase
{
    private readonly Customer _customer;

    public CustomerViewModel(Customer customer)
    {
        _customer = customer;
    }

    [Display(Header = "Id", PlaceholderText = "Customer Id")]
    public string Id
    {
        get => _customer.Id;
        set
        {
            Log.Verbose("Customer Id changed from {OldId} to {NewId}", _customer.Id,value);
            _customer.Id = value;
            RaisePropertyChanged();
        }
    }

    [Display(Header = "Name", PlaceholderText = "Customer name")]
    public string Name
    {
        get => _customer.Name;
        set
        {
            Log.Verbose("Customer Name changed from {OldName} to {NewName}", _customer.Name, value);
            _customer.Name = value;
            RaisePropertyChanged();
        }
    }

    [Display(Header = "Address", PlaceholderText = "Customer address")]
    public string Address
    {
        get => _customer.Address;
        set
        {
            Log.Verbose("Customer Address changed from {OldAddress} to {NewAddress}", _customer.Address, value);
            _customer.Address = value;
            RaisePropertyChanged();
        }
    }

    [Display(Header = "City", PlaceholderText = "Customer city")]
    public string City
    {
        get => _customer.City;
        set
        {
            Log.Verbose("Customer City changed from {OldCity} to {NewCity}", _customer.City, value);
            _customer.City = value;
            RaisePropertyChanged();
        }
    }

    [Display(Header = "Country", PlaceholderText = "Customer country")]
    public string Country
    {
        get => _customer.Country;
        set
        {
            Log.Verbose("Customer Country changed from {OldCountry} to {NewCountry}", _customer.Country, value);
            _customer.Country = value;
            RaisePropertyChanged();
        }
    }

    [Display(Header = "Phone", PlaceholderText = "Customer phone")]
    public string Phone
    {
        get => _customer.Phone;
        set
        {
            Log.Verbose("Customer Phone changed from {OldPhone} to {NewPhone}", _customer.Phone, value);
            _customer.Phone = value;
            RaisePropertyChanged();
        }
    }
}

The Display attribute will tell the form what is the label that must be shown and the placeholder to show in the edit box when it’s empty. One note here is that the Display attribute that must be used isn’t in System.ComponentModel.DataAnnotations, but it is in Telerik.Data.Core. You must add the correct namespace to use the Header and PlaceHolderText properties. Once you make these changes, the labels appear in the form:

Adding a chart to the view

Now that our program is working like the old one with the Telerik controls, let’s enhance  it with a Pie Chart that shows the percentage of customers by country. To do that, we must create a new property in the CustomersViewModel,  CustomersByCountry. It will be initialized in the ViewModel’s constructor:

public CustomersViewModel()
{
    _selectedCustomer = _customers.Count > 0 ? _customers[0] : null;
    _newCustomerCommand = new RelayCommand(AddCustomer);
    _deleteCustomerCommand = new RelayCommand(DeleteCustomer, () => SelectedCustomer != null);
    CustomersByCountry = _customers.GroupBy(c => c.Country)
        .Select(g => new CustomerCountry(g.Key, g.Count()))
        .OrderByDescending(c => c.NumCustomers);
}

public IEnumerable<CustomerCountry> CustomersByCountry { get; }

We use LINQ to group the customers by country and generate the data ordered in descending order by the customer count. We have created a new class named CustomerCountry to store the total data for each country:

public class CustomerCountry
{
    public CustomerCountry(string country, int numCustomers)
    {
        Country = country;
        NumCustomers = numCustomers;
    }

    public string Country { get; }
    public int NumCustomers { get; }
}

Once we have that in place, we can create our chart view. In the View folder, create a new UserControl and name it CustomersChart. In the chart, add this code:

<UserControl
    x:Class="TelerikGrid.View.CustomersChart"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:chart="using:Telerik.UI.Xaml.Controls.Chart"
    xmlns:primitives="using:Telerik.UI.Xaml.Controls.Primitives"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    DataContext="{Binding Customers, Source={StaticResource Locator}}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="20">
        <chart:RadPieChart x:Name="Chart" PaletteName="DefaultLight" >
            <chart:PieSeries ItemsSource="{Binding CustomersByCountry}" ShowLabels="True" RadiusFactor="0.8">
                <chart:PieSeries.ValueBinding>
                    <chart:PropertyNameDataPointBinding PropertyName="NumCustomers" />
                </chart:PieSeries.ValueBinding>
                <chart:PieSeries.LegendTitleBinding>
                    <chart:PropertyNameDataPointBinding PropertyName="Country" />
                </chart:PieSeries.LegendTitleBinding>
            </chart:PieSeries>
        </chart:RadPieChart>
        <primitives:RadLegendControl LegendProvider="{Binding ElementName=Chart}">
            <primitives:RadLegendControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsWrapGrid Orientation="Vertical"/>
                </ItemsPanelTemplate>
            </primitives:RadLegendControl.ItemsPanel>
            <primitives:RadLegendControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Width="110">
                        <Ellipse Fill="{Binding Fill}" Stroke="{Binding Stroke}"
                                 StrokeThickness="1" Width="10" Height="10"/>
                        <TextBlock Text="{Binding Title}" Foreground="{Binding Fill}"
                                   Margin="10" />
                    </StackPanel>
                </DataTemplate>
            </primitives:RadLegendControl.ItemTemplate>
        </primitives:RadLegendControl>
    </Grid>
</UserControl>

We are adding a RadPieChart with a PieSeries in it. This PieSeries has the ItemsSource property set to the ViewModel’s CustomersByCountry property. Its values are set to the NumCustomers property and the Legends are set to the country names. To add a legend, we must add a RadLegendControl and set its LegendProvider property bound to the chart. The ItemsPanel property is set to a ItemsWrapGrid, in a way that the items span to a new column if there is no available space at the bottom. The labels of the legend have the same color of the pie.

Now, we must add the new view to the main view. I’ve chosen to replace the log view with this new view, In MainPage.xaml, put this code:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <view:Customers />
    <!--<view:Log Grid.Row="1"/>-->
    <view:CustomersChart Grid.Row="1"/>
</Grid>

When you run the app, you will see something like this:

Note: this chart is not dynamic – if you change the country for a customer or add a new customer, the chart doesn’t update, because it’s calculated in the constructor, To make it dynamic, you should recalculate it when the country of a customer has changed.

With these changes, we have an improved application, with a lot of new features.

Conclusions

As you can see, with small changes we got a lot of improvements for our app. With the DataGrid we gor sorting, filtering and grouping for our data with almost no effort. With the DataForm we can edit the customer without having to add TexBlocks and TextBoxes. If we change the underlying class, the form will be updated automatically. With the chart we could show a new visualization for our data. The Telerik controls for UWP are open source and free for you to use.

All the source code in this article is in https://github.com/bsonnino/TelerikGrid

Introduction

One important step in developing an application is to debug it. This task will take you a lot of time and it will be especially difficult to debug an application that is live in the field (that doesn’t happen in my machine ;-)). One solution is to add logging to the application and analyze the logs after the error occurred.

You can develop your own logging framework, but this will take some time and will be cumbersome and not standard, so I tend to search a tested framework out there to do this task for me. The most used frameworks, like Log4Net don’t work for UWP, and you cannot log to some places, like a Sql Server database.  I’ve found two that work nicely with UWP apps: MetroLog (https://github.com/onovotny/MetroLog) and Serilog (https://serilog.net/)

I’ve used Metrolog for a long time, it’s very easy to use and very flexible, but Serilog has two features that make it better: Structured Logging and Sinks (logging providers).

Structured Logging

When you are logging, you usually do something like this:

Log.Information("The current time is " + DateTime.Now);

That’s fine when you have a small log, but what about a log that has thousands of lines? How can you extract some useful information from the log? You end up with some program that will use Regex patterns to extract the information you want from a large log. With Serilog you will log something like this:

 

Log.Information("The current time is {CurrentTime}", DateTime.Now);

This will log a property with name CurrentTime along with the log record that can be retrieved later, when you are analyzing the log. This can be used for simple objects and for complex classes.  Let’s say you have a class named Customer like this one:

 

public class Customer
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    ...
}

If you use something like this:

 

Customer customer = new Customer();
...
Log.Information("Customer Data: {@Customer}", customer);

Serilog will generate a log record with a Customer property and will list all properties for that customer. Notice we are using the @ character before the variable name. That instructs Serilog to preserve the structure of the class, instead of flattening it in a single string. That way it will be easier to extract this information from the log file.

Sinks

The second feature in Serilog that makes it a good choice is the concept of Sinks, or logging providers. There are many logging providers for Serilog and you can even log to more than one place at once. A fast search of “Serilog Sinks” lists 342 packages, including some common ones, like files, console, event viewer, MS Sql Server, email, some to No-SQL DBs, like DocumentDB, RavenDB and MongoDB, and some not so common like Slack, Seq, or even a Reactive Extensions sink, that uses an Observable sequence of events for the logging.

As I’m always interested in Rx, I thought it would be very nice to use this sink to show the logging in UWP with Serilog. So, let’s start our project.

Creating an UWP app

In Visual Studio, create a new UWP blank project. In the solution explorer, right click on the References node and select “Manage NuGet packages”. Install the Serilog, Serilog.Sinks.Observable and System.Reactive packages, then install the MVVM Light package – we will use it as a MVVM framework for this project.

Usually, adding the MVVM Light package adds a ViewModelLocator and a Main ViewModel automatically, but this doesn’t happen in UWP, so we must add them manually. In the Solution Explorer, create a ViewModel folder and add a MainViewModel class:

public class MainViewModel
{
}

Then add a ViewModelLocator class in this folder:

public class ViewModelLocator
{
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoc.Default.Register();
    }
    public MainViewModel Main => ServiceLocator.Current.GetInstance();
}

The next step is to add a reference to the ViewModelLocator in App.xaml:

<Application
    x:Class="LoggingSerilog.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:LoggingSerilog.ViewModel"
    RequestedTheme="Light">
    <Application.Resources>
        <vm:ViewModelLocator x:Key="Locator" />
    </Application.Resources>
</Application>

Finally, we set the DataContext of the Main View to the MainViewModel in MainPage.xaml:

<Page
    x:Class="LoggingSerilog.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding Main, Source={StaticResource Locator}}">

With all this in place, we can start our project. We will create a small project that allows you to add, modify or delete Customers from a list. Every change will be logged to a view below the main view.

We will create our model, that will be used for the project. Create a new folder named Model and insert a new class named Customer:

public class Customer
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string Phone { get; set; }


    public static IEnumerable<Customer> GetCustomers()
    {
        string customerXml = Path.Combine(Package.Current.InstalledLocation.Path, "Customers.xml");
        return XDocument.Load(customerXml).Descendants("Customer").Select(c =>
            new Customer
            {
                Id = c.Element("CustomerID")?.Value,
                Name = c.Element("CompanyName")?.Value,
                Address = c.Element("Address")?.Value,
                City = c.Element("City")?.Value,
                Country = c.Element("Country")?.Value,
                Phone = c.Element("Phone")?.Value,
            });
    }
}

This class also has a static method to get all the customers from a xml file based on the Customers table in the Northwind database. You can get this file from the source code for this article. When you get the file, you must add it to the project, so it will be copied to the install folder for the app. One note here: as the install folder is read-only, you will not be able to save the data once it’s updated. To do that, you must copy the file to the local app data store and work from there, but I let this for you.

As you can see, this class is a POCO (Plain Old Clr Object) class, it doesn’t have any mechanism to trigger updates when it’s changed, so we’ll create a ViewModel for it. In the ViewModel folder, create a new CustomerViewModel class:

public class CustomerViewModel : ViewModelBase
{
    private readonly Customer _customer;

    public CustomerViewModel(Customer customer)
    {
        _customer = customer;
    }

    public string Id
    {
        get => _customer.Id;
        set
        {
            _customer.Id = value;
            RaisePropertyChanged();
        }
    }

    public string Name {
        get => _customer.Name;
        set
        {
            _customer.Name = value;
            RaisePropertyChanged();
        }
    }

    public string Address
    {
        get => _customer.Address;
        set
        {
            _customer.Address = value;
            RaisePropertyChanged();
        }
    }

    public string City
    {
        get => _customer.City;
        set
        {
            _customer.City = value;
            RaisePropertyChanged();
        }
    }

    public string Country
    {
        get => _customer.Country;
        set
        {
            _customer.Country = value;
            RaisePropertyChanged();
        }
    }

    public string Phone
    {
        get => _customer.Phone;
        set
        {
            _customer.Phone = value;
            RaisePropertyChanged();
        }
    }
}

The ViewModel is derived from the MVVM Light’s ViewModelBase class, which implements the INotifyPropertyChanged interface. That way, all changes in the values for the customer will be reflected in the UI. We also need to create a CustomersViewModel class to expose all customers to the UI:

public class CustomersViewModel : ViewModelBase
{
    readonly ObservableCollection<CustomerViewModel> _customers = 
        new ObservableCollection<CustomerViewModel>(
            Customer.GetCustomers().Select(c => new CustomerViewModel(c)));

    public CustomersViewModel()
    {
        _selectedCustomer = _customers.Count > 0 ? _customers[0] : null;
    }

    private CustomerViewModel _selectedCustomer;

    public ObservableCollection<CustomerViewModel> Customers => _customers;

    public CustomerViewModel SelectedCustomer
    {
        get => _selectedCustomer;
        set
        {
          _selectedCustomer = value;
            RaisePropertyChanged();
        }
    }
}

The ViewModel has two properties: Customers, an ObservableCollection of CustomerViewModel, and SelectedCustomer, the customer selected in the list.

The next step is to create a new view to show the data. Create a new folder called View and add a new UserControl named Customers. I am using here a UserControl because this will be added to the Main page – the main page will be the shell for the customer view and the logging view. The view will be like this:

<UserControl
    x:Class="LoggingSerilog.View.Customers"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="500"
    d:DesignWidth="700"
    DataContext="{Binding Customers, Source={StaticResource Locator}}">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ListView ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Id}" FontWeight="Bold"/>
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Grid Grid.Column ="1" Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid DataContext="{Binding SelectedCustomer}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="35"/>
                    <RowDefinition Height="35"/>
                    <RowDefinition Height="35"/>
                    <RowDefinition Height="35"/>
                    <RowDefinition Height="35"/>
                    <RowDefinition Height="35"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text ="Id" Grid.Row="0" VerticalAlignment="Center"/>
                <TextBlock Text ="Name" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"/>
                <TextBlock Text ="Address" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"/>
                <TextBlock Text ="City" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center"/>
                <TextBlock Text ="Country" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center"/>
                <TextBlock Text ="Phone" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding Id, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding Name, Mode=TwoWay}" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding Address, Mode=TwoWay}" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding City, Mode=TwoWay}" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding Country, Mode=TwoWay}" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center"/>
                <TextBox Text ="{Binding Phone, Mode=TwoWay}" Grid.Row="5" Grid.Column="1" VerticalAlignment="Center"/>
            </Grid>
            <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                <Button Content="New" Margin="5,0" Width="65" Command="{Binding NewCustomerCommand}"/>
                <Button Content="Delete" Margin="5,0" Width="65" Command="{Binding DeleteCustomerCommand}"/>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

The main grid has two columns: the first one shows the customers list and the second one shows the details for the selected customer. At the bottom of the second column there are two buttons to create or delete a customer. These two buttons are bound to two commands in the CustomersViewModel:

public class CustomersViewModel : ViewModelBase
{
    readonly ObservableCollection<CustomerViewModel> _customers = 
        new ObservableCollection<CustomerViewModel>(
            Customer.GetCustomers().Select(c => new CustomerViewModel(c)));

    public CustomersViewModel()
    {
        _selectedCustomer = _customers.Count > 0 ? _customers[0] : null;
        _newCustomerCommand = new RelayCommand(AddCustomer);
        _deleteCustomerCommand = new RelayCommand(DeleteCustomer, () => SelectedCustomer != null);
    }
    private CustomerViewModel _selectedCustomer;
    private readonly RelayCommand _newCustomerCommand;
    private readonly RelayCommand _deleteCustomerCommand;

    public ObservableCollection<CustomerViewModel> Customers => _customers;

    public CustomerViewModel SelectedCustomer
    {
        get => _selectedCustomer;
        set
        {
            _selectedCustomer = value;
            RaisePropertyChanged();
            _deleteCustomerCommand.RaiseCanExecuteChanged();
        }
    }

    public ICommand NewCustomerCommand => _newCustomerCommand ;

    private void AddCustomer()
    {
        var customer = new CustomerViewModel(new Customer());
        _customers.Add(customer);
        SelectedCustomer = customer;
    }

    public ICommand DeleteCustomerCommand => _deleteCustomerCommand;

    private void DeleteCustomer()
    {
        _customers.Remove(SelectedCustomer);
        SelectedCustomer = null;
    }
}

One note here: we want to disable the delete command if there is no selected customer. This is done with the overload when we create the delete command, but we must tell Windows that the command has changed when the selected customer has changed. We do that by calling the RaiseCanExecuteChanged method in the setter of the SelectedCustomer property.

As you can see in the top of the usercontrol, I’ve set the DataContext of the view to a property in the locator. We must create this property in ViewModelLocator.cs:

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

        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<CustomersViewModel>();
    }

    public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
    public CustomersViewModel Customers => ServiceLocator.Current.GetInstance<CustomersViewModel>();
}

Then we must add the usercontrol to the Main page. In MainPage.xaml add this code:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <view:Customers />
</Grid>

Now, when you run the application, you will see something like this:

Adding logging to our app

Once we have our app ready, we can add logging to it. I want to add some kind of live logging, where the log records are shown in the main window when they are generated. For that, we must create a new view. In the View folder, add a new UserControl and name it Log. Add this code in the UserControl:

<UserControl
    x:Class="LoggingSerilog.View.Log"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:events="using:Serilog.Events"
    xmlns:l="using:LoggingSerilog"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    DataContext="{Binding Log, Source={StaticResource Locator}}">
    <Grid>
        <ListView ItemsSource="{Binding LogItems}" Margin="0,10,0,0"
                ItemContainerStyleSelector="{StaticResource LogStyleSelector}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="events:LogEvent">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{x:Bind Timestamp.ToString('hh:MM:ss.fff', x:Null)}" Margin="10,0"/>
                        <TextBlock Text="[" Margin="10,0,0,0"/>
                        <TextBlock Text="{x:Bind Level}"/>
                        <TextBlock Text="]" />
                        <TextBlock Text="{x:Bind RenderMessage(x:Null)}"  Margin="10,0"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</UserControl>

We’ve added a listview to show the log items. To display the items we’ve added an ItemTemplate with a horizontal StackPanel with five TextBlocks in it. As you can see, I’ve not used the Binding markup for data binding, but I’ve used the x:Bind markup. I’ve done it because when I use it I can bind to functions, and not only to properties. That way, I can use the RenderMessage and the ToString messages in the bindings. To use this feature, you must set the project to use the Windows 10 Anniversary update. You can change that in the project properties:

The next step is to create the LogViewModel to store the log events, that will be used as the data context for the view. In the ViewModel folder, add a new class and name it LogViewModel:

 public class LogViewModel :ViewModelBase
 {
     public LogViewModel()
     {
         LogItems = new ObservableCollection<LogEvent>();
         Log.Logger = new LoggerConfiguration()
             .MinimumLevel.Verbose()
             .WriteTo.Observers(events => events
                 .Do(evt => LogItems.Add(evt))
                 .Subscribe())
             .CreateLogger();
     }
     public ObservableCollection<LogEvent> LogItems { get; }
 }

This class has only one property, LogItems, an ObservableCollection that stores the log events. In the constructor of the ViewModel, I initialize the logger, setting the minimum level to be logged to Verbose and writing the log events to the log items inside the observer. This is enough to capture all the log events emitted everywhere in the program. We must also declare it in the ViewModelLocator:

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

        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<CustomersViewModel>();
        SimpleIoc.Default.Register<LogViewModel>();
    }

    public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
    public CustomersViewModel Customers => ServiceLocator.Current.GetInstance<CustomersViewModel>();
    public LogViewModel Log => ServiceLocator.Current.GetInstance<LogViewModel>();
}

With all the infrastructure in place, we can add the logging in CustomerViewModel.cs and CustomersViewModel.cs:

public class CustomerViewModel : ViewModelBase
{
    private readonly Customer _customer;

    public CustomerViewModel(Customer customer)
    {
        _customer = customer;
    }

    public string Id
    {
        get => _customer.Id;
        set
        {
            Log.Verbose("Customer Id changed from {OldId} to {NewId}", _customer.Id,value);
            _customer.Id = value;
            RaisePropertyChanged();
        }
    }

    public string Name {
        get => _customer.Name;
        set
        {
            Log.Verbose("Customer Name changed from {OldName} to {NewName}", _customer.Name, value);
            _customer.Name = value;
            RaisePropertyChanged();
        }
    }

    public string Address
    {
        get => _customer.Address;
        set
        {
            Log.Verbose("Customer Address changed from {OldAddress} to {NewAddress}", _customer.Address, value);
            _customer.Address = value;
            RaisePropertyChanged();
        }
    }

    public string City
    {
        get => _customer.City;
        set
        {
            Log.Verbose("Customer City changed from {OldCity} to {NewCity}", _customer.City, value);
            _customer.City = value;
            RaisePropertyChanged();
        }
    }

    public string Country
    {
        get => _customer.Country;
        set
        {
            Log.Verbose("Customer Country changed from {OldCountry} to {NewCountry}", _customer.Country, value);
            _customer.Country = value;
            RaisePropertyChanged();
        }
    }

    public string Phone
    {
        get => _customer.Phone;
        set
        {
            Log.Verbose("Customer Phone changed from {OldPhone} to {NewPhone}", _customer.Phone, value);
            _customer.Phone = value;
            RaisePropertyChanged();
        }
    }
public class CustomersViewModel : ViewModelBase
{
    readonly ObservableCollection<CustomerViewModel> _customers = 
        new ObservableCollection<CustomerViewModel>(
            Customer.GetCustomers().Select(c => new CustomerViewModel(c)));

    public CustomersViewModel()
    {
        _selectedCustomer = _customers.Count > 0 ? _customers[0] : null;
        _newCustomerCommand = new RelayCommand(AddCustomer);
        _deleteCustomerCommand = new RelayCommand(DeleteCustomer, () => SelectedCustomer != null);
    }
    private CustomerViewModel _selectedCustomer;
    private readonly RelayCommand _newCustomerCommand;
    private readonly RelayCommand _deleteCustomerCommand;

    public ObservableCollection<CustomerViewModel> Customers => _customers;

    public CustomerViewModel SelectedCustomer
    {
        get => _selectedCustomer;
        set
        {
            _selectedCustomer = value;
            RaisePropertyChanged();
            _deleteCustomerCommand.RaiseCanExecuteChanged();
            Log.Debug("Customer changed to {@Customer}", SelectedCustomer);
        }
    }

    public ICommand NewCustomerCommand => _newCustomerCommand ;

    private void AddCustomer()
    {
        var customer = new CustomerViewModel(new Customer());
        _customers.Add(customer);
        SelectedCustomer = customer;
        Log.Information("Added new Customer");
    }

    public ICommand DeleteCustomerCommand => _deleteCustomerCommand;

    private void DeleteCustomer()
    {
        Log.Warning("Deleting Customer {@Customer}", SelectedCustomer);
        _customers.Remove(SelectedCustomer);
        SelectedCustomer = null;
    }
}

Note that we are using Serilog’s structured logging, where we have the logging message and level (like any other logging framework) and the properties attached to the log message, that can be used to process the log files in a later time. With that, you can run the program and get something like this:

Coloring the log list

While running the app, I’ve seen that all messages have the same color, and it’s difficult to differentiate between message levels, so I decided to color the list items, depending on their level. For that, I’ve used a resource called StyleSelector. You must declare a class inherited from StyleSelector, overrride its method SelectStyleCore and return a style that will be used to render the current item. Then you set the ListView’s ItemContainerStyleSelector property to the instance of the class.

To declare the class. you must create a new class and name it LogStyleSelector and add this code:

public class LogStyleSelector : StyleSelector
{
    public Style VerboseStyle { get; set; }
    public Style DebugStyle { get; set; }
    public Style InformationStyle { get; set; }
    public Style WarningStyle { get; set; }
    public Style ErrorStyle { get; set; }
    public Style FatalStyle { get; set; }
    protected override Style SelectStyleCore(object item, DependencyObject container)
    {
        var logEvent = item as LogEvent;
        if (logEvent == null)
            return null;

        switch (logEvent.Level)
        {
            case LogEventLevel.Verbose:
                return VerboseStyle;
            case LogEventLevel.Debug:
                return DebugStyle;
            case LogEventLevel.Information:
                return InformationStyle;
            case LogEventLevel.Warning:
                return WarningStyle;
            case LogEventLevel.Error:
                return ErrorStyle;
            case LogEventLevel.Fatal:
                return FatalStyle;
            default:
                return null;
        }
    }
}

In this class, I have declared six Style properties, each one pointing to a log message level. These properties will be filled in the xaml files with the styles wanted for each kind of message. Then, in  SelectStyleCore, we get the level for the current item and return the corresponding style.

In Log.xaml we must declare the selector and the six styles in the Resources section:

<UserControl.Resources>
    <l:LogStyleSelector x:Key="LogStyleSelector" >
        <l:LogStyleSelector.VerboseStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="Beige" />
            </Style>
        </l:LogStyleSelector.VerboseStyle>
        <l:LogStyleSelector.DebugStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="LightGreen" />
            </Style>
        </l:LogStyleSelector.DebugStyle>
        <l:LogStyleSelector.InformationStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="Green" />
                <Setter Property="Foreground" Value="White" />
            </Style>
        </l:LogStyleSelector.InformationStyle>
        <l:LogStyleSelector.WarningStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="Yellow" />
            </Style>
        </l:LogStyleSelector.WarningStyle>
        <l:LogStyleSelector.ErrorStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="Red" />
                <Setter Property="Foreground" Value="White" />
            </Style>
        </l:LogStyleSelector.ErrorStyle>
        <l:LogStyleSelector.FatalStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="DarkRed" />
                <Setter Property="Foreground" Value="White" />
            </Style>
        </l:LogStyleSelector.FatalStyle>
    </l:LogStyleSelector>
</UserControl.Resources>

and set the ItemContainerStyleSelector property of the listview to it:

<ListView ItemsSource="{Binding LogItems}" Margin="0,10,0,0"
          ItemContainerStyleSelector="{StaticResource LogStyleSelector}">

That’s all we need to color the list items depending on the item’s level. Much better, no?

Conclusions

It’s been a long journey, but we’ve come to an end. We’ve seen how to use Serilog to use logging in a UWP app, and we’ve used a Rx sink, getting all log messages in an observable – it would be a simple change to log the messages to another place, like a Sqlite database, a flat file on disk or even a remote destination, like HoockeyApp. You can take advantage of the structured logging, where you can check the properties logged (that can be a big timesaver when examining long logs).

In the middle of the project I’ve added some extra features, like the x:Bind compiled data binding, and use functions and other kinds of bindings, and the style selector to change the background color of the list items depending on the value of the level property.

All the source code in this project is available on https://github.com/bsonnino/LoggingSerilog