views:

57

answers:

2

I have created an application that searches a directory and loads all of the usercontrols into the form and then uses a "GetResult()" method to grab the answers from the form. I did not do this OOP style because I am still learning how to fully utilize OOP and I am now going back to design it with OOP so I can move onto the next part which will be a lot easier if I was working with objects. Right now I have created my "RequestForm" class and I want RequestForm.Result to reach into the UC and call the GetResults() method. I am having a difficult time getting it to do this though due to my lack of knowledge perhaps someone can point me in the right direction.

FormUserControl - Abstract Class

namespace AccessRequest
{
    public abstract class FormUserControl : UserControl
    {
        public abstract string Name();
        public abstract string GetResults();
        public abstract string EmailUsers();
    }
}

RequestForm - Object Class

namespace AccessRequest
{
public class RequestForm
{
    public string Name { get; set; }
    public string ControlID { get; set; }
    public string StepName { get; set; }
    public string FilePath { get; set; }
    public string Results { 
        get
        {
            //How would I pull my usercontrol results with Control.GetReults() from within this area?
            //I have since then turned this into a method. How would I get it to access my UserControl loaded on the asp page to grab the results?
        }
        set;
    }
    public string Emails { get; set; }
    public int Position { get; set; }
    public bool Visible { get; set; }

    public RequestForm()
    {

    }
    /// <summary>
    /// FormResults gathers all needed information about the forms
    /// </summary>
    /// <param name="formName">Name of the Form</param>
    /// <param name="formControlID">ID of the User Control </param>
    /// <param name="wizardStepID">ID of the Wizard Step</param>
    /// <param name="formFilePath">File path of the physical form</param>
    /// <param name="formResults">Results from the form</param>
    /// <param name="formEmails">Other emails to include</param>
    public RequestForm(string formName, string formControlId, string wizardStepID, int wizardStepPosition, string formFilePath, string formResults, string formEmails)
    {
        this.Name = formName;
        this.ControlID = formControlId;
        this.StepName = wizardStepID;
        this.Position = wizardStepPosition;
        this.FilePath = formFilePath;
        this.Results = formResults;
        this.Emails = formEmails;
        this.Visible = false;
    }

    public void SaveList(List<RequestForm> formList)
    {
      //  HttpContext.Current.Session["FormList"] = formList;
    }
}

}

Here is the LoadForms() method I put in OnInit to load all of my forms, I have not fully implemented the RequestForm piece but this is where I believe it should go to builder my object list.

private void LoadForms()
    {
        string dotColor = "Black";
        string formColor = "#808080";

        int loc = 3;
        foreach (ListItem item in chklApplications.Items)
        {
            string formPath = (string)item.Value;

            WizardStepBase newStep = new WizardStep();
            newStep.ID = "wzStep" + item.Text;
            newStep.Title = String.Format("<font color='{0}'>  ¤</font> <font color='{1}'>{2} Request</font>", dotColor, formColor, item.Text);


            var form = LoadControl(formPath);
            form.ID = "uc" + item.Text;
            newStep.Controls.Add(form);
            wzAccessRequest.WizardSteps.AddAt(loc, newStep);

            requestForm.Add(new RequestForm(
                                            item.Text,                      //Form name
                                            form.ID.ToString(),             //User Control ID
                                            newStep.ID.ToString(),          //Wizardstep ID
                                            loc,                            //Wizardstep Position
                                            item.Value.ToString(),          //File Path    
                                            null,                           //Form Results
                                            null                            //Form Emails
                                            ));
            loc++;
        }

    }

Here is where I set whether they are visible or not in my side menu of the Wizard Control. BTW, does anyone know how I can prevent it from even creating the table tags for this? Right now it is growing a large space where I am inserting the forms.

protected void SideBarList_ItemDataBound(object sender, DataListItemEventArgs e)
    {
        if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
        {
            if (!ShowWizardStep(e.Item.DataItem))
            {
                e.Item.CssClass = "Hidden";

            }

        }
    }

Thanks for any advise I receive!! :D

A: 

