Using HostBuilder, ServiceProvider and Dependency Injection with Windows Forms on .NET Core 3

09/03/2020 2 comments

In the last posts we talked about how to use .NET Core 3.0 Dependency Injection and Service Provider with WPF. But also Windows Forms is supported by .NET Core 3.x, so it’s time to show how to use the same concepts of HostBuilder, Service Provider and Dependency Injection with this application model.

First of all, after creating a .NET Core Windows Forms application, we need to add the NuGet package Microsoft.Extensions.Hosting to the project. It will allow us to use HostBuilder and, moreover, it automatically imports a bunch of other required packages. Now let’s open the Program.cs file and add the following code:

private static void Main()
{
    // ...
    var host = Host.CreateDefaultBuilder()
             .ConfigureAppConfiguration((context, builder) =>
             {
                 // Add other configuration files...
                 builder.AddJsonFile("appsettings.local.json", optional: true);
             })
             .ConfigureServices((context, services) =>
             {
                 ConfigureServices(context.Configuration, services);
             })
             .ConfigureLogging(logging =>
             {
                 // Add other loggers...
             })
             .Build();

    var services = host.Services;
    var mainForm = services.GetRequiredService<MainForm>();
    Application.Run(mainForm);
}

private static void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    // ...
    services.AddSingleton<MainForm>();
}

HostBuilder configuration at lines 4-18 follows the same structure we have already presented in the previous article, so refer to it for more information. After that, at lines 21 we retrive the MainForm we added in the Service Collection (line 29) and finally we start the application using the Application.Run method (line 22).

We can now run the application: everything will work as expected. And now we can leverage all the features that .NET Core 3 provides. Let’s add also a file named appsettings.json to the root folder of the project. Set its Build Action property to Content and Copy to Output Directory to Copy if newer:

{
  "AppSettings": {
    "StringSetting": "Value",
    "IntegerSetting": 42,
    "BooleanSetting": true
  }
}

This file is automatically loaded and made available to the application by the CreateDefaultBuilder method we saw before. Then, we create an AppSettings.cs file to hold configuration settings. This file will map the settings that we write in appsettings.json:

public class AppSettings
{
    public string StringSetting { get; set; }
 
    public int IntegerSetting { get; set; }
 
    public bool BooleanSetting { get; set; }
}

Moreover, create also a sample service with its interface:

public interface ISampleService
{
    string GetCurrentDate();
}
 
public class SampleService : ISampleService
{
    public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}

Now we must register these services in the IoC Container, as usual:

private void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    services.Configure<AppSettings>
            (configuration.GetSection(nameof(AppSettings)));

    services.AddScoped<ISampleService, SampleService>();

    //...
}

Remember that the MainForm itself is in the IoC Container. So, when we get it from the Service Provider, it will automatically be injected with all the required services. We just need to modify its constructor:

private readonly IServiceProvider serviceProvider;
private readonly ISampleService sampleService;
private readonly AppSettings settings;

public MainForm(IServiceProvider serviceProvider,
                ISampleService sampleService,
                IOptions<AppSettings> settings)
{
    InitializeComponent();

    this.serviceProvider = serviceProvider;
    this.sampleService = sampleService;
    this.settings = settings.Value;
}

Running this code, we’ll obtain a result like the following:

The .NET Core 3.0 Windows Forms application with dependecies injected

The .NET Core 3.0 Windows Forms application with dependecies injected

Finally, let’s try to add a second Form to the application and open it using a button from the Main Form. But, before doing that, remember that, at the time of writing, the Windows Forms Designer for .NET Core is available only in Visual Studio 16.5 Preview. If you haven’t it yet, you can refer to the complete sample you’ll find at the end of the article.

So, register the new form in the Service Collection (inside Program.cs file):

private static void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    // ...
    services.AddTransient<SecondForm>();
}

