locked
Structure for a UserControl? RRS feed

  • Question

  • User-1211892105 posted

    Hi guys -

    I'm playing with the ASP.NET/IronPython integration, trying to work out how best to structure the code-behind for a user control.

    I want to write a control that is essentially an editor for a simple data structure - a list of items, where each item has a title and a body. The interface is a repeater of the titles of the items as linkbuttons - clicking on a title displays the title and body of the item, and includes an edit button, which switches to displaying the item's title and body in textboxes for editing (with cancel and save buttons).

    In C#, I would write a user control class that exposes a Value property, and I'd save the item list, the currently selected page and whether we're in edit mode in the viewstate, maintaining those three pieces of information as instance variables between the load viewstate and save viewstate events. In IronPython for ASP.NET, I can't see how I would do that - since there's no class (that I have access to), and module-level variables are shared by all the executing requests, there's no way to maintain state between the different event handlers for one request.

    Is having each event handler load the viewstate and save it again how other people are handling this?

    Thanks,
    Christian
     

    Tuesday, May 29, 2007 3:24 AM

All replies

  • User1641955678 posted

    Hi Christian,

    Note that the IronPython ASP.NET support was updated as part of the May 2007 ASP.NET Futures, which you can get here: http://asp.net/downloads/futures/default.aspx?tabid=62.  In that release, module level variables are actually not shared, unlike in the initial release.  As an alternative solution that will work in the first release, you can write 'page.SomeState = someValue', where 'page' is a magic token that is made available for that purpose.  But please upgrade to the latest bits if at all possible.

    thank!
    David

     

    Tuesday, May 29, 2007 1:24 PM
  • User-1211892105 posted

    Thanks David.

    I've upgraded to the ASP.NET Futures May 2007 release, and read through the quickstarts - it all sounds very exciting!

    One problem I've had with my UserControl after upgrading is that I can't import from packages in the App_Script folder - this is something that I had working with the IronPython CTP. I have a directory main/ in App_Script, containing __init__.py  and model.py, where model.py defines the Document class. I get an import error at the line "from main.model import Document". Any idea why this might be?

    To work around this, I've moved the modules from the package directly into App_Script (and changed the imports within the package modules). Now I'm having trouble customising the viewstate that my page is saving. I've defined LoadViewState(state) and SaveViewState functions, as follows:

    def LoadViewState(state):
        ScriptPage.LoadViewState(page, state["baseState"])
        pickledDoc = state.get("document")
        if pickledDoc is not None:
            page.document = pickle.loads(pickledDoc)
        pickledPage = state.get("currentPage")
        if pickledPage is not None:
            page.currentPage = pickle.loads(pickledPage)
        page.editing = state.get("editing", False)


    def SaveViewState():
        state = {}
        state["baseState"] = ScriptPage.SaveViewState(page)
        state["document"] = pickle.dumps(page.document)
        state["currentPage"] = pickle.dumps(page.currentPage)
        state["editing"] = page.editing
        return state

    (At the moment this is in an aspx file, rather than an ascx - I'm still trying to work out how to structure this.) 

    As you can see, I'm trying to override ScriptPage.LoadViewState and ScriptPage.SaveViewState, making sure to call the base methods, but with breakpoints set in both functions the debugger never reaches either - they aren't being called. Is this possible with the current system? How would you recommend I implement this?

    Thanks,

    Christian

     

    Wednesday, June 6, 2007 3:31 AM
  • User1641955678 posted

    Hi Christian,

    We'll need to investigate the App_Script behavior as I'm not sure what's going on.

    The LoadViewState behavior you're seeing is a result of the way the page model works in IronPython.  Instead of truly overriding the page class, the code behind is providing code for specific methods, but does not really have the ability to override Page methods like LoadViewState.  This is something we could address by having our base page (ScriptPage) override the methods and 'forward' all the relevant calls to the python code.

    In the meantime, I think you should be able to work around this by having your own custom base page.  e.g. you could have something like this:

    public class CustomScriptPage: ScriptPage {
        protected override void LoadViewState(object savedState) {
            base.LoadViewState(savedState);

            DynamicFunction f = ScriptTemplateControl.GetFunction("ScriptLoadViewState");
            if (f != null)
                ScriptTemplateControl.CallFunction(f, savedState);
        }
    }

    And then if you have a ScriptLoadViewState method in your Python code, it would get called.

    David

    Wednesday, June 6, 2007 3:42 PM
  • User-1211892105 posted

    Thanks David -

    That sounds like a sensible idea. I tried it using the following code:

        protected override object SaveViewState() {
            DynamicFunction f = this.ScriptTemplateControl.GetFunction("ScriptSaveViewState");
            if (f == null) {
                return base.SaveViewState();
            } else {
                return this.ScriptTemplateControl.CallFunction(f);
            }
        }

    I structured it this way, rather than using your definition, because this makes the code more closely match what it would look like if we were really overriding the method.

    Then the IronPython code for ScriptSaveViewState is:

        def ScriptSaveViewState():
            state = {}
            state["baseState"] = ScriptPage.SaveViewState(page)
            state["document"] = pickle.dumps(page.document)
            state["currentPage"] = pickle.dumps(page.currentPage)
            state["editing"] = page.editing
            return state

    It wraps the base class's viewstate in a dictionary with my page's extra state; I'm doing the super call using the usual Python idiom, being careful to directly call SaveViewState on ScriptPage rather than CustomScriptPage (which would lead to infinite recursion).

    For some reason, when this executes I do get an infinite recursion, and the dev web server blows up. Debugging I can see that the ScriptPage.SaveViewState(page) line is for some reason calling CustomScriptPage.SaveViewState instead - essentially it looks like it's doing a normal method dispatch on page (ie page.SaveViewState, which is CustomScriptPage.SaveViewState) when it should be using the specific method ScriptPage.SaveViewState. Do you know why this might be?

    Thanks for your help,
    Christian
     

    Friday, June 8, 2007 12:15 PM
  • User1641955678 posted

    It must be doing a virtual method call no matter what class you call it on.

    How about reworking the flow a little bit.  e.g.

        protected override object SaveViewState() {
            DynamicFunction f = this.ScriptTemplateControl.GetFunction("ScriptSaveViewState");
            if (f == null) {
                return base.SaveViewState();
            }
            else {
                return this.ScriptTemplateControl.CallFunction(f, base.SaveViewState());
            }
        }

      def ScriptSaveViewState(baseState):
        state = {}
        state["baseState"] = baseState
        state["document"] = pickle.dumps(page.document)
        state["currentPage"] = pickle.dumps(page.currentPage)
        state["editing"] = page.editing
        return state

     This way, there is no risk of an infinite recursion.
    Friday, June 8, 2007 3:04 PM
  • User-1211892105 posted

    Hi David -

    I ended up doing something very similar to this (using a Pair, since Python dicts aren't serialisable in the viewstate) - thanks for the advice.

    Is there any light on the problem with packages in App_Script? When is the next update of the IronPython integration likely to be made?

    Cheers,
    Christian
     

    Tuesday, October 16, 2007 5:09 PM