Dynamic DB lookup/eval to serve a user control bound to a repeater

G

Guest

I have what's probably a simple page lifecycle question related to
dynamically evaluating values that are placed by a repeater and dynmically
placing user controls that use those values.

I'm attempting to bind a user control I've written, "ImageBox", to a
repeater.

The user control takes a custom property, "ContentID", that will execute a
database lookup and load an image.

I am attempting to do those lookups based on the dynamic evaluation of
values that are placed from a SqlDataSource into the repeater:

<asp:SqlDataSource SelectCommand="Select Top (5)...
<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="NewsReleasesWithIcons" OnInit="Prerendering">
....
<ItemTemplate>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1" runat="server" ImageWidth="200"
ContentID='<%# Eval("Con_ConRelTo") %>' />
</ItemTemplate>

If the ContentID were passed from the repeater to the ImageBox control and
evaluated, the control would be dynamically generated. I have tested this
works by substituting a static value for the value I am trying to evalate: I
get my image control loaded and repeated five times.

The code above does not do what I intend: looking at the debugger, the
ContentID does not get passed to the UserControl "in time" -- on the first
iteration of the repeater, the ContentID is set to zero, rather than the
values that [will] come out of SqlDataSource. The evalution of <%#
Eval("Con_ConRelTo") %> does not happen before the repeater attempts to
instantiate my user control.

Is there a simple way to address this problem? One way or another, I need
the following sequence

1) Database lookup for a collection of ContentIDs (this is what I was
attempting to use SqlDataSource todo)

2) Binding the ContentIDs found in step 1 to a repeater, one for each item

3) Repeater calls User controls and passes them my ContentIDs

4) User controls do their own DB lookups, and place cutlines, images, etc.

Thank you,
-KF
 
S

Steven Cheng[MSFT]

Hello KF,

Regarding on the problem you meet here, I think it is due to the control's
initializing and databinding sequence in ASP.NET page. When you have a
usercontrol defined in Repeater's ItemTemplate, the ASP.NE page will
intializing the usercontrol in the following sequence:

i) first, when repeater is bound to a datasource, each repeaterItem is
created(any sub controls in it will also be created)

ii) after each repeater item and sub controls be created,it will perform
databinding on each repeaterItem.

Therefore, if you want to initialize the usercontrol(and its sub content)
in the sequence you want (1) .. 2) ..3) ..4) ...), you may need to consider
the following means:

** still using DataBinding to intiailze the public property on your
Usercontrol, and this databinding will occur after your usercontrol and its
content controls be created and initializing. So if you have other
initiailzing code(such as populating a databound list control from
database...), you need to put it in other event time (as below)

** move the code that populate other controls in Usercontrol(from database)
depend on the public property (be initiailzed through databinding in the
previous step) to other place. One proper event is the Repeater control's
ItemDataBound event, this event fires after each RepeaterItem has performed
databinding. And in that event you can try get your Usercontrol's instance
and call some public methods on your usercontro. e.g.

====================
protected void Repeater1_ItemDataBound(object sender,
RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
ListItemType.AlternatingItem)
{
uc_WebUserControl wuc = e.Item.FindControl("WebUserControl1")
as uc_WebUserControl;

//suppose you have expose a public function to do the further databinding
(from database) in your usercontrol

wuc.FillDataGrid();

}
}
=====================

#note that in ItemDataBound, we should not create new controls here, but
can adjust some control's states or peform some databinding operations here
because this event is fired only when the repeater is being databound to
datasource(not in each page request).

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead



==================================================

Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.



Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.

==================================================



This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Thanks Steven. The lifecycle explanation is helpful, though I still haven't
figured out a solution for my particular application. For others that might
stumble across this thread, this page is also helpful:
http://msdn2.microsoft.com/en-us/library/ms178472.aspx

I will explain bit more about what I'm tryiing to do and show some code. I
have a usercontrol called "ImageRepeater" that is intended to display a
series of photos. The photos are relationally linked to a parent article,
and I can query for a collection of photographs. The SqlDataSource that is
attached to ImageRepeater is intended to tell the repeater what ContentIDs
to load in each iteration of the repeater.

Within the "ImageRepeater" control, I'm attempting to bind a bunch of
"ImageBox" controls, another control I've made. Each ImageBox manages its
own data access.

The failure I'm having is that while the individual ImageBoxes in the
repeater will render OK if they're supplied a dummy value, they do not
dynamically evaluate the ID that I'm trying to supply through an Eval in the
declarative code.

I didn't completely follow your suggestion -- this stuff is not my forte --
but from reading the page above and reading your message, it would seem that
if I can move some aspects of this operation from declarative markup to
code, it may fix the problem. I can attach a method to ItemDataBound and
other events in the repeater's lifecycle, but I'm not sure that's the major
issue. The nested ImageControls controls WILL render properly if a hardcoded
ContentID is supplied to their parent; the nested controls are working
properly in the repeater once they know what it is they're supposed to be
rendering. What I need is some way to programmatically "grab" what should be
the active ContentID given the repeater's current position. Alternately,
maybe we could consider some sort of solution in which I first grabbed an
array of ContentIDs corresponding to the photos I wanted to display in the
imagerepeater, and subsequently, as each iteration of the repeater happened,
it would fire a "where am I now" function that would grab the right
ContentID and programmatically populate the variable being fed to the
individual ImageBox controls. I'm not exactly sure how to do that, but I
haven't played with this yet. I'm wondering if its the best approach.

