Home > .net 3.5, c#, Visual Studio 2008 > Using a Modal Popup to Modify LINQ GridViews

Using a Modal Popup to Modify LINQ GridViews

September 16, 2007

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

About these ads
  1. edcon
    January 25, 2008 at 8:29 pm | #1

    Hi!
    Need help!

    Do you know how to convert this to Vb?

    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;

    }

    thanks in advance,
    Ed

  2. January 27, 2008 at 1:06 pm | #2

    @edcon

    Private Function SelectRecord(ByVal productId As Integer) As Product

    ‘ 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

    End Function

  3. edcon
    January 28, 2008 at 6:06 pm | #3

    You really helped me, David!

    Thank you and more power !

  4. January 28, 2008 at 6:22 pm | #4

    @edcon-

    Not a problem. You make a good point, I tend to do most everything in C# since that’s what I use all day. I’ll try to mix it up more and put some VB code up and/or see if I can do something fancy using some of the online converters. :)

    Have a good one!

    -dl

  5. edcon
    February 12, 2008 at 11:09 am | #5

    Hi Dave,
    I noticed when I used your codes and made to VB, the Update button fires when hitting the refresh button in IE. Do you encounter this behavior?

    thank you,
    -edcon

  6. February 12, 2008 at 5:59 pm | #6

    Hmm, no, I haven’t had that trouble. Would you mind packaging up a sample of your implementation and emailing it to me? tiredstudent@gmail.com

    -dl

  7. edcon
    February 12, 2008 at 7:20 pm | #7

    Hi Dave,
    As per this below link, redirecting to the same page will resolve the issue but I’m not sure if this the right way to prevent the button being re-postback when hitting F5 or refresh in IE.

    http://forums.asp.net/t/1151989.aspx

    This issue might be out of topic… my apology.

    I sent the code already. Just let me know.

    I really appreciate your response.

    edcon

  8. John
    February 27, 2008 at 5:20 pm | #8

    Excellent work! I was looking all over for a solution in the ASP.NET forums, but had no luck. I have posted my solution there using your technique.

    http://forums.asp.net/p/1224034/2200052.aspx

  9. Reg
    April 11, 2008 at 2:14 am | #9

    Hi David
    I try to do the same thing.
    I don’t know where can I find the ModalPopupExtender?

    what is up1? Could u tell me ?
    ModalPopupExtender mpe =up1.ContentTemplateContainer.FindControl(“mpe”) as ModalPopupExtender;

    Do u have the sample file ? Could u mail me that?
    Thanks

    Reg

  10. April 11, 2008 at 5:40 am | #10

    @Reg-

    up1 is the updatePanel control. Since our GridView is enclosed in an updatePanel (which has a ContentTemplate for controls), we have to dig into that control to find our ModalPopupExtender.

    There used to be a solution attached to this blog post, but appears it was misplaced when our ftp server crashed–I’ll work up something and post it both to this page and the comments today.

  11. April 12, 2008 at 7:13 pm | #11

    @Reg-

    I reworked the above code and have posted and example here.

    I didn’t use the LinqDataSource, instead simply bound it in code-behind. If you wish to use the LinqDataSource, simply add the LinqDataSource object onto the page, go to it’s events and add a _Selecting event, then move the BindProductsGrid method from Page_Load to the _Selecting event of the LinqDataSource.

    In my opinion, LinqDataSource is rarely, if ever, needed–a conclusion I’ve held to since I wrote this article originally.

    Hope this helps!

  12. bebandit
    April 17, 2008 at 2:42 pm | #12

    Hey,
    Thanks for the code. My client wanted a way to change the text color and size of text inside the gridview. The gridview was being used to organize content that was used on his website. I used your code and instead of using textboxes, I used fckEditors so that he could wysiwyg the heck outta the text and reinsert it back into the gridview. Thanks!

  13. April 17, 2008 at 4:56 pm | #13

    @bebandit-

    Great to hear; thanks!

  14. Ali
    June 8, 2008 at 11:17 pm | #14

    Hey this one is excellent.
    I was looking for this and you made it look so simple and so self explanatory..

    Thank you so much

    Cheers ‘
    Ali

  15. Maci
    August 16, 2008 at 12:15 pm | #15

    Hello!

    Does someone know how to adapt this very excellent LINQ-Example to ImageButton?

    I tried…

    ImageButton modifyButton = e.Row.FindControl("ModifyButton") as ImageButton;

    and

    Thanks!

  16. August 16, 2008 at 4:32 pm | #16

    @Maci-

    Is your ImageButton control trying to kick off the MPE? It looks like your second code block got cutoff. :(

  17. September 30, 2008 at 11:05 pm | #17

    Good work. I’ve used It in my solution, and I’ts works fine. I’ve made some changes because I’ve used ListView. Also i’ve stored unique key in HiddenField control. In future I want to use UpdateProgress while modal popup waited to be shown and while data to be updated in ListView.

  18. ASP
    October 15, 2008 at 1:30 am | #18

    This article helped me to made my website more pretty and userfrendly. Thanks!

  19. retox
    August 4, 2009 at 10:17 am | #19

    hi David is there any chance of you sending me this in vb as i have looked through the current source code and am confused between the linqdatasource and code behind

    • August 4, 2009 at 12:30 pm | #20

      I, unfortunately, don’t have this in VB. What seems to be throwing you off?

      The LINQDataSource is like any other DataSource object. You could use a SqlDataSource or roll your own via IEnumerable, List, etc. Those types should be the same in VB as they are in C#.

      • retox
        August 6, 2009 at 4:53 am | #21

        Thanks for the reply i understand the code better now i have tried to convert it over to vb just with a sql connection but when the page loads the gridview doesnt display could i send you the source code for you to have a look at for me??
        thanks

      • August 6, 2009 at 6:35 am | #22

        Yeah, email it to drlongnecker@gmail.com and I’ll take a look. :)

Comments are closed.
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: