Model View ViewModel

Wizard for WPF Forum

Posted 15 years ago by David V
Version: 4.5.0486
Avatar
Hi-- I'm using Model-View-ViewModel as the architectual pattern for a WPF app, including its wizards. That means each wizard has its own ViewModel, and the wizard's controls are bound directly to ICommands in the ViewModel. This approach eliminates most code-behind.

Here's my problem: I have a process that I want to have execute automatically when a wizard page is opened. For example, in a Restore File Wizard, an interior page gets file paths from the user, then instructs 'Click Next to begin the restore'. The user clicks, and when the next page opens, the restore operation should begin automatically.

I know I can use the WizardPage.Selected event to trigger the action I want. But I'd like to bind to a command, MMVM-style, if that can be done. Since the WizardPage object doesn't have a Command property, it looks like I won't be able to do that. So my question is, is there any other trick that will let me call a property or method on my ViewModel class from XAML when a WizardPage is selected? Thanks.

Comments (10)

Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
What you'd probably want to do is handle the Selected event in your view and in that raise your command that your view model looks for.


Actipro Software Support

Posted 15 years ago by David V
Avatar
Yeah, that's what I've got now--a one-line event handler that calls the command's Execute() method. If I figure out some way to invoke the command from XAML, I'll post it here, for the benefit of others.

In looking at the WizardPage in the ObjectBrowser, it appears to be an Actipro creation, rather than an existing WPF type. If that's the case, then I have a wish list request: A WizardPage.Command property of some sort, so that a process can be bound to an ICommand in Xaml, to run when the page is first selected. Either that, or a lookless control with acommand property that fires when its host page is selected. Thanks.
Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi David,

We don't have command on the pages right now since Wizard is analogous to a TabControl and tabs don't fire commands when they are selected.

However you can always inherit our WizardPage class and make a custom page class that raises a command when it is selected if you need that functionality. That's the great part about WPF, you can fully extend it as needed.


Actipro Software Support

Posted 15 years ago by David V
Avatar
I see your point. And that's a great suggestion about extending the WizardPage class. I saw another thread on the subject that seems to address the issues in doing that. Thanks.
Posted 15 years ago by David V
Avatar
I'm having problems extending the PageWizard to add the Command property, and I suspect that (beyond the mechanics of creating the derived class) I'm going about it wrong.

Could you provide some general guidance on how to do this? Not asking you to write my code for me, although code is always welcome! :) I really just need to be pointed in the right direction.

Thanks for your help.

David Veeneman
Foresight Systems.
Posted 15 years ago by David V
Avatar
I've solved my problem. I had trouble finding the OnSelected() method, so that I could override it.

BTW, for anyone else researching this issue, you basically create a command property as a 'command source' (an object that knows how to invoke a command), by implementing ICommandSource. MSDN discusses it here.

[Modified at 04/08/2009 04:56 PM]
Posted 15 years ago by David V
Avatar
For the benefit of any other MVVM developers out there, here is the PageWizardEx class that I developed to extend the PageWizard class. It adds a Command property that invokes a command when the page is selected (becomes the active page). You would use the command if you have a process (such as a file copy) that you want to start automatically when the page is opened. The MSDN pages cited in the code provide a walthrough:

using System.Windows.Input;
using ActiproSoftware.Windows.Controls.Wizard;
using System.Windows;
using System;

namespace ForesightSystems.WizardEx
{
    public class WizardPageEx : WizardPage, ICommandSource
    {
        /* MSDN Sources:
         * How to: http://msdn.microsoft.com/en-us/library/ms748978.aspx
         * Sample code: http://msdn.microsoft.com/en-us/library/ms771361.aspx */
        
         #region Fields

        // Member variables
        private static EventHandler m_CanExecuteChangedHandler;

        #endregion

        #region Constructor

        public WizardPageEx() : base()
        {
        }

        #endregion

        #region Method Overrides

        /// <summary>
        /// If the Command property holds a command, this method invokes it when 
        /// this wizard page is selected (becomes the active page).
        /// </summary>
        protected override void OnSelected(WizardSelectedPageChangeEventArgs e)
        {
            base.OnSelected(e);

            if (this.Command != null)
            {
                // Attempt to cast command as RoutedCommand (null if an ICommand)
                RoutedCommand command = Command as RoutedCommand;

                // Execute the command
                if (command != null)
                {
                    // Command is a RoutedCommand
                    command.Execute(CommandParameter, CommandTarget);
                }
                else
                {
                    // Command is an ICommand
                    ((ICommand)Command).Execute(CommandParameter);
                }
            }
        }

        #endregion

        #region ICommandSource Members