I'm attaching my declarative markup below. If there's an optimum way (or any
way) to make this happen, please let me know.

This is the declarative code for ImageRepeater:

<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="Uwn_ImageRepeater.ascx.cs"
Inherits="App_UserControls_Presentational_Show_ImageRelated_Uwn_ImageRepeater"
%>
<%@ Register Src="Uwn_ImageBox.ascx" TagName="Uwn_ImageBox" TagPrefix="uc1"
%>



<asp:SqlDataSource ID="NewsReleasesWithIcons" runat="server"
ConnectionString="<%$ ConnectionStrings:XXXX %>"
SelectCommand="SELECT TOP (5)
Contentitems_RelatedContentitems.Con_ConRelID,
Contentitems_RelatedContentitems.Con_ConRelTo, ...)">
</asp:SqlDataSource>
<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="NewsReleasesWithIcons" >

<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1"
runat="server" ImageWidth="200" ContentID='<%# Eval("Con_ConRelTo") %>' />

</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
 
S

Steven Cheng[MSFT]

Hello KF,

Thanks for your reply.

Your code snippet makes it very clear. Now I understand the problem is the
usercontrol in repeater template can not correctly work when set its
"ContentID" property through databinding, but works when assigned the
contentID statically in aspx template, correct?

From the databinding expression, it is quite straighforward and clear

<uc1:Uwn_ImageBox ID="Uwn_ImageBox1"
runat="server" ImageWidth="200" ContentID='<%# Eval("Con_ConRelTo") %>' />


The ImageBox control accept a ContentID through databinding in repeater,
however, I'd like to know how and when do you populate the Imagebox's
content in its code logic? For example, you may put the code logic in Init,
Load or any other event of the UserControl... And a potential issue here
is that when the repeater perform databinding, it does set the correct
ContentID into each ImageBox in repeater item, however, the Imagebox's
internal structure population code logic has already get executed, thus
make the databinding assigned value not take effect yet. Would you provide
some code logic about the usercontrol (just some dummy code that can
describe its lifecycle should be ok).

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Thanks for sticking with this Steven. I will describe the problem in three ways: first as a summary, second as a more detailed description of how the controls are interrelated, and third, a discussion of what works and what doesn't.

SUMMARY
I want to be able to build pages that will call a nested user controls with the goal of repeating image display. This will faciliate flexible layouts on a page. The controls are responsible for encapsulating/simplifying business logic. They feed parameters to each other dynamically, many of which are passed in series from "parent" controls to nested "child" controls.

1) In the "outermost" layer there is the container page, say, "Article.aspx."
2) In the next innermost layer there is ImageRepeater.ascx, a user control that takes parameters.
3) In the next innermost layer there is ImageBox, which elaborates what the basic Image control can do.
DETAILED
1) Article.aspx has a database ID associated with it. In the database, images are linked relationally to this articleID.
2) ImageRepeater, my first user control, tries to find the images that are related to the parent article by a database lookup on the parent article ID, and by evaluating the ID of what is returned. It uses its own SQLDataSource control to do the lookup. So far I haven't parameterized the lookup by ID, but that's coming. For now the SQLDataSource on ImageRepeater looks like this: SelectCommand="SELECT TOP (5) Contentitems_RelatedContentitems.Con_ConRelID ...
3) The SelectCommand shown above, which remember is associated with each instance of ImageRepeater, returns five records. Within the ImageRepeater's template, it then tries to dynamically place five ImageBox controls by dynamically evaluating each one of the five values returned:

<ItemTemplate>
<tr>
<td>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1"
runat="server" ImageWidth="200" ContentID='<%# Eval("Con_ConRelTo") %>' />

</td>
</tr>
</ItemTemplate>
4) Each ImageBox invokes a custom method called LoadImage that I wrote, which uses the EntitySpaces ORM framework to make the calls.
public void LoadImage()