There are several problems you need to address — both in your code and both in your current knowledge :-) Start with this:

  1. Read a few articles about ASP.NET's page life-cycle, so you become more familiar with what to do in each life-cycle event handler (OnInit, OnLoad, …). Good starting point might be this MSDN overview. However, google for “ASP.NET page life cycle” and read a few other articles and examples as well.

    Also, you will need to get familiar with the request-response nature of ASP.NET page processing. In the beginning, bear in mind that when the user hits some 'Submit' button which in turn causes an HTTP POST request to occur, you are responsible to creating the page's control tree to match its structure, IDs, etc. as they were in the previous request. Otherwise ASP.NET doesn't know how in which controls to bind the data the user filled-in.

  2. Near the line tagged //Lost and confused here :/ you are creating a new control and immediately querying it for the 'results' (which I expect to be values of some edit boxes within it). This cannot work, because it's most probably too early for the form to have the data received from the Page object that drives the request processing.

  3. Your Results property is poorly designed. First, you shouldn't be using properties that actually 'generate' data in such a way they can change without notice. Properties shall be used as “smart fields”, otherwise you end up with less manageable and less readable code. Second, the setter you left there like “set;” causes any value assigned into the property to be actually lost, because there's no way to retrieve it. While in some rare cases this behavior may be intentional, in your case I guess it's just an error.

So, while you can currently leave behind any “good OOP way” to approach your problem, you certainly should get more familiar with the page life-cycle. Understanding it really requires some thinking about the principles how ASP.NET web applications are supposed to work, but I'm sure it will provide you with the kick forward you actually need.

UPDATE: In respect to Tony's code (see comments) the following shall be done to move the code forward:

  1. The list of form data in the requestForm property shall have tuple of [form ASCX path, control ID, the actual RequestForm data class]

  2. GatherForms shall be called only when the page is initially loaded (i.e. if (Page.IsPostBack)) and it shall fill requestForm with the respective tuples for available ASCX forms.

  3. Both chklApplications and wizard steps shall be created in LoadForms on the basis of requestForm's content.

  4. When results are to be gathered, the ID stored in the respective requestForm entry can be used to look-up the actual user control.

Ondrej Tucny
I appreciate the feed back unfortunately because I did not post my full source code you may have gotten a misrepresentation of where I am at with this. Granted this is my first project working with dynamic controls which is making me get more familiar with the life cycle I actually have it working perfectly for me now with out making my forms an object. I am trying to learn how to program the OOP method correctly and that is why I am "rebuilding" it to make it easier to work with. My OnInit gathers all of the forms and then loads all of them. In my PreRender I have them going back....
Tony
In my PreRender I have it deciding if it should actually show the wizard step or not. Why? Because I am managing the controls in sessions and I cannot retrieve the new session data because it does not set the new session data until after the page has completely loaded and my checkboxlist then updates the session. This project has helped me understand live cycle a lot better.
Tony
The "lost here" section is actually setup with {get;set;} currently I was just trying to make it reference the usercontrol for me automatically. What you say makes sense and I agree It will not have the data I need but THAT is the reason I posted this. I am not sure how to have it automatically call the (getResult) from the current usercontrol that is being refernced (which its name is stored in ControlID). Sorry if my post confused you next time I will be sure to post more source.
Tony
Tony, you need to decouple *page construction* from *gathering the results*. Construct — _just_ construct — all controls in your `OnInit` handlers, and gather the results *later* in the processing. Don't combine control instantiation and results gathering the in the `Results` property. I'd expect results gathering to be bound to a `Click` event of a button the page. Then it should work for you quite naturally.
Ondrej Tucny
I am sorry, I must have done a bad job explaining what I need. That is how I have it. OnInit gathers the forms from the directory and then loads them all into the page. PreRender then hide/shows the wizardstep/usercontrol depending on what the users checks. In the CheckBoxList control when they check a box it adds it to a list that determines what controls are displayed in the PreRender phase. My application is 100% working I do not need help with the dynamic aspect. I have taken the advice and removed the Results property and made it into a Method, GetResults().
Tony
Now my problem is using that method to reach into my usercontrol that has been loaded and filled out. I am assuming I would do something like MyRequestForm.GetResult(UserControlID) but I cannot get the method working to look into the UserControl because I do not understand how to allow it to reach into my page. I cannot access Session state in the class either. I am getting better at this stuff but I still dont grasp all of it and I have always had an issue understanding the communication with outside classes. I have used different access (public, protected, private) and I understand them.
Tony
Thank you for taking the time to try and assist me.
Tony
Are you able to make a simple, minimalistic version of your code and post it somewhere? I'm pretty sure the problem still lies somewhere in the way you construct the page + the time you query the results.
Ondrej Tucny
Ok this source code is in "shambles" think of it as a building being remodeled. I have a lot of commented out code from stuff I was testing or the old method I was using and the new method I am using now. It is not pretty but it is commented for the most past so hopefully you can see what I am talking about. Since using the OOP approach it is not working 100% now because I haven't finished the implementation but it is pretty close, the controls dont stay up now which previously they did so I have to work on that. (ugh no enough room!)
Tony
I am looking to have my object reach inside the usercontrol loaded in the form and call the GetResults(). I am basically trying to attach my Class object and results from the user control which in the end is part of the object? Maybe I should not be using both FormUserControl and should have only one class. So FormUserControl is the abstract class which is the usercontrol used to mandate methods the forms must implement.The RequestForm class is the actual object I want to work with to maintain all aspects of the RequestForm. I hope I make sense :/ here are the primary files with a form example
Tony
http://dl.dropbox.com/u/2453852/AccessForms.zip
Tony
I fixed it so the code at least compiles now. I haven't gotten to program all day :/ http://dl.dropbox.com/u/2453852/AccessRequest.zip Thanks for checking this out
Tony
Tony, I hope I understood your code well and outlined a possible solution in my edited answer. It's almost midnight in my timezone and I didn't load it up into VS to actually run and inspect the code, so it can easily be imperfect. However, I hope it will help you move forward.Your code is far from perfect, but I think you know that and you are learning how to improve it. It sounds like a god quest to supply you with a bit more code review. If you want, drop me an email at tucny(at)boldbrick(dot)com and we can discuss it more in-depth off-stackoverflow.
Ondrej Tucny
A: 