At line 5 we register the Form as transient dependency, meaning that, everytime we try to get a reference to it, we’ll get a new instance. Of course, we can use AddScoped or AddSingleton as well. Keep in mind that, in the context of a desktop application, Scoped and Singleton get always the same instance, because in this case we have a scope as long as our application runs.

Then, in MainForm.cs add the code to open the new Form when clicking the button:

private void OpenSecondFormButton_Click(object sender, EventArgs e)
{
    var form = serviceProvider.GetRequiredService<SecondForm>();
    form.ShowDialog(this);
}

We use the ServiceProvider we passed to the MainForm constructor to get a reference to SecondForm and we call its ShowDialog method (lines 3-4). But SecondForm is itself in the IoC Container, so in turn it can receive the dependencies it needs in the constructor:

public partial class SecondForm : Form
{
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public SecondForm(ISampleService sampleService,
                      IOptions<AppSettings> settings)
    {
        InitializeComponent();

        this.sampleService = sampleService;
        this.settings = settings.Value;
    }
}
Opening a second Form with the Service Provider

Opening a second Form with the Service Provider

You can download the sample app using the link below:

Using HostBuilder, ServiceProvider and Dependency Injection with Windows Forms on .NET Core 3

Categories: .NET Core, C#, Windows Forms

An MVVM-aware NavigationService for WPF running on .NET Core

13/01/2020 8 comments

In the last article we talked about how to use the MVVM pattern in WPF applications running on .NET Core. Today, we’ll add support for navigation through Window from View Models. For this purpose, we start from the code of the last article. Our goal is to extend it adding a method to open new Window from View Models and, at the same time, pass parameters to View Models.

So, let’s start with the NavigationService:

public interface IActivable
{
    Task ActivateAsync(object parameter);
}

public class NavigationService
{
    private Dictionary<string, Type> windows { get; }
        = new Dictionary<string, Type>();

    private readonly IServiceProvider serviceProvider;

    public void Configure(string key, Type windowType) 
        => windows.Add(key, windowType);

    public NavigationService(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    public async Task ShowAsync(string windowKey,
        object parameter = null)
    {
        var window = await GetAndActivateWindowAsync(windowKey, parameter);
        window.Show();
    }

    public async Task<bool?> ShowDialogAsync(string windowKey,
        object parameter = null)
    {
        var window = await GetAndActivateWindowAsync(windowKey, parameter);
        return window.ShowDialog();
    }

    private async Task<Window> GetAndActivateWindowAsync(string windowKey,
        object parameter = null)
    {
        var window = serviceProvider.GetRequiredService(windows[windowKey])
            as Window;

        if (window.DataContext is IActivable activable)
        {
            await activable.ActivateAsync(parameter);
        }

        return window;
    }
}

First of all, at lines 1-4 we define an IActivable interface to mark View Models that support “activation”, i.e. the ability to receive parameters when the corresponding Window is loaded. Then, the NavigationService class encapsulates all the logic we need:

  • it defines a Configure method (lines 13-14) to map a string identifier to a window type. We follow this approach because we want to use the NavigationService from View Models that don’t have to know the Views;
  • it provides methods to open a window (modeless or modal, lines 24-25 and 31-32 respectively), getting it from the ServiceProvider using the windowKey parameter (lines 37-38);
  • if the Window.DataContext is a View Model that implements the IActivable interface, it calls its ActivateAsync method (lines 41-44), passing to it the specified parameter, if any.

Now we need to properly register the NavigationService and all the Windows and corresponding View Models of our application in the ConfigureServices method of App.xaml.cs file we defined in the previous post:

public static class Windows
{
    public const string MainWindow = nameof(MainWindow);
    public const string DetailWindow = nameof(DetailWindow);
}

private void ConfigureServices(IConfiguration configuration, 
    IServiceCollection services)
{
    // ...

    services.AddScoped<NavigationService>(serviceProvider =>
    {
        var navigationService = new NavigationService(serviceProvider);
        navigationService.Configure(Windows.MainWindow, typeof(MainWindow));
        navigationService.Configure(Windows.DetailWindow, typeof(DetailWindow));

        return navigationService;
    });

    services.AddSingleton<MainViewModel>();
    services.AddSingleton<DetailViewModel>();

    services.AddTransient<MainWindow>();
    services.AddTransient<DetailWindow>();
}

At lines 1-5 we create a Windows class that holds strings corresponding to the Windows of the application, in this sample a MainWindow and a DetailWindow (we’ll talk about them later in this article). Then, we create the NavigationService, configure all the Windows and add it to the services collection (lines 12-19). Finally, at lines 21-25 we add Views Models and Windows themselves.

In the OnStartup method, we can use the new service to show the MainWindow:

protected override async void OnStartup(StartupEventArgs e)
{
    await host.StartAsync();

    var navigationService = 
        ServiceProvider.GetRequiredService<NavigationService>();

    await navigationService.ShowAsync(Navigation.Windows.MainWindow);

    base.OnStartup(e);
}

Now let’s see how to use it. Our MainWindow already contains a TextBox and a Button from the last sample. These objects are already bound to the MainViewModel, so we need to modify it to obtain something like this:

public class MainViewModel : ViewModelBase
{
    private string input;
    public string Input
    {
        get => input;
        set => Set(ref input, value);
    }

    private readonly NavigationService navigationService;
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public RelayCommand ExecuteCommand { get; }

    public MainViewModel(NavigationService navigationService, 
        ISampleService sampleService, IOptions<AppSettings> options)
    {
        this.navigationService = navigationService;
        this.sampleService = sampleService;
        settings = options.Value;

        ExecuteCommand = new RelayCommand(
            async () => await ExecuteAsync());
    }

    private Task ExecuteAsync()
    {
        Debug.WriteLine($"Current value: {input}");
        return navigationService.ShowDialogAsync(Windows.DetailWindow, input);
    }
}

First of all, in the constructor we pass a reference to the NavigationService (line 16). As usual, the .NET Core ServiceProvider is responsible to instantiate it and pass it to the class. Then, in the ExecuteAsync method we show the DetailWindow as dialog using the service, passing the specified input as parameter (line 30).

The DetailWindow can be something like the following:

<Window
    x:Class="WpfNetCoreMvvm.Views.DetailWindow"
    ...
    DataContext
        ="{Binding Source={StaticResource Locator}, Path=DetailViewModel}"
    >

    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Passed Parameter:" />
            <TextBlock Text="{Binding Parameter}" />
        </StackPanel>
    </Grid>
</Window>

Note in particular the DataContext property at line 4-5 and the binding at line 11. Now we need to define the DetailViewModel itself:

public class DetailViewModel : ViewModelBase, IActivable
{
    private string parameter;
    public string Parameter
    {
        get => parameter;
        set => Set(ref parameter, value);
    }

    public Task ActivateAsync(object parameter)
    {
        Parameter = parameter?.ToString();
        return Task.CompletedTask;
    }
}

When we navigate to DetailWindow from MainViewModel, we pass a parameter, so the DetailViewModel must be able to handle it. For this reason, it implements the IActivable interface (lines 10-13): in this way, the input value from MainViewModel is automatically passed to the ActivateAsync method.

Now we can finally try running the application:

Navigation in the WPF Application with MVVM running on top of .NET Core

Navigation in the WPF Application with MVVM running on top of .NET Core

You can download the sample application using the link below:

An MVVM-aware NavigationService for WPF running on .NET Core

Categories: .NET Core, C#, MVVM, WPF

Using the MVVM pattern in WPF applications running on .NET Core

07/01/2020 22 comments

Some time ago we talked about leveraging .NET Core features (like Host Builder, Service Provider and Dependency Injection) in WPF applications. Now it’s time to integrate them with the MVVM pattern. In this article we’ll use MVVM Light, but only for what concerns helpers methods and classes for View Models. In particular, instead of its SimpleIoc, we’ll show how to use the .NET Core ServiceProvider. So, let’s start from the code that we realized in the latest article and modify it to add MVVM support.

First of all, let’s add the MvvmLightLibsStd10 NuGet package to the project. Then, we need to create a ViewModel for our MainWindow. Keeping in mind that in our original sample the MainWindow receives two parameters in the constructor via Dependency Injection, we want to move them to the View Model. So, we’ll have something like this:

public class MainViewModel : ViewModelBase
{
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public MainViewModel(ISampleService sampleService, 
        IOptions<AppSettings> options)
    {
        this.sampleService = sampleService;
        settings = options.Value;
    }
}

Then, we need to modify the App.xaml.cs file to register this View Model. We also need to expose a reference to the ServiceProvider (we’ll use it later):

public partial class App : Application
{
    private readonly IHost host;

    public static IServiceProvider ServiceProvider { get; private set; }

    public App()
    {
        // Original host configuration...

        ServiceProvider = host.Services;
    }

    private void ConfigureServices(IConfiguration configuration, 
        IServiceCollection services)
    {
        services.Configure<AppSettings>(configuration
            .GetSection(nameof(AppSettings)));
        services.AddScoped<ISampleService, SampleService>();

        // Register all ViewModels.
        services.AddSingleton<MainViewModel>();

        // Register all the Windows of the applications.
        services.AddTransient<MainWindow>();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        await host.StartAsync();

        var window = ServiceProvider.GetRequiredService<MainWindow>();
        window.Show();

        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        // Original code...
    }
}

At line 5 we declare a static reference to the IServiceProvider, that we assign at the end of the App constructor using the IHost.Services property (line 11). In the ConfigureServices method, line 22, we register the MainViewModel. In this way, we can get it from the ServiceProvider and, more important, we can pass to its constructor all the dependencies it needs, that will be automatically resolved during instantiation (in this case, AppSettings and ISampleService from lines 17-19).

Note that, typically, View Models are registered as singleton (line 22), but of course we can decide to use the AddTransient method if necessary for our scenario. Moreover, for the sake of simplicity all the registrations are kept together in the same file, but of course it is possible to organize the code as we want.

Now we need to create the ViewModelLocator, i.e. a class that provides (as the name implies) references to View Models that will be bound to Views:

public class ViewModelLocator
{
    public MainViewModel MainViewModel 
        => App.ServiceProvider.GetRequiredService<MainViewModel>();
}

In this case, we have only one View Model, as at lines 3-4 we get it using the ServiceProvider property that is exposed by the App class. As we said earlier, all registrations are contained in the ConfigureServices method of the App class, so the ViewModelLocator need only to provide getter properties for View Models.

The last thing to do is to reference the ViewModelLocator in XAML and link the View Model to the View. So, in the App.xaml file we need to write something like this:

<Application
    x:Class="WpfNetCoreMvvm.App"
    ...>

    <Application.Resources>
        <ResourceDictionary>
            <vm:ViewModelLocator
                xmlns:vm="clr-namespace:WpfNetCoreMvvm.ViewModels"
                x:Key="Locator" />

            <!-- Application styles definition -->
        </ResourceDictionary>
    </Application.Resources>
</Application>

And, finally, set the DataContext property of MainWindow.xaml:

<Window
    x:Class="WpfNetCoreMvvm.Views.MainWindow"
    ...
    DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}">

Now everything is set. We can put a breakpoint in the MainViewModel constructor and verify that it will be correctly invoked when the application starts, with all the dependencies it needs:

The View Model of the .NET Core 3.1 WPF application with dependencies injected

The View Model of the .NET Core 3.1 WPF application with dependencies injected

To make a further test, we can add a bit of code to the View Model:

public class MainViewModel : ViewModelBase
{
    private string input;
    public string Input
    {
        get => input;
        set => Set(ref input, value);
    }

    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public RelayCommand ExecuteCommand { get; }

    public MainViewModel(ISampleService sampleService,
        IOptions<AppSettings> options)
    {
        this.sampleService = sampleService;
        settings = options.Value;

        ExecuteCommand = new RelayCommand(async () => await ExecuteAsync());
    }

    private Task ExecuteAsync()
    {
        Debug.WriteLine($"Current value: {input}");
        return Task.CompletedTask;
    }
}

Now we have an Input property (lines 3-8) and a RelayCommand (line 13, with the implementation at lines 24-28). So, as usual, we can use binding in our Window:

<Window
    ...>

    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Input:" />
            <TextBox Width="300" Text="{Binding Input, Mode=TwoWay}" />
            <Button Command="{Binding ExecuteCommand}" Content="Execute" />
        </StackPanel>
    </Grid>
</Window>

Let’s try running the application:

The WPF Application with MVVM running on top of .NET Core

The WPF Application with MVVM running on top of .NET Core

You can download the sample application using the link below:

Using the MVVM pattern in WPF application running on .NET Core

In the next article we’ll add an important feature that is currently missing: the ability to navigate through Window from View Models passing parameters to them upon activation.

Categories: .NET Core, C#, MVVM, WPF

JSON Serialization and Deserialization using System.Text.Json with Refit

13/11/2019 Comments off

Refit is a very useful library that allows to define an interface for a REST API and call it hiding all the HTTP and JSON serialization/deserialization bits. A lot of samples of its usage are available on GitHub.

By default, Refit uses JSON.NET under-the-hood to handle serializing and deserializing JSON. However, we can change this behavior and use the new System.Text.Json objects by defining a custom Content Serializer, i.e. a class that implements the IContentSerializer interface:

public interface IContentSerializer
{
    Task<T> DeserializeAsync<T>(HttpContent content);
    
    Task<HttpContent> SerializeAsync<T>(T item);
}

So, we can provide an implementation like the following:

public class JsonContentSerializer : IContentSerializer
{
    private readonly JsonSerializerOptions serializerOptions;

    public JsonContentSerializer(JsonSerializerOptions serializerOptions = null)
    {
        this.serializerOptions = serializerOptions ?? new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = true,
        };
    }

    public async Task<T> DeserializeAsync<T>(HttpContent content)
    {
        using var utf8Json = await content.ReadAsStreamAsync()
            .ConfigureAwait(false);

        return await JsonSerializer.DeserializeAsync<T>(utf8Json,
            serializerOptions).ConfigureAwait(false);
    }

    public Task<HttpContent> SerializeAsync<T>(T item)
    {
        var json = JsonSerializer.Serialize(item, serializerOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        return Task.FromResult((HttpContent)content);
    }
}

At lines 20-21 and 26, we’re using the JsonSerializer class from System.Text.Json to perform serialization and deserialization.

Finally, we need to tell Refit to use this implementation instead of the default one:

var settings = new RefitSettings
{
    ContentSerializer = new JsonContentSerializer()
};

var api = RestService.For<IMyServiceApi>("<service_url>", settings);

At lines 1-4, we create a RefitSettings object that specifies the Content Serializer to use. Then, we pass it to the static RestService.For method (line 6). Now all the JSON content will be handled with System.Text.Json.

Categories: .NET Core, C#

Update on using HostBuilder, Dependency Injection and Service Provider with .NET Core 3.0 WPF applications

07/11/2019 18 comments

Some months ago we talked about how to use .NET Core 3.0 Dependency Injection and Service Provider with WPF. Now that the final version of .NET Core 3.0 has reached its GA, it’s time to update that article showing the right approach to use HostBuilder, Dependency Injection and Service Provider in WPF applications.