{

string iurl = this.ImageUrl;

if (iurl == null && this.ContentID !=null )

{

Contentitems ima = new Contentitems();

ima.LoadByPrimaryKey(this.ContentID);

string fileRoot = ima.ConFilename;

string dateOnly = fileRoot.Substring(0, 8);

DateTime pubDateOfArticle = DatesAndTimes.ConvertYYYMMDDStringToDateTime(dateOnly);

if (this.ImageWidth == 0 || this.ImageWidth == null)

{

this.ImageWidth = 250;

}

this.ImageUrl = FileOps.BuildImageUrl(pubDateOfArticle, "/images/newsreleases/", ima.ConFilename, this.ImageWidth);

this.Cutline = ima.ConSubtitle;

this.Byline = ima.ConBylineCredit;

}

WHAT DOES AND DOESN'T WORK
Article.ascx --> ImageRepeater.ascx --> ImageBox.cs

Things that don't work
The most basic failure I'm trying to address here is that ImageRepeater.ascx attempts a database lookup to get a list of related photo IDs that it wants to render. Its template attempts to dynamically Eval the list of returned database IDs:

<ItemTemplate>
<tr>
<td>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1"
runat="server" ImageWidth="200" ContentID='<%# Eval("Con_ConRelTo") %>' />

</td>
</tr>
</ItemTemplate>

This fails. The debugger indicates that a ContentID is never passed to the template, and there is nothing to look up.

Things that work

Embedding a hardcoded value into the markup for ImageRepeater.ascx results in a successfully rendered series of five indentical images
<ItemTemplate>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1" runat="server" ImageWidth="200" ContentID="28937" />
</ItemTemplate>

The LoadImage method in ImageBox.cs is successfully invoked on each interation, the DB lookups associated with a single instance of the ImageBox are performed and the ImageBox dynamically populates itself .

Experimenting a bit more, I've had mixed success with binding functions to onItemDatabound associated with ImageRepeater.ascx, and/or embedding lookup functions in the markup for ImageRepeater.ascx. This proves only that the system is capable of rendering dynamically retrieved IDs in some instances.
<ItemTemplate>
<uc1:Uwn_ImageBox ID="Uwn_ImageBox1" runat="server" ImageWidth="200" ContentID="<%# (int) (GetRandomItem())%>" />
</ItemTemplate>

I really want to get this control working in a way that doesn't break the modularity/flexibility of the pieces I'm designing, and whose parts are reasonably encapsulated.

Any thoughts?

Thanks,
-KF
 
S

Steven Cheng[MSFT]

Thanks a lot for your further followup KF,

Your complete description and summary makes the scenario and problem very
clear. One thing I still wonder and (haven't mentioned in your description)
is when do you call the "LoadImage()" function in your ImageBox control(in
Init event or Load event or ....)? This is very very important which
directly determine whether your control can work correctly in databinding
scenario(nested in other template databound control such as repeater).

As you mentioned that you found the following symptom:

===============
** The debugger indicates that a ContentID is never passed to the template,
and there is nothing to look up.

**Embedding a hardcoded value into the markup for ImageRepeater.ascx
results in a successfully rendered series of five indentical images

**The LoadImage method in ImageBox.cs is successfully invoked on each
interation, the DB lookups associated with a single instance of the
ImageBox are performed and the ImageBox dynamically populates itself .
===============

Sure, I can understand these behaviors and I think here is why the ImageBox
control behaves as above:

1. Your ImageRepeater did get the correct datasource items from
SqlDataSource and bind it to each ImageBox in the template

2. Each ImageBox in template did have got the "ContentID" from databinding
"Eval" expression. However, the PROBLEM here is that "LoadImage" method
has been called before the databinding occurs, that's why you found
"LoadImage" be called everytime, but the ContentID is not the correctly
value from databinding, do you think so?

So, assuming the above is the cause of the problem, you can tried the
following means to see whether it works:

** Not sure when do you call the "LoadImage" method in the ImageBox control
currently, but you can try moving it to "PreRender" event. Or move it to
the "setter" method of the "ContentID" property so as to make sure when
LoadImage method is called, the contentID property has held the latest
value.

** another means is using the ItemDataBound event of Repeater control(your
ImageRepeater). You can get the reference of each ImageBox(in each repeater
item) in this event and manually call the "LoadImage" method on it(if this
method has been publicly exposed).

Hope this helps. Please feel free to let me now if there is anything
unclear.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Steven,

Thanks so much for all of your help, Steven. My nested controls work great
now. Your follow-up was outstanding and will help us roll out a new content
management system and set of websites based in ASP.NET 2.

The issue, which you predicted in your last message, concerned the fact that
I had written a custom LoadImage function that worked against my database. I
had tried placing these functions in Page_Load and Page_Init. These are the
wrong places. Moving my custom initalization functions and my Load_Image
function for the user control to a Page_Prerender function solved the
problem (code to follow this message.)

For folks who want to learn from my mistakes: it is possible to
misunderstand what the debugger is telling you, and misread the source of
the problem. The debugger told me that the LoadImage function in my ImageBox
control -- my "innermost" nested control -- wasn't receiving an ID. This led
me to erroneously believe that maybe it wasn't being passed properly by the
parent. In fact, the ID was being passed just fine, but as Steven pointed
out, it was happening at the wrong time in the page lifecycle.

Some code for newbies and newbies at heart:
public void Page_Prerender(object sender, EventArgs e)

{


SetInitialVisibility();
LoadImage(); // will conditionally load either according to "dumb" mode or
"smart" mode
SetImageProperties();

}
 
S

Steven Cheng[MSFT]

Glad to hear that :)

Have a good day!

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top