Base Page Class

Topics: Web Client Software Factory
Jan 9, 2007 at 9:48 AM
Following my comments under "Get Accessor for Presenter in ASPX pages Posts" in the User Forum, I thought I'd post a base page class along the lines of what I was referring to. See below (difficult to read as it won't be formatted correctly - apologies).

<code>
public class WcsfBasePage<T, Q> : System.Web.UI.Page
where T : Presenter<Q>
where Q : class
{
private T _presenter;

protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this._presenter.OnViewInitialized();
}
this._presenter.OnViewLoaded();
}

CreateNew
public T Presenter
{
get
{
return this._presenter;
}
set
{
this._presenter = value;
this._presenter.View = this as Q;
}
}
}
</code>

This is then used as follows;

<code>
public partial class Booking_Default : WcsfBasePage<DefaultViewPresenter, IDefaultView>, IDefaultView { class... }
</code>

The code behind file is then completely empty, except for code that the user adds, which uses the Presenter property on the base class. e.g.;

<code>
Presenter.Book(DateTextbox.Text);
</code>

Any thoughts?
Jan 9, 2007 at 11:16 AM
I vote for this solution.
Jan 9, 2007 at 11:22 AM
One small gotcha is that if people have their own base page class already that inherits from System.Web.UI.Page a slightly different approach would be needed for them;

public class TestBasePage<T, Q> : WcsfBasePage<T, Q>
where T : Presenter<Q>
where Q : class

i.e. they can still do it, but should instead inherit from the WcsfBasePage class, and need to include some of the generics statements to ensure constraints are met.

I'll shut up for a bit now and let someone else comment on whether this is worthwhile :-)
Jan 9, 2007 at 1:31 PM
The generic base page seems a good solution/practice, but I don't think you will benefit from having the codebehind class as a generic.

I would see something more like this:

public abstract WcsfBasePage<TPresenter, TView> : System.Web.UI.Page
where TPresenter : IPresenter<TView>
where TView : IView
{
private TPresenter presenter; // I REALLY HATE those _fieldNames

CreateNew
public TPresenter Presenter
{
protected get
{
return this.presenter;
}
set
{
this.presenter = value;
this.presenter.View = this;
}
}

protected override OnLoad(EventArgs e) // OnInit|OnPreInit ???
{
base.OnLoad(e);
if (!this.IsPostBack)
{
presenter.OnViewInitialized();
}
presenter.OnViewLoaded();
}
}

public class MyPageCodebehind : WcsfBasePage<MyPresenter<IMyView>, IMyView>, IMyView
{
}
Jan 9, 2007 at 1:46 PM
I'm not sure I see the difference, other than some syntactic changes to the generic constraints (and a correction to my Page_Load handling :-)?

Feel free to point out the obvious...

It sounds like you thought I meant that the code behind should be generic - what I really meant is that the code behind should inherit from the generic base class WcsfPage.

My follow up comment was referring to the not uncommon practice of defining a custom base page for a site - which would need to be generic, so that it can inherit from WcsfPage.

The code behind itself would never be generic - although it would inherit from the generic class.

Apologies if I've misunderstood your point!
Jan 9, 2007 at 1:49 PM
How about this: To generic?

public class WcsfPage<T, I, B> : B
where T : Presenter<I>
where I : class
where B : System.Web.UI.Page
{
// ... implementasjon
}

public partial class ElectronicFundsTransfer_NewTransferView : WcsfPage<NewTransferViewPresenter, INewTransferView, System.Web.UI.Page>, INewTransferView
{
/// ... implementasjon
}

Now it's just to replace the System.Web.UI.Page with your own class you want to inherit from.
Jan 9, 2007 at 2:13 PM
I miss editing my own posts....

It looks like we all simple are talking about the same solution.

But my lack of skills and knowlegde about generics shows big time.

My solution to give the inherited page as an input to the generic class do'snt work.

There are to solutions to this problem

Approach 1:
One global base page, where the developer replaces the System.Web.UI.Page with his own page to inherited from.

public class WcsfBasePage<TPresenter, TView> : System.Web.UI.Page // <== Replace this if u want to inherited from your own basepage.

Approach 2:

One base page for every module.

public class EFTModuleBasePage<TPresenter, TView> : System.Web.UI.Page // <== Replace this if u want to inherited from your own basepage.

Jan 9, 2007 at 2:16 PM
Looks like each one of us has its own taste for how to do things but the general idea is the same.
Jan 9, 2007 at 2:22 PM
Agreed! What do the p&p guys think?
Jan 10, 2007 at 1:08 PM
We didn’t include a base class for Web pages mainly because we wanted to keep the framework as less intrusive as possible. A lot of users already have base classes for Web pages and it might be difficult for them to change them, either because of a company policy or because they simply can’t access the source code.

