Asp.Net: User controls, javascripts and javascript libraries - c#

I have a user control which requires Javascript/Jquery per control. It is actually a control to represent data graphically using some javascript library. As a norm all my javascript links are located at the bottom of the page in the Master Page. This implies I cannot write any javascript in the control because it will appear before any of its dependencies. Also, Even if the script is located at the bottom of the page, it only works for the first control. Has anyone encountered similar challenges? I'm looking for a way out. Thanks

You can register client scripts from code-behind.
var clientScriptManager = Page.ClientScript;
if(!clientScriptManager.IsStartupScriptRegistered("a_key")) { //If multiple control instances are present on the page, register scripts only once
RegisterStartupScript(Page.GetType(), "a_key", "<script src=\"/js/a_library.js\"></script>"));
}
RegisterStartupScript will add <script> tag at the very bottom of the page, before </body> closing tag.

I had a similar issue "updating" a legacy site that had tons of inline JS... so I also ran into issues when I moved jQuery and other scripts to the bottom of the page. I solved it by adding a new ContentPlaceHolder to the master page, underneath the other script references:
<asp:ContentPlaceHolder ID="ScriptsPlace" runat="server"></asp:ContentPlaceHolder>
</body>
Then went through the pages and moved all the inline JS into a content block for ScriptsPlace.
<asp:Content ID="Content5" ContentPlaceHolderID="ScriptsPlace" runat="server">
<script>
//some awesome JS
</script>
</asp:Content>
Although this doesn't work for user controls... so I made a somewhat hacky solution that essentially involved putting all of the user controls JS into a div named _jsDiv and then from the code-behind moving that div into the placeholder (I'm not fond of RegisterStartupScript and it's not practical for a lot of code).
Since I did this in multiple controls, I did these steps:
Step 1 - Make a custom user control, ScriptMoverUserControl.cs:
public class ScriptMoverUserControl : System.Web.UI.UserControl
{
protected void Page_PreRender(object sender, EventArgs e)
{
ContentPlaceHolder c = Page.Master.FindControl("ScriptsPlace") as ContentPlaceHolder;
HtmlGenericControl jsDiv = this.FindControl("_jsDiv") as HtmlGenericControl;
if (c != null && jsDiv != null)
{
jsDiv.ID = Guid.NewGuid().ToString(); //change the ID to avoid ID conflicts if more than one control on page is using this.
c.Controls.Add(jsDiv);
}
}
}
Step 2 - Make your user control use this new control class:
It will inherit ScriptMoverUserControl instead of System.Web.UI.UserControl
public partial class SomeGreatControl : ScriptMoverUserControl
Step 3 - Dump your user control scripts in a div named _jsDiv:
<div id="_jsDiv" runat="server">
<script>
//some awesome JS
</script>
</div>
It's been working fine for me, but if anyone knows a better/cleaner way, I'd like to know!

Related

C# access element in master page from logic layer

I have a content page connected to a master page. I can access an element on the master page and modify it directly from the content page .cs file by calling a method on the site master. (this is probably the most standard bug people have in this type of area)
My problem is that I wanted to extend this functionality to update the site master page from an AJAX request as well. The ajax file calls a different page which in turns starts an instance of the logic layer which I use for all the calculations and connections. What I am trying to do is access the sitemaster directly from the logic layer (only a .cs file).
My current code is this:
SiteMaster sm = new SiteMaster();
sm.MyMethod("param1", "param2");
This successfully accesses the method called "MyMethod" in the site master but inside this method I have this code:
mySpan.InnerText = "this is a test";
which doesn't work because I get the "Object refernce not set to an instance of an object...." error. This is because mySpan is NULL. If I call it using this.mySpan.InnerText though, if I hover over "this" then I can see the ID "mySpan".
Does anyone know how I can get around this problem? Every search I have made is regarding people who want to access the elements from the content page which already works for me.
I believe you've got a misunderstanding here. If I understand correctly you've got a page with a MasterPage. On that aspx page you're doing an ajax call (perhaps to a WebService) which does something like:
[WebMethod]
public void UpdateText(string message)
{
var master = new SiteMaster();
master.mySpan.Text = message;
}
There are a couple of things wrong here.
When you use this approach is an aspx page you're updating that Page's master. For example:
public void OnSomeRandomButtonClick(object sender, EventArgs e)
{
((SiteMaster)this.Page.Master).mySpan.Text = "Some Text";
}
What you're doing here is updating the span on the master page before it's being sent to your browser. The other subtly is that you're not creating a new SiteMaster, you're using the Page's existing Master and casting it to a SiteMaster.
There are a couple of reasons you can't do this with ajax:
A webservice doesn't have a MasterPage
By the time you send an ajax request your Master page has already been created and sent to the browser.
So your question becomes how do we update a span in the Master without posting back to the server?
Lets look at the html which is actually on your box, it will look something like this:
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
<h1>This is my Awesome Website</h1>
<span id="mySpan">I'm sure you'll like it</span>
<div>
<p>Page Content</p>
<div>
</body>
</html>
Lets assume that everything here is generated by the master and only the <p>Page Content</p> is your aspx page (There will also be loads of ASP.NET junk added, we'll ignore that for the time being).
What you want to do is update the text in mySpan without posting back to the server. You can do this via the javascript - don't get ajax involved at all!
I'm going to assume you're using jQuery (mostly because I'm more familiar with it that plain old JS). You've got the ID of your span ("mySpan") so the rest is easy:
$('#mySpan').html('This is the updated message');
You can put this in either a click or a page load.
No. You can not simply construct an ASP.NET page and use its state.
ASP.NET pages (and controls and Master pages) are being constructed and initialized from inside the ASP.NET engine based on the Markup provided for them. There is for example no initialization for mySpan inside the codeBehind of your master page, that will be constructed when the code generated based on the Markup is invoked based on a user request.
So you define this in your class:
protected HtmlGenericControl mySpan;
But the ASP.NET engine will compile this markup
<span id="mySpan" style="color:green"></span>
to this code:
this.mySpan = new HtmlGenericControl();
this.mySpan.Style.Add("color", "green);
and that is why you can use this object inside your code.
So if you want to use a property of your Master page from your Business layer, you have so many choices. On of the fastest one to implement is to make your Logic class singleton inside the Session scope, store the value you want to use inside the master page into that singleton object and then read that value from the master Page. This is an example of what you should do, of course it is rough.
class Logic
{
public static Logic Instance
{
get
{
if (HttpContext.Current.Session["LogicInstance"] == null)
HttpContext.Current.Session["LogicInstance"] = new Logic();
return (Logic) HttpContext.Current.Session["LogicInstance"];
}
}
public string TextForSpan {get;}
// The rest of your implementation
}
Instead of the code to assign the inner text, write:
Logic.Instance.TextForSpan = "This is my text";
And inside your master page:
this.mySpan.InnerText = Logic.Instance.TextForSpan;

Update label of MasterPage from content page when there is no ScriptManager in master page

I had not used ScriptManager on my MasterPage as it was not required when I started developing the project, so I have an individual ScriptManager and UpdatePanel for each content page.
In the MasterPage now I've added a label which shows the current system time, and my client wants it to change asynchronously and give a live clock feeling. Can I change the label text in the master page from the content page using Timer? So far I'm unable to achieve this, as I can't use an UpdatePanel also in the MasterPage because there is no ScriptManager tag.
So is there a way to achieve this?
Thanks # Yuriy Rozhovetskiy, From your suggestion i had created a simple javascript and called that javascript on Master page body on load and its working fine. Please find code below which may help to some one who also have same requirement:-
Javascript Function:-
<script type="text/jscript">
function clock(){
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
$('#clock').html(h+"<span class='colon'>:</span>"+m+"<span class='colon'>:</span>"+s);
// $('.colon').fadeTo(1000, .2);
setTimeout(clock, 1000);
}
<body onload="clock()">
Added this to display clock on master page.
<span id="clock" style="font-family:Calibri;color:White;font-weight:bold;font-size:1.3em;"></span>

Asp.Net - UserControl - Inherit from MasterPage

Is it possible for an user control (ascx) to doing something like herit from a MasterPage ?
My point is, in one of my user control (only use by one kind of MasterPage), I would like to use an <asp:content> tag, but I can't, I can only in the page that use that user control...
So, I have to repeat some code into each of the page that use that user control...
Update
For example, each time, I need to add something like that :
<asp:Content ID="cntJavascript" ContentPlaceHolderID="cphJavascript" runat="server">
<script type="text/javascript" src="<%= Url.Content("~/Content/js/UcJs.js") %>"></script>
</asp:Content>
That because, I want all my Js files on the same place in my MasterPage and I only want to add Js that is needed
I think the answer you're looking for is to use Page.RegisterClientScriptInclude in each of the controls that require the supporting Javascript code, using the same key value each time.
In this way, the Javascript file will be included only once no matter how many controls require it.
internal static void RegisterJavascriptInclude(Page page, string includeFile)
{
string key = includeFile.ToLowerInvariant();
if (!page.ClientScript.IsClientScriptIncludeRegistered(key))
page.ClientScript.RegisterClientScriptInclude(key, page.ResolveClientUrl(includeFile));
}
I assume your user controls output the javascript url which is different for each user control. There are a couple ways I can think of to do this.
Add a method in your masterpage like:
void AddJavascriptFile(string url) {
this.ContentPlaceHolder1.Controls.Add(
new LiteralControl(
"<script type='text/javascript' src='" + url + "'></script>"));
}
...then in your user control you can add your javascript like so:
((MyMasterPage)((System.Web.UI.Page)this.Parent).Master).AddJavascriptFile(
"http://example.com/javascript.js");
This might point you in the right direction - I'm still not entirely sure what you're trying to do.

Nested Masterpages and .FindControl

On one site, I'm only using a single level Masterpage and in a page using that master, I can do this.Master.FindControl("controlName") to access the control. Works fine.
However, using the same code on a site with two masterpage levels. MainMaster and SpecificMaster which has MainMaster as its Master.
So on the page which uses SpecificMaster, FindControl is returning null for the object. The only difference I'm seeing is the nesting of the masterpages.
When I set breakpoint and look at page.Master, it's showing SpecificMaster and SpecificMaster is showing MainMaster as its master correctly, but FindControl is still failing.
When I view source in IE, the control is correctly named, no .NET munging going on.
Any thoughts here?
TIA!
When you're nesting master pages, you'll get an extra container "Content" you need to look through.
As a result, if you're trying to use FindControl from a given child page the usual approach is something to the effect of:
Label myLabel = (Label)this.Master.FindControl("myLabel");
myLabel.Text = "Success!";
Since we have a nested master page, with "myLabel" in the child master, this control will be contained within a content control.
So, this changes the code to:
ContentPlaceHolder ph = (ContentPlaceHolder)this.Master.Master.FindControl("yourContentPane");
Label myLabel = (Label)ph.FindControl("myLabel");
myLabel.Text = "Success!";
and in VB.NET
Dim ph As ContentPlaceHolder = DirectCast(Me.Master.Master.FindControl("yourContentPane"), ContentPlaceHolder)
Dim myLabel As Label = DirectCast(ph.FindControl("myLabel"), Label)
myLabel.Text = "Success!"
The content from the child page is loaded into the first master page control, which is subsequently loaded into the grandparent master page.
have you tried this.Master.Master.FindControl("controlname"); ?
It is working as well for cross-page postback:
ContentPlaceHolder ph = (ContentPlaceHolder)PreviousPage.Master.FindControl("ContentPlaceHolder");
string txt = ((TextBox)(ph.FindControl("UserTextBox"))).Text;
I usually do this:
(TextBox)this.Master.FindControl("ContentplaceHolder1").FindControl("TextBox1");
HyperLink hl = (HyperLink)Master.Master.FindControl("HyperLink3");
This is the easiest way to find controls from the nested master pages.
My scenario was as follows. Not sure if this setup is the correct one, but it allowed me to have master-submaster page setup, and be able to find control.
MasterPage-> SubMasterPage -> ASPX page
MasterPage:
<asp:ContentPlaceHolder ID="MasterPageContentPlaceHolder" runat="server">
</asp:ContentPlaceHolder>
SubMasterPage:
<asp:Content ID="ModuleMainContent" ContentPlaceHolderID="MasterPageContentPlaceHolder" runat="server">
<asp:ContentPlaceHolder ID="MainContent" runat="server">
</asp:ContentPlaceHolder>
ASPX.cs:
ContentPlaceHolder MainContent = (ContentPlaceHolder)this.Master.Master.FindControl("MasterPageContentPlaceHolder").FindControl("MainContent");
TextBox var_type = MainContent.FindControl("air") as TextBox;
try this
string txt = ((TextBox)this.Master.FindControl("ContentIDName").FindControl("TextBox1")).Text;

Ajax Control Toolkit HTML Editor Customization Problem?

How to Change default setting for ACT HTML Editor? I want to load editor with for example Selected Bold Button or with rtl direction instead of ltr defaultly.
How can I perform that?
I overrided FillTopToolbar() method to add Custom buttons but I dont Know how to change default settings. as Default ltr is selected I want to change it to rtl.
I edited my answer to correct some things
The HTMLEditor doesn't provide a way to set the state of those buttons using serverside code. Although, on the client, it initializes by using Sys.Application.load Event. If you ran your code after their initializers, but before the user will interact with the UI, you could then set whatever properties you want to set in that event handler.
Here is the code you need to set the bold button and the rtl buttons states. You can take it from here if you want to change the states of other buttons:
// Attach a handler to the load event.
Sys.Application.add_load(myOnLoadLoader);
function myOnLoadLoader() {
//This will run JUST after ALL code that was set to run during the load event has run
window.setTimeout(myOnLoad, 0);
}
function myOnLoad() {
var editor = $find('<% =editor.ClientID %>');
var toolbar = editor.get_changingToolbar();
var toolbarButtons = toolbar.get_buttons();
for (var i = 0; i < toolbarButtons.length; i++) {
var toolbarButton = toolbarButtons[i];
if (toolbarButton instanceof AjaxControlToolkit.HTMLEditor.ToolbarButton.Rtl ||
toolbarButton instanceof AjaxControlToolkit.HTMLEditor.ToolbarButton.Bold) {
toolbarButton.set_activeEditPanel(editor.get_editPanel());
toolbarButton.callMethod();
}
}
}
Sys (and therefore Sys.Application) is a namespace that comes from the ASP.Net AJAX javascript (file(s) that are added thanks to the ScriptManager that you add to your page). If you use this, you need to be sure that this line Sys.Application.add_load(myOnLoad); runs after the ASP.Net AJAX files load. You can do this a couple of ways:
Add this script lower in the page than the scriptManager.
Move your script into a separate JS file, and use the ScriptManager to load it (recommended).
If you move your script into a separate file, you'll notice that var editor = $find('<% =youreditor.ClientID %>'); no longer works. That is because javascript files do not parse out server tags and replace them with the server side value (as aspx pages do). So the part that is a problem here is <% =youreditor.ClientID %>.
To fix that, here is what you do:
Add this to your aspx markup (in the head section):
<script language="javascript">
var myEditorId = '<%= youreditor.ClientID %>';
</script>
So it looks something like this:
<head runat="server">
<script language="javascript">
var myEditorId = '<%= youreditor.ClientID %>';
</script>
<title></title>
</head>
(If you are using a Master Page, you'll just add the script tag below the ScriptManager in your page)
And in your JS file, replace this
var editor = $find('<% =youreditor.ClientID %>');
with this
var editor = $find(myEditorId);
You will need to do this using CSS as the editor control doesn't support rtl natively. The following CSS will set direction to rtl -
div
{
direction:rtl;
}
The default styles for the HTML editor can be found in the Editor.css file.

Categories