Search Results

The ModalPopupTemplate control is a Composite Control—or a control that is a collection of other controls and implements the CreateChildControls method.

This post demonstrates how the ModalPopupTemplate (MPT) was created and also points out a few of its current weaknesses and areas for improvement (hey, I’m honest…).

Attaching Custom Events

As with any control, the MPT has an array of external properties available. Most importantly are the three events and the styling properties.

To attach custom events to a control, they are implemented similar to a property; however, the method used to attach the event is a virtual method. Let’s take a look at our OkClick event. The other events follow suit in functionality.

// Events of Popup

publiceventEventHandler OkClick;

protectedvirtualvoid OnOkClick(EventArgs e)

{

if (OkClick != null)

{

OkClick(this, e);

}

}

Our OkClick accepts EventHandler events and can be used for UpdatePanel triggers, etc; however, our control needs action methods (On_) to link events to methods on your page. That’s where the virtual method comes in.

Later in the code, when the Ok, Close, and Cancel buttons are generated, there’s one more bit of code. The Click events are optional so we still need to tie a bit of functionality to them to close the popup window. This is done by mixing a bit of JavaScript into the mix.

if (OkClick != null)

{

// If an OK Behavior is assigned, use it.

okButton.Click += OkClick;

}

else

{

// If not, the OK button just hides the popup.

okButton.OnClientClick = “$find(‘popup’).hide(); return false;”;

}

Prefabricated Style Sheets and Custom Style Sheets

A template has to have a few styles built into it. This is currently an “under construction” area of the control as there has to be a way to generic it up a bit and lose the ugly case statements.

When the DefaultStyle property is set to Custom, this code is bypassed or simply there isn’t a “style” attached to the control. You’ll get plain black text, white background, and no borders. If DefaultStyle isn’t specified in the control, the YUI theme is applied.

if (DefaultStyle != PopupStyle.Custom)

{

string includeLocation = “”;

switch (DefaultStyle)

{

casePopupStyle.YUI:

includeLocation =

Page.ClientScript.GetWebResourceUrl(this.GetType(),

“ModalPopupTemplate.Resources.ModalPopupTemplateStyle_YUI.css”);

break;

casePopupStyle.Clean:

includeLocation =

Page.ClientScript.GetWebResourceUrl(this.GetType(),

“ModalPopupTemplate.Resources.ModalPopupTemplateStyle_Clean.css”);

break;

default:

break;

}

HtmlLink cssLink = newHtmlLink();

cssLink.Href = includeLocation;

cssLink.Attributes.Add(“rel”, “stylesheet”);

cssLink.Attributes.Add(“type”, “text/css”);

this.Page.Header.Controls.Add(cssLink);

}

This code retrieves the embedded resource URL from the library and adds a link to it in the page’s header.

Generating the ModalPopupExtender

The GenerateExtender method accepts one parameter: the control you wish to add the popup extender into. You could remove the parameter and simply add it to the composite control’s ControlCollection, but I wanted to keep it open for later development.

privatevoid GenerateExtender(Control container)

{

// Todo: Fix these hardcoded ID values for PopupControlId.

ModalPopupExtender mpe = newModalPopupExtender();

mpe.BehaviorID = popupBehaviorId;

mpe.BackgroundCssClass = ModalBackgroundStyle;

mpe.PopupControlID = “popupPanel”;

mpe.TargetControlID = TargetControlId;

container.Controls.Add(mpe);

}

The code sets every property that you’d normally set on a ModalPopupExtender. The only matter of disconnect at the moment is the PopupControlID’s value—I don’t like that hardcoded between this and the GeneratePanel method, but it’s minor for the time being.

Generating the Popup Panel

The popup panel’s code is cut down a bit for the post as it’s repetative: generate a Panel control, several labels for the header and body text, a few buttons and a link button, and then put it all together.

The most important part is to ensure that the DIV containers enclose the proper controls and that every tag closes itself, etc.

First off, we see the popupPanel setting it’s ID (to match the ID set in the ModalPopupExtender), CSS Class, and an additional style attribute to hide it (to prevent the blip of it appearing on page load).

The rest of the HTML and controls are interwoven between LiteralControls for exact HTML output.

popupPanel.ID = “popupPanel”;

popupPanel.CssClass = PanelStyle;

popupPanel.Style.Add(HtmlTextWriterStyle.Display, “none”);

popupPanel.Controls.Add(

newLiteralControl(“<div class='” +

ContainerStyle + “‘>\n\t<div class='” +

HeaderStyle + “‘>\n\t\t”));

popupPanel.Controls.Add(headerText);

popupPanel.Controls.Add(closeButton);

popupPanel.Controls.Add(

newLiteralControl(“\n\t</div>”));

popupPanel.Controls.Add(

newLiteralControl(“\n\t<div class='” +

BodyStyle + “‘>\n\t\t”));

popupPanel.Controls.Add(bodyText);

popupPanel.Controls.Add(

newLiteralControl(“\n\t</div>”));

popupPanel.Controls.Add(

newLiteralControl(“\n\t<div class='” +

FooterStyle + “‘>”));

popupPanel.Controls.Add(okButton);

popupPanel.Controls.Add(cancelButton);

popupPanel.Controls.Add(

newLiteralControl(“\n\t</div>\n</div>”));

Finally, this method, GeneratePanel, also accepts a control parameter to add the control to a specified collection.

Putting it together — CreateChildControls

The Composite Control’s method — CreateChildControls — is where the magic happens.

protectedoverridevoid CreateChildControls()

{

base.Controls.Clear();

if (!this.ChildControlsCreated)

{

GenerateExtender(this);

GeneratePanel(this);

}

base.CreateChildControls();

}

It’s usually a good practice to clear the composite control’s ControlCollection before starting—just to be sure that you know exactly what’s being rendered. From there, call our Extender and Panel generation methods and place them in Composite Control’s control collection (this).

That’s it, you’re ready to add it to the page. For more information about how to add it to the page and the control’s parameters, as well as the full source code and a demonstration site, visit the Creating a Modal Popup Template Control posting from 5 November 2007.

The ModalPopup Control of the AJAX Control Toolkit is great. It goes a long way to help add some “client” behaviors to web applications and make presenting data very flexible on the web. I have a few applications that use these to collect data and then return it to the main web page—rather than placing all of the controls right in the face of the user—and it’s gotten rave reviews from our customers.

One “feature” is that you must attach a style to hide the Panel control ({display: none;}) or you’ll see a quick blip of it when the page renders. Okay, no problem. But, there’s a catch.

If you embed the style directly on the control, such as:

<asp:PanelID=”LookupPanel”runat=”server”Style=”display:none;”>

It works like a charm. However, what if you wanted to be smart and use the CssClass attribute of the Panel object and then define your style in your theme or style sheet?

<asp:PanelID=”LookupPanel”runat=”server”CssClass=”PopupPanel”>

.PopupPanel

{

width: 500px;

border: solid2px#5D7B9D;

background-color: #F5F5DC;

padding: 10px10px10px10px;

display: none;

}

Unfortunately, for some odd reason, that doesn’t work. Or, perhaps, works too well. Setting the display style in the stylesheet TOTALLY hides it. The event activates, but the window never appears. You can define all other style attributes in your style sheet but leave the display attribute right on the control. It’s a bit annoying, but still works like a champ. I’d love to know if this is a “feature” or just simply an issue with how it hides/shows the window… input?

I’m sure this it out there if I’d Google it a bit more, but I had an issue where I had the ModalPopupExtender that prompted for “OK” after setting a few options. I do not like to include script in my MasterPages and wanted to use a bit of BusinessLogic in the code-behind. So, when they Click OK, I want it the button to run it’s Button_Click event, not the OnOkScript event of the Extender.

The OkControlID is not specified. Yep… that’s the entire solution. To bypass the functionality of the Extender and use the object’s own event model, simply do not link the objects. I’m sure the same works for the CancelControlID as well.

Searching the web, I found an article by Damien White discussing his his same pains with this. His solution involved using the IE-specific CSS “expressions” to calculate the height and width of the window.

height:

expression(

document.documentElement.scrollTop +

document.documentElement.clientHeight + “px”);

width: expression(document.body.clientWidth + “px”);

However, at least for me, Damien’s expressions wouldn’t handle scrolling down the page.

Damien explains:

The thinking behind this was to take the window height (which document.documentElement.clientHeight gives us) and then add the scroll top position, which will give us the upper portion if the user scrolls up. The problem shows itself when the user scrolls down; that area is not covered. The good thing about this is that I didn’t need to mess with the body height, but the solution isn’t optimal in the long haul.

