0

I'm trying to recreate what the ASP.NET Wizard control does using a custom control. I can't use the wizard control because that reloads the page and wraps the HTML in table.

Control Criteria: - Async Reload - Ability to change step from control ID (.Next(), .Prev())

It's currently made up of 2 parts:

  • MultiStep
  • Step

Markup:

<asp:UpdatePanel id="upTest" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <asp:Label ID="lblbActiveStep" runat="server" Text="Label"></asp:Label>

        <customControl:MultiStep ID="msExample" Visible="True" ActiveStepIndex="0" runat="server">
            <customControl:Step runat="server" Name="Step 1">
                <p>Step 1</p>
            </customControl:Step>

            <customControl:Step runat="server" Name="Step 2">
                <p>Step 2</p>
            </customControl:Step>

            <customControl:Step runat="server" Name="Step 3">
                <p>Step 3</p>
            </customControl:Step>
        </customControl:MultiStep> 

        <asp:Button ID="btnTest" OnClick="btnTest_OnClick" runat="server" Text="Button" />
    </ContentTemplate>
</asp:UpdatePanel>

My current code works for displaying the active step but when updating the ActiveStepIndex it does not re-render.

MultiStep Control:

[ParseChildren(true, "Steps"), PersistChildren(false)]
public class MultiStep : CompositeControl
{
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public StepList Steps { get; set; }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public int ActiveStepIndex { get; set; }

    public MultiStep()
    { 
        Steps = new StepList();
        ActiveStepIndex = 0;
    }

    protected override void RenderChildren(HtmlTextWriter writer)
    {
        for (int i = 0; i < Steps.Count; i++)
        {
            Steps[i].Visible = (i == ActiveStepIndex);

            if (Steps[i].Visible)
                Steps[i].InstantiateIn(this);
        }

        base.RenderChildren(writer);
    }

    public void Next()
    {
        if (ActiveStepIndex < Steps.Count )
        {
            ActiveStepIndex++;
            UpdateVisible(ActiveStepIndex);
        }
    }

    private void UpdateVisible(int stepIndex)
    {
        foreach (Step step in Steps)
        {
            step.Visible = false;
        }

        Steps[ActiveStepIndex].Visible = true;
    }
}

Step Sub-Control:

[ParseChildren(true, "Content"), PersistChildren(false)]
public class Step : CompositeControl
{

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public string Name { get; set; }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(MultiStep))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate Content { get; set; }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public Panel Panel { get; set; }

    public Step()
    {
    }

    public void InstantiateIn(Control container)
    {
        Content?.InstantiateIn(container);
    }
}

StepList Collection

public class StepList : Collection<Step> { }

When clicking the testing button it runs this code:

protected void testing_OnClick(object sender, EventArgs e)
{
    msExample.Next();
}

The next step does not appear: Multi Step not working

Update

The step only updates once and then the button no longer works for some strange reason. Button update control once

Riddell
  • 1,429
  • 11
  • 22
  • from your markup it's not clear how the steps are associated with the main multistep control. And your "Next" method simply increments a counter. It doesn't appear to try and actually make anything visible or invisible, which would go a long way to accounting for why nothing happens when you click. And anyway since all this is server-side code, how are you planning to avoid the page reloads? Using UpdatePanels perhaps? – ADyson Apr 11 '18 at 15:24
  • @ADyson The children are parsed on the `MultiStep` control and put into the collection `StepList` from there I control the children. Do I need to link the children to parent any other way? – Riddell Apr 11 '18 at 15:26
  • Ok your code formatting is poor but now I see they are within the multistep element. But still nothing gets displayed because your Next method is simply incrementing a counter. You need to actually manipulate the controls. – ADyson Apr 11 '18 at 15:29
  • Surely if I increment `MultiStep` `ActiveStepIndex` that would force a postback? What would I need to change to render the next step? Sorry, not worked with custom controls before – Riddell Apr 11 '18 at 15:30
  • Why do you think adding 1 to a variable would cause a postback?? Anyway since it's C# code, the postback has already happened when you clicked the button, you're way past that point. – ADyson Apr 11 '18 at 15:32
  • If I make the `Step` inherit from `Panel` and set the `Visible` property would that cause updates to the front-end? – Riddell Apr 11 '18 at 15:35
  • As for what it shows, I would maybe check the lifecycle of the control (it should be documented somewhere), because maybe the RenderChildren method already ran before you called the Next() method, and so it doesn't change which step is considered active. Be interesting what it does after you press Next for a second time in a row. Because by then the previous increment should have taken effect. But not sure if the render method runs every time. Again, check the lifecycle. – ADyson Apr 11 '18 at 15:35
  • "If I make the Step inherit from Panel and set the Visible property would that cause updates to the front-end?" well since you're not even rendering the step unless it's active, that doesn't make much sense. Anyway almost all controls have a Visible property, as far as i remember, not just panels. A Panel is just a way to render a `
    `
    – ADyson Apr 11 '18 at 15:37
  • Yes of course, but I can instatiate all the panels and then use `Visible` to set the `Active` step – Riddell Apr 11 '18 at 15:39
  • That would be a reasonable solution. But you don't need to inherit from Panel. `Control` (parent class for all controls) already has a `Visible` property, so you should just be able to use that I think, unless you've created the controls in an odd way – ADyson Apr 11 '18 at 15:43
  • I've changed the code to use visible and all steps render to the page. Even though I've set visible to false unless the `ActiveStepIndex` matches the Index of the step. – Riddell Apr 11 '18 at 15:50
  • can you show the updated code please? – ADyson Apr 11 '18 at 18:45
  • @ADyson I've updated the snippets. The final GIF explains the issue I am having. The button changes the panel once... – Riddell Apr 12 '18 at 10:47

0 Answers0