User Profile & Activity

Eric Member
Page
of 19

Hi Handy,

Ok, I found out how to get the client side events of the button clicks.

In case anybody else needs this, add to <LayoutSettings><ClientSideEvents> a tag OnButtonClick="xxx", where xxx is the name of your JavaScript function. In that JavaScript function I can do a postback or so:

function xxx(gridId,tblName,colName,cellValue)
{
var webGrid=wgGetGridById(gridId);

if(webGrid.GetSelectedObject()==null)
return;

var row=webGrid.GetSelectedObject().GetRowObject();
var cell=wgGetCellByName(row.GetElement(),'ColName');

webGrid.AddInput('BlockId',cell.MyCustomAttr);
webGrid.SendCustomRequest();

return true;
}

The MyCustomAttr is being fetched from the CustomAttributes of another column. We have to do this, because the data needed is in a hidden column and JavaScript has no access to hidden columns, because they are not rendered. To assign the CustomAttributes I used the InitializeRow event and there I set col.CustomAttributes="MyCustomAttr=""" & value & """"

Now my questions:

Why should I need to store anything in a database? This is just temporary. I'd like to store this merge/unmerge flag in the ViewState (or something similar). I just don't get this working. If I set the Autopostback flag to true, like mentioned earlier, I still get a full postback and my ViewState is lost somehow on the postback.

Also, If you use all this client-side stuff like mentioned above, wouldn't it be better to directly use the ButtonClick event? I don't see the need to involve JavaScript at all.

So you know the requirements now. To repeat: I have a read-only hierarchical WebGrid. I need a button per row (is implemented) and I need a binary value per row with the merge/unmerge display status. Clicking on the button should invert this status. This status doesn't need to get stored anywhere, but it must be preserved during postbacks etc. Changing this status per row also changes all shown data in the hierarchical grid below the clicked node, so the data in the grid needs to get refreshed in that case. How would you implement all this?

Regards

And another related problem: The Button_Click event comes after the Render event (actually I only tested PreRender). This is a problem, because if I change the ViewState in the Button_Click event handler, then my ViewState changes will not get rendered. What could I do to avoid this? I want to save the merge/unmerge-status of the clicked row somewhere. I don't think it I should use the database for this temporary data.

Hi Handy,

The grid is read-only. There is no need to save any data from the client-side. Except of the status of the open/closed nodes and that status of the merged/unmerged status of each node. For the latter, this changes the whole dataset, so it must be done on the server side always. And for the open/closed status of the nodes, I'll use the standard JavaScript code where I have examples already.

So I think I can do a full postback without problems. The only disadvantage would be the full page refresh, right? Going this way would mean that I would have to store in the OnClick event the changed merge/unmerge status and in the normal page or grid OnLoad event (actually in the InitializeDataSource) get the full data set in the new way.

Ok, you propose a smarter refresh through JavaScript. How would I attach an event handler to each button? And what should I do there to get the data in the grid updated and the display refreshed?

 

Ok, the problem is now almost solved. I could solve problem (1.), getting the event to the server. I fixed it this way:

In the grid_Init event handler, when I create (add) the columns to the grid, I just have to add WebGridColumn.ButtonAutoPostback = True for the column with ColumnType.ButtonImage.

Then the grid_ButtonClick event handler gets called. I'm working on changing the data in the RootTable there now.

So only question (2.) remains. If I call grid.ClearCachedDataSource(), grid.RebindDataSource() everything goes away. But I need to keep the data in the modified RootTable and just refresh the display somehow after this postback. How should I do this?

In the meantime I solved the two visual problems (3. and 4.) like this:

  • In event handler InitializeRow: Set col.ButtonImage always to an image. If the image should not be shown, assign it an empty image (1x1 pixel, transparent).
  • In the grid's LayoutSettings, set <ButtonStyle CssClass="ISButtonStyle />
  • In CSS define this CSS class:
    .ISButtonStyle
    {
    	border:none 0 Red;
    	background-color:Transparent;
    }
    

    The color of the border will be ignored.

But questions 1. and 2. are still open.

Regards

Ok, I changed the column type from ColumnType.Image to ColumnType.ButtonImage and also the code to set the image in InitializeRow to use col.ButtonImage instead of col.Image.

Now I have a bunch of buttons in that column, but I have more problems than before. But maybe these are easier to solve:

  1. How do I get the events of these button clicks in server-side code? Adding an event handler and declaring it with "Handles grid.ButtonClick" doesn't seem to be enough.
  2. How would I modify the existing data in the grid in this handler? I know how to replace the data in the RootTable, but how would I tell that to WebGrid to refresh the content, preferably just the screen part located in the selected node?
  3. Now I have a visual problem: The image appears on a button. Any way I could make the button disappear visually? Maybe define a CSS to render the button with no border and surface (except the image) to be transparent. How would I do that?
  4. Some rows have no such image and should not be clickable. Until now, I just didn't set the Image property to a value. If I now don't set ButtonImage to a value, i still get the button, even with an image, just with a failed to load image. How do I make all that disappear visually only on certain rows (in InitializeRow)?

I hope you can help here.

Thanks.

Ok. Forget about all that hierarchical stuff in the first step.

I have a WebGrid. On aspx there is just the root WebGrid tag, an empty RootTable, some LayoutSettings and a tag for FlyPostBackSettings.

In code behind:

In the event grid_InitializeDataSource, I get the data from the DB and assign it to e.DataSource = ds:

Dim ds As DataSet = DBCall...
ds.Tables(0).Columns.Add(New System.Data.DataColumn("Merge", Type.GetType("System.Boolean")))
e.DataSource = ds

in grid_Init:

AddGridColumn(...)
grid.RootTable.DataKeyField = ".."
grid.RootTable.SelfReferencingSettings...

in grid_InitializeSelfReferenceDataSource:

Dim ds as DataSet = DBCall(e.ParentKeyValues("..."))
dt.Merge(ds.Tables(0), True, MissingSchemaAction.Error)

in grid_InitializeRow:

Dim Col As WebGridCell = e.Row.Cells.GetNamedItem("...")
Col.Image = "merge_plus.gif"

As you can see in the last line mentioned, I add an image to the column. That image appears. I want to change the data of the grid when the user clicks on that image.

You were talking about some Listener object. That looks like a JavaScript object, not .NET. How would I use this Listener object to bind an event to the Col.Image above? In any case I need a postback to get the new data, so preferrably I don't use JavaScript at all.

Hii Glenn,

Thanks for clarification. Here some comments from my side:

Two database calls is not an option for two reasons:

  • It would be slow. If you make two different calls, the first one only calling a COUNT, there would probably be no big difference, except the call overhead and the actual database counting.
  • Bad design, risking a crash. If the data changes between the two calls, the result would be undefined or completely wrong.

Yes, calling the db in OnLoad and caching it somehow, like in a member variable, would be an option. I will consider this. Or remove the freezeing altogether.

But why is PreRender too late? I think this is a design flaw in WebGrid. Before the PreRender event, no data should get rendered to the client. And enabling/disabling column freezing, which can also be done on the client-side, is clearly a rendering issue. Don't you agree with this?

Hi Handy,

Sorry if I didn't explain enough. You didn't understand me. We have two (2) such (+)/(-) boxes per row. One is the usual one drawn by WebGrid to expand and collapse the nodes. The second one, in a separate column, is to merge or unmerge all data below that node. These are two independent functions.

To illustrate this further, here an example of a tree view where the node 'Root' is expanded, but not the two nodes below (called "N1 (parent is Root)" and "N2 (parent is Root)"):

(-) Root (+)

(+) N1 (parent is Root)

(+) N2 (parent is Root)

As you can see, the node 'Root' has a second box on its right to merge (not expand) an a separate column.

If we expand the nodes N1 and N2, you would get this view:

(-) Root (+)

(-) N1 (parent is Root)

Leaf1 (parent is N1)

Leaf2 (parent is N1)

(-) N2 (parent is Root)

Leaf3 (parent is N2)

Leaf4 (parent is N2)

This is just the same as before, but expanded. As you can see, the (+) on the root node remains ready to get clicked, although everything is expanded. The nodes N1 and N2 don't have this box, because they don't have nodes below (they only have leaf entries). In a more complex (deeper nested) tree, N1 and N2 would also have this (+) to get them merged.

If we click on the remaining (+), you would get this view:

(-) Root (-)

Leaf1 (parent is N1)

Leaf2 (parent is N1)

Leaf3 (parent is N2)

Leaf4 (parent is N2)

As you can see, this second (+)/(-) has a totally different functionality. The normal (+)/(-) are for the nodes to expand and collapse, whereas the second is to merge and unmerge the data below it.

I can draw the second column correctly by setting the MergeCol.Image="..." in InitializeRow.

My question was on how I could handle the clicks on this second column's images and force a reload of the data below that node after the click.

I hope this explanation was better.

Hi Glenn,

Thanks for your idea. I didn't try it, because I think it cannot work by design - tell me if I'm wrong. What you are doing here is to enable/disable freezing in Page_Load, depending on whether the filter is set to a value that doesn't return data.

We don't know if there is data available or not, just by looking at the filter selection. In this hardcoded sample it might work, but what you are doing here would mean to query the database in Page_Load to see if there is data available or not. Is it that what you intended here?

For me that would mean we have two database requests: one in Page_Load, the other one in grid_InitializeDataSource. One must be enough. Did you want to move this all to the Page_Load event? If yes, then that part is missing. Or did I misunderstand anything?

Any other ideas?

Regards,

Eric

 

All times are GMT -5. The time now is 3:44 AM.
Previous Next