I am using autofac in an UWP application. In my App
instance, I am setting up the dependency, like this:
public sealed partial class App
{
privat
Because UWP is responsible for the Page
's instantiation it removes the ability to explicitly inject dependencies into views.
The simplest approach would be to have an accessible service locator and register your dependencies with it.
public sealed partial class App {
public App() {
InitializeComponent();
Container = ConfigureServices();
Suspending += OnSuspending;
}
public static IContainer Container { get; set; }
private IContainer ConfigureServices() {
var containerBuilder = new ContainerBuilder();
// Registers all the platform-specific implementations of services.
containerBuilder.RegisterType<LoggingService>()
.As<ILoggingService>()
.SingleInstance();
containerBuilder.RegisterType<SQLitePlatformService>()
.As<ISQLitePlatformService>()
.SingleInstance();
containerBuilder.RegisterType<DiskStorageService>()
.As<IDiskStorageService>()
.SingleInstance();
containerBuilder.RegisterType<Facade>()
.As<IFacade>();
//...Register ViewModels as well
containerBuilder.RegisterType<SomePageViewModel>()
.AsSelf();
//...
var container = containerBuilder.Build();
return container;
}
//...
}
And then resolve dependencies as needed in the views.
internal sealed partial class SomePage {
public SomePage() {
InitializeComponent();
ViewModel = App.Container.Resolve<SomePageViewModel>();
this.DataContext = ViewModel;
}
public SomePageViewModel ViewModel { get; private set; }
protected override void OnNavigatedTo(NavigationEventArgs e) {
ViewModel.LoadAsync();
base.OnNavigatedTo(e);
}
}
Another more complicated way would be to use a convention base approach and tapping into the application's Frame navigation.
The plan would be to subscribe to the NavigatedTo
event
public interface INavigationService {
bool Navigate<TView>() where TView : Page;
bool Navigate<TView, TViewModel>(object parameter = null) where TView : Page;
}
public class NavigationService : INavigationService {
private readonly Frame frame;
private readonly IViewModelBinder viewModelBinder;
public NavigationService(IFrameProvider frameProvider, IViewModelBinder viewModelBinder) {
frame = frameProvider.CurrentFrame;
frame.Navigating += OnNavigating;
frame.Navigated += OnNavigated;
this.viewModelBinder = viewModelBinder;
}
protected virtual void OnNavigating(object sender, NavigatingCancelEventArgs e) { }
protected virtual void OnNavigated(object sender, NavigationEventArgs e) {
if (e.Content == null)
return;
var view = e.Content as Page;
if (view == null)
throw new ArgumentException("View '" + e.Content.GetType().FullName +
"' should inherit from Page or one of its descendents.");
viewModelBinder.Bind(view, e.Parameter);
}
public bool Navigate<TView>() where TView : Page {
return frame.Navigate(typeof(TView));
}
public bool Navigate<TView, TViewModel>(object parameter = null) where TView : Page {
var context = new NavigationContext(typeof(TViewModel), parameter);
return frame.Navigate(typeof(TView), context);
}
}
and once there using the navigation argument to pass the view model type to be resolved and data bind to the view.
public interface IViewModelBinder {
void Bind(FrameworkElement view, object viewModel);
}
public class ViewModelBinder : IViewModelBinder {
private readonly IServiceProvider serviceProvider;
public ViewModelBinder(IServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
public void Bind(FrameworkElement view, object viewModel) {
InitializeComponent(view);
if (view.DataContext != null)
return;
var context = viewModel as NavigationContext;
if (context != null) {
var viewModelType = context.ViewModelType;
if (viewModelType != null) {
viewModel = serviceProvider.GetService(viewModelType);
}
var parameter = context.Parameter;
//TODO: figure out what to do with parameter
}
view.DataContext = viewModel;
}
static void InitializeComponent(object element) {
var method = element.GetType().GetTypeInfo()
.GetDeclaredMethod("InitializeComponent");
method?.Invoke(element, null);
}
}
The Frame
is accessed via a wrapper service that extracts it from the current window
public interface IFrameProvider {
Frame CurrentFrame { get; }
}
public class DefaultFrameProvider : IFrameProvider {
public Frame CurrentFrame {
get {
return (Window.Current.Content as Frame);
}
}
}
And the following extension classes provide utility support
public static class ServiceProviderExtension {
/// <summary>
/// Get service of type <typeparamref name="TService"/> from the <see cref="IServiceProvider"/>.
/// </summary>
public static TService GetService<TService>(this IServiceProvider provider) {
return (TService)provider.GetService(typeof(TService));
}
/// <summary>
/// Get an enumeration of services of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>
/// </summary>
public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType) {
var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
return (IEnumerable<object>)provider.GetService(genericEnumerable);
}
/// <summary>
/// Get an enumeration of services of type <typeparamref name="TService"/> from the <see cref="IServiceProvider"/>.
/// </summary>
public static IEnumerable<TService> GetServices<TService>(this IServiceProvider provider) {
return provider.GetServices(typeof(TService)).Cast<TService>();
}
/// <summary>
/// Get service of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>.
/// </summary>
public static object GetRequiredService(this IServiceProvider provider, Type serviceType) {
if (provider == null) {
throw new ArgumentNullException("provider");
}
if (serviceType == null) {
throw new ArgumentNullException("serviceType");
}
var service = provider.GetService(serviceType);
if (service == null) {
throw new InvalidOperationException(string.Format("There is no service of type {0}", serviceType));
}
return service;
}
/// <summary>
/// Get service of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
/// </summary>
public static T GetRequiredService<T>(this IServiceProvider provider) {
if (provider == null) {
throw new ArgumentNullException("provider");
}
return (T)provider.GetRequiredService(typeof(T));
}
}
public class NavigationContext {
public NavigationContext(Type viewModelType, object parameter = null) {
ViewModelType = viewModelType;
Parameter = parameter;
}
public Type ViewModelType { get; private set; }
public object Parameter { get; private set; }
}
public static class NavigationExtensions {
public static bool Navigate<TView>(this Frame frame) where TView : Page {
return frame.Navigate(typeof(TView));
}
public static bool Navigate<TView, TViewModel>(this Frame frame, object parameter = null) where TView : Page {
var context = new NavigationContext(typeof(TViewModel), parameter);
return frame.Navigate(typeof(TView), context);
}
}
You could configure the application like you would before at start up but the container will be used to initialize the navigation service and it will handle the rest.
public sealed partial class App {
public App() {
InitializeComponent();
Container = ConfigureServices();
Suspending += OnSuspending;
}
public static IContainer Container { get; set; }
private IContainer ConfigureServices() {
//... code removed for brevity
containerBuilder
.RegisterType<DefaultFrameProvider>()
.As<IFrameProvider>()
.SingleInstance();
containerBuilder.RegisterType<ViewModelBinder>()
.As<IViewModelBinder>()
.SingleInstance();
containerBuilder.RegisterType<AutofacServiceProvider>()
.As<IServiceProvider>()
containerBuilder.RegisterType<NavigationService>()
.AsSelf()
.As<INavigationService>();
var container = containerBuilder.Build();
return container;
}
protected override void OnLaunched(LaunchActivatedEventArgs e) {
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null) {
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;
}
//Activating navigation service
var service = Container.Resolve<INavigationService>();
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<SomePage, SomePageViewModel>();
}
// Ensure the current window is active
Window.Current.Activate();
}
}
public class AutofacServiceProvider : IServiceProvider
public object GetService(Type serviceType) {
return App.Container.Resolve(serviceType);
}
}
//...
}
By using the above convention the Frame
extensions allow some generic navigation.
ContentFrame.Navigate<SomeOtherPage, SomeOtherPageViewModel>();
Views can be as simple as
internal sealed partial class SomePage {
public SomePage() {
InitializeComponent();
}
public SomePageViewModel ViewModel { get { return (SomePageViewModel)DataContext;} }
protected override void OnNavigatedTo(NavigationEventArgs e) {
ViewModel.LoadAsync();
base.OnNavigatedTo(e);
}
}
As the navigation service would also set the data context of the view after navigation.
Navigation can also be initiated by view models that have the INavigationService
injected
public class SomePageViewModel : ViewModel {
private readonly INavigationService navigation;
private readonly IFacade facade;
public SomePageViewModel(IFacade facade, INavigationService navigation) {
this.navigation = navigation;
this.facade = facade;
}
//...
public void GoToSomeOtherPage() {
navigation.Navigate<SomeOtherPage, SomeOtherPageViewModel>();
}
//...
}
Just to add to Nkosi's fantastic and deep question, when I create pages in UWP I use the following pattern:
private IDependency _dep1;
public Page()
{
_dep1 = ServiceLocator.Current.Resolve<IDependency>();
init();
}
public Page(IDependency dep1, ...)
{
_dep1 = dep1;
init();
}
private void init()
{
/* Do initialization here, i.e. InitializeComponent() */
}
The benefit that this gives is that it allows you to still write testable code because you inject your dependencies in your unit tests. The service locator only runs at run time.
As Nkosi points out, the Frame
is responsible ultimately for instantiation of the Page
via the Navigate
method. At the point that Microsoft exposes the ability to intercept or override the instantiation, it'll be possible to have a DI container do the instantiation. Until then, we're stuck with using a Service Locator pattern at runtime.
I'm very new to UWP (but a very old hand at .NET/WPF etc etc), so this might be hacky, but I by-passed the Frame.Navigate method altogether by putting the following at the top of my OnLaunched method:
if (Window.Current.Content == null)
{
var windowFrame = new Frame()
{
Content = new MainPage()
};
Window.Current.Content = windowFrame;
Window.Current.Activate();
return;
}
You then control the creation of your mainpage - if you want to support DI, then you can use something like:
var windowFrame = new Frame()
{
Content = container.GetInstance<MainPage>()
};
This then means that MainPage becomes your CompositionRoot and your DI flows from there.
My guess is that you then don't get any navigate back/forward functionality, although I may be wrong...my app is only single-page so I've not seen if that is a problem or not...
Instead of using a smelly locator or some other hack to intercept the frame's navigation, I recommend to avoid the Frame
as content host and also avoid Page
as content.
In WPF, the recommended view management should be based on the view-model-first pattern. Also in WPF it is not recommended to use the heavy Frame
as content host. Both recommendations also apply to UWP.
We should view a UWP application as single-page application (SPA) in terms of pages based on the Page
class and the hosting root frame.
This means MainPage.xaml
is only used as host for our custom page system, which is based on a ContentControl
, a set of page view models and a set of DataTemplate
definition for each page view model and a PageNavigationViewModel
class that controls the page navigation. MainPage
(or Page
) is the equivalent to the WPF Window
: the element tree root or visual host.
The following example shows the pattern by seetting up a basic two page application (a landing page and a settings page) using dependency injection with Autofac (which of course can be replaced with any other IoC framework).
The important detail is not the IoC framework or the IoC container configuration, but the way the application is structured to allow page navigation combined with dependency injection.
The Goal
The goal is to display a LandingPage
view (UserControl
) based on a LandingPageViewModel
and a SettingsPage
view based on a SettingsPageViewModel
.
All view model instances are created by the IoC container, while all associated views are instantiated implicitly by the UWP framework using DataTemplate
.
The example is structured into three sections:
There is some extra complexity like factories introduced due to depndency injection. In a real world example, we would depend on interfaces instead of concrete implementations (Dependencsy Inversion principle). For simplicity there are no interfaces used except those that are relevant to the infrastructure.
PageId.cs
Each view is identified by an enum. This makes selecting a page model e.g., via command parameter and refactoring easier. It also eliminates magic strings.
public enum PageId
{
Undefined = 0,
LandingPage,
SettingsPage
}
Factory delegates (Autofac specific details)
The delegates are required to allow Autofac to create factories. Other IoC frameworks may h ave a different requirement to generate factories. MEF for example uses the ExportFactory<T>
type as constructor depndency. The framework would then automatically generate the appropriate factory.
IoC generated factories allow dynamic type creation, where the instances are wired up according to the IoC container configuration. Factories are used to avoid passing around a reference to the IoC container (or even worse making the container a Singleton). Such practice is an anti-pattern, which contradicts the use of the IoC container.
The delagates should be added to the common namespace of the PageIndexFactory
and Bootstrapper
classes (see below) and marked as internal
.
private LandingPageModelFactory LandingPageModelFactory { get; }
private SettingsPageModelFactory SettingsPageModelFactory { get; }
PageIndexFactory.cs
The individual page view models are initialized by a PageIndexFactory
, which is injected into the PageNavigationViewModel
. The purpose is to create the navigation index. PageIndexFactory
makes use of delegate factories.
Every serious IoC framework supports auto-generation of factories. This way the IoC container is still able to wire up the dependencies (note that passing around the original IoC container instance is an anti-pattern).
public class PageIndexFactory
{
private LandingPageModelFactory LandingPageModelFactory { get; }
private SettingsPageModelFactory SettingsPageModelFactory { get; }
public PageIndexFactory(LandingPageModelFactory landingPageModelFactory,
SettingsPageModelFactory settingsPageModelFactory)
{
this.LandingPageModelFactory = landingPageModelFactory;
this.SettingsPageModelFactory = settingsPageModelFactory;
}
public Dictionary<PageId, IPageModel> CreateIndex()
{
var index = new Dictionary<PageId, IPageModel>()
{
{PageId.LandingPage, this.LandingPageModelFactory.Invoke()},
{PageId.SettingsPage, this.SettingsPageModelFactory.Invoke()}
};
return index;
}
}
PageNavigationViewModel.cs
This is the view model that handles the navigation. It exposes a SelectViewCommand
, which can be assigned to an ICommandSource
like a Button
. The CommandParameter
must be the PageId
, which actually selects the IPageModel
from the page index.
The PageNavigationViewModel
is assigned to the application's original MainPage
, which is the host of the custom navigation infrastructure.
The PageNavigationViewModel
exposes a SelectedView
property which holds a view model e.g., IPageModel
. This property is bound to the hosting ContentControl.Content
property.
public class PageNavigationViewModel : INotifyPropertyChanged
{
public PageNavigationViewModel(PageIndexFactory pageIndexFactory)
{
this.PageIndex = pageIndexFactory.CreateIndex();
if (this.PageIndex.TryGetValue(PageId.LandingPage, out IPageModel welcomePageModel))
{
this.SelectedView = welcomePageModel;
}
}
private void ExecuteSelectPage(object commandParameter)
{
var pageId = (PageId) commandParameter;
if (this.PageIndex.TryGetValue(pageId, out IPageModel selectedPageModel))
{
this.SelectedView = selectedPageModel;
}
}
public ICommand SelectViewCommand => new RelayCommand(ExecuteSelectPage);
private Dictionary<PageId, IPageModel> PageIndex { get; }
private IPageModel selectedView;
public IPageModel SelectedView
{
get => this.selectedView;
set
{
this.selectedView = value;
OnPropertyChanged();
}
}
}
IPageModel.cs
The interface, which must be implemented by the individual page view models.
public interface IPageModel : INotifyPropertyChanged
{
string PageTitle { get; }
}
INavigationHost.cs
This interface is implemented by the application Page
host e.g., MainPage
. It allows to assign the PageNavigationViewModel
anonymously.
interface INavigationHost
{
PageNavigationViewModel NavigationViewModel { get; set; }
}
MainPage.xaml.cs
The host of the custom navigation infrastructure. This instance is created via reflection by the hosting Frame
. We use the implementation of INavigationHost
to initialze this class with an instance of PageNavigationviewModel
(see App.xaml.cs* below).
MainPage
is the only class that is not instantiated by the IoC container. As this class has no reponsibilities, except exposing the PageNavigationViewModel
and hosting the ContentControl
(to host the real pages/views), it will have no relevant dependencies.
public sealed partial class MainPage : Page, INavigationHost
{
private PageNavigationViewModel navigationViewModel;
public PageNavigationViewModel NavigationViewModel
{
get => this.navigationViewModel;
set
{
this.navigationViewModel = value;
this.DataContext = this.NavigationViewModel;
}
}
public MainPage()
{
this.InitializeComponent();
}
}
MainPage.xaml
Hosts the real view and the DataTemplateSelector
for the ContentControl
. host and optionally the navigation elements like navigation buttons.
The ContentControl
loads the DataTemplate
, that is associated with the IPageModel
instance. This DataTemplate
contains e.g., a UserControl
, which hosts the actual page content.
The DataContext
of each view is set by the ContentControl
and is the current Content
(which is the PageNavigationViewModel.SelectedView
).
<Page>
<Page.Resources>
<local:PageTemplateSelector x:Key="PageTemplateSelector">
<local:PageTemplateSelector.DataTemplateCollection>
<DataTemplate x:DataType="local:LandingPageViewModel"
local:Element.DataType="local:LandingPageViewModel">
<local:LandingPage />
</DataTemplate>
<DataTemplate x:DataType="local:SettingsPageViewModel"
local:Element.DataType="local:SettingsPageViewModel">
<local:SettingsPage />
</DataTemplate>
</local:PageTemplateSelector.DataTemplateCollection>
</local:PageTemplateSelector>
</Page.Resources>
<StackPanel>
<!-- Optional navigation section -->
<Button Content="Show Settings Page"
Command="{x:Bind NavigationViewModel.SelectViewCommand}">
<Button.CommandParameter>
<local:PageId>SettingsPage</local:PageId>
</Button.CommandParameter>
</Button>
<Button Content="Show Welcome Page"
Command="{x:Bind NavigationViewModel.SelectViewCommand}">
<Button.CommandParameter>
<local:PageId>LandingPage</local:PageId>
</Button.CommandParameter>
</Button>
<!-- The host of the views -->
<ContentControl Content="{x:Bind NavigationViewModel.SelectedView, Mode=OneWay}"
ContentTemplateSelector="{StaticResource PageTemplateSelector}" />
</StackPanel>
</Page>
PageTemplateSelector.cs
UWP does not support implicit data templates like WPF. Therefore we have to use a template selector that is assigned to the hosting ContentControl
. ContentControl.Content
will hold the PageNavigationViewModel.SelectedViw
view model and the PageTemplateSelector
will select the matching DataTemplate
. Since DataTemplate
has not DataType
property (opposed to the WPF version), we have to introduce an attached property to hoöd this value. Note, that x:DataType
is a compiler directive and not accessible by code.
public class PageTemplateSelector : DataTemplateSelector
{
public DataTemplateCollection DataTemplateCollection { get; set; }
#region Overrides of DataTemplateSelector
public PageTemplateSelector()
{
this.DataTemplateCollection = new DataTemplateCollection();
}
/// <inheritdoc />
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item != null
&& this.DataTemplateCollection.First(template => Element.GetDataType(template) == item.GetType()) is DataTemplate dataTemplate)
{
return dataTemplate;
}
return base.SelectTemplateCore(item, container);
}
/// <inheritdoc />
protected override DataTemplate SelectTemplateCore(object item)
{
if (item != null
&& this.DataTemplateCollection.First(template => Element.GetDataType(template) == item.GetType()) is
DataTemplate dataTemplate)
{
return dataTemplate;
}
return base.SelectTemplateCore(item);
}
#endregion
}
DataTemplateCollection.cs
The XAML collection to hold the page view DataTemples
definitions. It is used by the PageTemplateSelector
.
public class DataTemplateCollection : List<DataTemplate>
{}
Element.cs
Class that defines the attached DataType
property, which is used by the PageTemplateSelector
to filter the DataTemplate
definitions. This attached property should be therefore set on every DataTemplate
that is associated with a page view model.
public class Element : DependencyObject
{
#region Type attached property
public static readonly DependencyProperty DataTypeProperty = DependencyProperty.RegisterAttached(
"DataType",
typeof(Type),
typeof(Element),
new PropertyMetadata(default(Type)));
public static void SetDataType([NotNull] DependencyObject attachingElement, Type value) => attachingElement.SetValue(Element.DataTypeProperty, value);
public static Type GetDataType([NotNull] DependencyObject attachingElement) =>
(Type) attachingElement.GetValue(Element.DataTypeProperty);
#endregion
}
All dependencies are resolved by the IoC container. The PageIndexfactory
controls the instantiation via auto-generated factories.
To keep it short, the following example implementation only shows the LandingPage
related UserControl
and LandingPageViewModel
. The same pattern applies to the SettingsPage
and SettingsPageViewModel
.
LandingPageViewModel.cs
The view model for the welcome view.
public class LandingPageViewModel : IPageModel, INotifyPropertyChanged
{
public LandingPageViewModel(IFacade someExampleDependency)
{
this.Facade = someExampleDependency;
this.PageTitle = "Welcome Page";
}
public string PageTitle { get; }
private string IFacade Facade { get; }
}
LandingPage.xaml.cs
The DataContext
is set implicitly by the hosting ContentControl
.
public sealed partial class LandingPage : UserControl
{
// Enable x:Bind
public LandingPageViewModel ViewModel { get; private set; }
public LandingPage()
{
this.InitializeComponent();
// Delegate the DataContext to the ViewModel property to enable x:Bind
this.DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
this.ViewModel = args.NewValue as LandingPageViewModel;
}
}
LandingPage.xaml
The view that is displayed via DataTemplate
by the ContentControl
.
<UserControl>
<Grid>
<TextBlock Text="{x:Bind ViewModel.PageTitle}" />
</Grid>
</UserControl>
The following section shows how to set up the IoC container and bootstrap the UWP application.
Bootstrapper.cs
Encapsulates the IoC container configuration and bootstrapping of the application.
internal sealed class Bootstrapper
{
internal static void InitializeApplication(INavigationHost navigationHost)
{
// Don't use the dependency container outside this class
using (IContainer services = Bootstrapper.BuildDependencies())
{
PageNavigationViewModel navigationViewModel = services.Resolve<PageNavigationViewModel>();
navigationHost.NavigationViewModel = navigationViewModel;
}
}
internal static IContainer BuildDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterType<PageIndexFactory>();
builder.RegisterType<PageNavigationViewModel>();
builder.RegisterType<LandingPageViewModel>();
builder.RegisterType<SettingsPageViewModel>();
// Dependency to address your question
builder.RegisterType<Facade>().As<IFacade>();
// Don't forget to dispose the IContainer instance (caller's responsibility)
return builder.Build();
}
}
App.xaml.cs
Bootstarpping the UWP application.
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <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);
/****************** Build depndencies and initialize navigation *************/
if (rootFrame.Content is INavigationHost navigationHost)
{
Bootstrapper.InitializeApplication(navigationHost);
}
}
// Ensure the current window is active
Window.Current.Activate();
}
}
...
}