How do I have two user controls with one code-behind file?

A

Alan Silver

Hello,

I have a user control which I am trying to load dynamically, but am
running into problems. I think the problem is because I have two .ascx
files that refer to the same .ascx.cs file.

A simple example is like this...

Ferret1.ascx
=========
<%@ Control Language="C#" CodeFile="Ferret.ascx.cs" Inherits="Ferret" ClassName="Ferret" %>
One - <asp:Literal ID="litFerretName" Runat="Server" />

I have another one Ferret2.ascx which is exactly the same, except for
the fact that the "One" is changed to "Two" so I can see which is which
on the page.

The code-behind file looks like this...

Ferret.ascx.cs
===========
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class Ferret : UserControl {
public void DisplayFerret() {
litFerretName.Text = "hello";
}
}

Now, in the calling page, I have a Placeholder called plcFerret. The
code in the code-behind for the page looks like this...

Ferret ctlFerret1 = (Ferret)LoadControl("Ferret1.ascx");
ctlFerret1.DisplayFerret();
plcVole.Controls.Add(ctlFerret1);

Ferret ctlFerret2 = (Ferret)LoadControl("Ferret2.ascx");
ctlFerret2.DisplayFerret();
plcVole.Controls.Add(ctlFerret2);

The first control loads fine, but the second one gives an error of
"Unable to cast object of type 'ASP.Ferret' to type 'Ferret'" on the
line where it tries to load Ferret2.ascx.

Any ideas? This has been driving me mad!! TIA
 
T

Teemu Keiski

Hi,

1. Create a class into App_Code

public class BaseFerret : System.Web.UI.UserControl
{
protected Literal litFerretName;

public void DisplayFerret()
{

litFerretName.Text = "hello";
}
}

2. Modify Ferret.ascx.cs class to derive from this

public partial class Ferret : BaseFerret
{


}

3. Modify @Control directive in both user controls so that it additionally
contains

< % @Control ... CodeFileBaseClass="BaseFerret" % >
 
T

Teemu Keiski

This one was missing

4, modify the code on Page to cast to the base type

BaseFerret ctlFerret1 = (BaseFerret)LoadControl("Ferret.ascx");

ctlFerret1.DisplayFerret();

plcVole.Controls.Add(ctlFerret1);

BaseFerret ctlFerret2 = (BaseFerret)LoadControl("Ferret2.ascx");

ctlFerret2.DisplayFerret();

plcVole.Controls.Add(ctlFerret2);



Teemu


Teemu Keiski said:
Hi,

1. Create a class into App_Code

public class BaseFerret : System.Web.UI.UserControl
{
protected Literal litFerretName;

public void DisplayFerret()
{

litFerretName.Text = "hello";
}
}

2. Modify Ferret.ascx.cs class to derive from this

public partial class Ferret : BaseFerret
{


}

3. Modify @Control directive in both user controls so that it additionally
contains

< % @Control ... CodeFileBaseClass="BaseFerret" % >
 
T

Teemu Keiski

True but you can "tell" the compilation system in v2.0 to "look" for the
members from code.behind's base class with CodeFileBaseClass attribute in
@Control directive. I just demonstrated it above.

For the reference: http://msdn2.microsoft.com/en-us/library/d19c0t4b.aspx

CodeFileBaseClass

Specifies a path to a base class for a control and its associated
code-behind class. This attribute is optional, but when it is used the
CodeFile attribute must also be present. Use this attribute when you want to
implement a shared scenario, where you define common fields (and optionally,
associated events) in a base class to reference the controls declared in a
user control. Because of the ASP.NET code generation model, if you defined
the fields in a base class without using the this attribute, at compile time
new member definitions would be generated for the controls declared in the
user control (within a separate partial class stub), and your desired
scenario would not work. But if you use the CodeFileBaseClass attribute to
associate the base class with the user control, and you make your partial
class (its name is assigned to the Inherits attribute and its source file is
referenced by the CodeFile attribute) inherit from the base class, then the
fields in the base class will be able to reference the controls in the user
control after code generation

Thanks,

Teemu
 
G

Guest

Agreed.

In the scenario that Alan posted; the second control did not extend the
first, it just changed the display of a label on it.

I wonder what is the benefit of using the CodeFileBaseClass in such a
scenario compared to loading the same control twice and changing the label
display through a public property; e.g.

Ferret ctlFerret1 = (Ferret)LoadControl("Ferret1.ascx");
ctlFerret1.DisplayFerret();
plcVole.Controls.Add(ctlFerret1);

Ferret ctlFerret2 = (Ferret)LoadControl("Ferret1.ascx");
//change ctlrFerret2.somePublicProperty
ctlFerret2.DisplayFerret();
plcVole.Controls.Add(ctlFerret2);
 
T

Teemu Keiski

I agree that not much in this specific case,

CodeFileBaseClass does add complexity compared to a simple property, no
doubt. I suppose its real value comes when you write bigger Control or Page
libraries with deeper inheritance.

Teemu

Phillip Williams said:
Agreed.

In the scenario that Alan posted; the second control did not extend the
first, it just changed the display of a label on it.

I wonder what is the benefit of using the CodeFileBaseClass in such a
scenario compared to loading the same control twice and changing the label
display through a public property; e.g.

Ferret ctlFerret1 = (Ferret)LoadControl("Ferret1.ascx");
ctlFerret1.DisplayFerret();
plcVole.Controls.Add(ctlFerret1);

Ferret ctlFerret2 = (Ferret)LoadControl("Ferret1.ascx");
//change ctlrFerret2.somePublicProperty
ctlFerret2.DisplayFerret();
plcVole.Controls.Add(ctlFerret2);
 
A

Alan Silver

Phillip said:

First, thanks to both of you for your help. I'm too tired to try it out
now, but will hopefully investigate further tomorrow.
In the scenario that Alan posted; the second control did not extend the
first, it just changed the display of a label on it.

I wonder what is the benefit of using the CodeFileBaseClass in such a
scenario compared to loading the same control twice and changing the label
display through a public property; e.g.
<snip>

Ah, that's because I deliberately hacked down the problem to the
simplest case that I could find that demonstrated the issue.

The real situation has a much more complex scenario, where the code
behind pulls info from a database and binds various controls to the
data. It is coded to account for the presence, or absence of various
repeaters and/or datagrids, allowing the one code behind file to be used
for multiple .ascx files. This allows me to display basically the same
information in many different ways, just by adding a new .ascx file to
the collection.

I agree that in the simple case I posted it was not necessary to go to
such lengths!!

Thanks again. I'll try the idea out tomorrow and see if it works.
 
A

Alan Silver

Teemu Keiski said:
1. Create a class into App_Code
<snip>

Thanks you *so* much, that worked fine.

The only other question, to which I guess I already know the answer, is
that the way you showed requires all the controls in the .ascx file to
be declared explicitly in the base class file. I've got lazy, being used
to partial classes, so was hoping I could get away without having to
declare them explicitly. Is this a false dream, or do I have to settle
for declaring them explicitly?

I know it's not a big issue, and it's only in the one file, but you know
what it's like when you get used to something easy!!

Thanks again
 
T

Teemu Keiski

Trick with it is that CodeFileBaseClass does prevent those fields from being
emitted in the partial classes. But in practise it concerns only those
fields that you expect to be accessible in base class, e.g those which are
used there, they ened to be declared. Note that the base class isn't partial
so its fields won't be emitted by the compiler.

--
Teemu Keiski
ASP.NET MVP, AspInsider
Finland, EU
http://blogs.aspadvice.com/joteke
 

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

Top