context: // Copyright (c) Microsoft Corporation.  All rights reserved.
  
  //Copyright (c) Microsoft Corporation.  All rights reserved.
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Runtime.InteropServices;
  using System.Text;
  using System.Windows;
  using System.Windows.Interop;
  using System.Windows.Markup;
  using Microsoft.SDK.Samples.VistaBridge.Library;
  using Microsoft.SDK.Samples.VistaBridge.Interop;
  using System.Security.Permissions;
  
  namespace Microsoft.SDK.Samples.VistaBridge.Library
  {
      /// <summary>
      /// Encapsulates a new-to-Vista Win32 TaskDialog window 
      /// - a powerful successor to the MessageBox available
      /// in previous versions of Windows.
      /// </summary>
      [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
      [ContentProperty("Controls")]
      public class TaskDialog : IDialogControlHost, IDisposable
      {
          // Global instance of TaskDialog, to be used by static Show() method.
          // As most parameters of a dialog created via static Show() will have
          // identical parameters, we'll create one TaskDialog and treat it
          // as a NativeTaskDialog generator for all static Show() calls.
          private static TaskDialog staticDialog;
  
          // Main current native dialog.
          private NativeTaskDialog nativeDialog;
  
          private List<TaskDialogButtonBase> buttons;
          private List<TaskDialogButtonBase> radioButtons;
          private List<TaskDialogButtonBase> commandLinks;
          private Window ownerWindow;
  
          #region Public Properties
  
          public event EventHandler<TaskDialogTickEventArgs> Tick;
          public event EventHandler<TaskDialogHyperlinkClickedEventArgs> HyperlinkClick;
          public event EventHandler<TaskDialogClosingEventArgs> Closing;
          public event EventHandler HelpInvoked;
          public event EventHandler Opened;
  
          public Window Owner
          {
              get { return ownerWindow; }
              set
              {
                  ThrowIfDialogShowing("Dialog owner cannot be modified while dialog is showing.");
                  ownerWindow = value;
              }
          }
  
          // Main content (maps to MessageBox's "message"). 
          private string content;
          public string Content
          {
              get { return content; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  content = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateContent(content);
              }
          }
  
          private string instruction;
          public string Instruction
          {
              get { return instruction; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  instruction = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateInstruction(instruction);
              }
          }
  
          private string caption;
          public string Caption
          {
              get { return caption; }
              set
              {
                  ThrowIfDialogShowing("Dialog caption can't be set while dialog is showing.");
                  caption = value;
              }
          }
  
          private string footerText;
          public string FooterText
          {
              get { return footerText; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  footerText = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateFooterText(footerText);
              }
          }
  
          private string checkBoxText;
          public string CheckBoxText
          {
              get { return checkBoxText; }
              set
              {
                  ThrowIfDialogShowing("Checkbox text can't be set while dialog is showing.");
                  checkBoxText = value;
              }
          }
  
          private string expandedText;
          public string ExpandedText
          {
              get { return expandedText; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  expandedText = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateExpandedText(expandedText);
              }
          }
  
          private bool expanded;
          public bool Expanded
          {
              get { return expanded; }
              set
              {
                  ThrowIfDialogShowing("Expanded state of the dialog can't be modified while dialog is showing.");
                  expanded = value;
              }
          }
  
          private string expandedControlText;
          public string ExpandedControlText
          {
              get { return expandedControlText; }
              set
              {
                  ThrowIfDialogShowing("Expanded control text can't be set while dialog is showing.");
                  expandedControlText = value;
              }
          }
  
          private string collapsedControlText;
          public string CollapsedControlText
          {
              get { return collapsedControlText; }
              set
              {
                  ThrowIfDialogShowing("Collapsed control text can't be set while dialog is showing.");
                  collapsedControlText = value;
              }
          }
  
          private bool cancelable;
          public bool Cancelable
          {
              get { return cancelable; }
              set
              {
                  ThrowIfDialogShowing("Cancelable can't be set while dialog is showing.");
                  cancelable = value;
              }
          }
  
          private TaskDialogStandardIcon mainIcon;
          public TaskDialogStandardIcon MainIcon
          {
              get { return mainIcon; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  mainIcon = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateMainIcon(mainIcon);
              }
          }
  
          private TaskDialogStandardIcon footerIcon;
          public TaskDialogStandardIcon FooterIcon
          {
              get { return footerIcon; }
              set
              {
                  // Set local value, then update native dialog if showing.
                  footerIcon = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateFooterIcon(footerIcon);
              }
          }
  
          private TaskDialogStandardButtons standardButtons = TaskDialogStandardButtons.None;
          public TaskDialogStandardButtons StandardButtons
          {
              get { return standardButtons; }
              set
              {
                  ThrowIfDialogShowing("Standard buttons can't be set while dialog is showing.");
                  standardButtons = value;
              }
          }
  
          private DialogControlCollection<TaskDialogControl> controls;
          public DialogControlCollection<TaskDialogControl> Controls
          {
              // "Show protection" provided by collection itself, 
              // as well as individual controls.
              get { return controls; }
          }
  
          private bool hyperlinksEnabled;
          public bool HyperlinksEnabled
          {
              get { return hyperlinksEnabled; }
              set
              {
                  ThrowIfDialogShowing("Hyperlinks can't be enabled/disabled while dialog is showing.");
                  hyperlinksEnabled = value;
              }
          }
  
          private bool? checkBoxChecked = null;
          public bool? CheckBoxChecked
          {
              get 
              {
                  if (!checkBoxChecked.HasValue)
                      return false;
                  else
                      return checkBoxChecked; 
              }
              set
              {
                  // Set local value, then update native dialog if showing.
                  checkBoxChecked = value;
                  if (NativeDialogShowing)
                      nativeDialog.UpdateCheckBoxChecked(checkBoxChecked.Value);
              }
          }
  
          private TaskDialogExpandedInformationLocation expansionMode;
          public TaskDialogExpandedInformationLocation ExpansionMode
          {
              get { return expansionMode; }
              set
              {
                  ThrowIfDialogShowing("Expanded information mode can't be set while dialog is showing.");
                  expansionMode = value;
              }
          }
  
          private TaskDialogStartupLocation startupLocation;
          public TaskDialogStartupLocation StartupLocation
          {
              get { return startupLocation; }
              set
              {
                  ThrowIfDialogShowing("Startup location can't be changed while dialog is showing.");
                  startupLocation = value;
              }
          }
          private TaskDialogProgressBar progressBar;
          public TaskDialogProgressBar ProgressBar
          {
              get { return progressBar; }
              set
              {
                  ThrowIfDialogShowing("Progress bar can't be changed while dialog is showing");
                  if (value != null)
                  {
                      if (value.HostingDialog != null) 
                          throw new InvalidOperationException("Progress bar cannot be hosted in multiple dialogs.");
  
                      // Cannot have progress bar and marquee in same dialog.
                      if (marquee != null)
                      {
                          throw new NotSupportedException("Dialog cannot contain marquee progress bar and a regular progress bar.");
                      }
                      value.HostingDialog = this;
                  }
                  progressBar = value;
              }
          }
          private TaskDialogMarquee marquee;
          public TaskDialogMarquee Marquee
          {
              get { return marquee; }
              set
              {
                  ThrowIfDialogShowing("Marquee can't be changed while dialog is showing.");
                  if (value != null)
                  {
                      if (value.HostingDialog != null)
                          throw new InvalidOperationException("Marquee cannot be hosted in multiple dialogs.");
  
                      // Cannot have progress bar and marquee in same dialog.
                      if (progressBar != null)
                      {
                          throw new NotSupportedException("Dialog cannot contain marquee progress bar and a regular progress bar.");
                      }
                      value.HostingDialog = this;
                  }
                  marquee = value;
              }
          }
          #endregion
  
          #region Constructors
  
          // Constructors.
   
          
          public TaskDialog()
          {
              if (!Helpers.RunningOnVista)
                  throw new PlatformNotSupportedException("Task Dialog requires Windows Vista or later.");
  
              // Initialize various data structs.
              controls = new DialogControlCollection<TaskDialogControl>(this);
              buttons = new List<TaskDialogButtonBase>();
              radioButtons = new List<TaskDialogButtonBase>();
              commandLinks = new List<TaskDialogButtonBase>();
          }
  
  
  
          #endregion
  
          #region Static Show Methods
  
          // STATIC SHOW() METHODS -- these all forward to ShowCoreStatic()
          public static TaskDialogResult Show(string msg)
          {
              return ShowCoreStatic(
                  msg, 
                  TaskDialogDefaults.MainInstruction, 
                  TaskDialogDefaults.Caption);
          }
  
          public static TaskDialogResult Show(string msg, string mainInstruction)
          {
              return ShowCoreStatic(
                  msg, mainInstruction, 
                  TaskDialogDefaults.Caption);
          }
  
          public static TaskDialogResult Show(string msg, string mainInstruction, string caption)
          {
              return ShowCoreStatic(msg, mainInstruction, caption);
          }
          #endregion
  
          #region Instance Show Methods
          // INSTANCE SHOW() METHODS -- these all forward to ShowCore()
  
          public TaskDialogResult Show()
          {
              return ShowCore();
          }
          #endregion
  
          #region Core Show Logic
  
          // CORE SHOW METHODS:
          // All static Show() calls forward here - 
          // it is responsible for retrieving
          // or creating our cached TaskDialog instance, getting it configured,
          // and in turn calling the appropriate instance Show.
  
          private static TaskDialogResult ShowCoreStatic(
              string msg, 
              string mainInstruction, 
              string caption)
          {
              // If no instance cached yet, create it.
              if (staticDialog == null)
              {
                  // New TaskDialog will automatically pick up defaults when 
                  // a new config structure is created as part of ShowCore().
                  staticDialog = new TaskDialog();
              }
  
              // Set the few relevant properties, 
              // and go with the defaults for the others.
              staticDialog.content = msg;
              staticDialog.instruction = mainInstruction;
              staticDialog.caption = caption;
  
              return staticDialog.Show();
          }
  
          private TaskDialogResult ShowCore()
          {
              TaskDialogResult result;
  
              try
              {
                  // Populate control lists, based on current 
                  // contents - note we are somewhat late-bound 
                  // on our control lists, to support XAML scenarios.
                  SortDialogControls();
  
                  // First, let's make sure it even makes 
                  // sense to try a show.
                  ValidateCurrentDialogSettings();
  
                  // Create settings object for new dialog, 
                  // based on current state.
                  NativeTaskDialogSettings settings = 
                      new NativeTaskDialogSettings();
                  ApplyCoreSettings(settings);
                  ApplySupplementalSettings(settings);
  
                  // Show the dialog.
                  // NOTE: this is a BLOCKING call; the dialog proc callbacks
                  // will be executed by the same thread as the 
                  // Show() call before the thread of execution 
                  // contines to the end of this method.
                  nativeDialog = new NativeTaskDialog(settings, this);
                  nativeDialog.NativeShow();
  
                  // Build and return dialog result to public API - leaving it
                  // null after an exception is thrown is fine in this case
                  result = ConstructDialogResult(nativeDialog);
                  checkBoxChecked = result.CheckBoxChecked;
              }
              finally
              {
                  CleanUp();
                  nativeDialog = null;
              }
  
              return result;
          }
  
          // Helper that looks at the current state of the TaskDialog and verifies
          // that there aren't any abberant combinations of properties.
          // NOTE that this method is designed to throw 
          // rather than return a bool.
          private void ValidateCurrentDialogSettings()
          {
              if (checkBoxChecked.HasValue &&
                  checkBoxChecked.Value == true &&
                  String.IsNullOrEmpty(checkBoxText))
                  throw new InvalidOperationException(
                      "Checkbox text must be provided to enable the dialog checkbox.");
  
              // Progress bar validation.
              // Make sure we don't have both a progress bar and marquee, 
              // and that the progress bar values are valid.
              // the Win32 API will valiantly try to rationalize 
              // bizarre min/max/value combinations, but we'll save
              // it the trouble by validating.
              if (progressBar != null && marquee != null)
                  throw new NotSupportedException(
                      "Can't display both a progress bar and a marquee in same dialog.");
              if (progressBar != null)
                  if (!progressBar.HasValidValues)
                      throw new ArgumentException( 
                          "Progress bar must have a value between the minimum and maxium values.");
  
              // Validate Buttons collection.
              // Make sure we don't have buttons AND 
              // command-links - the Win32 API treats them as different
              // flavors of a single button struct.
              if (buttons.Count > 0 && commandLinks.Count > 0)
                  throw new NotSupportedException(
                      "Dialog cannot display both non-standard buttons and command links.");
              if (buttons.Count > 0 && standardButtons != TaskDialogStandardButtons.None)
                  throw new NotSupportedException(
                      "Dialog cannot display both non-standard buttons and standard buttons.");
          }
  
          // Analyzes the final state of the NativeTaskDialog instance and creates the 
          // final TaskDialogResult that will be returned from the public API
          private TaskDialogResult ConstructDialogResult(NativeTaskDialog native)
          {
              Debug.Assert(native.ShowState == NativeDialogShowState.Closed, "dialog result being constructed for unshown dialog.");
  
              string customButton = null;
              string radioButton = null;
              bool isCheckBoxChecked = false;
              TaskDialogButtonBase button;
  
              TaskDialogStandardButton standardButton = MapButtonIdToStandardButton(native.SelectedButtonID);
  
              // If returned ID isn't a standard button, let's fetch 
  
gt:             if (standardButton == TaskDialogStandardButton.None)