Showing posts with label context. Show all posts
Showing posts with label context. Show all posts

Wednesday, March 28, 2012

ContextMenus from PopupExntender without the xml script?

Hello,

A while back I figured out a way to implement "context menus" on top of the built-in Asp:menu control and documented that here:

http://apps.ultravioletconsulting.com/misc/ContextMenus.aspx

This approach uses a hyrbid server/client data-binding technique, but this approach still relied on rendering menu data from the server and relying on the messy HTML table that were output. I was able to drastically reduce the amount of data / javascript passed to the client to that point that it was nothing more than an array embedded in a click handler:

<table style="border:1px solid green"border="0"cellpadding="0"cellspacing="0">

<tr valign="top">
<td>
1: $5 <button value="Do it Now!">Do it Now!</button>
</td>
<td class="MenuCell"
onclick="ContextMenu_Place(event,'Menu1', [1,5])"></td
</tr>
</table>

I tried to use the PopupControl Extender instead to popup a more static block ot HTML / div tags, and it worked fine, except it generates a gigantic amount of XMLScript for each instance. I really need to do nothing more than pass the data values and the location on the screen where the popup should occur. I don't need all the extra Behavior tags, etc.


Any ideas?

Thanks,

Josh

Hi Josh,

The latest release (60914 on 9/15) updated a number of controls to use functionality similar to DynamicPopupate. PopupControl, and others, now have a DynamicContextKey property that may do exactly what you need.

Thanks,
Ted

Thanks Ted,

I ended up writing my own simple ContextMenu_Place function that calls a client-side template/data-binding function to handle this functionality. I am still having one problem though with __doPostBack

Is it possible to manually specify the second parameter in a __doPostBack handler?

I have this code:
<asp:LinkButton runat="server" ID="actTestServerSide"
Text="Test of Server Side Link Button"
CommandName="Action"
CommandArgument="$1"
/>
The rendered code looks like this:

BLOCKEDSCRIPT__doPostBack('ctl00$ctl00$ctlPagePlaceHolder$MyAccountContentPlaceHolder$ctlWonView$actTestServerSide','')

That second parameter is empty though!

Ineed to be able to have code on the _client side_ replace the $1 with avalue of my choice before the actual link gets submitted. The reasonfor this is that I have a GridView with 100 items in it, and I passdown one static block of HTML code as a client-side template for acontext menu that operates on the line item of each GridViewRowinstance. I then have a click handler which does something like:

onclick="ContextMenu_Place(event,'ctlPathGoesHere$blah$blah$blah','ctl00Path$blah$blah',[],['189884','44394743']);
TheContextMenu_Place function copies the template code and then uses thevalues of the second array to replace the placeholders. The firstarray, empty here, specifies the css class name of rows in the menuthat need to be grayed out.

The static block looks like this when rendered to the client side:

 <table id="blah_blah_Container"border="0"cellpadding="0"cellspacing="0"class="ActionsContextMenuContainer">
<tr valign="top">
<td>
<div id="blah$blah">
<div class="actContact"><a title="Contact"href="/Contact.aspx?Item=$1">Contact</a></div>
<div class="actPay"><a title="Pay"href="/Pay.aspx?Item=$1">Pay</a></div>
<div class="actTestServerSide">
<a id="blah$blah$blah"
href="BLOCKED SCRIPT__doPostBack('$blah$blah$blah','')">Test of Server Side Link Button</a>
</div>
</div>
 </td>
</tr>
</table
In the first two cases, the links are to external pages, not POSTBACKS. But in the last case, the action should be POSTBACK, so I need that last line to read:

href="BLOCKED SCRIPT__doPostBack('$blah$blah$blah','$1')">Test of Server Side Link Button</a
This will enable me to use my client side templating function to replace the argument with the actual line item ID.

But, I can't figure out how to get that second parameter of __doPostBack to be non-blank.

Is this feasible? I suppose I could manually replace instances of '') but that seems like a major hack if I can instead get my $1 in there properly.

 
Thanks,
Josh 



Hi Josh,

I'm definitely not an expert on__doPostBack (for that you should probably ask this in thegeneral ASP.NET forums). The only way I know of accessing the optional second event arg is by implementingSystem.Web.UI.IPostBackEventHandler.

Also - Davidblogged about how to get this scenario working withModalPopup and some of the new Toolkit features if you were interested.

Thanks,
Ted

Ted, thank you,

I was able to make a neat little control to accomplish this.


I created one control named ContextMenu inheriting from ASP:Menu.

This allowed me to use <asp:MenuItem> instances instead of hard coding the HTML like I had above.

