aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-05-06 18:55:18 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-05-11 19:11:19 -0500
commit8c27fbe1bc8a6d83859aead2105d6d528c307726 (patch)
tree1e26f482e119fd9bcb348412766eaba31bf4fc84 /src/test
parent3c88529e8e29f9763a6830f8d3ac29cd56a4cb33 (diff)
downloadwix-8c27fbe1bc8a6d83859aead2105d6d528c307726.tar.gz
wix-8c27fbe1bc8a6d83859aead2105d6d528c307726.tar.bz2
wix-8c27fbe1bc8a6d83859aead2105d6d528c307726.zip
Move WixToolset.WixBA into test/burn and use new PackageReferences.
Diffstat (limited to 'src/test')
-rw-r--r--src/test/burn/WixToolset.WixBA/BrowserProperties.cs40
-rw-r--r--src/test/burn/WixToolset.WixBA/Hresult.cs22
-rw-r--r--src/test/burn/WixToolset.WixBA/InstallationViewModel.cs683
-rw-r--r--src/test/burn/WixToolset.WixBA/Model.cs132
-rw-r--r--src/test/burn/WixToolset.WixBA/NewsItem.cs18
-rw-r--r--src/test/burn/WixToolset.WixBA/ProgressViewModel.cs245
-rw-r--r--src/test/burn/WixToolset.WixBA/PropertyNotifyBase.cs59
-rw-r--r--src/test/burn/WixToolset.WixBA/RelayCommand.cs45
-rw-r--r--src/test/burn/WixToolset.WixBA/Resources/logo-black-hollow.pngbin0 -> 47472 bytes
-rw-r--r--src/test/burn/WixToolset.WixBA/Resources/logo-white-hollow.pngbin0 -> 60557 bytes
-rw-r--r--src/test/burn/WixToolset.WixBA/RootView.xaml420
-rw-r--r--src/test/burn/WixToolset.WixBA/RootView.xaml.cs51
-rw-r--r--src/test/burn/WixToolset.WixBA/RootViewModel.cs202
-rw-r--r--src/test/burn/WixToolset.WixBA/Styles.xaml194
-rw-r--r--src/test/burn/WixToolset.WixBA/UpdateViewModel.cs207
-rw-r--r--src/test/burn/WixToolset.WixBA/WindowProperties.cs65
-rw-r--r--src/test/burn/WixToolset.WixBA/WixBA.BootstrapperCore.config16
-rw-r--r--src/test/burn/WixToolset.WixBA/WixBA.cs229
-rw-r--r--src/test/burn/WixToolset.WixBA/WixBAFactory.cs17
-rw-r--r--src/test/burn/WixToolset.WixBA/WixDistribution.cs118
-rw-r--r--src/test/burn/WixToolset.WixBA/WixToolset.WixBA.csproj50
21 files changed, 2813 insertions, 0 deletions
diff --git a/src/test/burn/WixToolset.WixBA/BrowserProperties.cs b/src/test/burn/WixToolset.WixBA/BrowserProperties.cs
new file mode 100644
index 00000000..c8fb6177
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/BrowserProperties.cs
@@ -0,0 +1,40 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System.Windows;
6 using System.Windows.Controls;
7
8 /// <summary>
9 /// Dependency Properties to support using a WebBrowser object.
10 /// </summary>
11 class BrowserProperties
12 {
13 /// <summary>
14 /// Dependency Propery used to pass an HTML string to the webBrowser object.
15 /// </summary>
16 public static readonly DependencyProperty HtmlDocProperty =
17 DependencyProperty.RegisterAttached("HtmlDoc", typeof(string), typeof(BrowserProperties), new PropertyMetadata(OnHtmlDocChanged));
18
19 public static string GetHtmlDoc(DependencyObject dependencyObject)
20 {
21 return (string)dependencyObject.GetValue(HtmlDocProperty);
22 }
23
24 public static void SetHtmlDoc(DependencyObject dependencyObject, string htmldoc)
25 {
26 dependencyObject.SetValue(HtmlDocProperty, htmldoc);
27 }
28
29 /// <summary>
30 /// Event handler that passes the HtmlDoc Dependency Property to MavigateToString method.
31 /// </summary>
32 /// <param name="d"></param>
33 /// <param name="e"></param>
34 private static void OnHtmlDocChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
35 {
36 var webBrowser = (WebBrowser)d;
37 webBrowser.NavigateToString((string)e.NewValue);
38 }
39 }
40}
diff --git a/src/test/burn/WixToolset.WixBA/Hresult.cs b/src/test/burn/WixToolset.WixBA/Hresult.cs
new file mode 100644
index 00000000..a5e552ac
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/Hresult.cs
@@ -0,0 +1,22 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6
7 /// <summary>
8 /// Utility class to work with HRESULTs
9 /// </summary>
10 internal class Hresult
11 {
12 /// <summary>
13 /// Determines if an HRESULT was a success code or not.
14 /// </summary>
15 /// <param name="status">HRESULT to verify.</param>
16 /// <returns>True if the status is a success code.</returns>
17 public static bool Succeeded(int status)
18 {
19 return status >= 0;
20 }
21 }
22}
diff --git a/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
new file mode 100644
index 00000000..2beebd02
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
@@ -0,0 +1,683 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Linq;
9 using System.Reflection;
10 using System.Windows;
11 using System.Windows.Input;
12 using IO = System.IO;
13 using WixToolset.Mba.Core;
14
15 /// <summary>
16 /// The states of detection.
17 /// </summary>
18 public enum DetectionState
19 {
20 Absent,
21 Present,
22 Newer,
23 }
24
25 /// <summary>
26 /// The states of installation.
27 /// </summary>
28 public enum InstallationState
29 {
30 Initializing,
31 Detecting,
32 Waiting,
33 Planning,
34 Applying,
35 Applied,
36 Failed,
37 }
38
39 /// <summary>
40 /// The model of the installation view in WixBA.
41 /// </summary>
42 public class InstallationViewModel : PropertyNotifyBase
43 {
44 private RootViewModel root;
45
46 private Dictionary<string, int> downloadRetries;
47 private bool downgrade;
48 private string downgradeMessage;
49
50 private ICommand licenseCommand;
51 private ICommand launchHomePageCommand;
52 private ICommand launchNewsCommand;
53 private ICommand launchVSExtensionPageCommand;
54 private ICommand installCommand;
55 private ICommand repairCommand;
56 private ICommand uninstallCommand;
57 private ICommand openLogCommand;
58 private ICommand openLogFolderCommand;
59 private ICommand tryAgainCommand;
60
61 private string message;
62 private DateTime cachePackageStart;
63 private DateTime executePackageStart;
64
65 /// <summary>
66 /// Creates a new model of the installation view.
67 /// </summary>
68 public InstallationViewModel(RootViewModel root)
69 {
70 this.root = root;
71 this.downloadRetries = new Dictionary<string, int>();
72
73 this.root.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.RootPropertyChanged);
74
75 WixBA.Model.Bootstrapper.DetectBegin += this.DetectBegin;
76 WixBA.Model.Bootstrapper.DetectRelatedBundle += this.DetectedRelatedBundle;
77 WixBA.Model.Bootstrapper.DetectComplete += this.DetectComplete;
78 WixBA.Model.Bootstrapper.PlanPackageBegin += this.PlanPackageBegin;
79 WixBA.Model.Bootstrapper.PlanComplete += this.PlanComplete;
80 WixBA.Model.Bootstrapper.ApplyBegin += this.ApplyBegin;
81 WixBA.Model.Bootstrapper.CacheAcquireBegin += this.CacheAcquireBegin;
82 WixBA.Model.Bootstrapper.CacheAcquireResolving += this.CacheAcquireResolving;
83 WixBA.Model.Bootstrapper.CacheAcquireComplete += this.CacheAcquireComplete;
84 WixBA.Model.Bootstrapper.ExecutePackageBegin += this.ExecutePackageBegin;
85 WixBA.Model.Bootstrapper.ExecutePackageComplete += this.ExecutePackageComplete;
86 WixBA.Model.Bootstrapper.Error += this.ExecuteError;
87 WixBA.Model.Bootstrapper.ApplyComplete += this.ApplyComplete;
88 }
89
90 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
91 {
92 if (("DetectState" == e.PropertyName) || ("InstallState" == e.PropertyName))
93 {
94 base.OnPropertyChanged("RepairEnabled");
95 base.OnPropertyChanged("InstallEnabled");
96 base.OnPropertyChanged("IsComplete");
97 base.OnPropertyChanged("IsSuccessfulCompletion");
98 base.OnPropertyChanged("IsFailedCompletion");
99 base.OnPropertyChanged("StatusText");
100 base.OnPropertyChanged("UninstallEnabled");
101 }
102 }
103
104 /// <summary>
105 /// Gets the version for the application.
106 /// </summary>
107 public string Version
108 {
109 get { return String.Concat("v", WixBA.Model.Version.ToString()); }
110 }
111
112 /// <summary>
113 /// The Publisher of this bundle.
114 /// </summary>
115 public string Publisher
116 {
117 get
118 {
119 string company = "[AssemblyCompany]";
120 return WixDistribution.ReplacePlaceholders(company, typeof(WixBA).Assembly);
121 }
122 }
123
124 /// <summary>
125 /// The Publisher of this bundle.
126 /// </summary>
127 public string SupportUrl
128 {
129 get
130 {
131 return WixDistribution.SupportUrl;
132 }
133 }
134 public string VSExtensionUrl
135 {
136 get
137 {
138 return WixDistribution.VSExtensionsLandingUrl;
139 }
140 }
141
142 public string Message
143 {
144 get
145 {
146 return this.message;
147 }
148
149 set
150 {
151 if (this.message != value)
152 {
153 this.message = value;
154 base.OnPropertyChanged("Message");
155 }
156 }
157 }
158
159 /// <summary>
160 /// Gets and sets whether the view model considers this install to be a downgrade.
161 /// </summary>
162 public bool Downgrade
163 {
164 get
165 {
166 return this.downgrade;
167 }
168
169 set
170 {
171 if (this.downgrade != value)
172 {
173 this.downgrade = value;
174 base.OnPropertyChanged("Downgrade");
175 }
176 }
177 }
178
179 public string DowngradeMessage
180 {
181 get
182 {
183 return this.downgradeMessage;
184 }
185 set
186 {
187 if (this.downgradeMessage != value)
188 {
189 this.downgradeMessage = value;
190 base.OnPropertyChanged("DowngradeMessage");
191 }
192 }
193 }
194
195 public ICommand LaunchHomePageCommand
196 {
197 get
198 {
199 if (this.launchHomePageCommand == null)
200 {
201 this.launchHomePageCommand = new RelayCommand(param => WixBA.LaunchUrl(this.SupportUrl), param => true);
202 }
203
204 return this.launchHomePageCommand;
205 }
206 }
207
208 public ICommand LaunchNewsCommand
209 {
210 get
211 {
212 if (this.launchNewsCommand == null)
213 {
214 this.launchNewsCommand = new RelayCommand(param => WixBA.LaunchUrl(WixDistribution.NewsUrl), param => true);
215 }
216
217 return this.launchNewsCommand;
218 }
219 }
220
221 public ICommand LaunchVSExtensionPageCommand
222 {
223 get
224 {
225 if (this.launchVSExtensionPageCommand == null)
226 {
227 this.launchVSExtensionPageCommand = new RelayCommand(param => WixBA.LaunchUrl(WixDistribution.VSExtensionsLandingUrl), param => true);
228 }
229
230 return this.launchVSExtensionPageCommand;
231 }
232 }
233
234 public ICommand LicenseCommand
235 {
236 get
237 {
238 if (this.licenseCommand == null)
239 {
240 this.licenseCommand = new RelayCommand(param => this.LaunchLicense(), param => true);
241 }
242
243 return this.licenseCommand;
244 }
245 }
246
247 public bool LicenseEnabled
248 {
249 get { return this.LicenseCommand.CanExecute(this); }
250 }
251
252 public ICommand CloseCommand
253 {
254 get { return this.root.CloseCommand; }
255 }
256
257 public bool IsComplete
258 {
259 get { return IsSuccessfulCompletion || IsFailedCompletion; }
260 }
261
262 public bool IsSuccessfulCompletion
263 {
264 get { return InstallationState.Applied == this.root.InstallState; }
265 }
266
267 public bool IsFailedCompletion
268 {
269 get { return InstallationState.Failed == this.root.InstallState; }
270 }
271
272 public ICommand InstallCommand
273 {
274 get
275 {
276 if (this.installCommand == null)
277 {
278 this.installCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Install), param => this.root.DetectState == DetectionState.Absent && this.root.InstallState == InstallationState.Waiting);
279 }
280
281 return this.installCommand;
282 }
283 }
284
285 public bool InstallEnabled
286 {
287 get { return this.InstallCommand.CanExecute(this); }
288 }
289
290 public ICommand RepairCommand
291 {
292 get
293 {
294 if (this.repairCommand == null)
295 {
296 this.repairCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Repair), param => this.root.DetectState == DetectionState.Present && this.root.InstallState == InstallationState.Waiting);
297 }
298
299 return this.repairCommand;
300 }
301 }
302
303 public bool RepairEnabled
304 {
305 get { return this.RepairCommand.CanExecute(this); }
306 }
307
308 public ICommand UninstallCommand
309 {
310 get
311 {
312 if (this.uninstallCommand == null)
313 {
314 this.uninstallCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Uninstall), param => this.root.DetectState == DetectionState.Present && this.root.InstallState == InstallationState.Waiting);
315 }
316
317 return this.uninstallCommand;
318 }
319 }
320
321 public bool UninstallEnabled
322 {
323 get { return this.UninstallCommand.CanExecute(this); }
324 }
325
326 public ICommand OpenLogCommand
327 {
328 get
329 {
330 if (this.openLogCommand == null)
331 {
332 this.openLogCommand = new RelayCommand(param => WixBA.OpenLog(new Uri(WixBA.Model.Engine.GetVariableString("WixBundleLog"))));
333 }
334 return this.openLogCommand;
335 }
336 }
337
338 public ICommand OpenLogFolderCommand
339 {
340 get
341 {
342 if (this.openLogFolderCommand == null)
343 {
344 string logFolder = IO.Path.GetDirectoryName(WixBA.Model.Engine.GetVariableString("WixBundleLog"));
345 this.openLogFolderCommand = new RelayCommand(param => WixBA.OpenLogFolder(logFolder));
346 }
347 return this.openLogFolderCommand;
348 }
349 }
350
351 public ICommand TryAgainCommand
352 {
353 get
354 {
355 if (this.tryAgainCommand == null)
356 {
357 this.tryAgainCommand = new RelayCommand(param =>
358 {
359 this.root.Canceled = false;
360 WixBA.Plan(WixBA.Model.PlannedAction);
361 }, param => IsFailedCompletion);
362 }
363
364 return this.tryAgainCommand;
365 }
366 }
367
368 public string StatusText
369 {
370 get
371 {
372 switch(this.root.InstallState)
373 {
374 case InstallationState.Applied:
375 return "Complete";
376 case InstallationState.Failed:
377 return this.root.Canceled ? "Cancelled" : "Failed";
378 default:
379 return "Unknown"; // this shouldn't be shown in the UI.
380 }
381 }
382 }
383
384 /// <summary>
385 /// Launches the license in the default viewer.
386 /// </summary>
387 private void LaunchLicense()
388 {
389 string folder = IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
390 WixBA.LaunchUrl(IO.Path.Combine(folder, "License.txt"));
391 }
392
393 private void DetectBegin(object sender, DetectBeginEventArgs e)
394 {
395 this.root.DetectState = e.Installed ? DetectionState.Present : DetectionState.Absent;
396 WixBA.Model.PlannedAction = LaunchAction.Unknown;
397 }
398
399 private void DetectedRelatedBundle(object sender, DetectRelatedBundleEventArgs e)
400 {
401 if (e.Operation == RelatedOperation.Downgrade)
402 {
403 this.Downgrade = true;
404 }
405
406 if (!WixBA.Model.BAManifest.Bundle.Packages.ContainsKey(e.ProductCode))
407 {
408 WixBA.Model.BAManifest.Bundle.AddRelatedBundleAsPackage(e);
409 }
410 }
411
412 private void DetectComplete(object sender, DetectCompleteEventArgs e)
413 {
414 // Parse the command line string before any planning.
415 this.ParseCommandLine();
416 this.root.InstallState = InstallationState.Waiting;
417
418 if (LaunchAction.Uninstall == WixBA.Model.Command.Action &&
419 ResumeType.Arp != WixBA.Model.Command.Resume) // MSI and WixStdBA require some kind of confirmation before proceeding so WixBA should, too.
420 {
421 WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for uninstall");
422 WixBA.Plan(LaunchAction.Uninstall);
423 }
424 else if (Hresult.Succeeded(e.Status))
425 {
426 if (this.Downgrade)
427 {
428 this.root.DetectState = DetectionState.Newer;
429 var relatedPackages = WixBA.Model.BAManifest.Bundle.Packages.Values.Where(p => p.Type == PackageType.UpgradeBundle);
430 var installedVersion = relatedPackages.Any() ? new Version(relatedPackages.Max(p => p.Version)) : null;
431 if (installedVersion != null && installedVersion < new Version(4, 1) && installedVersion.Build > 10)
432 {
433 this.DowngradeMessage = "You must uninstall WiX v" + installedVersion + " before you can install this.";
434 }
435 else
436 {
437 this.DowngradeMessage = "There is already a newer version of WiX installed on this machine.";
438 }
439 }
440
441 if (LaunchAction.Layout == WixBA.Model.Command.Action)
442 {
443 WixBA.PlanLayout();
444 }
445 else if (WixBA.Model.Command.Display != Display.Full)
446 {
447 // If we're not waiting for the user to click install, dispatch plan with the default action.
448 WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for non-interactive mode.");
449 WixBA.Plan(WixBA.Model.Command.Action);
450 }
451 }
452 else
453 {
454 this.root.InstallState = InstallationState.Failed;
455 }
456
457 // Force all commands to reevaluate CanExecute.
458 // InvalidateRequerySuggested must be run on the UI thread.
459 root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested));
460 }
461
462 private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e)
463 {
464 // If we're able to run our BA, we don't want to install the .NET Framework since the framework on the machine is already good enough.
465 if ( e.PackageId.StartsWith("NetFx4", StringComparison.OrdinalIgnoreCase))
466 {
467 e.State = RequestState.None;
468 }
469 }
470
471 private void PlanComplete(object sender, PlanCompleteEventArgs e)
472 {
473 if (Hresult.Succeeded(e.Status))
474 {
475 this.root.PreApplyState = this.root.InstallState;
476 this.root.InstallState = InstallationState.Applying;
477 WixBA.Model.Engine.Apply(this.root.ViewWindowHandle);
478 }
479 else
480 {
481 this.root.InstallState = InstallationState.Failed;
482 }
483 }
484
485 private void ApplyBegin(object sender, ApplyBeginEventArgs e)
486 {
487 this.downloadRetries.Clear();
488 }
489
490 private void CacheAcquireBegin(object sender, CacheAcquireBeginEventArgs e)
491 {
492 this.cachePackageStart = DateTime.Now;
493 }
494
495 private void CacheAcquireResolving(object sender, CacheAcquireResolvingEventArgs e)
496 {
497 if (e.Action == CacheResolveOperation.Download && !this.downloadRetries.ContainsKey(e.PackageOrContainerId))
498 {
499 this.downloadRetries.Add(e.PackageOrContainerId, 0);
500 }
501 }
502
503 private void CacheAcquireComplete(object sender, CacheAcquireCompleteEventArgs e)
504 {
505 this.AddPackageTelemetry("Cache", e.PackageOrContainerId ?? String.Empty, DateTime.Now.Subtract(this.cachePackageStart).TotalMilliseconds, e.Status);
506
507 if (e.Status < 0 && this.downloadRetries.TryGetValue(e.PackageOrContainerId, out var retries) && retries < 3)
508 {
509 this.downloadRetries[e.PackageOrContainerId] = retries + 1;
510 switch (e.Status)
511 {
512 case -2147023294: //HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT)
513 case -2147024894: //HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
514 case -2147012889: //HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED)
515 break;
516 default:
517 e.Action = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION.Retry;
518 break;
519 }
520 }
521 }
522
523 private void ExecutePackageBegin(object sender, ExecutePackageBeginEventArgs e)
524 {
525 lock (this)
526 {
527 this.executePackageStart = e.ShouldExecute ? DateTime.Now : DateTime.MinValue;
528 }
529 }
530
531 private void ExecutePackageComplete(object sender, ExecutePackageCompleteEventArgs e)
532 {
533 lock (this)
534 {
535 if (DateTime.MinValue < this.executePackageStart)
536 {
537 this.AddPackageTelemetry("Execute", e.PackageId ?? String.Empty, DateTime.Now.Subtract(this.executePackageStart).TotalMilliseconds, e.Status);
538 this.executePackageStart = DateTime.MinValue;
539 }
540 }
541 }
542
543 private void ExecuteError(object sender, ErrorEventArgs e)
544 {
545 lock (this)
546 {
547 if (!this.root.Canceled)
548 {
549 // If the error is a cancel coming from the engine during apply we want to go back to the preapply state.
550 if (InstallationState.Applying == this.root.InstallState && (int)Error.UserCancelled == e.ErrorCode)
551 {
552 this.root.InstallState = this.root.PreApplyState;
553 }
554 else
555 {
556 this.Message = e.ErrorMessage;
557
558 if (Display.Full == WixBA.Model.Command.Display)
559 {
560 // On HTTP authentication errors, have the engine try to do authentication for us.
561 if (ErrorType.HttpServerAuthentication == e.ErrorType || ErrorType.HttpProxyAuthentication == e.ErrorType)
562 {
563 e.Result = Result.TryAgain;
564 }
565 else // show an error dialog.
566 {
567 MessageBoxButton msgbox = MessageBoxButton.OK;
568 switch (e.UIHint & 0xF)
569 {
570 case 0:
571 msgbox = MessageBoxButton.OK;
572 break;
573 case 1:
574 msgbox = MessageBoxButton.OKCancel;
575 break;
576 // There is no 2! That would have been MB_ABORTRETRYIGNORE.
577 case 3:
578 msgbox = MessageBoxButton.YesNoCancel;
579 break;
580 case 4:
581 msgbox = MessageBoxButton.YesNo;
582 break;
583 // default: stay with MBOK since an exact match is not available.
584 }
585
586 MessageBoxResult result = MessageBoxResult.None;
587 WixBA.View.Dispatcher.Invoke((Action)delegate()
588 {
589 result = MessageBox.Show(WixBA.View, e.ErrorMessage, "WiX Toolset", msgbox, MessageBoxImage.Error);
590 }
591 );
592
593 // If there was a match from the UI hint to the msgbox value, use the result from the
594 // message box. Otherwise, we'll ignore it and return the default to Burn.
595 if ((e.UIHint & 0xF) == (int)msgbox)
596 {
597 e.Result = (Result)result;
598 }
599 }
600 }
601 }
602 }
603 else // canceled, so always return cancel.
604 {
605 e.Result = Result.Cancel;
606 }
607 }
608 }
609
610 private void ApplyComplete(object sender, ApplyCompleteEventArgs e)
611 {
612 WixBA.Model.Result = e.Status; // remember the final result of the apply.
613
614 // Set the state to applied or failed unless the state has already been set back to the preapply state
615 // which means we need to show the UI as it was before the apply started.
616 if (this.root.InstallState != this.root.PreApplyState)
617 {
618 this.root.InstallState = Hresult.Succeeded(e.Status) ? InstallationState.Applied : InstallationState.Failed;
619 }
620
621 // If we're not in Full UI mode, we need to alert the dispatcher to stop and close the window for passive.
622 if (Display.Full != WixBA.Model.Command.Display)
623 {
624 // If its passive, send a message to the window to close.
625 if (Display.Passive == WixBA.Model.Command.Display)
626 {
627 WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window for non-interactive install");
628 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
629 }
630 else
631 {
632 WixBA.Dispatcher.InvokeShutdown();
633 }
634 return;
635 }
636 else if (Hresult.Succeeded(e.Status) && LaunchAction.UpdateReplace == WixBA.Model.PlannedAction) // if we successfully applied an update close the window since the new Bundle should be running now.
637 {
638 WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window since update successful.");
639 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
640 return;
641 }
642 else if (root.AutoClose)
643 {
644 // Automatically closing since the user clicked the X button.
645 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
646 return;
647 }
648
649 // Force all commands to reevaluate CanExecute.
650 // InvalidateRequerySuggested must be run on the UI thread.
651 root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested));
652 }
653
654 private void ParseCommandLine()
655 {
656 // Get array of arguments based on the system parsing algorithm.
657 string[] args = WixBA.Model.Command.CommandLineArgs;
658 for (int i = 0; i < args.Length; ++i)
659 {
660 if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase))
661 {
662 // Allow relative directory paths. Also validates.
663 string[] param = args[i].Split(new char[] {'='}, 2);
664 this.root.InstallDirectory = IO.Path.Combine(Environment.CurrentDirectory, param[1]);
665 }
666 }
667 }
668
669 private void AddPackageTelemetry(string prefix, string id, double time, int result)
670 {
671 lock (this)
672 {
673 string key = String.Format("{0}Time_{1}", prefix, id);
674 string value = time.ToString();
675 WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value));
676
677 key = String.Format("{0}Result_{1}", prefix, id);
678 value = String.Concat("0x", result.ToString("x"));
679 WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value));
680 }
681 }
682 }
683}
diff --git a/src/test/burn/WixToolset.WixBA/Model.cs b/src/test/burn/WixToolset.WixBA/Model.cs
new file mode 100644
index 00000000..a557fa4e
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/Model.cs
@@ -0,0 +1,132 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Net;
8 using WixToolset.Mba.Core;
9
10 /// <summary>
11 /// The model.
12 /// </summary>
13 public class Model
14 {
15 private const string BurnBundleInstallDirectoryVariable = "InstallFolder";
16 private const string BurnBundleLayoutDirectoryVariable = "WixBundleLayoutDirectory";
17 private const string BurnBundleVersionVariable = "WixBundleVersion";
18
19 /// <summary>
20 /// Creates a new model for the BA.
21 /// </summary>
22 /// <param name="bootstrapper">The BA.</param>
23 public Model(WixBA bootstrapper)
24 {
25 this.BAManifest = bootstrapper.BAManifest;
26 this.Bootstrapper = bootstrapper;
27 this.Command = bootstrapper.Command;
28 this.Engine = bootstrapper.Engine;
29 this.Telemetry = new List<KeyValuePair<string, string>>();
30 this.Version = this.Engine.GetVariableVersion(BurnBundleVersionVariable);
31 }
32
33 public IBootstrapperApplicationData BAManifest { get; }
34
35 /// <summary>
36 /// Gets the bootstrapper.
37 /// </summary>
38 public IDefaultBootstrapperApplication Bootstrapper { get; }
39
40 /// <summary>
41 /// Gets the bootstrapper command-line.
42 /// </summary>
43 public IBootstrapperCommand Command { get; }
44
45 /// <summary>
46 /// Gets the bootstrapper engine.
47 /// </summary>
48 public IEngine Engine { get; }
49
50 /// <summary>
51 /// Gets the key/value pairs used in telemetry.
52 /// </summary>
53 public List<KeyValuePair<string, string>> Telemetry { get; private set; }
54
55 /// <summary>
56 /// Get or set the final result of the installation.
57 /// </summary>
58 public int Result { get; set; }
59
60 /// <summary>
61 /// Get the version of the install.
62 /// </summary>
63 public string Version { get; private set; }
64
65 /// <summary>
66 /// Get or set the path where the bundle is installed.
67 /// </summary>
68 public string InstallDirectory
69 {
70 get
71 {
72 if (!this.Engine.ContainsVariable(BurnBundleInstallDirectoryVariable))
73 {
74 return null;
75 }
76
77 return this.Engine.GetVariableString(BurnBundleInstallDirectoryVariable);
78 }
79
80 set
81 {
82 this.Engine.SetVariableString(BurnBundleInstallDirectoryVariable, value, false);
83 }
84 }
85
86 /// <summary>
87 /// Get or set the path for the layout to be created.
88 /// </summary>
89 public string LayoutDirectory
90 {
91 get
92 {
93 if (!this.Engine.ContainsVariable(BurnBundleLayoutDirectoryVariable))
94 {
95 return null;
96 }
97
98 return this.Engine.GetVariableString(BurnBundleLayoutDirectoryVariable);
99 }
100
101 set
102 {
103 this.Engine.SetVariableString(BurnBundleLayoutDirectoryVariable, value, false);
104 }
105 }
106
107 public LaunchAction PlannedAction { get; set; }
108
109 /// <summary>
110 /// Creates a correctly configured HTTP web request.
111 /// </summary>
112 /// <param name="uri">URI to connect to.</param>
113 /// <returns>Correctly configured HTTP web request.</returns>
114 public HttpWebRequest CreateWebRequest(string uri)
115 {
116 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
117 request.UserAgent = String.Concat("WixInstall", this.Version.ToString());
118
119 return request;
120 }
121
122 /// <summary>
123 /// Gets the display name for a package if possible.
124 /// </summary>
125 /// <param name="packageId">Identity of the package to find the display name.</param>
126 /// <returns>Display name of the package if found or the package id if not.</returns>
127 public string GetPackageName(string packageId)
128 {
129 return this.BAManifest.Bundle.Packages.TryGetValue(packageId, out var package) ? package.DisplayName : packageId;
130 }
131 }
132}
diff --git a/src/test/burn/WixToolset.WixBA/NewsItem.cs b/src/test/burn/WixToolset.WixBA/NewsItem.cs
new file mode 100644
index 00000000..f8bf7aed
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/NewsItem.cs
@@ -0,0 +1,18 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6
7 /// <summary>
8 /// The model for an individual news item.
9 /// </summary>
10 public class NewsItem
11 {
12 public string Author { get; set; }
13 public string Title { get; set; }
14 public string Url { get; set; }
15 public string Snippet { get; set; }
16 public DateTime Updated { get; set; }
17 }
18}
diff --git a/src/test/burn/WixToolset.WixBA/ProgressViewModel.cs b/src/test/burn/WixToolset.WixBA/ProgressViewModel.cs
new file mode 100644
index 00000000..6f7bb028
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/ProgressViewModel.cs
@@ -0,0 +1,245 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.Collections.Generic;
5using System.ComponentModel;
6using System.Diagnostics;
7using System.Text.RegularExpressions;
8using WixToolset.Mba.Core;
9
10namespace WixToolset.WixBA
11{
12 public class ProgressViewModel : PropertyNotifyBase
13 {
14 private static readonly Regex TrimActionTimeFromMessage = new Regex(@"^\w+\s+\d+:\d+:\d+:\s+", RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.Singleline);
15
16 private RootViewModel root;
17 private Dictionary<string, int> executingPackageOrderIndex;
18
19 private int progressPhases;
20 private int progress;
21 private int cacheProgress;
22 private int executeProgress;
23 private string package;
24 private string message;
25
26 public ProgressViewModel(RootViewModel root)
27 {
28 this.root = root;
29 this.executingPackageOrderIndex = new Dictionary<string, int>();
30
31 this.root.PropertyChanged += this.RootPropertyChanged;
32
33 WixBA.Model.Bootstrapper.ExecutePackageBegin += this.ExecutePackageBegin;
34 WixBA.Model.Bootstrapper.ExecutePackageComplete += this.ExecutePackageComplete;
35 WixBA.Model.Bootstrapper.ExecuteProgress += this.ApplyExecuteProgress;
36 WixBA.Model.Bootstrapper.PauseAutomaticUpdatesBegin += this.PauseAutomaticUpdatesBegin;
37 WixBA.Model.Bootstrapper.SystemRestorePointBegin += this.SystemRestorePointBegin;
38 WixBA.Model.Bootstrapper.PlanBegin += this.PlanBegin;
39 WixBA.Model.Bootstrapper.PlannedPackage += this.PlannedPackage;
40 WixBA.Model.Bootstrapper.ApplyBegin += this.ApplyBegin;
41 WixBA.Model.Bootstrapper.Progress += this.ApplyProgress;
42 WixBA.Model.Bootstrapper.CacheAcquireProgress += this.CacheAcquireProgress;
43 WixBA.Model.Bootstrapper.CacheContainerOrPayloadVerifyProgress += CacheContainerOrPayloadVerifyProgress;
44 WixBA.Model.Bootstrapper.CachePayloadExtractProgress += CachePayloadExtractProgress;
45 WixBA.Model.Bootstrapper.CacheVerifyProgress += CacheVerifyProgress;
46 WixBA.Model.Bootstrapper.CacheComplete += this.CacheComplete;
47 }
48
49 public bool ProgressEnabled
50 {
51 get { return this.root.InstallState == InstallationState.Applying; }
52 }
53
54 public int Progress
55 {
56 get
57 {
58 return this.progress;
59 }
60
61 set
62 {
63 if (this.progress != value)
64 {
65 this.progress = value;
66 base.OnPropertyChanged("Progress");
67 }
68 }
69 }
70
71 public string Package
72 {
73 get
74 {
75 return this.package;
76 }
77
78 set
79 {
80 if (this.package != value)
81 {
82 this.package = value;
83 base.OnPropertyChanged("Package");
84 }
85 }
86 }
87
88 public string Message
89 {
90 get
91 {
92 return this.message;
93 }
94
95 set
96 {
97 if (this.message != value)
98 {
99 this.message = value;
100 base.OnPropertyChanged("Message");
101 }
102 }
103 }
104
105 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
106 {
107 if ("InstallState" == e.PropertyName)
108 {
109 base.OnPropertyChanged("ProgressEnabled");
110 }
111 }
112
113 private void PlanBegin(object sender, PlanBeginEventArgs e)
114 {
115 lock (this)
116 {
117 this.executingPackageOrderIndex.Clear();
118 }
119 }
120
121 private void PlannedPackage(object sender, PlannedPackageEventArgs e)
122 {
123 if (ActionState.None != e.Execute)
124 {
125 lock (this)
126 {
127 Debug.Assert(!this.executingPackageOrderIndex.ContainsKey(e.PackageId));
128 this.executingPackageOrderIndex.Add(e.PackageId, this.executingPackageOrderIndex.Count);
129 }
130 }
131 }
132
133 private void ExecutePackageBegin(object sender, ExecutePackageBeginEventArgs e)
134 {
135 lock (this)
136 {
137 this.Package = WixBA.Model.GetPackageName(e.PackageId);
138 this.Message = String.Format("Processing: {0}", this.Package);
139 e.Cancel = this.root.Canceled;
140 }
141 }
142
143 private void ExecutePackageComplete(object sender, ExecutePackageCompleteEventArgs e)
144 {
145 lock (this)
146 { // avoid a stale display
147 this.Message = String.Empty;
148 }
149 }
150
151 private void PauseAutomaticUpdatesBegin(object sender, PauseAutomaticUpdatesBeginEventArgs e)
152 {
153 lock (this)
154 {
155 this.Message = "Pausing Windows automatic updates";
156 }
157 }
158
159 private void SystemRestorePointBegin(object sender, SystemRestorePointBeginEventArgs e)
160 {
161 lock (this)
162 {
163 this.Message = "Creating system restore point";
164 }
165 }
166
167 private void ApplyBegin(object sender, ApplyBeginEventArgs e)
168 {
169 this.progressPhases = e.PhaseCount;
170 }
171
172 private void ApplyProgress(object sender, ProgressEventArgs e)
173 {
174 lock (this)
175 {
176 e.Cancel = this.root.Canceled;
177 }
178 }
179
180 private void CacheAcquireProgress(object sender, CacheAcquireProgressEventArgs e)
181 {
182 lock (this)
183 {
184 this.cacheProgress = e.OverallPercentage;
185 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
186 e.Cancel = this.root.Canceled;
187 }
188 }
189
190 private void CacheContainerOrPayloadVerifyProgress(object sender, CacheContainerOrPayloadVerifyProgressEventArgs e)
191 {
192 lock (this)
193 {
194 this.cacheProgress = e.OverallPercentage;
195 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
196 e.Cancel = this.root.Canceled;
197 }
198 }
199
200 private void CachePayloadExtractProgress(object sender, CachePayloadExtractProgressEventArgs e)
201 {
202 lock (this)
203 {
204 this.cacheProgress = e.OverallPercentage;
205 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
206 e.Cancel = this.root.Canceled;
207 }
208 }
209
210 private void CacheVerifyProgress(object sender, CacheVerifyProgressEventArgs e)
211 {
212 lock (this)
213 {
214 this.cacheProgress = e.OverallPercentage;
215 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
216 e.Cancel = this.root.Canceled;
217 }
218 }
219
220 private void CacheComplete(object sender, CacheCompleteEventArgs e)
221 {
222 lock (this)
223 {
224 this.cacheProgress = 100;
225 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
226 }
227 }
228
229 private void ApplyExecuteProgress(object sender, ExecuteProgressEventArgs e)
230 {
231 lock (this)
232 {
233 this.executeProgress = e.OverallPercentage;
234 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
235
236 if (WixBA.Model.Command.Display == Display.Embedded)
237 {
238 WixBA.Model.Engine.SendEmbeddedProgress(e.ProgressPercentage, this.Progress);
239 }
240
241 e.Cancel = this.root.Canceled;
242 }
243 }
244 }
245}
diff --git a/src/test/burn/WixToolset.WixBA/PropertyNotifyBase.cs b/src/test/burn/WixToolset.WixBA/PropertyNotifyBase.cs
new file mode 100644
index 00000000..f8264614
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/PropertyNotifyBase.cs
@@ -0,0 +1,59 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.ComponentModel;
7 using System.Diagnostics;
8
9 /// <summary>
10 /// It provides support for property change notifications.
11 /// </summary>
12 public abstract class PropertyNotifyBase : INotifyPropertyChanged
13 {
14 /// <summary>
15 /// Initializes a new instance of the <see cref="PropertyNotifyBase"/> class.
16 /// </summary>
17 protected PropertyNotifyBase()
18 {
19 }
20
21 /// <summary>
22 /// Raised when a property on this object has a new value.
23 /// </summary>
24 public event PropertyChangedEventHandler PropertyChanged;
25
26 /// <summary>
27 /// Warns the developer if this object does not have a public property with the
28 /// specified name. This method does not exist in a Release build.
29 /// </summary>
30 /// <param name="propertyName">Property name to verify.</param>
31 [Conditional("DEBUG")]
32 [DebuggerStepThrough]
33 public void VerifyPropertyName(string propertyName)
34 {
35 // Verify that the property name matches a real, public, instance property
36 // on this object.
37 if (null == TypeDescriptor.GetProperties(this)[propertyName])
38 {
39 Debug.Fail(String.Concat("Invalid property name: ", propertyName));
40 }
41 }
42
43 /// <summary>
44 /// Raises this object's PropertyChanged event.
45 /// </summary>
46 /// <param name="propertyName">The property that has a new value.</param>
47 protected virtual void OnPropertyChanged(string propertyName)
48 {
49 this.VerifyPropertyName(propertyName);
50
51 PropertyChangedEventHandler handler = this.PropertyChanged;
52 if (null != handler)
53 {
54 PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
55 handler(this, e);
56 }
57 }
58 }
59}
diff --git a/src/test/burn/WixToolset.WixBA/RelayCommand.cs b/src/test/burn/WixToolset.WixBA/RelayCommand.cs
new file mode 100644
index 00000000..d3ab2d7a
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/RelayCommand.cs
@@ -0,0 +1,45 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Diagnostics;
7 using System.Windows.Input;
8
9 /// <summary>
10 /// Base class that implements ICommand interface via delegates.
11 /// </summary>
12 public class RelayCommand : ICommand
13 {
14 private readonly Action<object> execute;
15 private readonly Predicate<object> canExecute;
16
17 public RelayCommand(Action<object> execute)
18 : this(execute, null)
19 {
20 }
21
22 public RelayCommand(Action<object> execute, Predicate<object> canExecute)
23 {
24 this.execute = execute;
25 this.canExecute = canExecute;
26 }
27
28 public event EventHandler CanExecuteChanged
29 {
30 add { CommandManager.RequerySuggested += value; }
31 remove { CommandManager.RequerySuggested -= value; }
32 }
33
34 [DebuggerStepThrough]
35 public bool CanExecute(object parameter)
36 {
37 return this.canExecute == null ? true : this.canExecute(parameter);
38 }
39
40 public void Execute(object parameter)
41 {
42 this.execute(parameter);
43 }
44 }
45}
diff --git a/src/test/burn/WixToolset.WixBA/Resources/logo-black-hollow.png b/src/test/burn/WixToolset.WixBA/Resources/logo-black-hollow.png
new file mode 100644
index 00000000..9d0290bd
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/Resources/logo-black-hollow.png
Binary files differ
diff --git a/src/test/burn/WixToolset.WixBA/Resources/logo-white-hollow.png b/src/test/burn/WixToolset.WixBA/Resources/logo-white-hollow.png
new file mode 100644
index 00000000..242c7350
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/Resources/logo-white-hollow.png
Binary files differ
diff --git a/src/test/burn/WixToolset.WixBA/RootView.xaml b/src/test/burn/WixToolset.WixBA/RootView.xaml
new file mode 100644
index 00000000..b7d535d1
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/RootView.xaml
@@ -0,0 +1,420 @@
1<?xml version="1.0" encoding="utf-8" ?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Window x:Class="WixToolset.WixBA.RootView"
5 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7 xmlns:wixba="clr-namespace:WixToolset.WixBA"
8 MinWidth="550"
9 MinHeight="400"
10 Width="750"
11 Height="400"
12 AllowsTransparency="False"
13 Background="{x:Null}"
14 Closing="Window_Closing"
15 ResizeMode="CanResizeWithGrip"
16 WindowStartupLocation="CenterScreen"
17 WindowStyle="ThreeDBorderWindow">
18
19 <Window.Resources>
20 <ResourceDictionary>
21 <ResourceDictionary.MergedDictionaries>
22 <ResourceDictionary Source="pack://application:,,,/WixBA;component/Styles.xaml" />
23 </ResourceDictionary.MergedDictionaries>
24 </ResourceDictionary>
25 </Window.Resources>
26
27 <Border Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
28 BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
29 BorderThickness="0">
30
31 <DockPanel x:Name="AppArtBoardArea"
32 Margin="20, 10">
33
34 <DockPanel x:Name="ActionArea"
35 DockPanel.Dock="Bottom">
36
37 <Grid x:Name="ActionGrid">
38 <Grid.RowDefinitions>
39 <RowDefinition Height="Auto" />
40 </Grid.RowDefinitions>
41 <Grid.ColumnDefinitions>
42 <ColumnDefinition Width="*" MinWidth="150" />
43 <ColumnDefinition Width="Auto" />
44 </Grid.ColumnDefinitions>
45
46 <StackPanel x:Name="LeftMarqueeDcIsland"
47 Grid.Column="0"
48 VerticalAlignment="Center"
49 DataContext="{Binding UpdateViewModel}">
50 <ProgressBar x:Name="CheckingUpdatesProgress"
51 MinHeight="10"
52 IsIndeterminate="{Binding CheckingEnabled}"
53 Style="{DynamicResource UpdateMarqueeStyle}"
54 Visibility="{Binding CheckingEnabled,
55 Converter={StaticResource BooleanToVisibilityConverter}}" />
56 </StackPanel>
57
58 <StackPanel x:Name="InitiateActionArea"
59 Grid.Column="1"
60 Margin="30, 0, 0, 0"
61 HorizontalAlignment="Right"
62 Orientation="Horizontal">
63 <!-- Displayed from left to right, when ordered from top to bottom. Most preferred is at the top. -->
64 <StackPanel x:Name="UpdateBtnDcIsland"
65 DataContext="{Binding UpdateViewModel}"
66 Style="{DynamicResource ActionBtnStkPnlStyle}">
67 <Button AutomationProperties.HelpText="An update is available. Click to get the update"
68 AutomationProperties.Name="Update"
69 Command="{Binding UpdateCommand}"
70 Content="Update"
71 Style="{DynamicResource ActionButtonStyle}"
72 Visibility="{Binding CanUpdate,
73 Converter={StaticResource BooleanToVisibilityConverter}}" />
74 </StackPanel>
75 <StackPanel x:Name="InstallBtnDcIsland"
76 DataContext="{Binding InstallationViewModel}"
77 Style="{DynamicResource ActionBtnStkPnlStyle}">
78 <Button AutomationProperties.HelpText="Click to accept license and install"
79 AutomationProperties.Name="Install"
80 Command="{Binding InstallCommand}"
81 Content="_Install"
82 IsDefault="True"
83 Style="{DynamicResource ActionButtonStyle}"
84 Visibility="{Binding InstallEnabled,
85 Converter={StaticResource BooleanToVisibilityConverter}}" />
86 </StackPanel>
87 <StackPanel x:Name="RepairBtnDcIsland"
88 DataContext="{Binding InstallationViewModel}"
89 Style="{DynamicResource ActionBtnStkPnlStyle}">
90 <Button x:Name="RepairButton"
91 AutomationProperties.HelpText="Click to Repair"
92 AutomationProperties.Name="Repair"
93 Command="{Binding RepairCommand}"
94 Content="Repair"
95 Style="{DynamicResource ActionButtonStyle}"
96 Visibility="{Binding RepairEnabled,
97 Converter={StaticResource BooleanToVisibilityConverter}}" />
98 </StackPanel>
99 <StackPanel x:Name="UninstallBtnDcIsland"
100 DataContext="{Binding InstallationViewModel}"
101 Style="{DynamicResource ActionBtnStkPnlStyle}">
102 <Button AutomationProperties.HelpText="Click to Uninstall"
103 AutomationProperties.Name="Uninstall"
104 Command="{Binding UninstallCommand}"
105 Content="Uninstall"
106 Style="{DynamicResource ActionButtonStyle}"
107 Visibility="{Binding UninstallEnabled,
108 Converter={StaticResource BooleanToVisibilityConverter}}" />
109 </StackPanel>
110 <StackPanel x:Name="TryAgainBtnDcIsland"
111 DataContext="{Binding InstallationViewModel}"
112 Style="{DynamicResource ActionBtnStkPnlStyle}">
113 <Button HorizontalAlignment="Center"
114 VerticalAlignment="Center"
115 AutomationProperties.HelpText="Click to try again"
116 AutomationProperties.Name="Try Again?"
117 Command="{Binding TryAgainCommand}"
118 Content="Try Again?"
119 Style="{DynamicResource ActionButtonStyle}"
120 Visibility="{Binding IsFailedCompletion,
121 Converter={StaticResource BooleanToVisibilityConverter}}" />
122 </StackPanel>
123
124 <!-- Final Actions - only one of these is expected to be displayed, if any -->
125 <StackPanel x:Name="CloseBtnDcIsland"
126 HorizontalAlignment="Right"
127 DataContext="{Binding InstallationViewModel}"
128 Style="{DynamicResource ActionBtnStkPnlStyle}">
129 <Button x:Name="CloseButton"
130 AutomationProperties.HelpText="Click to Close"
131 AutomationProperties.Name="Close"
132 Command="{Binding CloseCommand}"
133 Content="Close"
134 Style="{DynamicResource FinalActionButtonStyle}"
135 Visibility="{Binding IsSuccessfulCompletion,
136 Converter={StaticResource BooleanToVisibilityConverter}}" />
137 </StackPanel>
138 <StackPanel x:Name="CancelBtnDcIsland"
139 HorizontalAlignment="Right"
140 DataContext="{Binding}"
141 Style="{DynamicResource ActionBtnStkPnlStyle}">
142 <Button AutomationProperties.HelpText="Press to Cancel"
143 AutomationProperties.Name="Cancel"
144 Command="{Binding CancelCommand}"
145 Content="Cancel"
146 Style="{DynamicResource FinalActionButtonStyle}"
147 Visibility="{Binding CancelAvailable,
148 Converter={StaticResource BooleanToVisibilityConverter}}" />
149 </StackPanel>
150 </StackPanel>
151 </Grid>
152 </DockPanel>
153
154 <StackPanel x:Name="FinalStatusArea"
155 DataContext="{Binding InstallationViewModel}"
156 HorizontalAlignment="Right"
157 Margin="0,10,0,20"
158 Orientation="Horizontal"
159 Visibility="{Binding IsComplete,
160 Converter={StaticResource BooleanToVisibilityConverter}}"
161 DockPanel.Dock="Bottom">
162 <TextBlock x:Name="StatusTextBlk"
163 Style="{DynamicResource LabelTextBlkStyle}"
164 Text="{Binding StatusText, StringFormat={}{0}:}"
165 TextBlock.FontWeight="Bold" />
166 <Button x:Name="ViewLogLink"
167 Margin="10,2,10,0"
168 Command="{Binding OpenLogCommand}"
169 Style="{StaticResource HyperlinkedButtonStyle}">
170 <TextBlock>
171 <Hyperlink Command="{Binding OpenLogCommand}"><Run FontSize="{DynamicResource FontSizeButton}" Text="View Log" /></Hyperlink>
172 </TextBlock>
173 </Button>
174 <Button x:Name="ViewLogFolderLink"
175 Command="{Binding OpenLogFolderCommand}"
176 Style="{StaticResource HyperlinkedButtonStyle}">
177 <TextBlock>
178 <Hyperlink Command="{Binding OpenLogFolderCommand}"><Run FontSize="{DynamicResource FontSizeButton}" Text="View Log Folder" /></Hyperlink>
179 </TextBlock>
180 </Button>
181 </StackPanel>
182
183 <StackPanel x:Name="StatusStkPnlDcIsland"
184 Margin="0,10"
185 DataContext="{Binding ProgressViewModel}"
186 DockPanel.Dock="Bottom">
187
188 <StackPanel x:Name="ActionPackageNameTextStkPnl"
189 VerticalAlignment="Top">
190 <TextBlock Style="{StaticResource StatusTextStyle}"
191 Text="{Binding Message}"
192 TextWrapping="WrapWithOverflow"
193 Visibility="{Binding ProgressEnabled,
194 Converter={StaticResource BooleanToVisibilityConverter}}" />
195 </StackPanel>
196
197 <StackPanel x:Name="ActionProgressStkPnl"
198 Margin="0,5,0,0">
199 <ProgressBar x:Name="ActionProgress"
200 Height="20"
201 VerticalAlignment="Center"
202 Style="{DynamicResource ActionProgressStyle}"
203 Visibility="{Binding ProgressEnabled,
204 Converter={StaticResource BooleanToVisibilityConverter}}"
205 Value="{Binding Progress}" />
206 </StackPanel>
207 </StackPanel>
208
209 <StackPanel x:Name="TitleAndLogoStkPnl"
210 DockPanel.Dock="Top">
211 <Grid x:Name="TitleGrid">
212 <Grid.RowDefinitions>
213 <RowDefinition Height="Auto" />
214 </Grid.RowDefinitions>
215 <Grid.ColumnDefinitions>
216 <ColumnDefinition Width="Auto" />
217 <ColumnDefinition Width="*" />
218 <ColumnDefinition Width="Auto" />
219 </Grid.ColumnDefinitions>
220
221 <TextBlock x:Name="TitleTextBlk"
222 Grid.Column="0"
223 Style="{DynamicResource TitleTextBlkStyle}"
224 Text="{Binding Title,
225 Mode=OneTime}" />
226 <Image x:Name="Logo"
227 Grid.Column="2"
228 Style="{DynamicResource LogoStyle}">
229 </Image>
230 </Grid>
231 </StackPanel>
232
233 <Grid x:Name="MainStkPnl">
234 <Grid.RowDefinitions>
235 <RowDefinition Height="*" />
236 <RowDefinition Height="Auto" />
237 </Grid.RowDefinitions>
238 <Grid.ColumnDefinitions>
239 <ColumnDefinition Width="Auto" />
240 <ColumnDefinition Width="1*" />
241 <ColumnDefinition Width="4*" MinWidth="200" />
242 </Grid.ColumnDefinitions>
243
244 <Grid x:Name="SkuInfo"
245 Grid.Column="0"
246 Grid.Row="0"
247 DataContext="{Binding InstallationViewModel}">
248 <Grid.RowDefinitions>
249 <RowDefinition Height="Auto" />
250 <RowDefinition Height="Auto" />
251 <RowDefinition Height="Auto" />
252 <RowDefinition Height="Auto" />
253 <RowDefinition Height="Auto" />
254 <RowDefinition Height="Auto" />
255 <RowDefinition Height="Auto" />
256 </Grid.RowDefinitions>
257 <Grid.ColumnDefinitions>
258 <ColumnDefinition Width="Auto" />
259 <ColumnDefinition Width="*" />
260 </Grid.ColumnDefinitions>
261
262 <TextBlock x:Name="SkuPublisherLabel"
263 Grid.Row="0"
264 Grid.Column="0"
265 Style="{DynamicResource LabelTextBlkStyle}"
266 Text="Publisher:" />
267 <TextBlock x:Name="SkuPublisherData"
268 Grid.Row="0"
269 Grid.Column="1"
270 Style="{DynamicResource DataTextBlkStyle}"
271 Text="{Binding Publisher,
272 Mode=OneTime}" />
273
274 <TextBlock x:Name="SkuVersionLabel"
275 Grid.Row="1"
276 Grid.Column="0"
277 Style="{DynamicResource LabelTextBlkStyle}"
278 Text="Version:" />
279 <TextBlock Grid.Row="1"
280 Grid.Column="1"
281 Style="{DynamicResource DataTextBlkStyle}"
282 Text="{Binding Version}" />
283
284 <TextBlock x:Name="SkuLicenseLabel"
285 Grid.Row="2"
286 Grid.Column="0"
287 Style="{DynamicResource LabelTextBlkStyle}"
288 Text="License:" />
289 <Button x:Name="SkuLicenseBtn"
290 Grid.Row="2"
291 Grid.Column="1"
292 HorizontalAlignment="Left"
293 AutomationProperties.HelpText="View the license"
294 AutomationProperties.Name="License"
295 Command="{Binding LicenseCommand}"
296 KeyboardNavigation.IsTabStop="False"
297 Style="{StaticResource HyperlinkedButtonStyle}">
298 <TextBlock HorizontalAlignment="Left">
299 <Hyperlink Command="{Binding LicenseCommand}"
300 IsEnabled="True"
301 KeyboardNavigation.IsTabStop="False"><Run FontSize="{DynamicResource FontSizeButton}" Text="View License" /></Hyperlink>
302 </TextBlock>
303 </Button>
304
305 <TextBlock x:Name="SkuNewsLabel"
306 Grid.Row="3"
307 Grid.Column="0"
308 Style="{DynamicResource LabelTextBlkStyle}"
309 Text="News:" />
310 <Button x:Name="SkuNewsBtn"
311 Grid.Row="3"
312 Grid.Column="1"
313 HorizontalAlignment="Left"
314 AutomationProperties.HelpText="Latest News"
315 AutomationProperties.Name="News"
316 Command="{Binding LaunchNewsCommand}"
317 KeyboardNavigation.IsTabStop="False"
318 Style="{StaticResource HyperlinkedButtonStyle}">
319 <TextBlock HorizontalAlignment="Left">
320 <Hyperlink Command="{Binding LaunchNewsCommand}"
321 IsEnabled="True"
322 KeyboardNavigation.IsTabStop="False"><Run FontSize="{DynamicResource FontSizeButton}" Text="Latest News" /></Hyperlink>
323 </TextBlock>
324 </Button>
325
326 <TextBlock x:Name="SkuSupportLabel"
327 Grid.Row="4"
328 Grid.Column="0"
329 Style="{DynamicResource LabelTextBlkStyle}"
330 Text="Support:" />
331 <Button x:Name="SkuSupportBtn"
332 Grid.Row="4"
333 Grid.Column="1"
334 HorizontalAlignment="Left"
335 AutomationProperties.HelpText="View Home Page for Support"
336 AutomationProperties.Name="Home Page"
337 Command="{Binding LaunchHomePageCommand}"
338 KeyboardNavigation.IsTabStop="False"
339 Style="{StaticResource HyperlinkedButtonStyle}">
340 <TextBlock HorizontalAlignment="Left">
341 <Hyperlink Command="{Binding LaunchHomePageCommand}"
342 IsEnabled="True"
343 KeyboardNavigation.IsTabStop="False"><Run FontSize="{DynamicResource FontSizeButton}" Text="{Binding SupportUrl, Mode=OneTime}" /></Hyperlink>
344 </TextBlock>
345 </Button>
346
347 <TextBlock x:Name="SkuVSExtensionLabel1"
348 Grid.Row="5"
349 Grid.Column="0"
350 Style="{DynamicResource LabelTextBlkStyle}"
351 Text="Visual Studio" />
352 <TextBlock x:Name="SkuVSExtensionLabel2"
353 Grid.Row="6"
354 Grid.Column="0"
355 Style="{DynamicResource LabelTextBlkStyle}"
356 Text="Extension:" />
357 <Button x:Name="SkuVSExtensionBtn"
358 Grid.Row="6"
359 Grid.Column="1"
360 HorizontalAlignment="Left"
361 AutomationProperties.HelpText="View Releases Page for VS Extension"
362 AutomationProperties.Name="Releases Page"
363 Command="{Binding LaunchVSExtensionPageCommand}"
364 KeyboardNavigation.IsTabStop="False"
365 Style="{StaticResource HyperlinkedButtonStyle}">
366 <TextBlock HorizontalAlignment="Left">
367 <Hyperlink Command="{Binding LaunchVSExtensionPageCommand}"
368 IsEnabled="True"
369 KeyboardNavigation.IsTabStop="False"><Run FontSize="{DynamicResource FontSizeButton}" Text="{Binding VSExtensionUrl, Mode=OneTime}" /></Hyperlink>
370 </TextBlock>
371 </Button>
372 </Grid>
373
374 <DockPanel x:Name="UpdateChangesStkPnlDcIsland"
375 Grid.Row="0"
376 Grid.Column="2"
377 DataContext="{Binding UpdateViewModel}"
378 Visibility="{Binding IsUpdateAvailable,
379 Converter={StaticResource BooleanToVisibilityConverter}}">
380
381 <Grid x:Name="UpdateInfoGrid"
382 DockPanel.Dock="Top">
383 <Grid.RowDefinitions>
384 <RowDefinition Height="Auto" />
385 </Grid.RowDefinitions>
386 <Grid.ColumnDefinitions>
387 <ColumnDefinition Width="Auto" />
388 <ColumnDefinition Width="*" />
389 </Grid.ColumnDefinitions>
390
391 <TextBlock x:Name="UpdateTitleLabel"
392 Grid.Row="0"
393 Grid.Column="0"
394 Style="{DynamicResource LabelTextBlkStyle}"
395 Text="Available Update:" />
396
397 <TextBlock x:Name="UpdateVersionLabel"
398 Grid.Row="0"
399 Grid.Column="1"
400 Style="{DynamicResource DataTextBlkStyle}"
401 Text="{Binding UpdateVersion}" />
402 </Grid>
403
404 <WebBrowser DockPanel.Dock="Bottom"
405 wixba:BrowserProperties.HtmlDoc="{Binding UpdateChanges}" />
406 </DockPanel>
407
408 <TextBlock x:Name="DowngradeMessageTextBlk"
409 Grid.Row="1"
410 Grid.Column="0"
411 Grid.ColumnSpan="3"
412 DataContext="{Binding InstallationViewModel}"
413 Style="{DynamicResource LabelTextBlkStyle}"
414 Text="{Binding DowngradeMessage}"
415 Visibility="{Binding Downgrade,
416 Converter={StaticResource BooleanToVisibilityConverter}}" />
417 </Grid>
418 </DockPanel>
419 </Border>
420</Window>
diff --git a/src/test/burn/WixToolset.WixBA/RootView.xaml.cs b/src/test/burn/WixToolset.WixBA/RootView.xaml.cs
new file mode 100644
index 00000000..e1ee19db
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/RootView.xaml.cs
@@ -0,0 +1,51 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System.ComponentModel;
6 using System.Windows;
7 using System.Windows.Interop;
8
9 /// <summary>
10 /// Interaction logic for View.xaml
11 /// </summary>
12 public partial class RootView : Window
13 {
14 /// <summary>
15 /// Creates the view populated with it's model.
16 /// </summary>
17 /// <param name="viewModel">Model for the view.</param>
18 public RootView(RootViewModel viewModel)
19 {
20 this.DataContext = viewModel;
21
22 this.Loaded += (sender, e) => WixBA.Model.Engine.CloseSplashScreen();
23 this.Closed += (sender, e) => this.Dispatcher.InvokeShutdown(); // shutdown dispatcher when the window is closed.
24
25 this.InitializeComponent();
26
27 viewModel.Dispatcher = this.Dispatcher;
28 viewModel.ViewWindowHandle = new WindowInteropHelper(this).EnsureHandle();
29 }
30
31 /// <summary>
32 /// Event is fired when the window is closing.
33 /// </summary>
34 /// <param name="sender"></param>
35 /// <param name="e"></param>
36 private void Window_Closing(object sender, CancelEventArgs e)
37 {
38 RootViewModel rvm = this.DataContext as RootViewModel;
39 if ((null != rvm) && (InstallationState.Applying == rvm.InstallState))
40 {
41 rvm.CancelButton_Click();
42 if (rvm.Canceled)
43 {
44 // Defer closing until the engine has canceled processing.
45 e.Cancel = true;
46 rvm.AutoClose = true;
47 }
48 }
49 }
50 }
51}
diff --git a/src/test/burn/WixToolset.WixBA/RootViewModel.cs b/src/test/burn/WixToolset.WixBA/RootViewModel.cs
new file mode 100644
index 00000000..8cff7274
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/RootViewModel.cs
@@ -0,0 +1,202 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Windows;
7 using System.Windows.Input;
8 using System.Windows.Threading;
9 using WixToolset.Mba.Core;
10
11 /// <summary>
12 /// The errors returned from the engine
13 /// </summary>
14 public enum Error
15 {
16 UserCancelled = 1223,
17 }
18
19 /// <summary>
20 /// The model of the root view in WixBA.
21 /// </summary>
22 public class RootViewModel : PropertyNotifyBase
23 {
24 private ICommand cancelCommand;
25 private ICommand closeCommand;
26
27 private bool canceled;
28 private InstallationState installState;
29 private DetectionState detectState;
30
31 /// <summary>
32 /// Creates a new model of the root view.
33 /// </summary>
34 public RootViewModel()
35 {
36 this.InstallationViewModel = new InstallationViewModel(this);
37 this.ProgressViewModel = new ProgressViewModel(this);
38 this.UpdateViewModel = new UpdateViewModel(this);
39 }
40
41 public InstallationViewModel InstallationViewModel { get; private set; }
42 public ProgressViewModel ProgressViewModel { get; private set; }
43 public UpdateViewModel UpdateViewModel { get; private set; }
44 public Dispatcher Dispatcher { get; set; }
45 public IntPtr ViewWindowHandle { get; set; }
46 public bool AutoClose { get; set; }
47
48 public ICommand CloseCommand
49 {
50 get
51 {
52 if (this.closeCommand == null)
53 {
54 this.closeCommand = new RelayCommand(param => WixBA.View.Close());
55 }
56
57 return this.closeCommand;
58 }
59 }
60
61 public ICommand CancelCommand
62 {
63 get
64 {
65 if (this.cancelCommand == null)
66 {
67 this.cancelCommand = new RelayCommand(param =>
68 {
69 this.CancelButton_Click();
70 },
71 param => !this.Canceled);
72 }
73
74 return this.cancelCommand;
75 }
76 }
77
78 public bool CancelAvailable
79 {
80 get { return InstallationState.Applying == this.InstallState; }
81 }
82
83 public bool Canceled
84 {
85 get
86 {
87 return this.canceled;
88 }
89
90 set
91 {
92 if (this.canceled != value)
93 {
94 this.canceled = value;
95 base.OnPropertyChanged("Canceled");
96 }
97 }
98 }
99
100 /// <summary>
101 /// Gets and sets the detect state of the view's model.
102 /// </summary>
103 public DetectionState DetectState
104 {
105 get
106 {
107 return this.detectState;
108 }
109
110 set
111 {
112 if (this.detectState != value)
113 {
114 this.detectState = value;
115
116 // Notify all the properties derived from the state that the state changed.
117 base.OnPropertyChanged("DetectState");
118 }
119 }
120 }
121
122 /// <summary>
123 /// Gets and sets the installation state of the view's model.
124 /// </summary>
125 public InstallationState InstallState
126 {
127 get
128 {
129 return this.installState;
130 }
131
132 set
133 {
134 if (this.installState != value)
135 {
136 this.installState = value;
137
138 // Notify all the properties derived from the state that the state changed.
139 base.OnPropertyChanged("InstallState");
140 base.OnPropertyChanged("CancelAvailable");
141 }
142 }
143 }
144
145 /// <summary>
146 /// Gets and sets the state of the view's model before apply begins in order to return to that state if cancel or rollback occurs.
147 /// </summary>
148 public InstallationState PreApplyState { get; set; }
149
150 /// <summary>
151 /// Gets and sets the path where the bundle is currently installed or will be installed.
152 /// </summary>
153 public string InstallDirectory
154 {
155 get
156 {
157 return WixBA.Model.InstallDirectory;
158 }
159
160 set
161 {
162 if (WixBA.Model.InstallDirectory != value)
163 {
164 WixBA.Model.InstallDirectory = value;
165 base.OnPropertyChanged("InstallDirectory");
166 }
167 }
168 }
169
170 /// <summary>
171 /// The Title of this bundle.
172 /// </summary>
173 public string Title
174 {
175 get
176 {
177 return WixDistribution.ShortProduct;
178 }
179 }
180
181 /// <summary>
182 /// Prompts the user to make sure they want to cancel.
183 /// This needs to run on the UI thread, use Dispatcher.Invoke to call this from a background thread.
184 /// </summary>
185 public void CancelButton_Click()
186 {
187 if (this.Canceled)
188 {
189 return;
190 }
191
192 if (Display.Full == WixBA.Model.Command.Display)
193 {
194 this.Canceled = (MessageBoxResult.Yes == MessageBox.Show(WixBA.View, "Are you sure you want to cancel?", "WiX Toolset", MessageBoxButton.YesNo, MessageBoxImage.Error));
195 }
196 else
197 {
198 this.Canceled = true;
199 }
200 }
201 }
202}
diff --git a/src/test/burn/WixToolset.WixBA/Styles.xaml b/src/test/burn/WixToolset.WixBA/Styles.xaml
new file mode 100644
index 00000000..fa0afc4f
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/Styles.xaml
@@ -0,0 +1,194 @@
1<?xml version="1.0" encoding="utf-8" ?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6 xmlns:System="clr-namespace:System;assembly=mscorlib"
7 xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
8 xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
9 xmlns:wixba="clr-namespace:WixToolset.WixBA">
10
11 <!-- Converters -->
12 <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
13
14 <!-- Fonts -->
15 <FontFamily x:Key="FontFamily">Segoe UI, Arial</FontFamily>
16 <System:Double x:Key="FontSizeBranding">45</System:Double>
17 <System:Double x:Key="FontSizeMedium">12</System:Double>
18 <System:Double x:Key="FontSizeButton">14</System:Double>
19
20 <!-- Images -->
21 <BitmapImage x:Key="LogoOverLightBackground" UriSource="pack://application:,,,/WixBA;component/resources/logo-white-hollow.png" />
22 <BitmapImage x:Key="LogoOverDarkBackground" UriSource="pack://application:,,,/WixBA;component/resources/logo-black-hollow.png" />
23
24 <!-- Colors -->
25 <Color x:Key="ProgressIndicatorColor">#FF1EF1E8</Color>
26
27 <!-- Brushs -->
28 <SolidColorBrush x:Key="ProgressIndicatorBrush" Color="{DynamicResource ProgressIndicatorColor}" />
29
30 <LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill" StartPoint="0,0" EndPoint="1,0">
31 <LinearGradientBrush.GradientStops>
32 <GradientStopCollection>
33 <GradientStop Offset="0" Color="#000000FF" />
34 <GradientStop Offset="0.5" Color="#600000FF" />
35 <GradientStop Offset="0.54" Color="{DynamicResource {x:Static SystemColors.ControlTextColorKey}}" />
36 <GradientStop Offset="0.56" Color="{DynamicResource {x:Static SystemColors.ControlTextColorKey}}" />
37 <GradientStop Offset="0.6" Color="#600000FF" />
38 <GradientStop Offset="1" Color="#000000FF" />
39 </GradientStopCollection>
40 </LinearGradientBrush.GradientStops>
41 </LinearGradientBrush>
42
43 <!-- Control Templates -->
44 <ControlTemplate x:Key="HyperlinkedButtonTemplateKey" TargetType="{x:Type Button}">
45 <ContentPresenter Margin="{TemplateBinding Control.Padding}"
46 HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
47 VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
48 Content="{TemplateBinding ContentControl.Content}"
49 ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
50 SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
51 </ControlTemplate>
52
53 <ControlTemplate x:Key="ProgressBarTemplateKey" TargetType="{x:Type ProgressBar}">
54 <Border Name="TemplateRoot"
55 Margin="0,5"
56 BorderBrush="{TemplateBinding BorderBrush}"
57 BorderThickness="{TemplateBinding BorderThickness}"
58 CornerRadius="3">
59 <Grid ClipToBounds="True" SnapsToDevicePixels="true">
60 <Rectangle Fill="{TemplateBinding Background}" />
61 <Rectangle Name="PART_Track" ClipToBounds="True" />
62 <Decorator x:Name="PART_Indicator" HorizontalAlignment="Left">
63 <Grid Name="Foreground">
64 <Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}" />
65 <Grid x:Name="Animation">
66 <Rectangle x:Name="PART_GlowRect"
67 Width="80"
68 Margin="-100,0,0,0"
69 HorizontalAlignment="Left"
70 Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" />
71 </Grid>
72 </Grid>
73 </Decorator>
74 </Grid>
75 </Border>
76 </ControlTemplate>
77
78 <!-- Styles -->
79 <Style x:Key="ActionBtnStkPnlStyle" TargetType="StackPanel">
80 <Setter Property="Margin" Value="0,2,0,0" />
81 <Setter Property="HorizontalAlignment" Value="Center" />
82 </Style>
83
84 <Style x:Key="FinalActionsStkPnlStyle" TargetType="StackPanel">
85 <Setter Property="Margin" Value="80,2,0,0" />
86 </Style>
87
88 <Style x:Key="BrandStkPnlStyle" TargetType="StackPanel">
89 <Setter Property="Margin" Value="0,0,20,0" />
90 <Setter Property="VerticalAlignment" Value="Top" />
91 <Setter Property="HorizontalAlignment" Value="Right" />
92 <Setter Property="Width" Value="100" />
93 <Setter Property="Width" Value="100" />
94 </Style>
95
96 <Style x:Key="CommonTextBlkStyle" TargetType="TextBlock">
97 <Setter Property="VerticalAlignment" Value="Center" />
98 <Setter Property="HorizontalAlignment" Value="Center" />
99 <Setter Property="FontWeight" Value="Bold" />
100 <Setter Property="TextAlignment" Value="Center" />
101 <Setter Property="TextWrapping" Value="WrapWithOverflow" />
102 <Setter Property="FontFamily" Value="{DynamicResource FontFamily}" />
103 </Style>
104
105 <Style x:Key="TitleTextBlkStyle"
106 BasedOn="{StaticResource CommonTextBlkStyle}"
107 TargetType="TextBlock">
108 <Setter Property="VerticalAlignment" Value="Top" />
109 <Setter Property="HorizontalAlignment" Value="Left" />
110 <Setter Property="FontSize" Value="{DynamicResource ResourceKey=FontSizeBranding}" />
111 <Setter Property="FontWeight" Value="ExtraBold" />
112 <Setter Property="Margin" Value="0,5,0,0" />
113 </Style>
114
115
116 <Style x:Key="LabelTextBlkStyle"
117 BasedOn="{StaticResource CommonTextBlkStyle}"
118 TargetType="TextBlock">
119 <Setter Property="HorizontalAlignment" Value="Left" />
120 <Setter Property="FontSize" Value="{DynamicResource FontSizeButton}" />
121 <Setter Property="FontWeight" Value="Bold" />
122 <Setter Property="TextAlignment" Value="Left" />
123 <Setter Property="Margin" Value="0,2,3,0" />
124 </Style>
125
126 <Style x:Key="DataTextBlkStyle"
127 BasedOn="{StaticResource CommonTextBlkStyle}"
128 TargetType="TextBlock">
129 <Setter Property="HorizontalAlignment" Value="Left" />
130 <Setter Property="FontSize" Value="{DynamicResource FontSizeMedium}" />
131 <Setter Property="TextAlignment" Value="Left" />
132 <Setter Property="Margin" Value="2,2,3,0" />
133 </Style>
134
135 <Style x:Key="StatusTextStyle"
136 BasedOn="{StaticResource CommonTextBlkStyle}"
137 TargetType="TextBlock">
138 <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MessageFontFamilyKey}}" />
139 <Setter Property="FontSize" Value="{DynamicResource FontSizeMedium}" />
140 <Setter Property="Margin" Value="8" />
141 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
142 <Setter Property="VerticalAlignment" Value="Top" />
143 <Setter Property="HorizontalAlignment" Value="Left" />
144 </Style>
145
146 <Style x:Key="ActionButtonStyle" TargetType="Button">
147 <Setter Property="MinHeight" Value="30" />
148 <Setter Property="MinWidth" Value="100" />
149 <Setter Property="Margin" Value="5,0,0,0" />
150 <Setter Property="VerticalAlignment" Value="Center" />
151 <Setter Property="VerticalContentAlignment" Value="Center" />
152 <Setter Property="HorizontalAlignment" Value="Center" />
153 <Setter Property="HorizontalContentAlignment" Value="Center" />
154 <Setter Property="FontFamily" Value="{DynamicResource FontFamily}" />
155 </Style>
156
157 <Style x:Key="FinalActionButtonStyle"
158 BasedOn="{StaticResource ActionButtonStyle}"
159 TargetType="Button">
160 <Setter Property="Margin" Value="40,0,0,0" />
161 </Style>
162
163 <Style x:Key="HyperlinkedButtonStyle" TargetType="Button">
164 <Setter Property="Margin" Value="0,2,0,0" />
165 <Setter Property="Template" Value="{StaticResource HyperlinkedButtonTemplateKey}" />
166 <Setter Property="IsHitTestVisible" Value="True" />
167 </Style>
168
169 <Style x:Key="LogoStyle" TargetType="Image">
170 <Setter Property="Height" Value="65" />
171 <Setter Property="Width" Value="102" />
172 <Setter Property="VerticalAlignment" Value="Top" />
173 <Setter Property="IsHitTestVisible" Value="False" />
174 <Setter Property="Source" Value="{DynamicResource LogoOverLightBackground}" />
175 <Style.Triggers>
176 <DataTrigger Binding="{Binding Path=IsLightBackground, Source={x:Static wixba:WindowProperties.Instance}}" Value="false">
177 <Setter Property="Source" Value="{DynamicResource LogoOverDarkBackground}" />
178 </DataTrigger>
179 </Style.Triggers>
180 </Style>
181
182 <Style x:Key="UpdateMarqueeStyle" TargetType="ProgressBar">
183 <Setter Property="Foreground" Value="{DynamicResource ProgressIndicatorBrush}" />
184 <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
185 <Setter Property="BorderThickness" Value="1" />
186 </Style>
187
188 <Style x:Key="ActionProgressStyle" TargetType="ProgressBar">
189 <Setter Property="Foreground" Value="{DynamicResource ProgressIndicatorBrush}" />
190 <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
191 <Setter Property="BorderThickness" Value="1" />
192 <Setter Property="Template" Value="{StaticResource ProgressBarTemplateKey}" />
193 </Style>
194</ResourceDictionary>
diff --git a/src/test/burn/WixToolset.WixBA/UpdateViewModel.cs b/src/test/burn/WixToolset.WixBA/UpdateViewModel.cs
new file mode 100644
index 00000000..80d894cb
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/UpdateViewModel.cs
@@ -0,0 +1,207 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.ComponentModel;
7 using System.Windows.Input;
8 using WixToolset.Mba.Core;
9
10 /// <summary>
11 /// The states of the update view model.
12 /// </summary>
13 public enum UpdateState
14 {
15 Unknown,
16 Initializing,
17 Checking,
18 Current,
19 Available,
20 Failed,
21 }
22
23 /// <summary>
24 /// The model of the update view.
25 /// </summary>
26 public class UpdateViewModel : PropertyNotifyBase
27 {
28 private RootViewModel root;
29 private UpdateState state;
30 private ICommand updateCommand;
31 private string updateVersion;
32 private string updateChanges;
33
34
35 public UpdateViewModel(RootViewModel root)
36 {
37 this.root = root;
38 WixBA.Model.Bootstrapper.DetectUpdateBegin += this.DetectUpdateBegin;
39 WixBA.Model.Bootstrapper.DetectUpdate += this.DetectUpdate;
40 WixBA.Model.Bootstrapper.DetectUpdateComplete += this.DetectUpdateComplete;
41
42 this.root.PropertyChanged += new PropertyChangedEventHandler(this.RootPropertyChanged);
43
44 this.State = UpdateState.Initializing;
45 }
46
47 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
48 {
49 if ("InstallState" == e.PropertyName)
50 {
51 base.OnPropertyChanged("CanUpdate");
52 }
53 }
54
55 public bool CheckingEnabled
56 {
57 get { return this.State == UpdateState.Initializing || this.State == UpdateState.Checking; }
58 }
59
60 public bool CanUpdate
61 {
62 get
63 {
64 switch(this.root.InstallState)
65 {
66 case InstallationState.Waiting:
67 case InstallationState.Applied:
68 case InstallationState.Failed:
69 return this.IsUpdateAvailable;
70 default:
71 return false;
72 }
73 }
74 }
75
76 public ICommand UpdateCommand
77 {
78 get
79 {
80 if (this.updateCommand == null)
81 {
82 this.updateCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.UpdateReplace), param => this.CanUpdate);
83 }
84
85 return this.updateCommand;
86 }
87 }
88
89 public bool IsUpdateAvailable
90 {
91 get { return this.State == UpdateState.Available; }
92 }
93
94 /// <summary>
95 /// Gets and sets the state of the update view model.
96 /// </summary>
97 public UpdateState State
98 {
99 get
100 {
101 return this.state;
102 }
103
104 set
105 {
106 if (this.state != value)
107 {
108 this.state = value;
109 base.OnPropertyChanged("State");
110 base.OnPropertyChanged("CanUpdate");
111 base.OnPropertyChanged("CheckingEnabled");
112 base.OnPropertyChanged("IsUpdateAvailable");
113 }
114 }
115 }
116 /// <summary>
117 /// The version of an available update.
118 /// </summary>
119 public string UpdateVersion
120 {
121 get
122 {
123 return updateVersion;
124 }
125 set
126 {
127 if (this.updateVersion != value)
128 {
129 this.updateVersion = value;
130 base.OnPropertyChanged("UpdateVersion");
131 }
132 }
133 }
134
135 /// <summary>
136 /// The changes in the available update.
137 /// </summary>
138 public string UpdateChanges
139 {
140 get
141 {
142 return updateChanges;
143 }
144 set
145 {
146 if (this.updateChanges != value)
147 {
148 this.updateChanges = value;
149 base.OnPropertyChanged("UpdateChanges");
150 }
151 }
152 }
153
154 private void DetectUpdateBegin(object sender, DetectUpdateBeginEventArgs e)
155 {
156 // Don't check for updates if:
157 // the first check failed (no retry)
158 // if we are being run as an uninstall
159 // if we are not under a full UI.
160 if ((UpdateState.Failed != this.State) && (LaunchAction.Uninstall != WixBA.Model.Command.Action) && (Display.Full == WixBA.Model.Command.Display))
161 {
162 this.State = UpdateState.Checking;
163 e.Skip = false;
164 }
165 }
166
167 private void DetectUpdate(object sender, DetectUpdateEventArgs e)
168 {
169 // The list of updates is sorted in descending version, so the first callback should be the largest update available.
170 // This update should be either larger than ours (so we are out of date), the same as ours (so we are current)
171 // or smaller than ours (we have a private build). If we really wanted to, we could leave the e.StopProcessingUpdates alone and
172 // enumerate all of the updates.
173 WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Potential update v{0} from '{1}'; current version: v{2}", e.Version, e.UpdateLocation, WixBA.Model.Version));
174 if (WixBA.Model.Engine.CompareVersions(e.Version, WixBA.Model.Version) > 0)
175 {
176 WixBA.Model.Engine.SetUpdate(null, e.UpdateLocation, e.Size, UpdateHashType.None, null);
177 this.UpdateVersion = String.Concat("v", e.Version.ToString());
178 string changesFormat = @"<body style='overflow: auto;'>{0}</body>";
179 this.UpdateChanges = String.Format(changesFormat, e.Content);
180 this.State = UpdateState.Available;
181 }
182 else
183 {
184 this.State = UpdateState.Current;
185 }
186 e.StopProcessingUpdates = true;
187 }
188
189 private void DetectUpdateComplete(object sender, DetectUpdateCompleteEventArgs e)
190 {
191 // Failed to process an update, allow the existing bundle to still install.
192 if ((UpdateState.Failed != this.State) && !Hresult.Succeeded(e.Status))
193 {
194 this.State = UpdateState.Failed;
195 WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Failed to locate an update, status of 0x{0:X8}, updates disabled.", e.Status));
196 e.IgnoreError = true;
197 }
198 // If we are uninstalling, we don't want to check or show an update
199 // If we are checking, then the feed didn't find any valid enclosures
200 // If we are initializing, we're either uninstalling or not a full UI
201 else if ((LaunchAction.Uninstall == WixBA.Model.Command.Action) || (UpdateState.Initializing == this.State) || (UpdateState.Checking == this.State))
202 {
203 this.State = UpdateState.Unknown;
204 }
205 }
206 }
207}
diff --git a/src/test/burn/WixToolset.WixBA/WindowProperties.cs b/src/test/burn/WixToolset.WixBA/WindowProperties.cs
new file mode 100644
index 00000000..6d1e273c
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WindowProperties.cs
@@ -0,0 +1,65 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Windows;
7 using System.Windows.Media;
8
9 /// <summary>
10 /// Dependency Properties associated with the main Window object.
11 /// </summary>
12 public class WindowProperties : DependencyObject
13 {
14 /// <summary>
15 /// Dependency Property to hold the result of detecting the relative luminosity (or brightness) of a Windows background.
16 /// </summary>
17 public static readonly DependencyProperty IsLightBackgroundProperty = DependencyProperty.Register(
18 "IsLightBackground", typeof(bool), typeof(WindowProperties), new PropertyMetadata( false ));
19
20 private static Lazy<WindowProperties> _instance = new Lazy<WindowProperties>(() =>
21 {
22 WindowProperties wp = new WindowProperties();
23 wp.CheckBackgroundBrightness();
24 return wp;
25 });
26
27 public static WindowProperties Instance
28 {
29 get
30 {
31 return _instance.Value;
32 }
33 }
34
35
36 public bool IsLightBackground
37 {
38 get { return (bool)GetValue(IsLightBackgroundProperty); }
39 private set { SetValue(IsLightBackgroundProperty, value); }
40 }
41
42 /// <summary>
43 /// Use the Luminosity parameter of the background color to detect light vs dark theme settings.
44 /// </summary>
45 /// <remarks>
46 /// This approach detects both the common High Contrast themes (White vs Black) and custom themes which may have relatively lighter backgrounds.
47 /// </remarks>
48 public void CheckBackgroundBrightness()
49 {
50 SolidColorBrush windowbrush = System.Windows.SystemColors.WindowBrush;
51 System.Drawing.Color dcolor = System.Drawing.Color.FromArgb(windowbrush.Color.A, windowbrush.Color.R, windowbrush.Color.G, windowbrush.Color.B);
52
53 var brightness = dcolor.GetBrightness();
54 // Test for 'Lightness' at an arbitrary point, approaching 1.0 (White).
55 if (0.7 < brightness)
56 {
57 this.IsLightBackground = true;
58 }
59 else
60 {
61 this.IsLightBackground = false;
62 }
63 }
64 }
65}
diff --git a/src/test/burn/WixToolset.WixBA/WixBA.BootstrapperCore.config b/src/test/burn/WixToolset.WixBA/WixBA.BootstrapperCore.config
new file mode 100644
index 00000000..da8f8028
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WixBA.BootstrapperCore.config
@@ -0,0 +1,16 @@
1<?xml version="1.0" encoding="utf-8" ?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<configuration>
5 <configSections>
6 <sectionGroup name="wix.bootstrapper" type="WixToolset.Mba.Host.BootstrapperSectionGroup, WixToolset.Mba.Host">
7 <section name="host" type="WixToolset.Mba.Host.HostSection, WixToolset.Mba.Host" />
8 </sectionGroup>
9 </configSections>
10 <startup useLegacyV2RuntimeActivationPolicy="true">
11 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
12 </startup>
13 <wix.bootstrapper>
14 <host assemblyName="WixToolset.WixBA" />
15 </wix.bootstrapper>
16</configuration>
diff --git a/src/test/burn/WixToolset.WixBA/WixBA.cs b/src/test/burn/WixToolset.WixBA/WixBA.cs
new file mode 100644
index 00000000..2d680c7e
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WixBA.cs
@@ -0,0 +1,229 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.WixBA
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Net;
10 using System.Text;
11 using WixToolset.Mba.Core;
12
13 using Threading = System.Windows.Threading;
14 using WinForms = System.Windows.Forms;
15
16 /// <summary>
17 /// The WiX toolset bootstrapper application.
18 /// </summary>
19 public class WixBA : BootstrapperApplication
20 {
21 public WixBA(IEngine engine, IBootstrapperCommand command)
22 : base(engine)
23 {
24 this.Command = command;
25
26 this.BAManifest = new BootstrapperApplicationData();
27 }
28
29 internal IBootstrapperApplicationData BAManifest { get; }
30
31 internal IBootstrapperCommand Command { get; }
32
33 internal IEngine Engine => this.engine;
34
35 /// <summary>
36 /// Gets the global model.
37 /// </summary>
38 static public Model Model { get; private set; }
39
40 /// <summary>
41 /// Gets the global view.
42 /// </summary>
43 static public RootView View { get; private set; }
44 // TODO: We should refactor things so we dont have a global View.
45
46 /// <summary>
47 /// Gets the global dispatcher.
48 /// </summary>
49 static public Threading.Dispatcher Dispatcher { get; private set; }
50
51 /// <summary>
52 /// Launches the default web browser to the provided URI.
53 /// </summary>
54 /// <param name="uri">URI to open the web browser.</param>
55 public static void LaunchUrl(string uri)
56 {
57 WixBA.UseShellExecute(uri);
58 }
59
60 /// <summary>
61 /// Open a log file.
62 /// </summary>
63 /// <param name="uri">URI to a log file.</param>
64 internal static void OpenLog(Uri uri)
65 {
66 WixBA.UseShellExecute(uri.ToString());
67 }
68
69 /// <summary>
70 /// Open a log folder.
71 /// </summary>
72 /// <param name="string">path to a log folder.</param>
73 internal static void OpenLogFolder(string logFolder)
74 {
75 WixBA.UseShellExecute(logFolder);
76 }
77
78 /// <summary>
79 /// Open a log folder.
80 /// </summary>
81 /// <param name="uri">path to a log folder.</param>
82 private static void UseShellExecute(string path)
83 {
84 // Switch the wait cursor since shellexec can take a second or so.
85 System.Windows.Input.Cursor cursor = WixBA.View.Cursor;
86 WixBA.View.Cursor = System.Windows.Input.Cursors.Wait;
87 Process process = null;
88 try
89 {
90 process = new Process();
91 process.StartInfo.FileName = path;
92 process.StartInfo.UseShellExecute = true;
93 process.StartInfo.Verb = "open";
94
95 process.Start();
96 }
97 finally
98 {
99 if (null != process)
100 {
101 process.Dispose();
102 }
103 // back to the original cursor.
104 WixBA.View.Cursor = cursor;
105 }
106 }
107
108 /// <summary>
109 /// Starts planning the appropriate action.
110 /// </summary>
111 /// <param name="action">Action to plan.</param>
112 public static void Plan(LaunchAction action)
113 {
114 WixBA.Model.PlannedAction = action;
115 WixBA.Model.Engine.Plan(WixBA.Model.PlannedAction);
116 }
117
118 public static void PlanLayout()
119 {
120 // Either default or set the layout directory
121 if (String.IsNullOrEmpty(WixBA.Model.Command.LayoutDirectory))
122 {
123 WixBA.Model.LayoutDirectory = Directory.GetCurrentDirectory();
124
125 // Ask the user for layout folder if one wasn't provided and we're in full UI mode
126 if (WixBA.Model.Command.Display == Display.Full)
127 {
128 WixBA.Dispatcher.Invoke((Action)delegate()
129 {
130 WinForms.FolderBrowserDialog browserDialog = new WinForms.FolderBrowserDialog();
131 browserDialog.RootFolder = Environment.SpecialFolder.MyComputer;
132
133 // Default to the current directory.
134 browserDialog.SelectedPath = WixBA.Model.LayoutDirectory;
135 WinForms.DialogResult result = browserDialog.ShowDialog();
136
137 if (WinForms.DialogResult.OK == result)
138 {
139 WixBA.Model.LayoutDirectory = browserDialog.SelectedPath;
140 WixBA.Plan(WixBA.Model.Command.Action);
141 }
142 else
143 {
144 WixBA.View.Close();
145 }
146 }
147 );
148 }
149 }
150 else
151 {
152 WixBA.Model.LayoutDirectory = WixBA.Model.Command.LayoutDirectory;
153 WixBA.Plan(WixBA.Model.Command.Action);
154 }
155 }
156
157 /// <summary>
158 /// Thread entry point for WiX Toolset Bootstrapper Application.
159 /// </summary>
160 protected override void Run()
161 {
162 this.Engine.Log(LogLevel.Verbose, "Running the WiX BA.");
163 WixBA.Model = new Model(this);
164 WixBA.Dispatcher = Threading.Dispatcher.CurrentDispatcher;
165 RootViewModel viewModel = new RootViewModel();
166
167 // Kick off detect which will populate the view models.
168 this.Engine.Detect();
169
170 // Create a Window to show UI.
171 if (WixBA.Model.Command.Display == Display.Passive ||
172 WixBA.Model.Command.Display == Display.Full)
173 {
174 this.Engine.Log(LogLevel.Verbose, "Creating a UI.");
175 WixBA.View = new RootView(viewModel);
176 WixBA.View.Show();
177 }
178
179 Threading.Dispatcher.Run();
180
181 this.PostTelemetry();
182 this.Engine.Quit(WixBA.Model.Result);
183 }
184
185 private void PostTelemetry()
186 {
187 string result = String.Concat("0x", WixBA.Model.Result.ToString("x"));
188
189 StringBuilder telemetryData = new StringBuilder();
190 foreach (KeyValuePair<string, string> kvp in WixBA.Model.Telemetry)
191 {
192 telemetryData.AppendFormat("{0}={1}+", kvp.Key, kvp.Value);
193 }
194 telemetryData.AppendFormat("Result={0}", result);
195
196 byte[] data = Encoding.UTF8.GetBytes(telemetryData.ToString());
197
198 try
199 {
200 HttpWebRequest post = WixBA.Model.CreateWebRequest(String.Format(WixDistribution.TelemetryUrlFormat, WixBA.Model.Version.ToString(), result));
201 post.Method = "POST";
202 post.ContentType = "application/x-www-form-urlencoded";
203 post.ContentLength = data.Length;
204
205 using (Stream postStream = post.GetRequestStream())
206 {
207 postStream.Write(data, 0, data.Length);
208 }
209
210 HttpWebResponse response = (HttpWebResponse)post.GetResponse();
211 }
212 catch (ArgumentException)
213 {
214 }
215 catch (FormatException)
216 {
217 }
218 catch (OverflowException)
219 {
220 }
221 catch (ProtocolViolationException)
222 {
223 }
224 catch (WebException)
225 {
226 }
227 }
228 }
229}
diff --git a/src/test/burn/WixToolset.WixBA/WixBAFactory.cs b/src/test/burn/WixToolset.WixBA/WixBAFactory.cs
new file mode 100644
index 00000000..67fcc4b5
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WixBAFactory.cs
@@ -0,0 +1,17 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3// Identifies the class that derives from IBootstrapperApplicationFactory and is the BAFactory class that gets
4// instantiated by the interop layer
5[assembly: WixToolset.Mba.Core.BootstrapperApplicationFactory(typeof(WixToolset.WixBA.WixBAFactory))]
6namespace WixToolset.WixBA
7{
8 using WixToolset.Mba.Core;
9
10 public class WixBAFactory : BaseBootstrapperApplicationFactory
11 {
12 protected override IBootstrapperApplication Create(IEngine engine, IBootstrapperCommand command)
13 {
14 return new WixBA(engine, command);
15 }
16 }
17}
diff --git a/src/test/burn/WixToolset.WixBA/WixDistribution.cs b/src/test/burn/WixToolset.WixBA/WixDistribution.cs
new file mode 100644
index 00000000..0d20e585
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WixDistribution.cs
@@ -0,0 +1,118 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.Diagnostics;
5using System.Reflection;
6using System.Resources;
7
8[assembly: AssemblyCompany(".NET Foundation")]
9[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and contributors. All rights reserved.")]
10[assembly: AssemblyProduct("WiX Toolset")]
11
12#if DEBUG
13 [assembly: AssemblyConfiguration("DEBUG")]
14#else
15 [assembly: AssemblyConfiguration("")]
16#endif
17[assembly: NeutralResourcesLanguage("en-US")]
18
19namespace WixToolset
20{
21 /// <summary>
22 /// Distribution specific strings.
23 /// </summary>
24 internal static class WixDistribution
25 {
26 /// <summary>
27 /// News URL for the distribution.
28 /// </summary>
29 public static string NewsUrl = "http://wixtoolset.org/news/";
30
31 /// <summary>
32 /// Short product name for the distribution.
33 /// </summary>
34 public static string ShortProduct = "WiX Toolset";
35
36 /// <summary>
37 /// Support URL for the distribution.
38 /// </summary>
39 public static string SupportUrl = "http://wixtoolset.org/";
40
41 /// <summary>
42 /// Telemetry URL format for the distribution.
43 /// </summary>
44 public static string TelemetryUrlFormat = "http://wixtoolset.org/integrationtelemetry/v{0}/?r={1}";
45
46 /// <summary>
47 /// VS Extensions Landing page Url for the distribution.
48 /// </summary>
49 public static string VSExtensionsLandingUrl = "http://wixtoolset.org/releases/";
50
51 public static string ReplacePlaceholders(string original, Assembly assembly)
52 {
53 if (null != assembly)
54 {
55 FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
56
57 original = original.Replace("[FileComments]", fileVersion.Comments);
58 original = original.Replace("[FileCopyright]", fileVersion.LegalCopyright);
59 original = original.Replace("[FileProductName]", fileVersion.ProductName);
60 original = original.Replace("[FileVersion]", fileVersion.FileVersion);
61
62 if (original.Contains("[FileVersionMajorMinor]"))
63 {
64 Version version = new Version(fileVersion.FileVersion);
65 original = original.Replace("[FileVersionMajorMinor]", String.Concat(version.Major, ".", version.Minor));
66 }
67
68 AssemblyCompanyAttribute company;
69 if (WixDistribution.TryGetAttribute(assembly, out company))
70 {
71 original = original.Replace("[AssemblyCompany]", company.Company);
72 }
73
74 AssemblyCopyrightAttribute copyright;
75 if (WixDistribution.TryGetAttribute(assembly, out copyright))
76 {
77 original = original.Replace("[AssemblyCopyright]", copyright.Copyright);
78 }
79
80 AssemblyDescriptionAttribute description;
81 if (WixDistribution.TryGetAttribute(assembly, out description))
82 {
83 original = original.Replace("[AssemblyDescription]", description.Description);
84 }
85
86 AssemblyProductAttribute product;
87 if (WixDistribution.TryGetAttribute(assembly, out product))
88 {
89 original = original.Replace("[AssemblyProduct]", product.Product);
90 }
91
92 AssemblyTitleAttribute title;
93 if (WixDistribution.TryGetAttribute(assembly, out title))
94 {
95 original = original.Replace("[AssemblyTitle]", title.Title);
96 }
97 }
98
99 original = original.Replace("[NewsUrl]", WixDistribution.NewsUrl);
100 original = original.Replace("[ShortProduct]", WixDistribution.ShortProduct);
101 original = original.Replace("[SupportUrl]", WixDistribution.SupportUrl);
102 return original;
103 }
104
105 private static bool TryGetAttribute<T>(Assembly assembly, out T attribute) where T : Attribute
106 {
107 attribute = null;
108
109 object[] customAttributes = assembly.GetCustomAttributes(typeof(T), false);
110 if (null != customAttributes && 0 < customAttributes.Length)
111 {
112 attribute = customAttributes[0] as T;
113 }
114
115 return null != attribute;
116 }
117 }
118}
diff --git a/src/test/burn/WixToolset.WixBA/WixToolset.WixBA.csproj b/src/test/burn/WixToolset.WixBA/WixToolset.WixBA.csproj
new file mode 100644
index 00000000..72ef5795
--- /dev/null
+++ b/src/test/burn/WixToolset.WixBA/WixToolset.WixBA.csproj
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<Project Sdk="Microsoft.NET.Sdk">
4 <PropertyGroup>
5 <TargetFramework>net45</TargetFramework>
6 <AssemblyName>WixToolset.WixBA</AssemblyName>
7 <RootNamespace>WixToolset.WixBA</RootNamespace>
8 <DebugType>embedded</DebugType>
9 <RuntimeIdentifier>win-x86</RuntimeIdentifier>
10 <AssemblyTitle>WixToolset.WixBA</AssemblyTitle>
11 <Description>WiX Bootstrapper Application</Description>
12 <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
13 <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
14 <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
15 <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
16 </PropertyGroup>
17 <ItemGroup>
18 <Page Include="Styles.xaml">
19 <Generator>MSBuild:Compile</Generator>
20 <SubType>Designer</SubType>
21 </Page>
22 <Page Include="RootView.xaml">
23 <Generator>MSBuild:Compile</Generator>
24 <SubType>Designer</SubType>
25 </Page>
26 <None Include="WixBA.BootstrapperCore.config">
27 <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
28 <SubType>Designer</SubType>
29 </None>
30 </ItemGroup>
31 <ItemGroup>
32 <Resource Include="Resources\logo-white-hollow.png" />
33 <Resource Include="Resources\logo-black-hollow.png" />
34 </ItemGroup>
35 <ItemGroup>
36 <Reference Include="PresentationCore" />
37 <Reference Include="PresentationFramework" />
38 <Reference Include="System.Windows.Forms" />
39 <Reference Include="System.Xaml" />
40 <Reference Include="WindowsBase" />
41 </ItemGroup>
42 <ItemGroup>
43 <PackageReference Include="WixToolset.Mba.Core" Version="4.0.0-preview.0-build.0" />
44 </ItemGroup>
45
46 <ItemGroup>
47 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
48 <PackageReference Include="GitInfo" Version="2.1.2" PrivateAssets="All" />
49 </ItemGroup>
50</Project> \ No newline at end of file