So, after creating a .NET Core WPF application, we need to add the NuGet package Microsoft.Extensions.Hosting to the project. It will allow us to use HostBuilder and, moreover, it automatically imports a bunch of other required packages.

Now, Let’s open the App.xaml file and remove the StartupUri property of the Application class. Then, write the following code in the App.xaml.cs file:

public partial class App : Application
{
    private readonly IHost host;

    public App()
    {
        host = Host.CreateDefaultBuilder()
               .ConfigureServices((context, services) =>
               {
                   ConfigureServices(context.Configuration, services);
               })
               .Build();
    }

    private void ConfigureServices(IConfiguration configuration, 
        IServiceCollection services)
    {
        // ...
        services.AddSingleton<MainWindow>();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        await host.StartAsync();

        var mainWindow = host.Services.GetRequiredService<MainWindow>();
        mainWindow.Show();

        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        using (host)
        {
            await host.StopAsync(TimeSpan.FromSeconds(5));
        }

        base.OnExit(e);
    }
}

At line 7, we create a HostBuilder using the Host.CreateDefaultBuilder method. As we can read in the documentation, it automatically configures some defaults (that are tipically useful for every application):

If we don’t want these configuration, but instead we prefer to manually configure every setting, we can simply initialize a new HostBuilder. In any case, it is possible to invoke other methods to further configure our host:

host = Host.CreateDefaultBuilder()  // Use default settings
       //new HostBuilder()          // Initialize an empty HostBuilder
        .ConfigureAppConfiguration((context, builder) =>
        {
            // Add other configuration files...
            builder.AddJsonFile("appsettings.local.json", optional: true);
        }).ConfigureServices((context, services) =>
        {
            ConfigureServices(context.Configuration, services);
        })
        .ConfigureLogging(logging => 
        {
            // Add other loggers...
        })
        .Build();

Going back to the first App.xaml.cs file, at line 8 we call HostBuilder.ConfigureService, that is responsible to add all our services in the .NET Core IoC Container. This method in turn calls our ConfigureServices implementation (lines 15-20), in which we register all the services used by the application in the exact same way of ASP. NET Core. We’ll complete this method in a moment, but for now let’s notice that we register also the MainWindow class (line 19). This is important because, in this way, the window itself becomes part of the Dependency Injection chain. It means that, after calling this method, at line 26-27 we can get it from the ServiceProvider and then show it. But, more important, it means that we can pass to the MainWindow constructor all the dependencies it needs, as we do for ASP.NET Core Controllers.

In the OnStartup override (lines 22-30), we call the StartAsync method to start the program (line 24): at this moment, it calls IHostedService.StartAsync on each implementation of IHostedService that it finds in the DI container (we don’t have any of this in our app, so in fact this method does nothing). Finally, as said before, at line 26-27 we get the MainWindow from the ServiceProvider and then show it.

As last step, in the OnExit method (lines 32-39), we call StopAsync to gracefully stop the host.

Even if the actual services aren’t yet registered, we can run the application and see that everything works as expected.

Now it’s time to complicate the things a bit. Let’s add a file named appsettings.json to the root folder of the project. Set its Build Action property to Content and Copy to Output Directory to Copy if newer:

{
  "AppSettings": {
    "StringSetting": "Value",
    "IntegerSetting": 42,
    "BooleanSetting": true
  }
}

This file is automatically loaded and made available to the application by the CreateDefaultBuilder method we talked before. Then, we create an AppSettings.cs file to hold configuration settings. This file will map the settings that we write in appsettings.json:

public class AppSettings
{
    public string StringSetting { get; set; }
 
    public int IntegerSetting { get; set; }
 
    public bool BooleanSetting { get; set; }
}

Moreover, create also a sample service with its interface:

public interface ISampleService
{
    string GetCurrentDate();
}
 
public class SampleService : ISampleService
{
    public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}

Now we must register these services in the IoC Container, as usual:

