Adding WebParts via code behind?

  • Thread starter Bart Van Hemelen
  • Start date
B

Bart Van Hemelen

I'm working on a project where the user of a site will receive custom
content, depending on a set of parameters. The content will all be
contained in UserControls (.ascx), that will be used as webparts on a
page.

We need to add the webparts dynamically to the site, depending on the
status of the user: this involves a personalisation of a travel
website, so a user can be "before" a trip, "during" a trip and "after"
a trip, and depending on that status he'll see different content. (This
is just one of the parameters, actually.) Of course, this all needs to
integrate with the built-in personalisation offered by Profiles.

However, all my experiments with adding UCs/WebParts via code behind
have failed miserably: adding the works, but whenever the user clicks
on a button inside a webpart, the postback causes the webparts to
multiply.

I've looked around, but so far I haven't yet found a working example of
what I want to do. Can anyone help me out? Or am I trying to do
something that is impossible?

Tools & technologies: ASP.NET 2.0 / C# / Visual Studio 2005
 
C

Cowboy \(Gregory A. Beamer\)

The WebPart Framework is designed to declaratively do the work for you,
storing user choices in profile. When you dynamically add bits, you can end
up with dupes in one of two ways.

1. The user has an item in his profile and you have also added that item
or
2. The web part/control is held in ViewState and you dynamically add one,
thinking this is the only way to change a control's state.

In either case, you now have two objects (one created originally either by
the user's choice or your initial load on the page (and into ViewState) plus
the one you added when the form posted back).

User Controls can act a lot like web parts, although they have fewer moving
parts.

Another thing: Page_Load is for loading a page. While this sounds like
"well, duh" I find so few people really get the lifecycle. If you are
loading a page and then allowing the controls to reside in ViewState, you
need to make sure the initial load is ONLY in Not is Postback. In most
cases, the pattern for page load is this (pseudocode):

Page_Load

If this is not a postback
'Load elements necessary for page view
Else
'Only load things necessary for EVERY
' postback here (i.e. 100% of the time)
End

END

You handle all other manipulation in the events. If you have a Page_Load
that has a huge amount of code, you are probably working out a huge
anti-pattern. That is not wise.
What I would suggest is taking a step back and running through a full debug
cycle on a page that is "failing" (by your spec -- i.e. adding additional
web parts, etc.). Set watches on the number of controls, breakpoints in
different methods, etc. and watch what is happening. Turn on and off
ViewState. This exercise will not directly solve your issue, but it will
truly enlighten you as to what Microsoft is doing for you.

--
Gregory A. Beamer
MVP; MCP: +I, SE, SD, DBA

*************************************************
Think outside the box!
*************************************************
 
B

Bart Van Hemelen

Cowboy said:
The WebPart Framework is designed to declaratively do the work for you,
storing user choices in profile. When you dynamically add bits, you can end
up with dupes in one of two ways.

1. The user has an item in his profile and you have also added that item
or
2. The web part/control is held in ViewState and you dynamically add one,
thinking this is the only way to change a control's state.

I now do this in Page_Load:

if ( WebPartZone1.WebParts.Count)
{
// add UCs as WebParts here
}

which seems to have solved the problem: no duplicate webparts,
postbacks work as they should etc. The state of those webparts -- i.e.
if they are minimized or not -- also seems to get saved in the Profile.

(Of course, it also means that I cannot put any WebParts on the page
unless I do it via this method, but that's a price I'm willing to pay.)
You handle all other manipulation in the events. If you have a Page_Load
that has a huge amount of code, you are probably working out a huge
anti-pattern. That is not wise.

I realise that, but other factors are more important to my employers:
timely delivery, for at least a cost as possible. I'm particularly
interested in knowing how I "should" solve this problem, i.e. how
Microsoft wants me to solve this problem, since I do want to do this
the proper way.

However, after having gone through numerous examples I still haven't
encountered any that resemble what I need to do, which is serve users
with a custom list of webparts that depends on a number of factors
which will be derived from an existing framework.
 
B

Bart Van Hemelen