Ok, so I figured this out. I already had a method that would gather all the results from the form and then spit them out on the verification screen. I manipulated that code so that now it loads them directly into the object I was working with. Now my objects are full of all the dynamic information I need for my controls and I can manage them a lot easier. Here is the code in case anyone else is looking for the answer.

MyClass

public class RequestForm
{
    public string Name { get; set; }
    public string ControlID { get; set; }
    public string StepID { get; set; }
    public string FilePath { get; set; }
    public string Emails { get; set; }
    public string Results { get; set; }
    public int Position { get; set; }
    public bool Visible { get; set; }

    /// <summary>
    /// FormResults gathers all needed information about the forms
    /// </summary>
    /// <param name="formName">Name of the Form</param>
    /// <param name="formControlID">ID of the User Control </param>
    /// <param name="wizardStepID">ID of the Wizard Step</param>
    /// <param name="formFilePath">File path of the physical form</param>
    /// <param name="formResults">Results from the form</param>
    /// <param name="formEmails">Other emails to include</param>
    public RequestForm(string formName, string formControlId, string wizardStepID, int wizardStepPosition, string formFilePath, string formEmails,string formResults, bool formVisible = false)
    {
        this.Name = formName;
        this.ControlID = formControlId;
        this.StepID = wizardStepID;
        this.Position = wizardStepPosition;
        this.FilePath = formFilePath;
        this.Emails = formEmails;
        this.Results = formResults;
        this.Visible = formVisible;
    }
}

This list that holds all of the controls

    public List<RequestForm> requestForm
    {
        get
        {
            List<RequestForm> requestList = new List<RequestForm>();
            requestList = (List<RequestForm>)Session["RequestForms"];
            var v = Session["RequestForms"];
            return v != null ? (List<RequestForm>)v : null;
        }
        set
        {
            Session["RequestForms"] = value;
        }
    }

This is the method that I use to gather the results and then put them into the object.

    private void GatherFormsData()
    {
        if (requestForm != null)
        {
            foreach (RequestForm rform in requestForm)
            {
                if (rform.Visible)
                {
                    WizardStepBase step = (WizardStep)wzAccessRequest.FindControl(rform.StepID);
                    FormUserControl form = (FormUserControl)step.FindControl(rform.ControlID);
                    rform.Results = String.Format("{0}<br>Email: {1}<br><br>", form.GetResults(), form.EmailContact());
                }
            }
        }
    }

Hope this helps someone.

Tony