private void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    services.Configure<AppSettings>
            (configuration.GetSection(nameof(AppSettings)));

    services.AddScoped<ISampleService, SampleService>();

    //...
}

As said before, the MainWindow itself is in the IoC Container. So, when we get it from the Service Provider, it will automatically be injected with all the required services. So, we just need to modify its constructor:

public partial class MainWindow : Window
{
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;
 
    public MainWindow(ISampleService sampleService, 
                      IOptions<AppSettings> settings)
    {
        InitializeComponent();
 
        this.sampleService = sampleService;
        this.settings = settings.Value;
    }
 
    // ...
}

Running this code, we’ll obtain a result like the following:

The .NET Core 3.0 WPF application with dependecies injected

The .NET Core 3.0 WPF application with dependecies injected

You can download the sample app using the link below:

Update on using HostBuilder, Dependency Injection and Service Provider with .NET Core 3.0 WPF applications

Categories: .NET Core, C#, WPF

A simple NavigationService with Dependency Injection for WPF on .NET Core 3.0

21/05/2019 1 comment

We already talked about how to use Dependency Injection and Service Provider with WPF on .NET Core 3.0. In that occasion we said that windows themselves are registered in the IoC, so that we need to use the ServiceProvider to get and load them, for example:

var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();

This approach works correctly, but we need to use two instructions every time we want to open a new window. Moreover, we don’t have a straight way to pass parameters to the new window. To make things simpler, we can write a couple of classes to manage this task:

public interface IActivable
{
    Task ActivateAsync(object parameter);
}

public class SimpleNavigationService
{
    private readonly IServiceProvider serviceProvider;

    public SimpleNavigationService(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    public async Task ShowAsync<T>(object parameter = null) where T : Window
    {
        var window = serviceProvider.GetRequiredService<T>();
        if (window is IActivable activableWindow)
        {
            await activableWindow.ActivateAsync(parameter);
        }

        window.Show();
    }

    public async Task<bool?> ShowDialogAsync<T>(object parameter = null) 
        where T : Window
    {
        var window = serviceProvider.GetRequiredService<T>();
        if (window is IActivable activableWindow)
        {
            await activableWindow.ActivateAsync(parameter);
        }

        return window.ShowDialog();
    }
}

First of all, at lines 1-4 we define an IActivable interface to mark Windows that support “activation”, i.e. the ability to receive parameters upon loading. Then, the SimpleNavigationService class encapsulates all the logic we need:

  • it provides methods to open a window, getting it from the ServiceProvider (lines 15-36), so every dependency is automatically resolved and passed to its constructor;
  • if the window implements the IActivable interface, it calls the ActivateAsync method before actually showing it.

Now we just need to properly register the SimpleNavigationService and all the windows of our application in the ConfigureServices methods we defined in the previous post:

private void ConfigureServices(IServiceCollection services)
{
    // ...

    // Add SimpleNavigationService for the application.
    services.AddScoped<SimpleNavigationService>();

    // Register all the windows of the applications.
    services.AddTransient<MainWindow>();
    services.AddTransient<DetailWindow>();
}

At the end of the OnStartup method, we can use the new service to show the MainWindow:

protected override void OnStartup(StartupEventArgs e)
{
    // ....

    var navigationService = ServiceProvider
                            .GetRequiredService<SimpleNavigationService>();
    var task = navigationService.ShowAsync<MainWindow>();
}

Now let’s see how to use it. Let’s suppose to have a window with a TextBox and a Button:

<Window
    ...>

    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Parameter:" />
            <TextBox x:Name="ParameterTextBox" Width="300" />
            <Button
                x:Name="OpenDetailWindowButton"
                Click="OpenDetailWindowButton_Click"
                Content="Go to Detail Window" />
        </StackPanel>
    </Grid>
</Window>

We want that, pressing the Button at lines 8-11, the DetailWindow will be opened, receiving the value of the TextBox. So, the code-behind in MainWindow.xaml.cs is the following:

public partial class MainWindow : Window
{
    private readonly SimpleNavigationService navigationService;

