Modify <head> from ASP.NET AJAX - c#

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();

Related

ASP.Net Page Lifecycle and dynamic controls

I'm am a little bit stuck in the ASP.Net's page lifecycle. This is my first ASP.Net project after many years of doing React so I might be missing something;)
Simplified code:
protected void Page_Load(object sender, EventArgs e)
{
BuildView();
}
private void BuildView()
{
switch (pageViewMode.Value)
{
case "Overview": BuildOverview(); break;
case "Runs": BuildRunsOverview(); break;
}
}
private void BuildOverview()
{
var tilesContainer = new TilesContainer();
tilesContainer.OnTileClicked += (InfoTile targetTile) =>
{
pageViewMode.Value = targetTile.Value;
BuildView();
};
rootElement.Controls.Add(tilesContainer);
}
The problem is that the "OnTileClicked" event works only on the first load and not after the postback. I believe it has something to do with the page lifecycle and registering the events after the Control events ( https://learn.microsoft.com/en-us/previous-versions/aspnet/ms178472(v=vs.100)?redirectedfrom=MSDN ).
If it is really the case, how do I then dynamically build pages from the code behind? Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?
'Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?'
Answer is: yes.
You don't dynamically build pages from code behind - at least its not that well supported in asp.net pages.
In your case you need the TilesContainer on every postback and attach the event handler to it, else the event won't be called. So it would be easier to put all your controls in the markup (.aspx) and just set them to Visible = false/true depending on your code. Controls you set to Visible = false won't be rendered on the client side, so at least no overhead there.
If you use custom-controls (I assume your TilesContainer is a custom-control), then you need to implement the Visible-property the right way, e.g. if your TilesContainers main control is a Panel, override Visible and set the value there:
public override bool Visible
{
get { return base.Visible; }
// set all neccessary controls visibility here
set { this.pnlMain.Visible = base.Visible = value; }
}

How can I disallow WebView to open links on the browser in WinRT( target=_blank links )?

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>

How to remove ScriptManager from SharePoint Page via C#

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 );
}

Dynamically change MasterPage and ContentPlaceHolderID of asp content tag?

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.

Call javascript function in server side

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.

Categories

Resources