WPF y netcore3 con custom host

📅 Jan 20, 2020 · ☕ 4 min · ✍️ eiximenis

¡Buf! Hacía un porrón que no escribía sobre WPF, pero bueno, creo que vale la pena hablar un poco de las ventajas de usar WPF junto con .NET Core 3. Sí, se habla mucho de los aumentos de rendimiento pero a mi me interesa enfocarlo más en como usar, fácilmente, las ventajas intrínsecas de .NET Core al usar un proyecto de WPF. En concreto hay tres puntos que creo que son interesantes: DI, Configuración y Logging.

Usar el custom host

Vamos a ver como preparar nuestro código para usar el custom host de .Net Core en aplicaciones WPF y así, luego, poder usar todas sus ventajas.

Lo primero que debemos hacer es lo siguiente en App.xaml:

    1. Eliminar el StartupUri ya que vamos a crear nosotros la aplicación.
    1. Agregar el evento Startup: Startup="Application_Startup"
    1. Agregar el evento Exit : Exit="Application_Exit"

Ahora ya podemos poner el código en App.xml.cs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
private readonly IHost _host;

public App()
{
    _host = new HostBuilder().Build();
}

private async void Application_Startup(object sender, StartupEventArgs e)
{
    await _host.StartAsync();

    var mainWindow = new MainWindow();
    mainWindow.Show();
}

private async void Application_Exit(object sender, ExitEventArgs e)
{
    using (_host)
    {
        await _host.StopAsync(TimeSpan.FromSeconds(10));
    }
}

Eso levanta nuestra aplicación, exactamente igual que antes, pero ya podemos usar todas las ventajas del custom host. ¡Vamos allá!

Inyección de dependencias

Desde siempre .NET Core ha soportado inyección de dependencias y podemos aprovecharnos de nuestro amigo, el IServiceCollection para nuestras aplicaciones .NET Core. Del mismo modo que en un aplicación web MVC registramos los controladores y todas sus dependencias, eso mismo podemos hacer en una aplicación WPF. Simplemente usamos el método ConfigureServices del HostBuilder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public App()
{
    _host = new HostBuilder()
        .ConfigureServices(ConfigureServices)
        .Build();
}

private void ConfigureServices(IServiceCollection services)
{
    // Registramos todos los servicios y los view models. P. ej:
    services.AddTransient<MainWindowViewModel>();
    // Registramos WPF
    services.AddWpf();
}

El método AddWpf es un método de extensión chungo-pelotero que me he creado yo, que, básicamente, registra en el sistema de DI todas las ventanas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static IServiceCollection AddWpf(this IServiceCollection services)
{
    var assembly = Assembly.GetExecutingAssembly();
    var windows = assembly.GetTypes().
        Where(t => t.IsSubclassOf(typeof(Window)));
    foreach (var wtype in windows)
    {
        services.AddTransient(wtype);
    }

    return services;
}

En este caso solo registro las clases que hereden de Window, por supuesto lo puedes adaptar a tus necesidades :)

Con eso ya tenemos nuestro sistema de DI configurado y podemos inyectar el viewmodel a la ventana:

1
2
3
4
5
6
// El sistema de DI inyectará el MainWindowViewModel
public MainWindow(MainWindowViewModel vm)
{
    InitializeComponent();
    this.DataContext = vm;
}

Configuración

La gestión de la configuración es otra de las grandes ventajas que tenemos ya incorporadas en .NET Core. En este caso, simplemente llamamos al método ConfigureAppConfiguration de nuestro HostBuilder:

1
2
3
4
5
6
7
8
_host = new HostBuilder()
    .ConfigureAppConfiguration((ctx, cfg) =>
    {
        cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath);
        cfg.AddJsonFile("appsettings.json", optional: true);
        cfg.AddEnvironmentVariables();

    })

Solo ten presente de incluir el fichero appsettings con la opción “Copy to output”, puedes usar la ventana de propiedades del fichero en Visual Studio, o añadir lo siguiente en el .csproj:

1
2
3
4
5
<ItemGroup>
<None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

Logging

Oye, ya puestos podemos configurar el logging también, en este caso basta con llamar al método ConfigureLogging:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
_host = new HostBuilder()
    .ConfigureAppConfiguration((ctx, cfg) =>
    {
        cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath);
        cfg.AddJsonFile("appsettings.json", optional: true);
        cfg.AddEnvironmentVariables();

    })
    .ConfigureServices(ConfigureServices)
    .ConfigureLogging(log =>
    {
        log.AddConsole();
    })
    .Build();

¡Y ya está! Tenemos una aplicación de WPF ejecutándose bajo el custom host de .NET Core con todas sus ventajas, lo que es mucho mejor que la plantilla por defecto que incorpora Visual Studio, que usa “el estilo antiguo” del WPF clásico de .NET Framework :)

Si quieres, puedes invitarme a un café xD

eiximenis
ESCRITO POR
eiximenis
Compulsive Developer