    public MainWindow(SimpleNavigationService navigationService)
    {
        // ...
        this.navigationService = navigationService;
    }

    private async void OpenDetailWindowButton_Click(object sender, 
                                                    RoutedEventArgs e)
    {
        var result = await navigationService
                           .ShowDialogAsync<DetailWindow>(ParameterTextBox.Text);
    }
}

We pass the SimpleNavigationService in the MainWindow constructor through Dependency Injection, then in the button event handler we call the ShowDialogAsync method specifying the type of the Window to open and the parameter to initialize it. To be able to handle this, the DetailWindow code-behind should be something like that:

public partial class DetailWindow : Window, IActivable
{
    public DetailWindow()
    {
        // ...
    }

    public Task ActivateAsync(object parameter)
    {
        ParameterTextBox.Text = parameter?.ToString();
        return Task.CompletedTask;
    }
}

As DetailWindow implements the IActivable interface (line 1), SimpleNavigationService will ensure that the ActivateAsync method is called before showing the UI. In this sample, we just set the parameter in a TextBox (line 10). Of course, also the DetailWindow class could receive dependencies in its constructor, if necessary.

You can download the sample app using the link below:

A simple NavigationService with Dependency Injection for WPF on .NET Core 3.0

Categories: .NET Core, C#, WPF

Using Entity Framework Core with WPF on .NET Core 3.0

19/03/2019 2 comments

We talked about how to configure a .NET Core 3.0 WPF application in the same way as we do with ASP.NET Core. We can keep using this approach also if we need to use Entity Framework Core. It is important to note that tipically we use an external service to perform data access (for example, a REST Web APIs backend), but in some cases (especially if we’re working with a legacy system) it might be necessary to access a database directly from within our application.

And the story is again the same: you use the same code of ASP.NET Core. Let’s see how to do that. As always, take this sample as reference. First of all, we need to add the NuGet package for Entity Framework Core:

Adding Entity Framework Core to .NET Core 3.0 WPF application

Adding Entity Framework Core to .NET Core 3.0 WPF application

In this example, we’re using Entity Framework with SQL Server, but of course we can use every database supported by this OR/M.

Now, let’s add a connection string in the appsettings.json file:

{
  "ConnectionStrings": {
    "SqlConnection": ""
  }
}

Then, we must create our data context, i.e., a class that inherits form DbContext and provides access to the database:

public class DataContext : DbContext
{
    // DbSet properties declarations...

    public DataContext(DbContextOptions<DataContext> options) : base(options)
    {
    }
}

The DataContext class contains a constructor that takes a DbContextOptions as argument. It is used to pass configuration settings to the Context via Dependency Injection. For the rest, we can add to the class the usual DbSets we are accustomed to and configure the model by overriding the OnConfiguring and OnModelCreating methods.

Finally, we need to register the DbContext in the ServiceProvider collection, like we did in the previous post:

private void ConfigureServices(IServiceCollection services)
{
     services.AddDbContext<DataContext>
        (options => options.UseSqlServer(
                    Configuration.GetConnectionString("SqlConnection")));

            
     services.AddTransient(typeof(MainWindow));
}

At lines 4-5 we setup the DataContext to use SQL Server with the connection string from the appsettings.json file we defined before.

Now we’re ready to use the DataContext in every Window we need it. For example:

private readonly DataContext dataContext;

public MainWindow(DataContext dataContext)
{
    InitializeComponent();

    this.dataContext = dataContext;
}

As we can see, also in this case the approach is the same we use with ASP.NET Core. Of course, we can choose to pass the DataContext to a business service, instead of accessing it directly: as long as the service is itself in the IoC container, everything works in the same way.

Categories: .NET Core, C#, Entity Framework, WPF