Generics and user controls

M

mark.norgate

I've run into a few problems trying to use generics for user controls
(classes derived from UserControl). I'm using the Web Application model
rather than the Web Site model.

The first problem I'm having is that the partial class signature in my
projectDetails.ascx.cs file looks like this:

public partial class ProjectDetailsControl<TEntryServiceProvider> :
UserControl, INamingContainer where TEntryServiceProvider :
IEntryServiceProvider {...}

....and no matter what I put in the corresponding projectDetails.ascx
file's Inherits attribute, the compiler complains. I have tried:

Inherits="Epic.Timesheet.Controls.ProjectDetailsControl"

....but then this creates an entirely different class in the
projectDetails.ascx.designer.cs and so none of the controls are
available to the class, since the two do not merge at compile time. I
then tried:


Inherits="Epic.Timesheet.Controls.ProjectDetailsControl<TEntryServiceProvider>"

But then at run time I get:

Parser Error Message: Could not load type
'Epic.Timesheet.Controls.ProjectDetailsControl<TEntryServiceProvider>'.

....which I don't quite understand, so I tried:


Inherits="Epic.Timesheet.Controls.ProjectDetailsControl<TEntryServiceProvider>
where TEntryServiceProvider : IEntryServiceProvider"

....which results in the same message. Finally, I tried the whole
signature:


Inherits="Epic.Timesheet.Controls.ProjectDetailsControl<TEntryServiceProvider>
: UserControl, INamingContainer where TEntryServiceProvider :
IEntryServiceProvider"

....but then projectDetails.ascx.designer.cs fails to compile because
UserControl cannot be found.

Gah!

Is it possible to use generics with user controls in this way? Or
should I reluctantly resort to using inheritance rather than generics
to provide these varying behaviours?

Mark
 
D

Dave Sexton

Hi Mark,

Designers do not support generic Controls, AFAIK, but you should use a polymorphic approach anyway.
 
M

mark.norgate

Ok,

I reluctantly used inheritance to create my three different controls,
but still don't work. Along the lines of:

public partial class EntryDetailsControl : UserControl,
INamingContainer {...}

....and...

public partial class ProjectDetailsControl : EntryDetailsControl
{...}

....but there's a new problem now. When trying to create a
ProjectDetailsControl, I get a runtime error in the base class,
complaining that the controls on the page "Object reference not set to
an instance of an object" (i.e., they haven't been created). I tried
putting an EnsureChildControls() in the base's Page_Load(), but it
never gets called...

Any ideas?

Mark
 
D

Dave Sexton

Hi Mark,

In my response to your OP I was referring to the polymorphism of the generic Type, TEntryServiceProvider, and not to creating an
inheritance chain of UserControls. I don't think that you can subclass a UserControl from another anyway. I tried it just to see
and it worked without error, however the interface of the base UserControl was not rendered. I'm not sure what I have done
differently then you, but it seems pointless anyway if the base interface won't render.

If you need to combine user interface elements of multiple UserControls, try the following instead. It allows you to place any
UserControl into your EntryDetailsControl without the need for inheritance. It does this by using a PlaceHolder control, a public
property and the Page.LoadControl method:

{EntryDetailsControl.ascx}
<%@ Control ... %>
<h1>Entry Details</h1><!-- design user control here -->
<asp:placeHolder runat="server" id="placeHolderDetails" />
<!-- design user control here -->

{EntryDetailsControl.ascx.cs}
public partial class EntryDetailsControl : UserControl, INamingContainer
{
public PlaceHolder Details
{
get
{
return placeHolderDetails;
}
}
}

{ProjectDetailsControl.ascx}
<%@ Control ... %>
<h2>Project Details</h2>

{ProjectDetailsControl.ascx.cs}
public partial class ProjectDetailsControl : UserControl // must derive from UserControl
{
}

{WebPage.aspx}
<%@ Page AutoEventWireup="true" ... %>
<%@ Register Src="EntryDetailsControl.ascx" TagName="EntryDetailsControl" TagPrefix="markUC" %>

<script runat="server">
private void Page_Init()
{
EntryDetailsControl.Details.Controls.Add(Page.LoadControl("~/ProjectDetailsControl.ascx"));
}
</script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<markUC:EntryDetailsControl runat="server" id="EntryDetailsControl1" />
</form>
</body>
</html>
 
M

mark.norgate

Hi Dave

Thanks for your response.

In my original generic-based attempt, the type TEntryServiceProvider is
in fact one of three classes that implement the interface
IEntryServiceProvider as you can see from the class signature in my
original post:

public partial class ProjectDetailsControl<TEntryServiceProvider> :
UserControl, INamingContainer where TEntryServiceProvider :
IEntryServiceProvider {...}

