WpfFacade Class - rsmart8452/Wix4BurnTutorial GitHub Wiki

Bootstrapper.Models.WpfFacade

The facade pattern allows us to expose the few features of the UI framework that the app needs. Ideally, it should be easy to use.

Initialize method

We have to wait until the bootstrapper is running before building the UI, and that is what the Initialize method is for.

Initialize associates a Dispatcher with the current thread. Subscribing to UnhandledException gives us a handler for any exceptions that the various background threads could throw during the install.

_dispatcher = Dispatcher.CurrentDispatcher;
_dispatcher.UnhandledException += Dispatcher_UnhandledException; 

Next, a shell window and an HWND for it are created. Starting with WiX 4, a valid window handle is required even when there is no UI displayed, so we'll supply that.

_shell = new ShellView();
ShellWindowHandle = new WindowInteropHelper(_shell).EnsureHandle();

The Initialize method also ensures the Dispatcher's InvokeShutdown is called when the window is closed. This will stop the Windows message loop and allow the BA to shut down.

_shell.Closed += (sender, e) => _dispatcher.InvokeShutdown();

That's all the initialization needed if the install is running silently. But if the UI is going to be displayed, we want to associate VMs with the views. Here, a ShellViewModel is injected into the shell window, and then a progress handler is assigned to the facade's ProgressReporter. The Progress<T> class simplifies this by dispatching the processing of these reports to the message loop.

if (IsUiShown)
{
    _shellVm = new ShellViewModel(model);
    _shell.DataContext = _shellVm;
    ProgressReporter = new Progress<ProgressReport>(r => _shellVm.ProgressVm.ProcessProgressReport(r));
}

RunMessageLoop method

When running the BA, I like to get the UI displayed quickly, so Initialize is called as early as possible. After that, I get the detect phase running. Finally, I start the Windows message loop with the RunMessageLoop method on the facade.

With WPF, we simply call Dispatcher.Run. If you were to use WinForms, you'd call Application.Run.

Dispatcher.Run();

Dispatch method

The Dispatch method queues the given action on the UI thread. By default, it will wait until the action completes before returning to the caller, but it also allows the action to be queued for the message loop to handle later.

OnDetectPhaseComplete method

When the BA starts, the UI is displayed, then the detect phase is started, and finally the message loop is started. At this point the UI is responsive to user input, but the detect phase is still running. It makes a lot of sense that the user can't do much with the UI until detect completes. That's what the OnDetectPhaseComplete event handler is for. It's triggered once detect completes.

  • The UI can harvest whatever state was gathered during detect and display it.
  • The actions to suggest to the user, like install, uninstall, update, etc., can be determined.
  • UI elements can be enabled to allow the user to interact with the installer.

The BA can then transition into waiting for the user to start the plan phase. While the BA waits, the UI can walk the user through gathering whatever info is needed to successfully install the bundle. Once the user starts the plan phase, the UI should transition into reporting progress of the install.

Important

The user should be prevented from exiting the app while the plan and apply phases run. Instead, they should be allowed to cancel, giving the bundle the opportunity to roll the machine back to a stable state.

OnApplyPhaseComplete method

When the apply phase completes, the UI will need to be notified again. This event handler is triggered when that happens.

  • Display success or failure
  • Allow the user to close the app

Previous: Model Class || Next: AppState Class

⚠️ **GitHub.com Fallback** ⚠️