I am trying to remove the Scriptmanager from a SharePoint page in code so I can swap it with the Ajax toolkit. However, when I use the following code I get:
Only one instance of a ScriptManager can be added to the page
The code errors on the Add statement. This has to be done via code as access to modify the master page source is not an option.
The code from my webpart that needs to replace the scriptmanager:
protected void GroupCalenderSourceControl_Init(object sender, EventArgs e)
{
//throw new NotImplementedException();
ScriptManager sm = ScriptManager.GetCurrent(Page);
if (sm == null)
{
Page.Controls.Add(new AjaxControlToolkit.ToolkitScriptManager());
}
else
{
if (!(sm is AjaxControlToolkit.ToolkitScriptManager))
{
Page.Controls.Remove(Page.FindControl(sm.ID));
Page.Master.Controls.Remove(Page.FindControl(sm.ID));
sm = null;
Page.Controls.Add(new AjaxControlToolkit.ToolkitScriptManager());
}
}
}
The main problem is that the page needs to complete the post-back to fully unregister the original script manager. Doing it in the same code block isn't going to work too well.
However, having read around this subject it seems AJAX Control Toolkit controls work perfectly fine with the standard ASP.NET ScriptManager. Can you not stick with that instead?
Whilst ToolkitScriptManager is meant to improve some of the ScriptManager's behaviors in particular how it renders out behavior JS scripts ScriptManager will execute its requests as fast as ToolkitScriptManager
Source: http://blog.turlov.com/2008/05/scriptmanager-vs-toolkitscriptmanager.html
Try with this code
foreach (Control ctr in Page.Form.Controls)
{
if (ctr.GetType() == typeof(System.Web.UI.ScriptManager))
{
Page.Form.Controls.Remove(ctr);
break;
}
}
if (ScriptManager.GetCurrent(this.Page) == null)
{
ScriptManager scriptManager = new ScriptManager();
scriptManager .ID = "scriptManager" + RandomNo;
Page.Form.Controls.AddAt(0, scriptManager );
}
Related
I have a user control that is dynamically loaded in the the page load:
protected void Page_Load(object sender, EventArgs e)
{
MyControl ctl = (MyControl)LoadControl(controlPath);
ctl.ID = "mycontrol";
this.MyControlPlaceHolder.Controls.Add(ctl);
}
Front End of the Page:
<asp:PlaceHolder runat="server" ID="MyControlPlaceHolder"></asp:PlaceHolder>
I have an click event on the page that initiates and postback and calls a method where I'm trying to find the control and set some properties:
MyControl ctl = (MyControl)FindControl("mycontrol");
if (ctl != null){
ctl.MyProperty = true;
Response.Write("success");
}
else
Response.Write("fail");
This is writing fail after the postback, so it seems like I'm doing something incorrectly in finding the control. What is the best way to do this?
EDIT:
I switched it to MyControl ctl = (MyControl)this.MyControlPlaceHolder.FindControl("mycontrol");
This made it so it was finding the control, however, when the control loads after the postback, it appears as though the property is not set.
You have to use a recursive FindControl implementation because FindControl will only find direct childs. Your control is inserted in a naming container at a lower level. A generic FindControlRecurisve from MSDN:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
from MSDN
Or, if you only have one specific conatiner like in your sample:
MyControl ctl = this.MyControlPlaceHolder.FindControl("mycontrol");
if (ctl != null){
ctl.MyProperty = true;
Response.Write("success");
}
else
Response.Write("fail");
ViewState enable your control
public class MyControl:Control
{
public bool MyProperty
{
get
{
return ViewState["my"] != null? (bool) ViewState["my"]: false;
}
set
{
ViewState["my"] = value;
}
}
}
Try moving the code to dynamically add the control into Init instead of load. I can't be sure but there are a lot of things that happen between Init and Load and if your control is not present and accounted for it may cause issues like this.
You added the Control in the controls collection of a placeholder.
Apart of what control is your dynamically created control, if you want to looking for your dynamically added control in that way you have to do a recursive search starting from the root (maybe the page), so, if you surf over the net, you could find good solutions for that.
Personally I prefer solutions with: generics support and expressed as extension methods, so you could use the solution everywhere. These are some usefull links
Recursive Find Control with generics
Recursive Find Control with generics by extension method
Recursive Find Control with generics by extension method and linq support/example
hope this helps
I have a WebView on my app and I can't change the html file("target=_blank" link types). But some links on the page makes my app open them on the system browser. How can I disallow this action?
Thanks.
In the NavigationCompleted event handler run this script:
webView.InvokeScriptAsync("eval", new[]
{
#"(function()
{
var hyperlinks = document.getElementsByTagName('a');
for(var i = 0; i < hyperlinks.length; i++)
{
if(hyperlinks[i].getAttribute('target') != null)
{
hyperlinks[i].setAttribute('target', '_self');
}
}
})()"
});
On Windows 10, you can use WebView.NewWindowRequested:
private void WebView1_NewWindowRequested(
WebView sender,
WebViewNewWindowRequestedEventArgs args)
{
Debug.WriteLine(args.Uri);
args.Handled = true; // Prevent the browser from being launched.
}
There is a navigation starting event. It have a cancel property that can be used to cancel the navigation. Maybe this will work for you?
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.webview.navigationstarting
Stumbled on this myself recently, and I want to add that even though user2269867's answer is a viable solution, it might not work in certain situations.
For example, system browser will not only open if user click a link with target="_blank" attribute, but also if window.open() function called in javascript. Moreover, even removing all 'target' attributes won't work if a page loading some content dynamically and changing DOM after your script is already finished executing.
To solve all problems above, you need to override window.open function and also check for 'target' attribute not once, but every time user click something. Here is script that covers those cases:
function selfOrParentHasAttribute(e, attributeName) {
var el = e.srcElement || e.target;
if (el.hasAttribute(attributeName)) {
return el;
}
else {
while (el = el.parentNode) {
if (el.hasAttribute(attributeName)) {
return el;
}
}
}
return false;
}
var targetAttributeName = "target";
document.addEventListener("click", function (e) {
var el = selfOrParentHasAttribute(e, targetAttributeName);
if (el) {
if ((el.getAttribute(targetAttributeName) == "_blank") ||
(el.getAttribute(targetAttributeName) == "_new"))
{
el.removeAttribute(targetAttributeName);
}
}
});
window.open = function () {
return function (url) {
window.location.href = url;
};
}(window.open);
My js skills aren't ideal, so feel free to modify.
Also don't forget that, as kiewic mentioned, for Windows 10 there is WebView.NewWindowRequested event which solves this issue more natural.
If you just want to show the page and not allow any action to be done on that page I would look into WebViewBrush. The WebViewBrush will basically screenshot the website and the users will not be able to use any links or anything else on that page, it will turn into a read-only page. I believe this is what you are asking for.
More info on WebViewBrush can be found here: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.webviewbrush
If you can edit HTML of the page and NavigateToString(), then add <base target='_blank'/> in the <head>
I have a page which initially inherited from MastePage.master . And I want to use the same page but with different masterpage(MasterPage2.master) at some other place in my project. For that I am using the following code.
private void Page_PreInit(object sender, EventArgs e)
{
if (Request.QueryString["Update"].ToString() == "New")
{
this.MasterPageFile = "MasterPage2.master";
Content con = new Content();
con = (Content)this.FindControl("Content1");
this.Content1.ContentPlaceHolderID = "ContentPlaceHolder2";
}
}
I am also trying to set the asp content tag's ContentPlaceHolderID to ContentPlaceHolder2 which is from MasterPage2.master. Initially it was ContentPlaceHolder1.
But I am getting null value at con = (Content)this.FindControl("Content1");
Thanks
Page internally stores in private '_contentTemplateCollection' hashtable. it uses ContentPlaceHolderID property as key and stores special class (that will be used to build/initialize Content tag) as a value
- so to change ContentPlaceHolderID value (defined within markup) you need to modify this hashtable, remove old entry linked with old Id and add other entry with new Id
- you need to change ContentPlaceHolderId before creating master page otherwise an exception will be thrown in runtime
- best place to change Ids is Page 'preinit' event and if it is better to change Ids before change master page (if you will change master page at runtime)
To change ContentPlaceHolderID of Content tag, you can use following function in Page PreInit event
public static void AssignContentToNewPlaceHoldersWithinPage(Page pPage, string pOldId, string pNewId)
{
if (pPage == null || string.IsNullOrEmpty(pOldId) || string.IsNullOrEmpty(pNewId))
{
return;
}
// Try to get a reference to private hashtable using fasterflect free reflection library in codeplex (http://fasterflect.codeplex.com/)
// you can replace following line with standard reflection APIs
var lTmpObj = pPage.TryGetFieldValue("_contentTemplateCollection");
if (lTmpObj != null && lTmpObj is Hashtable)
{
var _contentTemplateCollection = lTmpObj as Hashtable;
if (_contentTemplateCollection.ContainsKey(pOldId) && !_contentTemplateCollection.ContainsKey(pNewId))
{
var lTemplate = _contentTemplateCollection[pOldId];
_contentTemplateCollection.Add(pNewId, lTemplate);
_contentTemplateCollection.Remove(pOldId);
}
}
}
function parameter are
pPage is reference to page instance contains content tag
pOldId is ContentPlaceHolderId property value in markup - the Id you want to change
pNewId is the new Id you want to use
I hope that my answer will be useful and I am sorry if my English language is not good
You can dynamically change the Master Page at runtime, but you need to use the same ContentPlaceHolder IDs. That way, your pages will work with either Master Page without adding extra code to change the IDs at runtime.
private void Page_PreInit(object sender, EventArgs e)
{
if (Request.QueryString["Update"].ToString() == "New")
{
this.MasterPageFile = "MasterPage2.master";
}
}
You can even test that your page will work with either Master Page in the Visual Studio design/markup view by changing the MasterPageFile in the <% Page %> directive in the .aspx markup.
The Master Page can be changed by overriding OnPreInit.
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
MasterPageFile = "~/MasterPages/MyOther.Master";
}
But for the ContentPlaceHolders I would suggest to create new ContentPlaceHolders with the same name in both of your MasterPages.
How to call a javascript function inside server side after the code is executed in page load/any events ? I am using UpdatePanel in this page. I had tried Page.RegisterStartUpScript, ClientScript.RegisterStartupScript. None of this works.
With an UpdatePanel you need to use ScriptManager.RegisterStartupScript, like this:
var script = "alert('hi);";
ScriptManager.RegisterStartupScript(this, GetType(), "MyScript", script, true);
You have to remember in an UpdatePanel, you're not sending the whole page back to the client, so the Page versions won't work, because their content never goes anywhere on a partial update. With ScriptManager it actively shoves this into the AJAX response it's sending as a script to execute, so it's behaving a little differently.
Just yesterday I did some research to help a fellow co-worker out and came up with the following solution. It relys on some techniques used in ajax control extenders in the use of registering data items. Since I wanted this to be more of a generic approach, I placed the following code in a script block in the master page just after the scriptmanager object:
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(PageLoadingHandler);
function PageLoadingHandler(sender, args) {
var dataItems = args.get_dataItems();
if ($get('<%=JSBridge.ClientID%>') !== null) {
eval(dataItems['<%=JSBridge.ClientID%>']);
}
}
Then somewhere in the markup of the master page I placed a hiddenfield as in:
asp:HiddenField ID="JSBridge" runat="server"
That's it for the master page. Now, all of my webpages inherit from a base page so I placed a method in the base page as in:
public void InvokeScriptMethod(string methodName, string[] methodArgs)
{
string sArgs = string.Empty;
string delim = string.Empty;
bool isNumeric = false;
int iArg = 0;
if (methodArgs != null && methodArgs.Length > 0)
{
foreach (string arg in methodArgs)
{
isNumeric = int.TryParse(arg, out iArg);
sArgs += delim + ((isNumeric) ? arg : "'" + arg + "'");
delim = ",";
}
}
ScriptManager manager = (ScriptManager)Master.FindControl("ScriptManager1");
if (manager.IsInAsyncPostBack)
{
manager.RegisterDataItem(Master.FindControl("JSBridge"), methodName + "(" + sArgs + ")");
}
}
So, assuming your content is inside an update panel, any button clicks or any event for that matter, on any web page, you can simply do the following:
protected void MyButton_Click(object sender, EventArgs e)
{
//-- Call base page method to invoke javascript call
InvokeScriptMethod("ShowMessage", new string[] { "David", "Whitten", "44" });
}
This is assuming you have a javascript method out there somewhere called "ShowMessage" with the necessary parameters. Obviously, one can specify any method name and any numbers of parameters.
Just wanted to share my findings. Maybe there is even a better way but I find this pretty simple and flexible.
David
ScriptManager.RegisterStartupScript
Registers a startup script block for a
control that is inside an UpdatePanel
by using the ScriptManager control,
and adds the script block to the page.
I have recently upgraded to ASP.NET AJAX application. I have two css files in an ASP.NET theme. I use any one of the css per postback depends upon a condition.
Previously with my non-AJAX ASP.NET application, what I used to do is
protected void Page_PreRender(object sender,
EventArgs eventArgs)
{
ControlCollection headCollection = Header.Controls;
for (int i = 0; i < headCollection.Count; i++)
{
Control temp = headCollection[i];
if (temp is HtmlLink)
{
if (/* condition to loads a.css */)
{
if (((HtmlLink) temp).Href.EndsWith("a.css", true, null))
{
headCollection.RemoveAt(i);
break;
}
}
else
{
if (((HtmlLink) temp).Href.EndsWith("b.css", true, null))
{
headCollection.RemoveAt(i);
break;
}
}
}
}
}
But this piece of code does not work when I migrated to ASP.NET AJAX. I have inspected and found that only the loaded css from the first request persists. i.e., if a.css is loaded on the first request, and then b.css is required to be loaded on a specific postback does not work.
Please comment if you are confused about the problem.
I think that you use UpdatePanel. And second postback is executed in partial rendering mode. You cannot change Head control during such postback. But you can try to register startup script by using ScriptManager: ScriptManager.RegisterStartupScript. And change head tag by javascript.
Thanks Unholy, solved the problem using jQuery and selector.
Just used this:
$("head > link[href$='a.css']").remove();