Search Results

Keyword: ‘modalpopup’

Behind the Scenes – ModalPopupTemplate

November 8, 2007 3 comments

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

public event EventHandler OkClick;

protected virtual void 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)

{

case PopupStyle.YUI:

       includeLocation =

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

       “ModalPopupTemplate.Resources.ModalPopupTemplateStyle_YUI.css”);

       break;

      

case PopupStyle.Clean:

       includeLocation =

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

       “ModalPopupTemplate.Resources.ModalPopupTemplateStyle_Clean.css”);

       break;

      

default:

       break;

}

HtmlLink cssLink = new HtmlLink();

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.

private void GenerateExtender(Control container)

{

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

ModalPopupExtender mpe = new ModalPopupExtender();

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(

new LiteralControl(“<div class=’” +

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

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

popupPanel.Controls.Add(headerText);

popupPanel.Controls.Add(closeButton);

popupPanel.Controls.Add(

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

 

popupPanel.Controls.Add(

new LiteralControl(“\n\t<div class=’” +

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

popupPanel.Controls.Add(bodyText);

popupPanel.Controls.Add(

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

 

popupPanel.Controls.Add(

new LiteralControl(“\n\t<div class=’” +

FooterStyle + “‘>”));

popupPanel.Controls.Add(okButton);

popupPanel.Controls.Add(cancelButton);

popupPanel.Controls.Add(

new LiteralControl(“\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. 

protected override void 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.

AJAX Control Toolkit – ModalPopup CSS “bug”?

July 13, 2007 1 comment

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:Panel ID=”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:Panel ID=”LookupPanel” runat=”server” CssClass=”PopupPanel”>

 

.PopupPanel

{

    width: 500px;

    border: solid 2px #5D7B9D;

    background-color: #F5F5DC;

    padding: 10px 10px 10px 10px;

    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?

ModalPopupExtender & Button (or anything) Events

February 27, 2007 8 comments

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 ModalPopupExtender control:

<cc1:ModalPopupExtender ID=”mPopPrintClassReports” runat=”server” TargetControlID=”lnkPrintClassReports” PopupControlID=”pnlOuterPrintClassReports” PopupDragHandleControlID=”pnlInnerPrintClassReports” CancelControlID=”btnCancelClassReports” Drag=”false” DropShadow=”true”></cc1:ModalPopupExtender>

Notice anything in here?

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.

Yeah, I felt not-so-smart after this… *sigh*  Sure it’s not Monday?

Tags: , ,

Categories: .net 2.0, c#

IE6 Causes Z-Index Nightmares…

Rather than update the post from yesterday, this chaos deserves it’s own post.

Yesterday, I discussed layering Modal Popup Extenders with the Update Progress controls.  In IE7, FF3, and, well, most everything except IE6, it works like a champ as-is.

The “bug”?  After quite a bit of research, the problem revolves around the following issues:

  • lack of support for the { position: fixed } property,
  • lack of support for the { right; bottom} properties,
  • … unreliable suport for {height: 100%, width: 100% } properties,
  • general pain and suffering
  • <SELECT> tags (or ASP:DropDownList objects) exist above any other z-index,

I’m sure there were other issues.  Really.

After spending a good part of the day trying code, looking it up on QuirksMode, and trying again, I have somewhat of a solution; however, I still greatly dislike how it works in IE6.

On the MasterPage, I have a single UpdateProgress that isn’t associated to a specific UpdatePanel.  Therefore, it’ll catch all Async postbacks (and I only have ONE UpdateProgress control).

<asp:UpdateProgress

runat=”server” DisplayAfter=”100″ ID=”UpdateProgress”>

<ProgressTemplate>

<div class=”UpdateProgressModalBackground”></div>

<div class=”UpdateProgressPanel”>

<h3>Please wait…</h3>

<img src=”Images/ajaxbar.gif”
alt=”Please wait…”

style=”text-align: center; width: 100%; height: 10px; />

</div>

</ProgressTemplate>

</asp:UpdateProgress>

This, again, references our UpdateProgressModalBackground and UpdateProgressPanel styles.  These two styles are unchanged from the post yesterday.  Here they are again for reference:

/* UpdateProgressPanel is above EVERYTHING ELSE,

even other modal popups */

.UpdateProgressPanel

{

       z-index: 99999999;

       background-color:#fff;

       color:#fff;

       width: 200px;

       text-align: center;

       vertical-align: middle;

       position: fixed;

       bottom: 50%;

       left: 45%;

       padding: 10px;

       border: solid 2px #5D7B9D;

}

 

.UpdateProgressModalBackground

{

    z-index: 99999998;

    background-color: #6D7B8D;

    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;

}

The UpdateProgress and these two classes work just fine in IE7+, FF2+.  So, now to fix IE6..

So, what’s the difference in IE6?  Well, we can’t use the positioning attributes in the above classes–-they won’t work properly. 

Issue #1 – Fitting the Popup and Background Without Positioning Attributes

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.

height: expression(

document.body.scrollHeight > document.body.offsetHeight
? document.body.scrollHeight
: document.body.offsetHeight + ‘px’ )
;

Actually, that works really well. Cool.

Finally, I’d recommend, as Damien did, breaking out your CSS into two files—one for “IE6” and one for everyone else.  This is easily done using the IE-specific conditional statements.

<!–[if lt IE 7]>
<link rel=”stylesheet” type=”text/css” href=”App_Themes/ie6/ie6.css” />
<![endif]–>

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.

<iframe id=”UpdateProgressHideSelect”></iframe>

In the default.css (or the non-ie6.css, whatever you’ve called it), I recommend setting the iframe’s style to {display: none}—it isn’t needed outside IE6, don’t render it. :)

On the ie6.css, add the UpdateProgressHideSelect in—along with another expression to place the iframe over the entire page (like the standard BackgroundCssClass of a ModalPopupExtender):

#UpdateProgressHideSelect

{

    z-index: 15000;

    position: fixed;

    top: 0;

    left: 0;

    background-color: #fff;

    border: none;

    filter: alpha(opacity=0);

    -moz-opacity: 0;

    opacity: 0;

    height: 100%;

    width: 100%;

    display: inline !important;

}

 

* html #UpdateProgressHideSelect

{

    position: absolute;

    height: expression(

document.body.scrollHeight > document.body.offsetHeight

? document.body.scrollHeight

: document.body.offsetHeight + ‘px’);

}

The z-index of 15000 for the iframe ensures that it appears above the normal 10000 of a ModalPopupExtender panel; however, under our crazy high UpdateProgress control.

Problem solved—for now.

Here’s how they look, side by side.

FireFox 3:

FireFox 3 Output

Nice and clean, properly centered given the size of the box and window size.  Can see drop down lists and MPE behind the UpdateProgress, but cannot access them.

IE 7:

IE7 Output

Output as expected and where expected.  Can see drop down lists and MPE behind the UpdateProgress, but cannot access them.

IE 6:

IE6 Output

Output as expected—basically where expected.  Drop down lists are hidden behind the IFRAME to prevent input.  Other controls are visible, including the MPE, but behind the background.

What fun!

Layering Modal Popups using CSS Z-Index

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.
  • Everything must be cross-browser (of course).

UPDATE: While this works BEAUTIFULLY in IE7, IE8, FF2, and FF3–-it totally borks out in IE6.  Working on a fix nowIE6 Causes Z-Index Nightmares

The Modal Popup Panel

The MPE

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: solid 2px #5D7B9D;

    background-color: #F5F5DC;

    padding: 10px 10px 10px 10px;

}

The UpdateProgress Popup Panel

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.

.UpdateProgressPanel

{

/* UpdateProgressPanel is above EVERYTHING ELSE, even other modal popups */

       z-index: 99999999;

       background-color:#fff;

       color:#fff;

       width: 200px;

       text-align: center;

       vertical-align: middle;

       position: fixed;

       bottom: 50%;

       left: 45%;

       padding: 10px;

       border: solid 2px #5D7B9D;

      

}

The Modal Backgrounds

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.

 

UpdatePanel over MPE

 

 

 

 

Bubbling up Methods in Composite Controls

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.

private ModalPopupExtender _control;

public void 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:

public void 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.

ASPX:

<tiredstudent:ModalPopupTemplate HeaderText=”ERC” runat=”server”

        ID=”PopupDialogBox” DefaultStyle=”YUI” TargetControlId=”fakeButton” />

<asp:Button ID=”fakeButton” runat=”server” style=”display: none” />

Code-behind:

foreach (var entry in results)

{

       sb.AppendLine(string.Format(“<p>{0} – {1}</p>”,

                     entry.Key, entry.Value));

}

 

PopupDialogBox.BodyText = sb.ToString();

 

PopupDialogBox.Show();

 

WebGallery 2.0 – #4 – Adding Admin Tools

February 25, 2008 12 comments

So far, we’ve discussed:

  1. Creating the basic data model for the WebGallery.
  2. Changes to the HttpHandler to increase speed and efficiency.
  3. Building and viewing the individual galleries–created using clean, simple HTML code.

Our galleries are now visible; however, we need a few administration tools to handle changes, such as updating the title and description or fixing a broken thumbnail (since they’re pre-generated now). 

#1 – Introduction
#2 – HttpHandler and LINQ Data Model
#3 – Building the Galleries

NOTE: Code downloads are available on the Introduction post.

This post will cover the topic of using the ModalPopupExtender to manage files once their added into the galleries.

Read more…

Creating a Modal Popup Template Control

November 5, 2007 8 comments

Download : .NET 3.5 Source Project

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.

ModalPopupTemplate with Matt Berseth's YUI CSS

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

The ModalPopupTemplate's class diagram.

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.

  • _Element_Style (BodyStyle, ContainerStyle, etc) properties accept strings from your embeded or attached CSS.
  • 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!)

Elements of the ModalPopupTemplate

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.

<asp:UpdatePanel ID=”updatePanel” runat=”server” UpdateMode=”Conditional”>

<ContentTemplate>

<asp:Button ID=”btnTrigger” runat=”server” Text=”Show Popup” />

<cc1:ModalPopupTemplate

ID=”ModalPopupTemplate1″

runat=”server”

DefaultStyle=”YUI”

HeaderText=”Load Product Information”

BodyText=”Are you sure you want to load the product information?”

TargetControlId=”btnTrigger”

OnOkClick=”LoadDataTable” />

</ContentTemplate>

</asp:UpdatePanel>

How it Works

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.

kick it on DotNetKicks.com

AjaxControlToolkit v10920 Released!

September 22, 2007 Comments off

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.
  • AutoComplete: Allow users to associate additional data with the AutoComplete suggestions.
  • 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.

 

Using a Modal Popup to Modify LINQ GridViews

September 16, 2007 23 comments

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):

protected void 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:LinqDataSource ID=”ProductsDataSource” runat=”server”

ContextTypeName=”NorthwindDataContext”

onselecting=”ProductsDataSource_Selecting” />

<asp:GridView ID=”ProductsGridView” runat=”server”

DataSourceID=”ProductsDataSource”

OnRowDataBound=”ProductsGridView_RowDataBound”

OnRowCommand=”ProductsGridView_RowCommand”>

<Columns>

<asp:TemplateField>

<ItemTemplate>

<asp:LinkButton ID=”ModifyButton”

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

</ItemTemplate>

</asp:TemplateField>

<asp:BoundField DataField=”ProductID”

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

<asp:BoundField DataField=”ProductName”

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

<asp:BoundField DataField=”UnitsInStock”

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

<asp:BoundField DataField=”UnitPrice”

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

SortExpression=”UnitPrice” />

<asp:BoundField DataField=”NumOrders”

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

<asp:BoundField DataField=”Revenue”

HeaderText=”Total Revenue” SortExpression=”Revenue”

DataFormatString=”{0:c}” />

<asp:BoundField DataField=”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:

protected void 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”) as LinkButton;

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:Button ID=”HiddenButton” runat=”server”

Style=”display: none; />

<ajaxToolkit:ModalPopupExtender ID=”mpe” runat=”server”

BackgroundCssClass=”ModalBackground”

CancelControlID=”CancelPopupButton”

PopupControlID=”UpdateRecordPanel”

TargetControlID=”HiddenButton” />

<asp:Panel ID=”UpdateRecordPanel” runat=”server”

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

<h2>Update Record</h2>

<p>

Record:

<asp:Label ID=”ProductId” runat=”server” /><br />

Product Name:

<asp:TextBox ID=”ProductName” runat=”server” /><br />

Units in Stock:

<asp:TextBox ID=”UnitsInStock” runat=”server” /><br />

Unit Price:

<asp:TextBox ID=”UnitPrice” runat=”server” /><br />

</p>

<p>

<asp:Button ID=”OkPopupButton” runat=”server” Text=”Update”

OnClick=”OkPopupButton_Click” />

<asp:Button ID=”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:

  1. Collect the row’s CommandArgument (which is the ProductId),
  2. LINQ up a new Product and preslug the UpdateRecordPanel’s fields with the data,
  3. 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.

protected void 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”)

as ModalPopupExtender;

mpe.Show();

}

}

private Product 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.

protected void 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();

}

public void 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.

Now, with all this talk of Chai tea, I need to go make some. Yum. =)

kick it on DotNetKicks.com

Follow

Get every new post delivered to your Inbox.