Anyway, you can add the base class to the guidance package if you want. After we publish the final release, I’ll write a post in my blog explaining how to do it.
The solution I had in my mind allows your Web pages to look like this:

public partial class Module1_View1 : PageBase<View1Presenter>, IView1
{

}

but it implies using a different presenter class, as shown in the following code:

public class View1Presenter : CompositeWeb.Extensions.Presenter<IView1>
{

}

public abstract class Presenter<TView>
: Microsoft.Practices.CompositeWeb.Presenter<TView>, IPresenter
{
#region IPresenter Members

object IPresenter.View
{
get { return View; }
set { View = (TView)value; }
}

#endregion
}

public interface IPresenter
{
object View { get; set; }
void OnViewInitialized();
void OnViewLoaded();
}

public class PageBase<TPresenter> : Page
where TPresenter : IPresenter
{
private TPresenter _presenter;

CreateNew
public TPresenter Presenter
{
get { return _presenter; }
set
{
_presenter = value;
_presenter.View = this;
}
}

protected virtual void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
_presenter.OnViewInitialized();
}
_presenter.OnViewLoaded();
}
}

Cheers,
Mariano Szklanny
http://staff.southworks.net/mariano
Jan 10, 2007 at 2:57 PM
I didnt understand this argument:

"A lot of users already have base classes for Web pages and it might be difficult for them to change them, either because of a company policy or because they simply can’t access the source code."

the viewpage in your solution does require that a developer has to replace the inheritance from System.Web.UI.Page to there own basepage for each view in each module (or change the recipe).

If you have one global basepage for all views or one local basepage for each module, the user needs only to change the global basepage or each local basepage once to inherit from their companys basepage. This will require less work than your solution.

Our solution as stated here doesnt require to change the base classes that already exists in a company, so no company policy is broken and it dosn't require to access the sourcecode for the baseclasses.

Correct me if im wrong. It wouldnt be the first time ;)
Jan 11, 2007 at 8:43 AM
Hmm... I can see both sides to this. I do see what you mean about there being a chance people are using frameworks that mandate a base page type, that they don't have the source code for.

I wonder if a tick box in the recipe would work - "use WCSF base page" or similar.

I just think it is a shame to lose the "cleaner" default code behind pages that so many developers have appreciated in .NET 2.0 / VS2005.

Looking forward to your blog post though! Cheers
Jan 14, 2007 at 12:05 AM
The instructions to add a base class for views in the guidance package is out there:
http://staff.southworks.net/blogs/mariano/archive/2007/01/13/How-to_3A00_-add-a-base-class-for-Web-pages.aspx

Enjoy :)
Feedback welcome, as always.

Mariano Szklanny
http://staff.southworks.net/mariano
Jan 14, 2007 at 7:48 AM
Thank u again, Mariano. Very nice example.

Now i understand your argument about developers not having access to their companys basepage.

Your solution is good. But my prefered solution is to generate one global basepage<> or one modulbased basepage<> and add it to the websiteproject in the solution, so the user can change the inheritance to his own basepage as needed.
Jan 14, 2007 at 2:42 PM
Another top post, Mariano. Both for the solution and the behind-the-scenes factory details.

Simon
http://www.dotnetblogs.co.uk/
Jan 24, 2007 at 12:13 AM
Conflicting Types,

The step by step manual posted by Mariano does a great job explaining how to setup a base page.

However, I decided to take it one step further and cleaning up name spaces usage within the templates (T4) which created compile errors. After some investigation, I realized the compile problem is because of both CompositeWeb.Extensions.Presenter and CompositeWeb.Extensions contain a type with the same name (Presenter).

Using the viewpresenter.cs template as an example works fine when referencing full name
using Microsoft.Practices.CompositeWeb;

namespace <#= this.ModuleNamespace #>.Views
{
public class <#= this.ViewName #>Presenter : CompositeWeb.Extensions.Presenter<I<#= this.ViewName #>>
}

But fails when shorten type name

using Microsoft.Practices.CompositeWeb;
using CompositeWeb.Extensions;

namespace <#= this.ModuleNamespace #>.Views
{
public class <#= this.ViewName #>Presenter : Presenter<I<#= this.ViewName #>>
}


So, this means we can’t use short typed qualified names, I’m not sure what the solution is but one possible option is renaming the presenter in CompositeWeb.Extensions, Any suggestions!

Cheers
Sonny M
Jan 24, 2007 at 8:51 AM
I'd argue that this is what namespaces are for... so I wouldn't change a thing!

Both are presenters, so call them both presenters.

I do realise this can be a pain for developers, though - in which case I would normally just reference one namespace, and use the other (less frequently used presenter) using a fully qualified name.

Simon
http://www.dotnetblogs.co.uk/