        /// <summary>
        /// Gets or sets the command to invoke when this wizard page is selected (becomes the active 
        /// wizard page). This is a dependency property. 
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        /// <summary>
        /// Gets or sets the parameter to pass to the Command property. This is a dependency property. 
        /// </summary>
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        /// <summary>
        /// Gets or sets the element on which to raise the specified command. This is a dependency property.
        /// </summary>
        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }

        #endregion

        #region Dependency Properties

        /// <summary>
        /// Command dependency property.
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command",
            typeof(ICommand),
            typeof(WizardPageEx),
            new PropertyMetadata((ICommand)null,
            new PropertyChangedCallback(CommandChanged)));

        /// <summary>
        /// CommandParameter dependency property.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter",
            typeof(object),
            typeof(WizardPageEx),
            new PropertyMetadata((object)null));

        /// <summary>
        /// CommandTarget dependency property.
        /// </summary>
        public static readonly DependencyProperty CommandTargetProperty =
            DependencyProperty.Register("CommandTarget", 
            typeof(IInputElement), 
            typeof(WizardPageEx),
            new PropertyMetadata((IInputElement)null));

        #endregion

        #region Dependency Property Callback Methods

        /// <summary>
        /// Property change callback method for the Command dependency property.
        /// </summary>
        private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WizardPageEx wizardPage = (WizardPageEx)d;
            wizardPage.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the CanExecuteChanged event.
        /// </summary>
        private void OnCanExecuteChanged(object sender, EventArgs e)
        {
            if (this.Command != null)
            {
                RoutedCommand command = this.Command as RoutedCommand;

                // If a RoutedCommand.
                if (command != null)
                {
                    this.IsEnabled = (command.CanExecute(CommandParameter, CommandTarget));
                }

                // If a not RoutedCommand.
                else
                {
                    this.IsEnabled = (Command.CanExecute(CommandParameter));
                }
            }
        }

        #endregion

        #region Utility Methods

        /// <summary>
        /// Connect the new command handlers when the Command property changes.
        /// </summary>
        /// <param name="oldCommand">The old command assigned to the Command property.</param>
        /// <param name="newCommand">The new command assigned to the Command property.</param>
        private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
        {
            // If oldCommand is not null, then we need to remove the handlers.
            if (oldCommand != null)
            {
                RemoveCommand(oldCommand);
            }
            AddCommand(newCommand);
        }

        /// <summary>
        /// Removes an old command from the Command property.
        /// </summary>
        /// <param name="oldCommand">The old command assigned to the Command property.</param>
        private void RemoveCommand(ICommand oldCommand)
        {
            EventHandler handler = OnCanExecuteChanged;
            oldCommand.CanExecuteChanged -= handler;
        }

        /// <summary>
        /// Adds a new command to the Command property.
        /// </summary>
        /// <param name="newCommand">The new command assigned to the Command property.</param>
        private void AddCommand(ICommand newCommand)
        {
            /* We assign the delegate we create below to a member variable, 
             * to ensure that it doesn't get garbage collected. */

            // Create a delegate to handle CanExecuteChanged event
            m_CanExecuteChangedHandler = new EventHandler(OnCanExecuteChanged);

            // Attach the delegate to the new command
            if (newCommand != null)
            {
                newCommand.CanExecuteChanged += m_CanExecuteChangedHandler;
            }
        }

        #endregion
    }
}

Posted 15 years ago by David V
Avatar
I had meant to post this a while back, but I got caught up in other things. In-house testing uncovered the fact that the WizardPageEx class rendered as an XP-style wizard page in an Aero wizard. I submitted the problem to ActiPro tech support, and here is the response I got:

Due to the way WPF applies styles, you may need to make a Style for your class that inherits:
So something like this...
<Style x:Key="yourxmlns:yourtypehere" TargetType="wizard:WizardPage" BasedOn="{StaticResource {x:Static wizardThemes:AeroWizardCommonDictionary.PageStyleKey}}"></Style>
Then in your type's static constructor, add:
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(yourtypehere), new FrameworkPropertyMetadata(typeof(yourtypehere)));
That in effect says that your page type has a custom style and in the resources you say that the Style for your type is based on our Aero one.
Posted 13 years ago by Doraiswamy
Avatar
Hi,

How to obtain the Currentwizard active page (WizardSelectedPageChangeEventArgs). Can you please provide sample application with MVVM.

To be more refined i could able to get the active wizardPage by CommandParameter. How can i get the WizardSelectedPageChangeEventArgs so that the navigation can be cancelled if the input was not correct in viewmodel.

[Modified at 08/02/2011 08:28 AM]
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
The WizardSelectedPageChangeEventArgs class, which is a parameter from the Wizard.SelectedPageChanging event, has OldSelectedPage and NewSelectedPage properties that tell you from which page is transitioning to which page.

You should tie to that Wizard event to handle the cancel if you need to do validation.


Actipro Software Support

The latest build of this product (v24.1.2) was released 10 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.