Wizard and xforms

Hello, Has anyone created a solution with xforms that is built as a wizard? Does that make sence? What I want to do is this: Create a wizard that collects information using multiple forms that can be designed in episerver (xforms). If anyone has a solution to this I would greatly apreciate some pointer and perhaps some source code. Thanks Pontus
Jul 04, 2007 14:14
Hi! Well, sometimes I wonder if there really isnt some form of universal consciousness connecting pesoples minds together telepathically, as some questions tends to popup almost simultaneously from various sources :) Seriously, I've been doing exactly this over the last few days, and I'd gladly share my experiences. It makes perfect sense to use XForms this way, there are however some pitfalls: 1) the XFormControl doesnt perists its values to ViewState 2) you must take care of the "reading" of postedvalues when navigating the wizardsteps I've solved them as such 1) derive a new control from XFormControl to persist Data to/from viewstate, something like this: public class XFormControl2 : EPiServer.XForms.WebControls.XFormControl, INamingContainer { public object WizardContext = null; public delegate bool IsMePostingDelegate( object wizardContext ); public IsMePostingDelegate IsMePosting = null; protected override void OnInit( EventArgs e ) { base.OnInit( e ); this.Load += new EventHandler(Control_Load); } protected override void LoadViewState( object savedState ) { // load Data from viewstate Data = (XFormData)savedState; EnsureChildControls(); } protected override object SaveViewState() { // persist Data to viewstate return Data; } protected void Control_Load( object sender, EventArgs e ) { // only read values if it was "our" form that was posted // otherwise we will read empty values "over" any previously read // this is crucial when placing xformcontrol on wizardsteps if( null==IsMePosting || IsMePosting( WizardContext )) ReadPostedValues( Data ); } } 2) I added a "IsMePosting"-delegate so that the containing wizard may specify a function to be executed to determine if it's _this_ wizardstep's thats being postbacked, in which case the Data will be read from posted values through the call to ReadPostedValues() This also requires you to "remember" the previous wizardstep, and in the delegated IsMePosting check if its the same as the idnex stored in the xformcontrol's WizardContext The wizardSteps are created dynamically during page OnInit() in code similar to this: //loop through all child xformpages of this page int stepindex = 0; foreach(PageData child in GetChildren( CurrentPage.PageLink )) { WizardStep wizardStep = new WizardStep(); wizardStep.Title = child.PageName; XFormControl2 xformControl = new XFormControl2(); xformControl.ID = "XFormControl"; wizardStep.Controls.Add( xformControl ); xformControl.FormDefinition = ( (PropertyXForm)child.Property["MainXForm"] ).Form; xformControl.WizardContext = stepindex; xformControl.IsMePosting = new SalesForceX.Gui.WebControls.XFormControl.IsMePostingDelegate( delegate( object Context ) { return PreviousStepIndex == (int)Context; } ); WizardSteps.Add( wizardStep ); } Then, to "collect" all values when clicking the Finish-button, you'd have to iterate over all WizarSteps and get all contained XFormControls and from them iterate over all XFormControlBase input controls to retrieve their instance values. You cannot use the normal XForms submitting to store these values as they're "coming from" different XForms forms. Regards, Johan Olofsson EPiServer AB
Jul 04, 2007 15:02
Thanks for you quick reply Johan! This looks like a nice solution. I have some question though. 1. The code for the OnInit() method; How do you run that? In the Page, UserControl or custom class? 2. I'm having some trouble with xformControl.IsMePosting = new XFormControl2.IsMePostingDelegate(delegate(object Context) { return PreviousStepIndex == (int)Context; }); The delegate always seem to return false, hence no previuos data is ever loaded. I think I'm missing somthing with the Context or PreviousStepIndex. My implementation of PreviousStepIndex is below. public int PreviousStepIndex { get { if (ViewState["PreviousStepIndex"] == null) return 0; return (int)ViewState["PreviousStepIndex"]; } set { ViewState["PreviousStepIndex"] = value; } } Mvh Pontus
Jul 05, 2007 14:16
1. I actually did derive a new control from ASP.Wizard, and put my initialize wizard code into the OnInit() of the usercontrol. 2. Yes, the problem is that you must assign PreviousStepIndex _after_ Contol.Load(). I did it in LoadComplete like so: Page.LoadComplete += new EventHandler( delegate( object sender, EventArgs ea ) { PreviousStepIndex = ActiveStepIndex; } ); Regards, Johan Olofsson EPiServer AB
Jul 05, 2007 14:21
Hi again! It's working really well now. The only issue now is that I can't get the wizard steps values in the finishbutton event. I only get the last one. If i do it in the PreRender event I get all of them. Any ideas or thought's about this? Regards Pontus
Jul 06, 2007 8:16
Hm, I just grab the data from the controls in the FinishClicked-event like so: Dictionary fieldValues = new Dictionary(); foreach(WizardStep step in WizardSteps) { XFormControl2 xformControl = step.Controls[0] as XFormControl2; if(null == xformControl) continue; foreach(EPiServer.XForms.WebControls.XFormControlBase input in xformControl.ExtractXFormControls()) { fieldValues.Add( input.Reference, input.GetInstanceValue() ); } } I think the trick might be to call "GetInstanceValue()" rather than "Value" for the XFormControlBase input controls. Regards, Johan
Jul 06, 2007 11:10
You where right, the trick was to use GetInstanceValue() instead of the Value property. Thanks a lot! Regards, Pontus
Jul 06, 2007 12:45
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.