/************************************************************************************* Toolkit for WPF Copyright (C) 2007-2020 Xceed Software Inc. This program is provided to you under the terms of the XCEED SOFTWARE, INC. COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at https://github.com/xceedsoftware/wpftoolkit/blob/master/license.md For more features, controls, and fast professional support, pick up the Plus Edition at https://xceed.com/xceed-toolkit-plus-for-wpf/ Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Collections.Specialized; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using Xceed.Wpf.Toolkit.Core; using System.ComponentModel; namespace Xceed.Wpf.Toolkit { public class Wizard : ItemsControl { #region Private Members private bool? _dialogResult = null; #endregion #region Properties public static readonly DependencyProperty BackButtonContentProperty = DependencyProperty.Register( "BackButtonContent", typeof( object ), typeof( Wizard ), new UIPropertyMetadata( "< Back" ) ); public object BackButtonContent { get { return ( object )GetValue( BackButtonContentProperty ); } set { SetValue( BackButtonContentProperty, value ); } } public static readonly DependencyProperty BackButtonVisibilityProperty = DependencyProperty.Register( "BackButtonVisibility", typeof( Visibility ), typeof( Wizard ), new UIPropertyMetadata( Visibility.Visible ) ); public Visibility BackButtonVisibility { get { return ( Visibility )GetValue( BackButtonVisibilityProperty ); } set { SetValue( BackButtonVisibilityProperty, value ); } } public static readonly DependencyProperty CanCancelProperty = DependencyProperty.Register( "CanCancel", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool CanCancel { get { return ( bool )GetValue( CanCancelProperty ); } set { SetValue( CanCancelProperty, value ); } } public static readonly DependencyProperty CancelButtonClosesWindowProperty = DependencyProperty.Register( "CancelButtonClosesWindow", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool CancelButtonClosesWindow { get { return ( bool )GetValue( CancelButtonClosesWindowProperty ); } set { SetValue( CancelButtonClosesWindowProperty, value ); } } public static readonly DependencyProperty CancelButtonContentProperty = DependencyProperty.Register( "CancelButtonContent", typeof( object ), typeof( Wizard ), new UIPropertyMetadata( "Cancel" ) ); public object CancelButtonContent { get { return ( object )GetValue( CancelButtonContentProperty ); } set { SetValue( CancelButtonContentProperty, value ); } } public static readonly DependencyProperty CancelButtonVisibilityProperty = DependencyProperty.Register( "CancelButtonVisibility", typeof( Visibility ), typeof( Wizard ), new UIPropertyMetadata( Visibility.Visible ) ); public Visibility CancelButtonVisibility { get { return ( Visibility )GetValue( CancelButtonVisibilityProperty ); } set { SetValue( CancelButtonVisibilityProperty, value ); } } public static readonly DependencyProperty CanFinishProperty = DependencyProperty.Register( "CanFinish", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( false ) ); public bool CanFinish { get { return ( bool )GetValue( CanFinishProperty ); } set { SetValue( CanFinishProperty, value ); } } public static readonly DependencyProperty CanHelpProperty = DependencyProperty.Register( "CanHelp", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool CanHelp { get { return ( bool )GetValue( CanHelpProperty ); } set { SetValue( CanHelpProperty, value ); } } public static readonly DependencyProperty CanSelectNextPageProperty = DependencyProperty.Register( "CanSelectNextPage", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool CanSelectNextPage { get { return ( bool )GetValue( CanSelectNextPageProperty ); } set { SetValue( CanSelectNextPageProperty, value ); } } public static readonly DependencyProperty CanSelectPreviousPageProperty = DependencyProperty.Register( "CanSelectPreviousPage", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool CanSelectPreviousPage { get { return ( bool )GetValue( CanSelectPreviousPageProperty ); } set { SetValue( CanSelectPreviousPageProperty, value ); } } #region CurrentPage public static readonly DependencyProperty CurrentPageProperty = DependencyProperty.Register( "CurrentPage", typeof( WizardPage ), typeof( Wizard ), new UIPropertyMetadata( null, OnCurrentPageChanged ) ); public WizardPage CurrentPage { get { return ( WizardPage )GetValue( CurrentPageProperty ); } set { SetValue( CurrentPageProperty, value ); } } private static void OnCurrentPageChanged( DependencyObject o, DependencyPropertyChangedEventArgs e ) { Wizard wizard = o as Wizard; if( wizard != null ) wizard.OnCurrentPageChanged( ( WizardPage )e.OldValue, ( WizardPage )e.NewValue ); } protected virtual void OnCurrentPageChanged( WizardPage oldValue, WizardPage newValue ) { RaiseRoutedEvent( Wizard.PageChangedEvent ); } #endregion //CurrentPage public static readonly DependencyProperty ExteriorPanelMinWidthProperty = DependencyProperty.Register( "ExteriorPanelMinWidth", typeof( double ), typeof( Wizard ), new UIPropertyMetadata( 165.0 ) ); public double ExteriorPanelMinWidth { get { return ( double )GetValue( ExteriorPanelMinWidthProperty ); } set { SetValue( ExteriorPanelMinWidthProperty, value ); } } public static readonly DependencyProperty FinishButtonClosesWindowProperty = DependencyProperty.Register( "FinishButtonClosesWindow", typeof( bool ), typeof( Wizard ), new UIPropertyMetadata( true ) ); public bool FinishButtonClosesWindow { get { return ( bool )GetValue( FinishButtonClosesWindowProperty ); } set { SetValue( FinishButtonClosesWindowProperty, value ); } } public static readonly DependencyProperty FinishButtonContentProperty = DependencyProperty.Register( "FinishButtonContent", typeof( object ), typeof( Wizard ), new UIPropertyMetadata( "Finish" ) ); public object FinishButtonContent { get { return ( object )GetValue( FinishButtonContentProperty ); } set { SetValue( FinishButtonContentProperty, value ); } } public static readonly DependencyProperty FinishButtonVisibilityProperty = DependencyProperty.Register( "FinishButtonVisibility", typeof( Visibility ), typeof( Wizard ), new UIPropertyMetadata( Visibility.Collapsed ) ); public Visibility FinishButtonVisibility { get { return ( Visibility )GetValue( FinishButtonVisibilityProperty ); } set { SetValue( FinishButtonVisibilityProperty, value ); } } public static readonly DependencyProperty HelpButtonContentProperty = DependencyProperty.Register( "HelpButtonContent", typeof( object ), typeof( Wizard ), new UIPropertyMetadata( "Help" ) ); public object HelpButtonContent { get { return ( object )GetValue( HelpButtonContentProperty ); } set { SetValue( HelpButtonContentProperty, value ); } } public static readonly DependencyProperty HelpButtonVisibilityProperty = DependencyProperty.Register( "HelpButtonVisibility", typeof( Visibility ), typeof( Wizard ), new UIPropertyMetadata( Visibility.Visible ) ); public Visibility HelpButtonVisibility { get { return ( Visibility )GetValue( HelpButtonVisibilityProperty ); } set { SetValue( HelpButtonVisibilityProperty, value ); } } public static readonly DependencyProperty NextButtonContentProperty = DependencyProperty.Register( "NextButtonContent", typeof( object ), typeof( Wizard ), new UIPropertyMetadata( "Next >" ) ); public object NextButtonContent { get { return ( object )GetValue( NextButtonContentProperty ); } set { SetValue( NextButtonContentProperty, value ); } } public static readonly DependencyProperty NextButtonVisibilityProperty = DependencyProperty.Register( "NextButtonVisibility", typeof( Visibility ), typeof( Wizard ), new UIPropertyMetadata( Visibility.Visible ) ); public Visibility NextButtonVisibility { get { return ( Visibility )GetValue( NextButtonVisibilityProperty ); } set { SetValue( NextButtonVisibilityProperty, value ); } } #endregion //Properties #region Constructors static Wizard() { DefaultStyleKeyProperty.OverrideMetadata( typeof( Wizard ), new FrameworkPropertyMetadata( typeof( Wizard ) ) ); } public Wizard() { CommandBindings.Add( new CommandBinding( WizardCommands.Cancel, ExecuteCancelWizard, CanExecuteCancelWizard ) ); CommandBindings.Add( new CommandBinding( WizardCommands.Finish, ExecuteFinishWizard, CanExecuteFinishWizard ) ); CommandBindings.Add( new CommandBinding( WizardCommands.Help, ExecuteRequestHelp, CanExecuteRequestHelp ) ); CommandBindings.Add( new CommandBinding( WizardCommands.NextPage, ExecuteSelectNextPage, CanExecuteSelectNextPage ) ); CommandBindings.Add( new CommandBinding( WizardCommands.PreviousPage, ExecuteSelectPreviousPage, CanExecuteSelectPreviousPage ) ); } #endregion //Constructors #region Base Class Overrides protected override DependencyObject GetContainerForItemOverride() { return new WizardPage(); } protected override bool IsItemItsOwnContainerOverride( object item ) { return ( item is WizardPage ); } protected override void OnInitialized( EventArgs e ) { base.OnInitialized( e ); if( Items.Count > 0 && CurrentPage == null ) CurrentPage = Items[ 0 ] as WizardPage; } protected override void OnItemsChanged( NotifyCollectionChangedEventArgs e ) { base.OnItemsChanged( e ); foreach( object o in this.Items ) { if( !( o is WizardPage ) ) throw new NotSupportedException( "Wizard should only contain WizardPages." ); } if( Items.Count > 0 && CurrentPage == null ) CurrentPage = Items[ 0 ] as WizardPage; } protected override void OnPropertyChanged( DependencyPropertyChangedEventArgs e ) { base.OnPropertyChanged( e ); if( ( e.Property.Name == "CanSelectNextPage" ) || ( e.Property.Name == "CanHelp" ) || ( e.Property.Name == "CanFinish" ) || ( e.Property.Name == "CanCancel" ) || ( e.Property.Name == "CanSelectPreviousPage" ) ) { CommandManager.InvalidateRequerySuggested(); } } #endregion //Base Class Overrides #region Commands private void ExecuteCancelWizard( object sender, ExecutedRoutedEventArgs e ) { RaiseRoutedEvent( Wizard.CancelEvent ); if( CancelButtonClosesWindow ) CloseParentWindow( false ); } private void CanExecuteCancelWizard( object sender, CanExecuteRoutedEventArgs e ) { if( CurrentPage != null ) { if( CurrentPage.CanCancel.HasValue ) e.CanExecute = CurrentPage.CanCancel.Value; else e.CanExecute = CanCancel; } } private void ExecuteFinishWizard( object sender, ExecutedRoutedEventArgs e ) { var eventArgs = new CancelRoutedEventArgs( Wizard.FinishEvent ); this.RaiseEvent( eventArgs ); if( eventArgs.Cancel ) return; if( FinishButtonClosesWindow ) CloseParentWindow( true ); } private void CanExecuteFinishWizard( object sender, CanExecuteRoutedEventArgs e ) { if( CurrentPage != null ) { if( CurrentPage.CanFinish.HasValue ) e.CanExecute = CurrentPage.CanFinish.Value; else e.CanExecute = CanFinish; } } private void ExecuteRequestHelp( object sender, ExecutedRoutedEventArgs e ) { RaiseRoutedEvent( Wizard.HelpEvent ); } private void CanExecuteRequestHelp( object sender, CanExecuteRoutedEventArgs e ) { if( CurrentPage != null ) { if( CurrentPage.CanHelp.HasValue ) e.CanExecute = CurrentPage.CanHelp.Value; else e.CanExecute = CanHelp; } } private void ExecuteSelectNextPage( object sender, ExecutedRoutedEventArgs e ) { WizardPage nextPage = null; if( CurrentPage != null ) { var eventArgs = new CancelRoutedEventArgs( NextEvent ); this.RaiseEvent( eventArgs ); if( eventArgs.Cancel ) return; //check next page if( CurrentPage.NextPage != null ) nextPage = CurrentPage.NextPage; else { //no next page defined use index var currentIndex = Items.IndexOf( CurrentPage ); var nextPageIndex = currentIndex + 1; if( nextPageIndex < Items.Count ) nextPage = Items[ nextPageIndex ] as WizardPage; } } CurrentPage = nextPage; } private void CanExecuteSelectNextPage( object sender, CanExecuteRoutedEventArgs e ) { if( CurrentPage != null ) { if( CurrentPage.CanSelectNextPage.HasValue ) //check to see if page has overriden default behavior { if( CurrentPage.CanSelectNextPage.Value ) e.CanExecute = NextPageExists(); } else if( CanSelectNextPage ) e.CanExecute = NextPageExists(); } } private void ExecuteSelectPreviousPage( object sender, ExecutedRoutedEventArgs e ) { WizardPage previousPage = null; if( CurrentPage != null ) { var eventArgs = new CancelRoutedEventArgs( PreviousEvent ); this.RaiseEvent( eventArgs ); if( eventArgs.Cancel ) return; //check previous page if( CurrentPage.PreviousPage != null ) previousPage = CurrentPage.PreviousPage; else { //no previous page defined so use index var currentIndex = Items.IndexOf( CurrentPage ); var previousPageIndex = currentIndex - 1; if( previousPageIndex >= 0 && previousPageIndex < Items.Count ) previousPage = Items[ previousPageIndex ] as WizardPage; } } CurrentPage = previousPage; } private void CanExecuteSelectPreviousPage( object sender, CanExecuteRoutedEventArgs e ) { if( CurrentPage != null ) { if( CurrentPage.CanSelectPreviousPage.HasValue ) //check to see if page has overriden default behavior { if( CurrentPage.CanSelectPreviousPage.Value ) e.CanExecute = PreviousPageExists(); } else if( CanSelectPreviousPage ) e.CanExecute = PreviousPageExists(); } } #endregion //Commands #region Events #region Cancel Event public static readonly RoutedEvent CancelEvent = EventManager.RegisterRoutedEvent( "Cancel", RoutingStrategy.Bubble, typeof( EventHandler ), typeof( Wizard ) ); public event RoutedEventHandler Cancel { add { AddHandler( CancelEvent, value ); } remove { RemoveHandler( CancelEvent, value ); } } #endregion //Cancel Event #region PageChanged Event public static readonly RoutedEvent PageChangedEvent = EventManager.RegisterRoutedEvent( "PageChanged", RoutingStrategy.Bubble, typeof( EventHandler ), typeof( Wizard ) ); public event RoutedEventHandler PageChanged { add { AddHandler( PageChangedEvent, value ); } remove { RemoveHandler( PageChangedEvent, value ); } } #endregion //PageChanged Event #region Finish Event public static readonly RoutedEvent FinishEvent = EventManager.RegisterRoutedEvent( "Finish", RoutingStrategy.Bubble, typeof( CancelRoutedEventHandler ), typeof( Wizard ) ); public event CancelRoutedEventHandler Finish { add { AddHandler( FinishEvent, value ); } remove { RemoveHandler( FinishEvent, value ); } } #endregion //Finish Event #region Help Event public static readonly RoutedEvent HelpEvent = EventManager.RegisterRoutedEvent( "Help", RoutingStrategy.Bubble, typeof( EventHandler ), typeof( Wizard ) ); public event RoutedEventHandler Help { add { AddHandler( HelpEvent, value ); } remove { RemoveHandler( HelpEvent, value ); } } #endregion //Help Event #region Next Event public delegate void NextRoutedEventHandler( object sender, CancelRoutedEventArgs e ); /// /// Identifies the Next routed event. /// public static readonly RoutedEvent NextEvent = EventManager.RegisterRoutedEvent( "Next", RoutingStrategy.Bubble, typeof( NextRoutedEventHandler ), typeof( Wizard ) ); /// /// Raised when WizardCommands.NextPage command is executed. /// This cancellable event can prevent the command execution from continuing. /// public event NextRoutedEventHandler Next { add { AddHandler( NextEvent, value ); } remove { RemoveHandler( NextEvent, value ); } } #endregion //Next Event #region Previous Event public delegate void PreviousRoutedEventHandler( object sender, CancelRoutedEventArgs e ); /// /// Identifies the Previous routed event. /// public static readonly RoutedEvent PreviousEvent = EventManager.RegisterRoutedEvent( "Previous", RoutingStrategy.Bubble, typeof( PreviousRoutedEventHandler ), typeof( Wizard ) ); /// /// Raised when WizardCommands.PreviousPage command is executed. /// This cancellable event can prevent the command execution from continuing. /// public event PreviousRoutedEventHandler Previous { add { AddHandler( PreviousEvent, value ); } remove { RemoveHandler( PreviousEvent, value ); } } #endregion //Previous Event #endregion //Events #region Methods private void CloseParentWindow( bool dialogResult ) { Window window = Window.GetWindow( this ); if( window != null ) { //we can only set the DialogResult if the window was opened as modal with the ShowDialog() method. Otherwise an exception would occur if( ComponentDispatcher.IsThreadModal ) { _dialogResult = dialogResult; window.Closing += this.Window_Closing; } window.Close(); } } private void Window_Closing( object sender, CancelEventArgs e ) { var window = sender as Window; if( window != null ) { if( !e.Cancel ) { // Set dialog result only when closing is not canceled. window.DialogResult = _dialogResult; } _dialogResult = null; window.Closing -= this.Window_Closing; } } private bool NextPageExists() { bool exists = false; if( CurrentPage.NextPage != null ) //check to see if a next page has been specified exists = true; else { //lets use an index to find the next page var currentIndex = Items.IndexOf( CurrentPage ); var nextPageIndex = currentIndex + 1; if( nextPageIndex < Items.Count ) exists = true; } return exists; } private bool PreviousPageExists() { bool exists = false; if( CurrentPage.PreviousPage != null ) //check to see if a previous page has been specified exists = true; else { //lets use an index to find the next page var currentIndex = Items.IndexOf( CurrentPage ); var previousPageIndex = currentIndex - 1; if( previousPageIndex >= 0 && previousPageIndex < Items.Count ) exists = true; } return exists; } private void RaiseRoutedEvent( RoutedEvent routedEvent ) { RoutedEventArgs newEventArgs = new RoutedEventArgs( routedEvent, this ); base.RaiseEvent( newEventArgs ); } #endregion //Methods } }