Bart said:
I now do this in Page_Load:

if ( WebPartZone1.WebParts.Count)
{
// add UCs as WebParts here
}

which seems to have solved the problem: no duplicate webparts,
postbacks work as they should etc. The state of those webparts -- i.e.
if they are minimized or not -- also seems to get saved in the Profile.

It seems I spoke too soon. I just wrote some quick code to check if
this would work, and after cleaning it up -- i.e. inserting the proper
titles for each fo the uc-webparts for instance -- and retesting it, of
course I got the webparts I expected, plus all the old ones that were
saved in the profile.

Considering that the website is going to be very dynamic in the future,
we must have 100% control of the uc-webparts the user will have access
to.

So I need to either a) find the proper way of adding uc-webparts
dynamically, or b) figure out a way to remove the improper ones from
the profile.
 
Joined
Jan 18, 2010
Messages
3
Reaction score
0
Finally figured it out

This has been hassling me for THREE FREAKIN MONTHS and I finally figured it out. There were seemingly-random duplicates of the web parts I was dynamically adding to an ASP.NET web page using the WebPartManager.AddWebPart( ... ) method. Kept accumulating more and more webparts over time, across sessions - AAARRGGHHHH. Set a breakpoint on the line of code that had the AddWebPart; it only stopped at that breakpoint ONCE golldarnit.... Varied by dev db server, user... Note this is plain vanilla ASP.NET web app, NOT inside SharePoint.

Finally figured out it had something to do with the web part personalization provider and although I am not sure why it was remembering web parts when it chose to and if there is a cleaner approach than this, I have wasted too much time on this already and have to forge forward with all the work I am already behind on...

You have to execute the following line of code:

[YourWebPartManagerObject].Personalization.ResetPersonalizationState();

at some point to "clean out" the web parts that the personalization provider has decided to squirrel away for the user. Just don't do it in Page_Load method or you get an infinite loop (google it; usually done in response to a button click or some other event handler; I'm going to try something at session start if I can refer to the one page that I have in my app that displays web parts, and get a reference to its WebPartManager - we'll see about that).

Plus as a catch-all semi-hack, in my web page that displays web parts, before starting to add the web parts that SHOULD be there, I execute the following code:

// Make absolutely sure there are no web parts "hanging around" due to personalization.
foreach (WebPart webPartItem in _wpmMain.WebParts)
{
_wpmMain.DeleteWebPart(webPartItem);
}


Note that this is only necessary because we are doing very simplistic web part stuff in this one asp.net web page. i.e., adding web parts dynamically, but they are "read only", no manipulation by the user of the web parts etc. Now, the personalization provider needs to be enabled even just for that basic usage of web parts to work, but that makes it so that the "mysterious duplicating web parts" crap can happen.

Hope this helps some other frustrated ASP.NET developers. :y:
 
Joined
Jan 18, 2010
Messages
3
Reaction score
0
Refinement for previous post

Otay, K.I.S.S. whenever possible, here's what I ended up doing to take care of the fact that the WebPartManager.Personalization.ResetPersonalizationState() does an automatic server.transfer back to the page that it is in (resulting in an infinite loop if you do it within the Page_Load). Just do the following at the top of the Page_Load of the page that you are doing the WebPartManager.AddWebPart() in:

protected void Page_Load(object sender, EventArgs e)
{
if (!(Page.IsPostBack))
{
// Cleanup of web parts "hanging around" due to personalization.
if (_wpmMain.WebParts.Count > 0)
{
_wpmMain.Personalization.ResetPersonalizationState();
}

.
.
.
}

Note that _wpmMain is the id of the WebPartManager in the aspx markup.

And that is all the code that is necessary to take care of this specific situation.
 
Joined
Jan 18, 2010
Messages
3
Reaction score
0
Sorry about formatting

Sorry about the lack of indenting in the code, I put appropriate indentation in by hitting the space bar but they were taken out when posted.
 

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,821
Messages
2,569,748
Members
45,726
Latest member
RaleighAll

Latest Threads

Top