I have embedded a WebGrid into a SharePoint web part and it works OK - I create the grid programatically and add it using CreateChildControls method.
I have one issue which is after I add my WegGrid-containing web part to the MOSS page. I do this in MOSS via Site Actions - Edit Page and edit the web part page, click a web part zone and pick my web part to add it. It goes onto the page fine, but I can't close the web part using the X in the top right corner. I can add other built-in web parts to the page and close them OK, e.g. content query web part, content editor web part etc. But when I try to close my own web part in MOSS the browser doesn't post back and just does nothing. The same thing happens if I click the Edit dropdown and select Close or Delete - again the web part is not removed from the page.
I know this isn't a MOSS forum but I wonder if anyone else has had this problem? Seems like it might be to do with Javascript on the page?
The browser is IE7. Firefox doesn't have the same issue but you need to use IE really for MOSS work.
I get the same issue even with a very simple grid as per the example below. It seems to jam up the postback on the page so none fo the SharePoint buttons will do a postback. If I comment out the call to Bind() then it works OK. Is there something else I need to do?
using System; using System.Collections.Generic; using System.Text; using Infragistics.WebUI.UltraWebGrid; using System.Data; namespace UltraGridDemoWP2 { public class UltraGridDemoWP2 : System.Web.UI.WebControls.WebParts.WebPart { private UltraWebGrid _grid; public UltraWebGrid Grid { get { EnsureChildControls(); return _grid; } } protected override void CreateChildControls() { base.CreateChildControls(); _grid = new UltraWebGrid("Grid"); this.Controls.Add(_grid); Bind(); // Comment this out and it doesn't jam up the postback } private void Bind() { Grid.DataSource = getdata(); Grid.DataBind(); } private DataTable getdata() { DataTable t = new DataTable(); t.Columns.Add(new DataColumn("Col1")); t.Columns.Add(new DataColumn("Col2")); for (int i = 0; i < 10; i++) { DataRow r = t.NewRow(); r["Col1"] = "col1-" + i.ToString(); r["Col2"] = "col2-" + i.ToString(); t.Rows.Add(r); } return t; } } }
OK I have stumbled on a solution but I'm not sure exactly why it works! I found if you set the grid to have an ID that is a GUID, everything works OK. So I changed the grid variable to intialise it:
private UltraWebGrid _grid = null;
protected override void CreateChildControls() { base.CreateChildControls(); if (_grid == null) { _grid = new UltraWebGrid(Guid.NewGuid().ToString()); } this.Controls.Add(_grid); Bind(); }
My thinking was that there might be a problem with invisible closed webgrids on the page and that I had to make sure that each webgrid had a unique ID on the page, so I gave it a GUID. However, I have tried replacing the Guid.NewGuid().ToString() with a very large random number generated from the Random class and that still causes the error even though the random ID of the grid is unique on the page. It just seems like the ID has to be a GUID string - can anyone explain?
Another workaround is to set the ReadOnly property of the webgrid within the CreateChildControls method - this reduces the amount of Javascript that the control renders onto the page:
Grid.DisplayLayout.ReadOnly = ReadOnly.LevelTwo;
Thanks Rumen for that.
Can you explain is it actually necessary to keep setting the ID of the grid to a new GUID?, or in fact why do you need to set the ID at all? I thought .NET takes care of all this and you should not need to set the ID to be unique?
I found actually in a larger app that I am writing that if you do reset the ID of the grid every time in CreateChildControls, it messes up the AJAX paging - presumably it loses track of the grid if the ID keeps changing. I tried setting a breakpoint on the web part and attached Visual Studio remotely to the W3WP process to debug it. Then I tried paging back and forth using the AJAX paging. I found that if you don't set the ID explicitly, it repeatedly references the Grid property via calls to get(). Because the grid is created without an explicit ID, it gets allocated an ID for example "ctl100". Presumably that ID is the way that .NET keeps track of the state of the grid - it's probably not a good idea to change it every time?
If so that it comes back to the original question as to why, if you don't set the ID on the grid, it messes up the postback on the page if you try to close or delete the web part. I don't have this problem with the normal .NET GridView control so I guess I just have to use that instead. But of course I'd like to have the extra functionality of the Infragistics grid - if anyone else has got this issue resolved it would be good to know.
Anyway here's another tip that I picked up on this project - if anyone else tries to embed a webgrid into a web part then you must override the OnInit() method as follows:
protected override void OnInit(EventArgs e) { base.OnInit(e); this.EnsureChildControls(); }
If you don't do that then the InitializeDataSource method is never called. You need to set the DataSource in that InitializeDataSource method. You don't need to call DataBind() directly - it will bind when it displays.
Oh, I see - so you are ahead of me with this one. So yes - in this case (with GUIDs) the ID will probably not match the ID expected on postback/AJAX callback. But then again, I am not sure that even with IDs provided from the framework we will always have exact match then - since the IDs are provided by the Control Tree and it might be different at the time you are adding the controls back - especially if you have added/deleted additional controls from the parent containers.
I will probably need to set this up and reproduce it before advising further. Just on top of my head a couple of suggestions - the ID can be equal to the container WebPart.ID + something unqiue, like "WebGrid" as a suffix. Also, you may mark the WebPart with INamingContainer inteface and see if it works better (ctrl000 as autogenerated ID seems weirds, since for child controls the ID in INamingContainers needs to be somethng like parentID_ctrl00 if ID of the child is not set explicitly).
But I must admit I am shooting in the dark here - hopefully someone more experienced will provide additional clues.
OK Thanks. I tried making my web part class implement INamingContainer but it still behaves the same, jams up the postback when you try to close it on the page.
I found this blog entry by Todd Snyder of Infragistics where he managed to do something similar to what I'm trying to do:
http://blogs.infragistics.com/blogs/tsnyder/archive/2008/07/29/getting-sharepoint-and-ms-ajax-to-work-together-is-lots-of-fun.aspx
He has embedded the WebGrid in an UpdatePanel, which I don't need to do. He doesn't set the ID property of the grid I notice - like I say when I do set the ID after creating the grid object, it seems to mess things up when the page refreshes.
Let us know if this works for you.
Direct Link to the sample: http://users.infragistics.com/tsnyder/SharePointAJAX/Sample-AJAX-Grid.zip
Quick followup. Make sure you have SP1 installed for SharePoint (MOSS). The service pack include support for running AJAX based web parts and includes a number of JavaScript related fixes.
Thanks for sharing the solution Lars - I've updated the "Known Issues" forum with your findings - surely will be useful to a lot of people.
Thanks again.
Ok I installed NetAdvantage 2008 Volume 2 and that has finally fixed the problem. Volume 2 installs as a separate product from Volume 1 and I upgraded my project to use the Volume 2 controls using the project upgrade utility. I didn't change any of my code, and I do not explicitly set the ID of the webgrid in code. By referencing volume 2 version of the webgrid it now closes correctly when embedded into a webpart.
Thanks for your help and advice
Thanks Todd. I have MOSS SP1. I have downloaded volume 2 as you suggested and installed it but it doesn't seem to go over the top of Volume 1 - Volume 2 pops up as a separate product. Should I uninstall Vol 1 first and then install Vol 2?