That’s a bad deal because that’s the whole point! Reading a bit more, there was a comment from Kunal Mukherjee on Damien’s post that solved the problem.

Kunal’s expressions looked at the scrollHeight of the window as compared to the offsetHeight and returned the larger.

I also included !important flags on each of the properties in the ie6.css file—just to be safe.

Issue #2 – IE6 Pushes <SELECT> tags above everything else…

This is where the solution gets dicey; however, I’m relying on Kunal’s solution again. In his comment, he pointed out a way to hide <SELECT> tags in IE6 without causing the disappearing act that the ModalPopupExtender causes—cover them with an IFRAME.

To me, this hack seems… sketchy at best, but it works.

In the ProgressTemplate of the UpdateProgress control, add in the IFRAME.

I really like the Modal Popup Extender (MPE) (see various articles here) and the “feel” that it adds to sites. It’s useful for collecting information, changing information, or simply informing the user that something has happened.

But what happens when you need two popups at the same time?

In a recent project, I used the MPE to provide options when a user added or imported records into a system. However, I also wanted to provide an UpdateProgress panel that would appear OVER the modal popup panel. To do this, I needed to redefine the z-index css attributes to properly layer the controls.

My requirements were simple:

The MPE should appear over all “pages”.

The MPE “Backgrounds” (the BackgroundCssClass of the MPE) should appear just behind the MPE.

The UpdateProgress and it’s background should appear over EVERYTHING else.

The UpdateProgress and the MPEs should scroll and resize according to the browser.

The modal popup panel is pretty standard. The z-index of 1000 basically puts it above most everything else on the screen.

.ModalPopupPanel

{

z-index: 1000;

width: 400px;

border: solid2px#5D7B9D;

background-color: #F5F5DC;

padding: 10px10px10px10px;

}

The UpdateProgress Popup Panel

The UpdateProgress panel is also quite standard—a white box, border, and animated .gif image to keep users entertained during longer processes. For this, the z-index is set outrageously high just for safety. Remember: I want the UpdateProgress above EVERYTHING else.

Behind the MPE and UpdateProgress backgrounds, you see a nice blue slate gray color, that’s handled by the ModalBackground class (the BackgroundCssClass attribute of the ModalPopupExtender control.

.ModalBackground,

.UpdateProgressModalBackground

{

background-color: #6D7B8D; /* Blue Slate Gray */

position: fixed;

top: 0;

left: 0;

height: 100%;

width: 100%;

min-height: 100%;

min-width: 100%;

filter:alpha(opacity=50);

opacity:0.5;

-moz-opacity: 0.5;

}

.ModalBackground

{

/* Just below a ModalPopupPanel */

z-index: 999;

}

.UpdateProgressModalBackground

{

/* Above everything else, except an UpdateProgressPanel */

z-index: 99999998;

}

To save some reuse, the ModalBackground and UpdateProgressModalBackground share the same attributes; however, I’ve specified the z-index separately to ensure that the UpdateProgressModalBackground appears ABOVE the regular ModalBackground.

These combine to create a nice layered effect. Now, even when a MPE is visible, the UpdateProgress still appears over it and prevents user entry.

A while back, I wrote a couple of articles (here and here) regarding encapsulating the ModalPopupExtender into a spiffy little template control that you could toss onto a page. It’s worked GREAT over the past few months, however, I hit a snag today.

I needed to call the base ModalPopupExtender’s .Show() method from code behind; however, I hadn’t bubbled that up to the Composite Control.

At first, I expected to simply add a private instance of the MPE (which is assigned to when the control is built) and then add a method to my composite control that calls the .Show() method.

privateModalPopupExtender _control;

publicvoid Show()

{

_control.Show();

}

That sounds good, but it never fired and the _control field was always null (even though I could step through and it was assigned).

What it needed was a little reminder—a reminder to EnsureChildControls existed before trying to call Show(). Now, a quick update to the code:

publicvoid Show()

{

this.EnsureChildControls();

_control.Show();

}

Now I can call the Show() method of the Composite Control and it works like a charm! Here’s an example (for what I’m working with at the moment) of dynamically iterating through an IDictionary and returning the values in a Modal Popup.

The ModalPopupExtender is one of my favorite AJAX extenders. It provides a slick UI experience for little overhead. The most difficult part of implementing the extender is recreating the popup panels over and over again for similar tasks.

To rectify this and create a “template” for use in our organization, I created a custom composite control that encapsulates

Creation of the AJAXToolkit’s ModalPopupExtender,

Creation of a Panel control,

Styles the Panel according to pre-set styles or custom styles,

Ties the Panel and Extender together,

Passes various events to the common UI elements of the popup,

Allows the popup’s events to be visible to other controls on the page.

As it stands, this is a prototype and needs quite a bit more tweaking before I’d put it into production; however, it stands as a fun project to tinker with. This will be an ongoing project and as I finish or update various aspects of the control, I’ll post up the changes. I’m also interested in feedback and ideas for improvement. 🙂

The Control’s Structure

Here you can see the properties that are configurable with the control as well as the public and private methods and events. I’ve also included the PopupStyle enumeration which prestyles the popup.

DefaultStyle is linked to the PopupStyle enumeration (and may lend itself to rename eventually). The example I showed above is the YUI theme which uses Matt Berseth’s YUI Css. I’ve also included a “Clean” theme similar to what we use in our projects at work. I hope to grow the collection of themes as time progresses.

OnOkClick, OnCancelClick, and OnCloseClick methods link to the corresponding buttons on the popup and throw the like named events. By default, if these events are not tied to methods in your project, they simply close the popup window.

BodyText and HeaderText are the two primary configuration elements of the popup.

Here’s an example including shaky arrow lines matching the properties to their relative location. For examples of the Style (ContainerStyle, BodySTyle, HeaderStyle, etc) check out Matt Berseth’s YUI example—the CSS is the same (Thanks Matt for an EXCELLENT job and template to follow!)

Control Usage

Using the control on your .NET page is quite simple. Remember, as with the normal ModalPopupExtender, your ModalPopupTemplate control must be in the same UpdatePanel as the TargetControl that activates it. You’ll also need to ensure that EnablePartialRendering is set to true for your ScriptManager control.

I’ll dig into how it works in the next posting—being somewhat late and being very sick and on some good drugs isn’t conducive to lots of writing. 🙂 If you can’t wait, download the source and go—it’s pretty well code commented.

The AJAX Control Toolkit project has been updated to v1.0.10920.0 with both AJAX 1.0 and .NET 3.5 versions. Downloads and the full work item list can be found on the CodePlex site.

Here’s a quick rundown of the updates from the project site:

General fixes:

Controls with Embedded styles (Calendar, Tabs and Slider): Toolkit controls no longer need explicit style references when loaded asynchronously. For example, if a Calendar control is placed inside an UpdatePanel and made visible on an UpdatePanel postback, the embedded styles are now loaded properly.

PopupBehavior positioning (AutoComplete, Calendar, DropDown, HoverMenu, ListSearch, PopupControl and ValidatorCallout): PopupBehavior now respects the position of its parent element even when the browser window is very narrow or the parent element is close the window edge.

Focusing extended controls (Accordion, CollapsiblePanel, DropShadow, Tabs): Pages that use Toolkit controls which re-parent DOM elements can use a workaround to focus a specific element on page load. The new method Utility.SetFocusOnLoad ensures that the desired control receives focus.

Control specific fixes:

Calendar: Property to specify the position of Calendar, a default date feature that allows the calendar to start out with a selected date, and a consistent show, hide and focus story that makes the Calendar user experience more intuitive.

ModalPopup: Ability to disable repositioning of the ModalPopup in response to window resize and scroll.

ConfirmButton: ModalPopup functionality now supported in addition to the regular windows alert dialog.

MaskedEdit: Extended Textbox no longer uses Invariant culture if no CultureName is specified and falls back to the Page Culture.

Slider: Slider can be easily customized using its various CSS properties.

For those of us using .net 3.5, we also gained JavaScript IntelliSense support and Extender support:

Features:

JavaScript IntelliSense support: We have added reference tags to all Toolkit JavaScript files that enables you to take advantage of new features in Visual Studio 2008 Beta 2. With the multi-targeting support in this Visual Studio Beta, IntelliSense will be available for the ASP.NET AJAX 1.0 flavor of the Toolkit as well. This article discusses the reference tag feature in detail.

Extender designer support: Enhanced designer support for Toolkit controls using the new “Add Extender” user interface.

LINQ queries are great for connecting to and displaying data in GridViews without much effort. They do lack; however, in some of the GridView update functionality if you are spanning between multiple tables.

When you try to Update, using a GridView’s normal Edit/Update commands, a LinqDataSource, you’ll recieve:

“LinqDataSource does not support the Select property when the Delete, Insert or Update operations are enabled.”

Scott Guthrie touched on this a bit in his recent LinqDataSource article and suggested either using a DetailsView or other means to modify it. Instead of that, I decided to place those fields in a ModalPopupExtender.

NOTE: This project is using Visual Studio 2008 with the AjaxControlToolkit for v3.5. For a copy of this solution, here. The solution includes the correct AjaxControlToolkit.dll and the Northwind database used for the examples.

UPDATE 4/12/2008: New version here in response to comments to this post.

To begin, I modified the LinqDataSource’s Selecting method to match what ScottGu used (for simplicity of getting to a LINQ expression):

protectedvoid ProductsDataSource_Selecting(object sender,

LinqDataSourceSelectEventArgs e)

{

var products = db.Products

.Select(i => new {

i.ProductID,

i.ProductName,

i.UnitsInStock,

i.UnitPrice,

NumOrders = i.Order_Details.Count,

Revenue = i.Order_Details.Sum(o =>

o.Quantity * o.UnitPrice),

Potential = i.UnitsInStock * i.UnitPrice});

e.Result = products;

}

This provides us with a result set of the simple data (ProductId, ProductName, UnitsInStock, and UnitPrice) and two computed fields (NumOrders, Revenue, and Potential). I added the potential revenue field for a bit of fun—it’s a computed field based on values we can change (UnitsInStock and UnitPrice).

We prepare our GridView accordingly.

<asp:LinqDataSourceID=”ProductsDataSource”runat=”server”

ContextTypeName=”NorthwindDataContext”

onselecting=”ProductsDataSource_Selecting” />

<asp:GridViewID=”ProductsGridView”runat=”server”

DataSourceID=”ProductsDataSource”

OnRowDataBound=”ProductsGridView_RowDataBound”

OnRowCommand=”ProductsGridView_RowCommand”>

<Columns>

<asp:TemplateField>

<ItemTemplate>

<asp:LinkButtonID=”ModifyButton”

runat=”server”Text=”Modify”CommandName=”Modify”/>

</ItemTemplate>

</asp:TemplateField>

<asp:BoundFieldDataField=”ProductID”

HeaderText=”Product ID”SortExpression=”ProductID”/>

<asp:BoundFieldDataField=”ProductName”

HeaderText=”Product Name”SortExpression=”ProductName”/>

<asp:BoundFieldDataField=”UnitsInStock”

HeaderText=”Units In Stock”SortExpression=”UnitsInStock”/>

<asp:BoundFieldDataField=”UnitPrice”

HeaderText=”Unit Price”DataFormatString=”{0:c}”

SortExpression=”UnitPrice”/>

<asp:BoundFieldDataField=”NumOrders”

HeaderText=”Num. Orders”SortExpression=”NumOrders”/>

<asp:BoundFieldDataField=”Revenue”

HeaderText=”Total Revenue”SortExpression=”Revenue”

DataFormatString=”{0:c}”/>

<asp:BoundFieldDataField=”Potential”

HeaderText=”Potential Revenue”SortExpression=”Potential”

DataFormatString=”{0:c}”/>

</Columns>

</asp:GridView>

(Click image to see in new window)

We’ll come back to that Modify LinkButton later—that’ll be how we’ll start the ModalPopupExtender.

You notice that there are two methods associated with our GridView: RowDataBound and RowCommand. RowDataBound will allow us to preslug in the ProductId into the CommandArgument so that RowCommand can use it later:

protectedvoid ProductsGridView_RowDataBound(object sender,

GridViewRowEventArgs e)

{

// For each edit button, place the ProductId in the CommandArgument

// so we can access it later.

LinkButton lb = e.Row.FindControl(“ModifyButton”) asLinkButton;

if (lb != null)

{

lb.CommandArgument = e.Row.Cells[1].Text;

}

}

Next, we’ll need to add our ModalPopupExtender in. Because we’re wanting to vary our content for each row of the GridView, we’ll want to manually call the MPE after selecting and populating our panel. We can do this by creating an invisible control to for the TargetControlId.

<asp:ButtonID=”HiddenButton”runat=”server”

Style=”display: none;“/>

<ajaxToolkit:ModalPopupExtenderID=”mpe”runat=”server”

BackgroundCssClass=”ModalBackground”

CancelControlID=”CancelPopupButton”

PopupControlID=”UpdateRecordPanel”

TargetControlID=”HiddenButton”/>

<asp:PanelID=”UpdateRecordPanel”runat=”server”

CssClass=”PopupPanel”Style=”display: none;“>

<h2>Update Record</h2>

<p>

Record:

<asp:LabelID=”ProductId”runat=”server”/><br/>

Product Name:

<asp:TextBoxID=”ProductName”runat=”server”/><br/>

Units in Stock:

<asp:TextBoxID=”UnitsInStock”runat=”server”/><br/>

Unit Price:

<asp:TextBoxID=”UnitPrice”runat=”server”/><br/>

</p>

<p>

<asp:ButtonID=”OkPopupButton”runat=”server”Text=”Update”

OnClick=”OkPopupButton_Click”/>

<asp:ButtonID=”CancelPopupButton”runat=”server”Text=”Cancel”/>

</p>

</asp:Panel>

To fire our MPE, we’ll use that RowCommand we specified earlier. The RowCommand will do three things:

Collect the row’s CommandArgument (which is the ProductId),

LINQ up a new Product and preslug the UpdateRecordPanel’s fields with the data,

Find the MPE and .Show() it.

Since our fields in our Product object match, we could reflect through; however, for simplicity, we’ll just assign them for now.

Below you’ll see the RowCommand method as well as a little Helper method to query up the Product objects.

protectedvoid ProductsGridView_RowCommand(object sender,

GridViewCommandEventArgs e)

{

if (e.CommandName == “Modify”)

{

// Fill the pop-up’s edit boxes with the

// information from the Product object.

Product record =

SelectRecord(Convert.ToInt32(e.CommandArgument));

ProductId.Text =

record.ProductID.ToString();

ProductName.Text =

record.ProductName.ToString();

UnitsInStock.Text =

record.UnitsInStock.Value.ToString();

UnitPrice.Text =

record.UnitPrice.Value.ToString();

// Now we want to “Show” our ModalPopupExtender,

// so find the control and .Show it.

ModalPopupExtender mpe =

up1.ContentTemplateContainer.FindControl(“mpe”)

asModalPopupExtender;

mpe.Show();

}

}

privateProduct SelectRecord(int productId)

{

// Helper method to fetch a Product

// object based on a specific ID.

Product record = db.Products

.Where(i => i.ProductID ==

Convert.ToInt32(productId)).Single();

return record;

}

Note the .Value for those fields that can be nullable.

Now that we’ve populated and displayed our UpdateRecordPanel, we need to capture the changes and save the back to the data source. This is where inline editing fails on our GridView, but we’ll have no problem calling a simple SubmitChanges() on the data source here.

protectedvoid OkPopupButton_Click(object sender, EventArgs e)

{

// This method is called by our Popup’s OK button.

// By not specifying a OkControlID on the MPE, we can use

// this method instead.

UpdateRecord();

}

publicvoid UpdateRecord()

{

// Fetch our record based on the ProductId (since it’s read-only)

// and update according to what’s been entered in the box.

Product record =

SelectRecord(Convert.ToInt32(ProductId.Text));

record.ProductName =

ProductName.Text.ToString();

record.UnitsInStock =

short.Parse(UnitsInStock.Text);

record.UnitPrice =

decimal.Parse(UnitPrice.Text);

// Save the changes back through LINQ.

db.SubmitChanges();

// Rebind the GridView to pick up the new changes.

ProductsGridView.DataBind();

}

Updating the Product Name is easy enough, but what about those computed fields? Looking above, we have 25 Chai in stock at $25. Let’s change that to 30 in stock. Our ModalPopup panel looks like:

When calling UpdateRecord() the GridView’s Potential Revenue updates accordingly to match the Units in Stock and Unit Price.

The code isn’t perfect, but it does give one option to updating LINQ data sources without using the built-in GridView Updates. At this point, personally, I don’t see a lot of value in the actual LinqDataSource object—you just as easily retrieved this data in a method using a query and never messed with the object itself. I suppose it’s personal preference and flexibility.