Unfortunately, this does not work. I don't want to use PlaceHolder as
you suggest, since I have three classes that function pretty much in
the same way, with a few minor exceptions, and look almost exactly the
same, with a few minor differences.

I think I'm just going to have to pass the service provider class and
make it a property of the UserControl.

Thanks,

Mark
 
D

Dave Sexton

Hi Mark,

I missed IEntryServiceProvider the first time around :), but the idea was to promote a more OOP approach anyway, within reason. I
figured that since you are looking for an unorthodox solution to combine the functionality of multiple user controls that your
requirements might be a bit special and therefore, now, I feel that you would benefit from choosing an appropriate design pattern.

Creating a property on your UserControl is probably the easiest way to handle your situation and may very well be the best choice,
but think about having to manage the components in the future. For example, what happens if new requirements are discovered and you
have to add or remove functionality? Will you have to make another UserControl and/or modify the IEntryServiceProvider interface,
possibly breaking the existing UserControls?

You might want to think about creating user process components to encapsulate the "differences" between your existing controls and
then only use one UserControl for the presentation. There might be another, better design approach that you can take but that
depends on your requirements, which you haven't specified.

Of course, if you are just exploring the possibilities to expanding your coding style then I completely understand your OP, but it
seems that you were looking to solve a problem, which you haven't really specified. If you aren't satisfied with using a property
then you might want to state the problem that you are trying to solve and you might get a better response from the group.
Specifically, one that addresses the problem at hand.

HTH
 
M

mark.norgate

Dave

"You might want to think about creating user process components to
encapsulate the "differences" between your existing controls and then
only use one UserControl for the presentation. There might be another,
better design approach that you can take but that depends on your
requirements, which you haven't specified."

This sounds interesting. Can you explain?

Perhaps I should elaborate on my requirements. I'm writing a timesheet
application, and there are three things to which we can charge time:
projects, potential projects and overheads. The page can contain any
number of these three controls, that represent time charged to a
particular project and so on.

They all use the same DataSet, but each has a different DataView on
that set. Also, some of the controls are absent for overheads because
they're not appropriate ("role", for example).

Perhaps I'm overcomplicating things, but I like to remain as generic as
possible, even if there's not the remotest possibility of the
requirements changing (which in this case, they probably won't).

Mark
 
D

Dave Sexton

Hi Mark,

User process components encapsulate the logic for a particular process, including state management, and abstract it from the
controls that actually render data and interact with the user. They are used to manage the flow of data and handle exceptions.

If your UserControls aren't managing a process that shares state or if you don't expect the need for frequent modifications to your
existing design then user process components will probably not be your best architectural choice. According to your specs, it
sounds like each distinct UserControl must manage its own data and each has its own presentation requirements that aren't part of a
larger process, such as a wizard, for example. From your description, it sounds like you might benefit from a different approach.

Here's an article about the MVC pattern on MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/DesMVC.asp

You might benefit from the use of other design patterns or business components. If your UserControls share any data, logic or
interface elements that you were trying to provide with generics or inheritance, you might want to extract that functionality from
each of the UserControls and place it in business components. The components can be activated in your UserControls by events or
handle events on the UserControl through event registration (observer pattern).

For instance, if your UserControls share some common interface you might want to abstract the details from each of the UserControls
and create a component that can render the common interface elements, such as TimeChargeSlotRenderer, which could render the space
in which any of the UserControls may be rendered and interact with your IEntryServiceProvider interface, if necessary, so that the
UserControls won't have to. TimeChargeSlotRenderer could render a border, background, or provide common buttons to each UserControl
that occupies a slot and forward events from those buttons to the UserControls through an ITimeChargeSlotEvents interface. Then,
each UserControl could render its own, distinct interface elements and provide an implementation for the ITimeChargeSlotEvents
interface to handle common events. Business components could be used to handle data validation, processing exceptions, and to
redirect the user to a different page, for example.

I'm sure that you haven't posted all of your requirements or details about your application, so take my suggestions with a grain of
salt. Just review some of the common design patterns and chose the ones that are the most appropriate, if any.

Here are some links that may be of interest:

ASP.NET web casts on architecture
http://www.microsoft.com/events/series/archdesignsystems.mspx

Microsoft Patterns & Practices
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/MSpatterns.asp

Data & object factory
http://www.dofactory.com/Patterns/Patterns.aspx

PatternShare (click the "Browse by Table" link, for instance)
http://www.patternshare.org/
 
M

mark.norgate

Hi Dave

Thanks for your extensive reply.

I shall be reading the articles you've posted today, was ill at the end
of last week so didn't get the opportunity.

Will let you know how I get on!

Thanks, Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
SterlingLa
Top