aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/WixToolset.WixBA/BrowserProperties.cs40
-rw-r--r--src/WixToolset.WixBA/Hresult.cs22
-rw-r--r--src/WixToolset.WixBA/InstallationViewModel.cs670
-rw-r--r--src/WixToolset.WixBA/Model.cs129
-rw-r--r--src/WixToolset.WixBA/NewsItem.cs18
-rw-r--r--src/WixToolset.WixBA/ProgressViewModel.cs194
-rw-r--r--src/WixToolset.WixBA/Properties/AssemblyInfo.cs22
-rw-r--r--src/WixToolset.WixBA/PropertyNotifyBase.cs59
-rw-r--r--src/WixToolset.WixBA/RelayCommand.cs45
-rw-r--r--src/WixToolset.WixBA/Resources/logo-black-hollow.pngbin0 -> 47472 bytes
-rw-r--r--src/WixToolset.WixBA/Resources/logo-white-hollow.pngbin0 -> 60557 bytes
-rw-r--r--src/WixToolset.WixBA/RootView.xaml420
-rw-r--r--src/WixToolset.WixBA/RootView.xaml.cs51
-rw-r--r--src/WixToolset.WixBA/RootViewModel.cs204
-rw-r--r--src/WixToolset.WixBA/Styles.xaml194
-rw-r--r--src/WixToolset.WixBA/UpdateViewModel.cs207
-rw-r--r--src/WixToolset.WixBA/WindowProperties.cs65
-rw-r--r--src/WixToolset.WixBA/WixBA.BootstrapperCore.config16
-rw-r--r--src/WixToolset.WixBA/WixBA.cs216
-rw-r--r--src/WixToolset.WixBA/WixBA.csproj64
20 files changed, 2636 insertions, 0 deletions
diff --git a/src/WixToolset.WixBA/BrowserProperties.cs b/src/WixToolset.WixBA/BrowserProperties.cs
new file mode 100644
index 00000000..f40d4ed9
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/Hresult.cs b/src/WixToolset.WixBA/Hresult.cs
new file mode 100644
index 00000000..68b4c5ea
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/InstallationViewModel.cs b/src/WixToolset.WixBA/InstallationViewModel.cs
new file mode 100644
index 00000000..6bec427a
--- /dev/null
+++ b/src/WixToolset.WixBA/InstallationViewModel.cs
@@ -0,0 +1,670 @@
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.UX
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.Bootstrapper;
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.CacheAcquireComplete += this.CacheAcquireComplete;
83 WixBA.Model.Bootstrapper.ExecutePackageBegin += this.ExecutePackageBegin;
84 WixBA.Model.Bootstrapper.ExecutePackageComplete += this.ExecutePackageComplete;
85 WixBA.Model.Bootstrapper.Error += this.ExecuteError;
86 WixBA.Model.Bootstrapper.ResolveSource += this.ResolveSource;
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.StringVariables["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.StringVariables["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.Bootstrapper.BAManifest.Bundle.Packages.ContainsKey(e.ProductCode))
407 {
408 WixBA.Model.Bootstrapper.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 IEnumerable<PackageInfo> relatedPackages = WixBA.Model.Bootstrapper.BAManifest.Bundle.Packages.Values.Where(p => p.Type == PackageType.UpgradeBundle);
430 Version 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 CacheAcquireComplete(object sender, CacheAcquireCompleteEventArgs e)
496 {
497 this.AddPackageTelemetry("Cache", e.PackageOrContainerId ?? String.Empty, DateTime.Now.Subtract(this.cachePackageStart).TotalMilliseconds, e.Status);
498 }
499
500 private void ExecutePackageBegin(object sender, ExecutePackageBeginEventArgs e)
501 {
502 lock (this)
503 {
504 this.executePackageStart = e.ShouldExecute ? DateTime.Now : DateTime.MinValue;
505 }
506 }
507
508 private void ExecutePackageComplete(object sender, ExecutePackageCompleteEventArgs e)
509 {
510 lock (this)
511 {
512 if (DateTime.MinValue < this.executePackageStart)
513 {
514 this.AddPackageTelemetry("Execute", e.PackageId ?? String.Empty, DateTime.Now.Subtract(this.executePackageStart).TotalMilliseconds, e.Status);
515 this.executePackageStart = DateTime.MinValue;
516 }
517 }
518 }
519
520 private void ExecuteError(object sender, ErrorEventArgs e)
521 {
522 lock (this)
523 {
524 if (!this.root.Canceled)
525 {
526 // If the error is a cancel coming from the engine during apply we want to go back to the preapply state.
527 if (InstallationState.Applying == this.root.InstallState && (int)Error.UserCancelled == e.ErrorCode)
528 {
529 this.root.InstallState = this.root.PreApplyState;
530 }
531 else
532 {
533 this.Message = e.ErrorMessage;
534
535 if (Display.Full == WixBA.Model.Command.Display)
536 {
537 // On HTTP authentication errors, have the engine try to do authentication for us.
538 if (ErrorType.HttpServerAuthentication == e.ErrorType || ErrorType.HttpProxyAuthentication == e.ErrorType)
539 {
540 e.Result = Result.TryAgain;
541 }
542 else // show an error dialog.
543 {
544 MessageBoxButton msgbox = MessageBoxButton.OK;
545 switch (e.UIHint & 0xF)
546 {
547 case 0:
548 msgbox = MessageBoxButton.OK;
549 break;
550 case 1:
551 msgbox = MessageBoxButton.OKCancel;
552 break;
553 // There is no 2! That would have been MB_ABORTRETRYIGNORE.
554 case 3:
555 msgbox = MessageBoxButton.YesNoCancel;
556 break;
557 case 4:
558 msgbox = MessageBoxButton.YesNo;
559 break;
560 // default: stay with MBOK since an exact match is not available.
561 }
562
563 MessageBoxResult result = MessageBoxResult.None;
564 WixBA.View.Dispatcher.Invoke((Action)delegate()
565 {
566 result = MessageBox.Show(WixBA.View, e.ErrorMessage, "WiX Toolset", msgbox, MessageBoxImage.Error);
567 }
568 );
569
570 // If there was a match from the UI hint to the msgbox value, use the result from the
571 // message box. Otherwise, we'll ignore it and return the default to Burn.
572 if ((e.UIHint & 0xF) == (int)msgbox)
573 {
574 e.Result = (Result)result;
575 }
576 }
577 }
578 }
579 }
580 else // canceled, so always return cancel.
581 {
582 e.Result = Result.Cancel;
583 }
584 }
585 }
586
587 private void ResolveSource(object sender, ResolveSourceEventArgs e)
588 {
589 int retries = 0;
590
591 this.downloadRetries.TryGetValue(e.PackageOrContainerId, out retries);
592 this.downloadRetries[e.PackageOrContainerId] = retries + 1;
593
594 e.Action = retries < 3 && !String.IsNullOrEmpty(e.DownloadSource) ? BOOTSTRAPPER_RESOLVESOURCE_ACTION.Download : BOOTSTRAPPER_RESOLVESOURCE_ACTION.None;
595 }
596
597 private void ApplyComplete(object sender, ApplyCompleteEventArgs e)
598 {
599 WixBA.Model.Result = e.Status; // remember the final result of the apply.
600
601 // Set the state to applied or failed unless the state has already been set back to the preapply state
602 // which means we need to show the UI as it was before the apply started.
603 if (this.root.InstallState != this.root.PreApplyState)
604 {
605 this.root.InstallState = Hresult.Succeeded(e.Status) ? InstallationState.Applied : InstallationState.Failed;
606 }
607
608 // If we're not in Full UI mode, we need to alert the dispatcher to stop and close the window for passive.
609 if (Bootstrapper.Display.Full != WixBA.Model.Command.Display)
610 {
611 // If its passive, send a message to the window to close.
612 if (Bootstrapper.Display.Passive == WixBA.Model.Command.Display)
613 {
614 WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window for non-interactive install");
615 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
616 }
617 else
618 {
619 WixBA.Dispatcher.InvokeShutdown();
620 }
621 return;
622 }
623 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.
624 {
625 WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window since update successful.");
626 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
627 return;
628 }
629 else if (root.AutoClose)
630 {
631 // Automatically closing since the user clicked the X button.
632 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
633 return;
634 }
635
636 // Force all commands to reevaluate CanExecute.
637 // InvalidateRequerySuggested must be run on the UI thread.
638 root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested));
639 }
640
641 private void ParseCommandLine()
642 {
643 // Get array of arguments based on the system parsing algorithm.
644 string[] args = WixBA.Model.Command.GetCommandLineArgs();
645 for (int i = 0; i < args.Length; ++i)
646 {
647 if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase))
648 {
649 // Allow relative directory paths. Also validates.
650 string[] param = args[i].Split(new char[] {'='}, 2);
651 this.root.InstallDirectory = IO.Path.Combine(Environment.CurrentDirectory, param[1]);
652 }
653 }
654 }
655
656 private void AddPackageTelemetry(string prefix, string id, double time, int result)
657 {
658 lock (this)
659 {
660 string key = String.Format("{0}Time_{1}", prefix, id);
661 string value = time.ToString();
662 WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value));
663
664 key = String.Format("{0}Result_{1}", prefix, id);
665 value = String.Concat("0x", result.ToString("x"));
666 WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value));
667 }
668 }
669 }
670}
diff --git a/src/WixToolset.WixBA/Model.cs b/src/WixToolset.WixBA/Model.cs
new file mode 100644
index 00000000..9f03e95b
--- /dev/null
+++ b/src/WixToolset.WixBA/Model.cs
@@ -0,0 +1,129 @@
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.UX
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Net;
8 using WixToolset.Bootstrapper;
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 UX.
21 /// </summary>
22 /// <param name="bootstrapper">Bootstrapper hosting the UX.</param>
23 public Model(BootstrapperApplication bootstrapper)
24 {
25 this.Bootstrapper = bootstrapper;
26 this.Telemetry = new List<KeyValuePair<string, string>>();
27 this.Version = this.Engine.VersionVariables[BurnBundleVersionVariable];
28 }
29
30 /// <summary>
31 /// Gets the bootstrapper.
32 /// </summary>
33 public BootstrapperApplication Bootstrapper { get; private set; }
34
35 /// <summary>
36 /// Gets the bootstrapper command-line.
37 /// </summary>
38 public Command Command { get { return this.Bootstrapper.Command; } }
39
40 /// <summary>
41 /// Gets the bootstrapper engine.
42 /// </summary>
43 public Engine Engine { get { return this.Bootstrapper.Engine; } }
44
45 /// <summary>
46 /// Gets the key/value pairs used in telemetry.
47 /// </summary>
48 public List<KeyValuePair<string, string>> Telemetry { get; private set; }
49
50 /// <summary>
51 /// Get or set the final result of the installation.
52 /// </summary>
53 public int Result { get; set; }
54
55 /// <summary>
56 /// Get the version of the install.
57 /// </summary>
58 public Version Version { get; private set; }
59
60 /// <summary>
61 /// Get or set the path where the bundle is installed.
62 /// </summary>
63 public string InstallDirectory
64 {
65 get
66 {
67 if (!this.Engine.StringVariables.Contains(BurnBundleInstallDirectoryVariable))
68 {
69 return null;
70 }
71
72 return this.Engine.StringVariables[BurnBundleInstallDirectoryVariable];
73 }
74
75 set
76 {
77 this.Engine.StringVariables[BurnBundleInstallDirectoryVariable] = value;
78 }
79 }
80
81 /// <summary>
82 /// Get or set the path for the layout to be created.
83 /// </summary>
84 public string LayoutDirectory
85 {
86 get
87 {
88 if (!this.Engine.StringVariables.Contains(BurnBundleLayoutDirectoryVariable))
89 {
90 return null;
91 }
92
93 return this.Engine.StringVariables[BurnBundleLayoutDirectoryVariable];
94 }
95
96 set
97 {
98 this.Engine.StringVariables[BurnBundleLayoutDirectoryVariable] = value;
99 }
100 }
101
102 public LaunchAction PlannedAction { get; set; }
103
104 /// <summary>
105 /// Creates a correctly configured HTTP web request.
106 /// </summary>
107 /// <param name="uri">URI to connect to.</param>
108 /// <returns>Correctly configured HTTP web request.</returns>
109 public HttpWebRequest CreateWebRequest(string uri)
110 {
111 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
112 request.UserAgent = String.Concat("WixInstall", this.Version.ToString());
113
114 return request;
115 }
116
117 /// <summary>
118 /// Gets the display name for a package if possible.
119 /// </summary>
120 /// <param name="packageId">Identity of the package to find the display name.</param>
121 /// <returns>Display name of the package if found or the package id if not.</returns>
122 public string GetPackageName(string packageId)
123 {
124 PackageInfo package;
125
126 return this.Bootstrapper.BAManifest.Bundle.Packages.TryGetValue(packageId, out package) ? package.DisplayName : packageId;
127 }
128 }
129}
diff --git a/src/WixToolset.WixBA/NewsItem.cs b/src/WixToolset.WixBA/NewsItem.cs
new file mode 100644
index 00000000..a8350104
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/ProgressViewModel.cs b/src/WixToolset.WixBA/ProgressViewModel.cs
new file mode 100644
index 00000000..30aee5f1
--- /dev/null
+++ b/src/WixToolset.WixBA/ProgressViewModel.cs
@@ -0,0 +1,194 @@
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.Bootstrapper;
9
10namespace WixToolset.UX
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.PlanBegin += this.PlanBegin;
37 WixBA.Model.Bootstrapper.PlanPackageComplete += this.PlanPackageComplete;
38 WixBA.Model.Bootstrapper.ApplyBegin += this.ApplyBegin;
39 WixBA.Model.Bootstrapper.Progress += this.ApplyProgress;
40 WixBA.Model.Bootstrapper.CacheAcquireProgress += this.CacheAcquireProgress;
41 WixBA.Model.Bootstrapper.CacheComplete += this.CacheComplete;
42 }
43
44 public bool ProgressEnabled
45 {
46 get { return this.root.InstallState == InstallationState.Applying; }
47 }
48
49 public int Progress
50 {
51 get
52 {
53 return this.progress;
54 }
55
56 set
57 {
58 if (this.progress != value)
59 {
60 this.progress = value;
61 base.OnPropertyChanged("Progress");
62 }
63 }
64 }
65
66 public string Package
67 {
68 get
69 {
70 return this.package;
71 }
72
73 set
74 {
75 if (this.package != value)
76 {
77 this.package = value;
78 base.OnPropertyChanged("Package");
79 }
80 }
81 }
82
83 public string Message
84 {
85 get
86 {
87 return this.message;
88 }
89
90 set
91 {
92 if (this.message != value)
93 {
94 this.message = value;
95 base.OnPropertyChanged("Message");
96 }
97 }
98 }
99
100 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
101 {
102 if ("InstallState" == e.PropertyName)
103 {
104 base.OnPropertyChanged("ProgressEnabled");
105 }
106 }
107
108 private void PlanBegin(object sender, PlanBeginEventArgs e)
109 {
110 lock (this)
111 {
112 this.executingPackageOrderIndex.Clear();
113 }
114 }
115
116 private void PlanPackageComplete(object sender, PlanPackageCompleteEventArgs e)
117 {
118 if (ActionState.None != e.Execute)
119 {
120 lock (this)
121 {
122 Debug.Assert(!this.executingPackageOrderIndex.ContainsKey(e.PackageId));
123 this.executingPackageOrderIndex.Add(e.PackageId, this.executingPackageOrderIndex.Count);
124 }
125 }
126 }
127
128 private void ExecutePackageBegin(object sender, ExecutePackageBeginEventArgs e)
129 {
130 lock (this)
131 {
132 this.Package = WixBA.Model.GetPackageName(e.PackageId);
133 this.Message = String.Format("Processing: {0}", this.Package);
134 e.Cancel = this.root.Canceled;
135 }
136 }
137
138 private void ExecutePackageComplete(object sender, ExecutePackageCompleteEventArgs e)
139 {
140 lock (this)
141 { // avoid a stale display
142 this.Message = String.Empty;
143 }
144 }
145
146 private void ApplyBegin(object sender, ApplyBeginEventArgs e)
147 {
148 this.progressPhases = e.PhaseCount;
149 }
150
151 private void ApplyProgress(object sender, ProgressEventArgs e)
152 {
153 lock (this)
154 {
155 e.Cancel = this.root.Canceled;
156 }
157 }
158
159 private void CacheAcquireProgress(object sender, CacheAcquireProgressEventArgs e)
160 {
161 lock (this)
162 {
163 this.cacheProgress = e.OverallPercentage;
164 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
165 e.Cancel = this.root.Canceled;
166 }
167 }
168
169 private void CacheComplete(object sender, CacheCompleteEventArgs e)
170 {
171 lock (this)
172 {
173 this.cacheProgress = 100;
174 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
175 }
176 }
177
178 private void ApplyExecuteProgress(object sender, ExecuteProgressEventArgs e)
179 {
180 lock (this)
181 {
182 this.executeProgress = e.OverallPercentage;
183 this.Progress = (this.cacheProgress + this.executeProgress) / this.progressPhases;
184
185 if (WixBA.Model.Command.Display == Display.Embedded)
186 {
187 WixBA.Model.Engine.SendEmbeddedProgress(e.ProgressPercentage, this.Progress);
188 }
189
190 e.Cancel = this.root.Canceled;
191 }
192 }
193 }
194}
diff --git a/src/WixToolset.WixBA/Properties/AssemblyInfo.cs b/src/WixToolset.WixBA/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..58d88046
--- /dev/null
+++ b/src/WixToolset.WixBA/Properties/AssemblyInfo.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
3using System;
4using System.Reflection;
5using System.Runtime.InteropServices;
6using WixToolset.Bootstrapper;
7using WixToolset.UX;
8
9
10[assembly: AssemblyTitle("WixBA")]
11[assembly: AssemblyDescription("WiX User Experience")]
12
13// Setting ComVisible to false makes the types in this assembly not visible
14// to COM components. If you need to access a type in this assembly from
15// COM, set the ComVisible attribute to true on that type.
16[assembly: ComVisible(false)]
17[assembly: Guid("0ffc4944-9295-40b7-adac-3a6864b5219b")]
18[assembly: CLSCompliantAttribute(true)]
19
20// Identifies the class that derives from UserExperience and is the UX class that gets
21// instantiated by the interop layer
22[assembly: BootstrapperApplication(typeof(WixBA))]
diff --git a/src/WixToolset.WixBA/PropertyNotifyBase.cs b/src/WixToolset.WixBA/PropertyNotifyBase.cs
new file mode 100644
index 00000000..03174306
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/RelayCommand.cs b/src/WixToolset.WixBA/RelayCommand.cs
new file mode 100644
index 00000000..ecc482da
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/Resources/logo-black-hollow.png b/src/WixToolset.WixBA/Resources/logo-black-hollow.png
new file mode 100644
index 00000000..9d0290bd
--- /dev/null
+++ b/src/WixToolset.WixBA/Resources/logo-black-hollow.png
Binary files differ
diff --git a/src/WixToolset.WixBA/Resources/logo-white-hollow.png b/src/WixToolset.WixBA/Resources/logo-white-hollow.png
new file mode 100644
index 00000000..242c7350
--- /dev/null
+++ b/src/WixToolset.WixBA/Resources/logo-white-hollow.png
Binary files differ
diff --git a/src/WixToolset.WixBA/RootView.xaml b/src/WixToolset.WixBA/RootView.xaml
new file mode 100644
index 00000000..bbf2f9f2
--- /dev/null
+++ b/src/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.UX.RootView"
5 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7 xmlns:ux="clr-namespace:WixToolset.UX"
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 ux: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/WixToolset.WixBA/RootView.xaml.cs b/src/WixToolset.WixBA/RootView.xaml.cs
new file mode 100644
index 00000000..1d4301f2
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/RootViewModel.cs b/src/WixToolset.WixBA/RootViewModel.cs
new file mode 100644
index 00000000..1de89adf
--- /dev/null
+++ b/src/WixToolset.WixBA/RootViewModel.cs
@@ -0,0 +1,204 @@
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.UX
4{
5 using System;
6 using System.Diagnostics;
7 using System.Reflection;
8 using System.Windows;
9 using System.Windows.Input;
10 using System.Windows.Threading;
11 using WixToolset.Bootstrapper;
12
13 /// <summary>
14 /// The errors returned from the engine
15 /// </summary>
16 public enum Error
17 {
18 UserCancelled = 1223,
19 }
20
21 /// <summary>
22 /// The model of the root view in WixBA.
23 /// </summary>
24 public class RootViewModel : PropertyNotifyBase
25 {
26 private ICommand cancelCommand;
27 private ICommand closeCommand;
28
29 private bool canceled;
30 private InstallationState installState;
31 private DetectionState detectState;
32
33 /// <summary>
34 /// Creates a new model of the root view.
35 /// </summary>
36 public RootViewModel()
37 {
38 this.InstallationViewModel = new InstallationViewModel(this);
39 this.ProgressViewModel = new ProgressViewModel(this);
40 this.UpdateViewModel = new UpdateViewModel(this);
41 }
42
43 public InstallationViewModel InstallationViewModel { get; private set; }
44 public ProgressViewModel ProgressViewModel { get; private set; }
45 public UpdateViewModel UpdateViewModel { get; private set; }
46 public Dispatcher Dispatcher { get; set; }
47 public IntPtr ViewWindowHandle { get; set; }
48 public bool AutoClose { get; set; }
49
50 public ICommand CloseCommand
51 {
52 get
53 {
54 if (this.closeCommand == null)
55 {
56 this.closeCommand = new RelayCommand(param => WixBA.View.Close());
57 }
58
59 return this.closeCommand;
60 }
61 }
62
63 public ICommand CancelCommand
64 {
65 get
66 {
67 if (this.cancelCommand == null)
68 {
69 this.cancelCommand = new RelayCommand(param =>
70 {
71 this.CancelButton_Click();
72 },
73 param => !this.Canceled);
74 }
75
76 return this.cancelCommand;
77 }
78 }
79
80 public bool CancelAvailable
81 {
82 get { return InstallationState.Applying == this.InstallState; }
83 }
84
85 public bool Canceled
86 {
87 get
88 {
89 return this.canceled;
90 }
91
92 set
93 {
94 if (this.canceled != value)
95 {
96 this.canceled = value;
97 base.OnPropertyChanged("Canceled");
98 }
99 }
100 }
101
102 /// <summary>
103 /// Gets and sets the detect state of the view's model.
104 /// </summary>
105 public DetectionState DetectState
106 {
107 get
108 {
109 return this.detectState;
110 }
111
112 set
113 {
114 if (this.detectState != value)
115 {
116 this.detectState = value;
117
118 // Notify all the properties derived from the state that the state changed.
119 base.OnPropertyChanged("DetectState");
120 }
121 }
122 }
123
124 /// <summary>
125 /// Gets and sets the installation state of the view's model.
126 /// </summary>
127 public InstallationState InstallState
128 {
129 get
130 {
131 return this.installState;
132 }
133
134 set
135 {
136 if (this.installState != value)
137 {
138 this.installState = value;
139
140 // Notify all the properties derived from the state that the state changed.
141 base.OnPropertyChanged("InstallState");
142 base.OnPropertyChanged("CancelAvailable");
143 }
144 }
145 }
146
147 /// <summary>
148 /// 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.
149 /// </summary>
150 public InstallationState PreApplyState { get; set; }
151
152 /// <summary>
153 /// Gets and sets the path where the bundle is currently installed or will be installed.
154 /// </summary>
155 public string InstallDirectory
156 {
157 get
158 {
159 return WixBA.Model.InstallDirectory;
160 }
161
162 set
163 {
164 if (WixBA.Model.InstallDirectory != value)
165 {
166 WixBA.Model.InstallDirectory = value;
167 base.OnPropertyChanged("InstallDirectory");
168 }
169 }
170 }
171
172 /// <summary>
173 /// The Title of this bundle.
174 /// </summary>
175 public string Title
176 {
177 get
178 {
179 return WixDistribution.ShortProduct;
180 }
181 }
182
183 /// <summary>
184 /// Prompts the user to make sure they want to cancel.
185 /// This needs to run on the UI thread, use Dispatcher.Invoke to call this from a background thread.
186 /// </summary>
187 public void CancelButton_Click()
188 {
189 if (this.Canceled)
190 {
191 return;
192 }
193
194 if (Display.Full == WixBA.Model.Command.Display)
195 {
196 this.Canceled = (MessageBoxResult.Yes == MessageBox.Show(WixBA.View, "Are you sure you want to cancel?", "WiX Toolset", MessageBoxButton.YesNo, MessageBoxImage.Error));
197 }
198 else
199 {
200 this.Canceled = true;
201 }
202 }
203 }
204}
diff --git a/src/WixToolset.WixBA/Styles.xaml b/src/WixToolset.WixBA/Styles.xaml
new file mode 100644
index 00000000..89ccd2ea
--- /dev/null
+++ b/src/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:ux="clr-namespace:WixToolset.UX">
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 ux: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/WixToolset.WixBA/UpdateViewModel.cs b/src/WixToolset.WixBA/UpdateViewModel.cs
new file mode 100644
index 00000000..6b60112c
--- /dev/null
+++ b/src/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.UX
4{
5 using System;
6 using System.ComponentModel;
7 using System.Windows.Input;
8 using WixToolset.Bootstrapper;
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, Bootstrapper.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, Bootstrapper.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 (e.Version > WixBA.Model.Version)
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, Bootstrapper.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/WixToolset.WixBA/WindowProperties.cs b/src/WixToolset.WixBA/WindowProperties.cs
new file mode 100644
index 00000000..bead5cc1
--- /dev/null
+++ b/src/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.UX
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/WixToolset.WixBA/WixBA.BootstrapperCore.config b/src/WixToolset.WixBA/WixBA.BootstrapperCore.config
new file mode 100644
index 00000000..8e1d4729
--- /dev/null
+++ b/src/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.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore">
7 <section name="host" type="WixToolset.Bootstrapper.HostSection, BootstrapperCore" />
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="WixBA" />
15 </wix.bootstrapper>
16</configuration>
diff --git a/src/WixToolset.WixBA/WixBA.cs b/src/WixToolset.WixBA/WixBA.cs
new file mode 100644
index 00000000..fb69a346
--- /dev/null
+++ b/src/WixToolset.WixBA/WixBA.cs
@@ -0,0 +1,216 @@
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.UX
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 System.Windows.Input;
12 using Threading = System.Windows.Threading;
13 using WinForms = System.Windows.Forms;
14
15 using WixToolset.Bootstrapper;
16
17 /// <summary>
18 /// The WiX toolset user experience.
19 /// </summary>
20 public class WixBA : BootstrapperApplication
21 {
22 /// <summary>
23 /// Gets the global model.
24 /// </summary>
25 static public Model Model { get; private set; }
26
27 /// <summary>
28 /// Gets the global view.
29 /// </summary>
30 static public RootView View { get; private set; }
31 // TODO: We should refactor things so we dont have a global View.
32
33 /// <summary>
34 /// Gets the global dispatcher.
35 /// </summary>
36 static public Threading.Dispatcher Dispatcher { get; private set; }
37
38 /// <summary>
39 /// Launches the default web browser to the provided URI.
40 /// </summary>
41 /// <param name="uri">URI to open the web browser.</param>
42 public static void LaunchUrl(string uri)
43 {
44 WixBA.UseShellExecute(uri);
45 }
46
47 /// <summary>
48 /// Open a log file.
49 /// </summary>
50 /// <param name="uri">URI to a log file.</param>
51 internal static void OpenLog(Uri uri)
52 {
53 WixBA.UseShellExecute(uri.ToString());
54 }
55
56 /// <summary>
57 /// Open a log folder.
58 /// </summary>
59 /// <param name="string">path to a log folder.</param>
60 internal static void OpenLogFolder(string logFolder)
61 {
62 WixBA.UseShellExecute(logFolder);
63 }
64
65 /// <summary>
66 /// Open a log folder.
67 /// </summary>
68 /// <param name="uri">path to a log folder.</param>
69 private static void UseShellExecute(string path)
70 {
71 // Switch the wait cursor since shellexec can take a second or so.
72 System.Windows.Input.Cursor cursor = WixBA.View.Cursor;
73 WixBA.View.Cursor = System.Windows.Input.Cursors.Wait;
74 Process process = null;
75 try
76 {
77 process = new Process();
78 process.StartInfo.FileName = path;
79 process.StartInfo.UseShellExecute = true;
80 process.StartInfo.Verb = "open";
81
82 process.Start();
83 }
84 finally
85 {
86 if (null != process)
87 {
88 process.Dispose();
89 }
90 // back to the original cursor.
91 WixBA.View.Cursor = cursor;
92 }
93 }
94
95 /// <summary>
96 /// Starts planning the appropriate action.
97 /// </summary>
98 /// <param name="action">Action to plan.</param>
99 public static void Plan(LaunchAction action)
100 {
101 WixBA.Model.PlannedAction = action;
102 WixBA.Model.Engine.Plan(WixBA.Model.PlannedAction);
103 }
104
105 public static void PlanLayout()
106 {
107 // Either default or set the layout directory
108 if (String.IsNullOrEmpty(WixBA.Model.Command.LayoutDirectory))
109 {
110 WixBA.Model.LayoutDirectory = Directory.GetCurrentDirectory();
111
112 // Ask the user for layout folder if one wasn't provided and we're in full UI mode
113 if (WixBA.Model.Command.Display == Display.Full)
114 {
115 WixBA.Dispatcher.Invoke((Action)delegate()
116 {
117 WinForms.FolderBrowserDialog browserDialog = new WinForms.FolderBrowserDialog();
118 browserDialog.RootFolder = Environment.SpecialFolder.MyComputer;
119
120 // Default to the current directory.
121 browserDialog.SelectedPath = WixBA.Model.LayoutDirectory;
122 WinForms.DialogResult result = browserDialog.ShowDialog();
123
124 if (WinForms.DialogResult.OK == result)
125 {
126 WixBA.Model.LayoutDirectory = browserDialog.SelectedPath;
127 WixBA.Plan(WixBA.Model.Command.Action);
128 }
129 else
130 {
131 WixBA.View.Close();
132 }
133 }
134 );
135 }
136 }
137 else
138 {
139 WixBA.Model.LayoutDirectory = WixBA.Model.Command.LayoutDirectory;
140 WixBA.Plan(WixBA.Model.Command.Action);
141 }
142 }
143
144 /// <summary>
145 /// Thread entry point for WiX Toolset UX.
146 /// </summary>
147 protected override void Run()
148 {
149 this.Engine.Log(LogLevel.Verbose, "Running the WiX BA.");
150 WixBA.Model = new Model(this);
151 WixBA.Dispatcher = Threading.Dispatcher.CurrentDispatcher;
152 RootViewModel viewModel = new RootViewModel();
153
154 // Kick off detect which will populate the view models.
155 this.Engine.Detect();
156
157 // Create a Window to show UI.
158 if (WixBA.Model.Command.Display == Display.Passive ||
159 WixBA.Model.Command.Display == Display.Full)
160 {
161 this.Engine.Log(LogLevel.Verbose, "Creating a UI.");
162 WixBA.View = new RootView(viewModel);
163 WixBA.View.Show();
164 }
165
166 Threading.Dispatcher.Run();
167
168 this.PostTelemetry();
169 this.Engine.Quit(WixBA.Model.Result);
170 }
171
172 private void PostTelemetry()
173 {
174 string result = String.Concat("0x", WixBA.Model.Result.ToString("x"));
175
176 StringBuilder telemetryData = new StringBuilder();
177 foreach (KeyValuePair<string, string> kvp in WixBA.Model.Telemetry)
178 {
179 telemetryData.AppendFormat("{0}={1}+", kvp.Key, kvp.Value);
180 }
181 telemetryData.AppendFormat("Result={0}", result);
182
183 byte[] data = Encoding.UTF8.GetBytes(telemetryData.ToString());
184
185 try
186 {
187 HttpWebRequest post = WixBA.Model.CreateWebRequest(String.Format(WixDistribution.TelemetryUrlFormat, WixBA.Model.Version.ToString(), result));
188 post.Method = "POST";
189 post.ContentType = "application/x-www-form-urlencoded";
190 post.ContentLength = data.Length;
191
192 using (Stream postStream = post.GetRequestStream())
193 {
194 postStream.Write(data, 0, data.Length);
195 }
196
197 HttpWebResponse response = (HttpWebResponse)post.GetResponse();
198 }
199 catch (ArgumentException)
200 {
201 }
202 catch (FormatException)
203 {
204 }
205 catch (OverflowException)
206 {
207 }
208 catch (ProtocolViolationException)
209 {
210 }
211 catch (WebException)
212 {
213 }
214 }
215 }
216}
diff --git a/src/WixToolset.WixBA/WixBA.csproj b/src/WixToolset.WixBA/WixBA.csproj
new file mode 100644
index 00000000..6858b172
--- /dev/null
+++ b/src/WixToolset.WixBA/WixBA.csproj
@@ -0,0 +1,64 @@
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 DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
4 <PropertyGroup>
5 <ProjectGuid>{7C27518B-84AD-4679-8EF4-29DF552CF1AC}</ProjectGuid>
6 <AssemblyName>WixBA</AssemblyName>
7 <OutputType>Library</OutputType>
8 <RootNamespace>WixToolset.UX</RootNamespace>
9 </PropertyGroup>
10 <ItemGroup>
11 <Compile Include="Hresult.cs" />
12 <Compile Include="Model.cs" />
13 <Compile Include="BrowserProperties.cs" />
14 <Compile Include="NewsItem.cs" />
15 <Compile Include="ProgressViewModel.cs" />
16 <Compile Include="RelayCommand.cs" />
17 <Compile Include="InstallationViewModel.cs" />
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 <Compile Include="PropertyNotifyBase.cs" />
27 <Compile Include="RootView.xaml.cs">
28 <DependentUpon>RootView.xaml</DependentUpon>
29 </Compile>
30 <Compile Include="RootViewModel.cs" />
31 <Compile Include="UpdateViewModel.cs" />
32 <Compile Include="WindowProperties.cs" />
33 <Compile Include="WixBA.cs" />
34 <Compile Include="Properties\AssemblyInfo.cs" />
35 <None Include="WixBA.BootstrapperCore.config">
36 <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
37 <SubType>Designer</SubType>
38 </None>
39 </ItemGroup>
40 <ItemGroup>
41 <Reference Include="PresentationCore" />
42 <Reference Include="PresentationFramework" />
43 <Reference Include="System" />
44 <Reference Include="System.ComponentModel.DataAnnotations" />
45 <Reference Include="System.Core" />
46 <Reference Include="System.Drawing">
47 <Private>False</Private>
48 </Reference>
49 <Reference Include="System.ServiceModel" />
50 <Reference Include="System.Windows.Forms" />
51 <Reference Include="System.Xml.Linq" />
52 <Reference Include="System.Xml" />
53 <Reference Include="System.Xaml" />
54 <Reference Include="WindowsBase" />
55 <ProjectReference Include="..\..\ext\BalExtension\mba\core\core.csproj" />
56 </ItemGroup>
57 <ItemGroup>
58 <Resource Include="Resources\logo-white-hollow.png" />
59 </ItemGroup>
60 <ItemGroup>
61 <Resource Include="Resources\logo-black-hollow.png" />
62 </ItemGroup>
63 <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
64</Project> \ No newline at end of file