Next, I overrode the CreateChildControls method to create essentially that same blob of HTML above instead of letting the built in behavior render the HTML.

I added an ASP:Button and an ASP:HiddenField and wrapped them in a DIV with display:none.

Added a Command event to the ContextMenu class of type CommandEventHandler.

Next I made it a convention that if the Target property of the menuItem was set to "POSTBACK", then I would use the Page.ClientScript.GetEventReference(object, argument, registerForEventValidation) method. This actually creates the call to __doPostBack. So, I specified my hidden ASP:Button as the target. In the Value property of the asp:MenuItem, I made it so that "actSomeCommand|$0" would mean that actSomeCommand would be the CommandName, while $0 would be passed down to the client side to allow for the client-side data-binding.

In the href for the anchor tag, I prefixed the call to __doPostBack with a call to document.getElementById('id_of_the_hidden_field_here').value = '$0'. So this allowed my line-item data to be populated just in time for the pop up.

Within a private Command handler for the actual Asp:Button, I read the value of the hidden field -- because I still could not get the real argument to get passed-- then created my own CommandEventArsg and fired that event up to the calling client code.

The end result lets me do this:

<auc:ContextMenu ID="ctlContextMenu" runat="server">
<Items>
<asp:MenuItem
Value="actBuyNow"
ToolTip="Buy this item now!"
Text="Buy Now!"
NavigateUrl="~/Site/ViewItem.aspx?Item=$0#BuyItem"
Target="__self"
/>
<asp:MenuItem
Value="actBid"
ToolTip="Bid on this item"
Text="Bid"
NavigateUrl="~/Site/ViewItem.aspx?Item=$0#PlaceBid"
Target="__self"
/>
</Items>
</auc:ContextMenu
And in my GridView:

<asp:TemplateField HeaderText="Actions" HeaderStyle-CssClass="TextCentered" ItemStyle-CssClass="GridItemActions">
<ItemTemplate>
<auc:ContextMenuPopup
runat="server"
ContentControlContainerID="<%# ctlContextMenu.ClientID %>"
ContentControlID="<%# ctlContextMenu.ContentControlID %>"
id="ctlContextMenuPopup"
/>
</ItemTemplate>
</asp:TemplateField>

-- Note, I'm not so sure I really need both a ContentControlContainerID and ContentControlID on second thought. I could have just had a private convention with my javascript for fetching the actual menu item Divs out of the container, but this works...

Finally, in my code behind:

First, wire up the event handler on the ContextMenu:

ctlContextMenu.Command +=
new CommandEventHandler(ctlContextMenu_Command);

Later, during data binding of my GridView:

ContextMenuPopup menu = e.Row.FindControl("ctlContextMenuPopup")
as ContextMenuPopup;
if (null != menu)
{
menu.DataBind();
}

if (null != menu)
{
string[] lineItemData = new string[2];
lineItemData[0] = item.ItemID.ToString();
lineItemData[1] = item.SellerUserID.ToString();

List<string> hiddenItemIds = new List<string>();

if (0 == item.IsActive())
{
hiddenItemIds.AddRange(new string[] { "actBuyNow", "actBid" });
}

menu.CompleteContextMenu(hiddenItemIds.ToArray(), lineItemData);
}

This call to CompleteContextMenu generates the necessary javascript handler to be added to the container level. It also specifies, depending on the state of the item, which items of the menu should be disabled upon popup.

Last but not least,

protected void ctlContextMenu_Command(object sender, CommandEventArgs e)
{
string action = e.CommandName;
string data = e.CommandArgument as string;

AddFeedback(string.Format("Received command: {0} with argument {1}", action, data));
}

On the client side:

<div class="ContextMenuContainer">
<span id="ctl00_ctl00_ctlPagePlaceHolder_ctlMyView_ctlItems_ctl02_ctlContextMenuPopup_ctlActionsContextMenuLink"
onclick="ContextMenu_Place(event,'ctl00_ctl00_ctlPagePlaceHolder_ctlMyView_ctlContextMenu',
'ctl00_ctl00_ctlPagePlaceHolder_ctlMyView_ctlContextMenu_ctlContextMenuContent',
[],
['189884','44394743'],
this,
);">
Select <img src="../../Controls/ContextMenu_Resources/dwn.gif"align="absmiddle"style="height:27px;width:30px;border-width:0px;"/>
</span>
</div>

Looking back, it was quite a challenge to get this thing working! But, it was fun to write and worth it...

Josh

Context.Cache not available intermittently using UpdatePanel

I store some settings used by my application in the Context.Cache. When I postback using an UpdatePanel, sometimes it is available and other times it is simply Nothing as if it doesn't exist. There seems to be no consistent pattern and the code has not changed. It just works sometimes and other times it does not.

Is this a known issue with the ASP.NET Ajax team or is there something I am not aware of that needs to be set or configured to support getting the Context.Cache properly?

Thanks for any help or insight.

Regards,

Don

Hi,

The cache returned by the Context object is actually a static object shared by the whole application. It's not likely to be null.

I suspect that the NullReferenceException is thrown by the Context object, please try using HttpRuntime.Cache to access it rather than Context.Cache.


Another possibility is that the cache is expired.Please trying specifying disableExpiration="true" [ http://msdn2.microsoft.com/en-us/library/ms228248.aspx ].

Hi Raymond - Thanks for the reply and the help. Unfortunately, using the HttpRuntime.Cache instead of Context.Cache produces the same intermittent results. I am convinced this is a problem with ASP.NET Ajax v1.0. I have used the Cache functionality for some time on other projects and have never had an issue like this. Also, this is not a timeout error as I am testing immediately after I fire up the application and as I said, sometimes it works and then for no reason on an Ajax partial page postback it will fail and nothing will be there.

My solution for this is to rely on the Session and so far I have had no issues with that.

Thanks again for your help and if you have any other suggestions, or would like me to post my code, let me know. Thanks!

Don


Hi Don,

Please show me a small, self-sufficient sample.

Thanks.


Hi Raymond - Below are the basic steps I go through:

In my Global.asax:

----------------------------------------------------------

Sub Session_Start(ByVal senderAsObject,ByVal eAs EventArgs)
HttpRuntime.Cache.Insert("MCSApplicationSettings", ApplicationSettingsLoader.GetApplicationSettings(ConfigurationManager.AppSettings("MCSApplicationSettingsURL")))

EndSub

----------------------------------------------------------

This gets populated for each session that fires up.

In my ASPX Page:

----------------------------------------------------------

Private applicationSettingsAs Hashtable =Nothing

PrivateSub Page_Init(ByVal senderAsObject,ByVal eAs System.EventArgs)HandlesMe.Init

_applicationSettings = CType(HttpRuntime.Cache.Get("MCSApplicationSettings"), Hashtable)

EndSub

----------------------------------------------------------

Everything works fine until I do a partial postback and try to access the Cache. Sometimes it is there and sometimes it is not.

Thanks,

Don

context parameter in pagemethods

The context parameter in pagemethods is being lost

Here is what I do

var contextArray = new Array(2)

PageMethods.DoSomeMethod(param1,param2,param3,OnSucceeded, null,null,null,contextArray)

function OnSucceeded(result, response, context)

the context array shows ASPX_defult_DoSomeMethod

Why is it not returning the contextArray that I passed?Angry

So I checked the internals of PageMethods using debug.traceDump and I can see that after calling PageMethods.SomeMethod the _usercontext variable does not get set

Infact I have to set it using PageMethods.set_defaultUserContext

Am I missing a library or are the parameters for PageMethods.DoSomething not correct !!!

Please AdviceConfused

Context menus, Ajax and repeaters

I have been looking and looking for a context menu that can be applied to an asp:XXX object within a repeater. My problem with a lot of the context menus out there is they do not allow me to specify databind values to the menu items based on what row inside the repeater was right clicked. My needs are this: the elements inside the context menu must execute a function in the cs file. The menu must pass values from the element that was right-clicked such as name, ID etc. and it "should" be quick.

I have gotten hover menus working atm but they drag the system down if the repeater has a large dataset returned.

Hi,

I fail to reproduce the issue. Can you show me your code?


Hi, ongjt

We are changing the issue state to "No status" because you have not followed up with the necessary information. If you have more time to look at the issue and provide more information, please feel free to change the issue state back to "Not Resolved". If the issue is resolved, we will appreciate it if you can share the solution so that the answer can be found and used by other community members having similar questions.

Thank you!

Context

Hi,
how can I have something like a context for the requests that is available in the callback and timeout handler? I think sometimes it is very important to have such a context to have one handler for several different requests.
CIAO
Michael

It's already there, look at the webRequest and webResponse classes.

Can you please give me a URL where I can find more for webRequest/webResponse?

CIAO
Michael


There is unfortunately no documentation for the client-side classes for the moment. We're working on it. For the moment, you can look at the signatures of the constructors and methods in the javascript files. I know it's a little hard as they've been formatted to save space.
Hi,
ok, I got it. It is not supported by default, but I can re-write the generated code to add this feature. For more details see:http://weblogs.asp.net/mschwarz/
CIAO
Michael