I have done a bit of searching, and tried several solutions from different posts, but can't seem to get them to work. The basic idea is this... I am customizing an existing usercontrol that lays dynamically generated data out into a single column table of rows. It then has an "edit" link button that does a postback and rebuilds the table with editable fields. I found some jQuery I am using to convert the table into a 2 row table, broken into multiple columns (much easier then trying to re-engineer the data creation/markup within the c#). When the page loads the first time, it works perfectly. However, when I click the "edit" linkbutton, it properly does the postback, but the jQuery doesn't fire. I have tried several configurations to no avail. Here is the jQuery:
private void RegisterScripts()
{
StringBuilder sbScript = new StringBuilder();
sbScript.Append("\tvar tables = $('table[id*=\"tblAttribute\"]');\n");
sbScript.Append("\tfor (var i = 0; i < tables.length; i++) {\n");
sbScript.Append("\t\tvar newTable = document.createElement('table');\n");
sbScript.Append("\t\tvar columns = 2;\n");
sbScript.Append("\t\tfor(var c = 0; c < columns; c++) {\n");
sbScript.Append("\t\t\tnewTable.insertRow(c);\n");
sbScript.Append("\t\t\tfor(var r = 1; r < tables[i].rows.length ; r++) {\n");
sbScript.Append("\t\t\t\tnewTable.rows[c].insertCell(r-1);\n");
sbScript.Append("\t\t\t\tnewTable.rows[c].cells[r-1].innerHTML = tables[i].rows[r].cells[c].innerHTML;\n");
sbScript.Append("\t\t\t\tnewTable.rows[c].cells[r-1].className = tables[i].rows[r].cells[c].className;\n");
sbScript.Append("\t\t\t\ttables[i].rows[r].style.display = 'none';\n");
sbScript.Append("\t\t\t}\n");
sbScript.Append("\t\t}\n");
sbScript.Append("\t\tnewTable.className = tables[i].className;\n");
sbScript.Append("\t\ttables[i].parentNode.appendChild(newTable);\n");
sbScript.Append("\t}\n");
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "RowsToColumnsScript", sbScript.ToString(), true);
}
Here is the call within the Page_Load cycle:
protected void Page_Load(object sender, EventArgs e)
{
RegisterScripts();
// Other Stuff //
}
I have also tried replacing the RegisterClientScriptBlock() with a RegisterStartupScript() and got the same results. What am I missing?
EDIT 2:
Here is the script as it is being added to the client page. I copied right out of the page source (minus my abbreviation):
<script type="text/javascript">
//<![CDATA[
var tables = $('table[id*="tblAttribute"]');
for (var i = 0; i < tables.length; i++) {
var newTable = document.createElement('table');
var columns = 2;
for(var c = 0; c < columns; c++) {
newTable.insertRow(c);
for(var r = 1; r < tables[i].rows.length ; r++) {
newTable.rows[c].insertCell(r-1);
newTable.rows[c].cells[r-1].innerHTML = tables[i].rows[r].cells[c].innerHTML;
newTable.rows[c].cells[r-1].className = tables[i].rows[r].cells[c].className;
tables[i].rows[r].style.display = 'none';
}
}
newTable.className = tables[i].className;
tables[i].parentNode.appendChild(newTable);
}
// Other js registered from other usercontrols
</script>
try wrapping your jquery codes inside the ready function
$(function(){
// place your code here
});
.
<script type="text/javascript">
$(function(){
//<![CDATA[
var tables = $('table[id*="tblAttribute"]');
for (var i = 0; i < tables.length; i++) {
var newTable = document.createElement('table');
var columns = 2;
for(var c = 0; c < columns; c++) {
newTable.insertRow(c);
for(var r = 1; r < tables[i].rows.length ; r++) {
newTable.rows[c].insertCell(r-1);
newTable.rows[c].cells[r-1].innerHTML = tables[i].rows[r].cells[c].innerHTML;
newTable.rows[c].cells[r-1].className = tables[i].rows[r].cells[c].className;
tables[i].rows[r].style.display = 'none';
}
}
newTable.className = tables[i].className;
tables[i].parentNode.appendChild(newTable);
}
// Other js registered from other usercontrols
});
</script>
First off, why don't you do something this...
string script = #"var tables = $('table[id*=\'tblAttribute\']');
for (var i = 0; i < tables.length; i++) {
//rest of your script
";
This will make your script much easier to read and make changes to. White space will be respected. So you don't need the \n and \t characters.
After that, view the resulting HTML in your browser and make sure it made it on there properly. Use your browser's debug tools to execute the script and see if any errors result.
Or just embed the script on the .aspx page instead of adding it from the Code Behind.
Your javascript is expecting your tables to have an id that contains (*=) the string tblAttribute. It would appear that the javascript that creates the newly editable table does not add an id attribute to it. So, while your code-behind registers the script and it executes on each postback, you aren't seeing it because your newly editable table doesn't match the criteria $('table[id*="tblAttribute"]').
You will need to set up an id for the newly created table, but I can't guarantee that this methodology will work (depending on how your usercontrol builds up the various tables you may already have on the screen):
newTable.setAttribute("id", "tblAttribute" + i);
Obviously, id needs to be unique so simply adding your iterator to tblAttribute might create conflicts, but this should get you pointed in the right direction.
EDIT
Seeing your updated comment relating to the UpdatePanel, you might find this answer helpful:
Registering scripts with an UpdatePanel
Related
so i have an HTML table with dynamically added rows and ASP.NET text boxes. I have the rows and controls re-instantiated on page_load if the viewstate[dataonpage] = true, and I'm declaring it as true in the method that adds the rows and controls. (I need them to persist on other postbacks)
The problem is that I'm now I've added a CLEAR button that removes all of the html rows (excluding the headers) when it's clicked, and for some reason on button click it gets an index error, or if using Try/Catch it only removes half of the rows (every other row). I believe the problem is something to do with that the viewstate[dataonpage] is still "true", and the data is being re-added on page load. If i add viewstate["dataonpage"] = "false" into the clear button method, the same happens but at least this way on the second click it removes the second half of the rows.
I understand this happens because the button event handler isn't fired until after the page_load which is why it doesn't work on the first click. But what I don't fully understand is why even without this my clear button code doesn't clear all of the rows in the first place.
Any help on understanding why it doesn't work, and a work around will be greatly appreciated!
protected void Page_Load(object sender, EventArgs e)
{
if (Convert.ToString(ViewState["DataOnPage"]) == "true")
{
Getmarketdata();
}
}
protected void Getdatabtn_Click(object sender, EventArgs e)
{
ViewState["DataOnPage"] = "true";
Getmarketdata();
}
Below is method that creates adds table rows and controls:
public void Getmarketdata()
{
String url = "https://api.rightmove.co.uk/api/rent/find?index=0&sortType=1&maxDaysSinceAdded=" + Dayssinceuploadtext.Text + "&locationIdentifier=OUTCODE%5e" + Outcodetext.Text + "&apiApplication=IPAD";
Response.Write(url);
using (var webclient = new WebClient())
{
String Rawjson = webclient.DownloadString(url);
ViewState["VSMarketDataJSONString"] = Rawjson;
dynamic dobj = JsonConvert.DeserializeObject<dynamic>(Rawjson);
int NoOfHouses = dobj["properties"].Count;
Response.Write("<br />" + NoOfHouses);
for (int i = 0; i < NoOfHouses; i++)
{
System.Web.UI.HtmlControls.HtmlTableRow tRow = new System.Web.UI.HtmlControls.HtmlTableRow();
GeneratorTable.Rows.Add(tRow);
String RMlink = String.Format("https://www.rightmove.co.uk/property-to-rent/property-" + dobj["properties"][i]["identifier"].ToString()) + ".html";
HyperLink hypLink = new HyperLink();
hypLink.Text = dobj["properties"][i]["identifier"].ToString();
hypLink.Target = "_blank";
hypLink.NavigateUrl = RMlink;
using (System.Web.UI.HtmlControls.HtmlTableCell tb1 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
tRow.Cells.Add(tb1);
tb1.Controls.Add(hypLink);
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb2 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbEPCe = new TextBox();
tRow.Cells.Add(tb2);
tb2.Controls.Add(tbEPCe);
String txtboxID = (("EPCETxtBox") + i);
tbEPCe.ID = txtboxID;
tbEPCe.Style.Add("background", "none"); tbEPCe.Style.Add("border", "1px solid black"); tbEPCe.Style.Add("border-radius", "2px");
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb3 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbEPCp = new TextBox();
tRow.Cells.Add(tb3);
tb3.Controls.Add(tbEPCp);
String txtboxID = (("EPCPTxtBox") + i);
tbEPCp.ID = txtboxID;
tbEPCp.Style.Add("background", "none"); tbEPCp.Style.Add("border", "1px solid black"); tbEPCp.Style.Add("border-radius", "2px");
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb4 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbBbl = new TextBox();
tRow.Cells.Add(tb4);
tb4.Controls.Add(tbBbl);
String txtboxID = (("BblTxtBox") + i);
tbBbl.ID = txtboxID;
tbBbl.Style.Add("background", "none"); tbBbl.Style.Add("border", "1px solid black"); tbBbl.Style.Add("border-radius", "2px");
}
}
}
}
Below is clear table rows method: (this is the one that isn't working)
public void ClearTableRows()
{
System.Web.UI.HtmlControls.HtmlTable Htmlgeneratortable = ((System.Web.UI.HtmlControls.HtmlTable)GeneratorTable);
int NoOfRows = Htmlgeneratortable.Rows.Count;
for (int j = 1; j < NoOfRows; j++)
{
try
{
Htmlgeneratortable.Rows.RemoveAt(j);
}
catch
{ }
}
}
I'm going to explain what's going on as you have the code written now; I don't have faith in my ability to provide an answer including the exact code changes to be made, so here is what is wrong with your current approach:
Your table, GeneratorTable exists for all clients. That doesn't mean every time someone navigates to your website a table is generated, it means that there is one table, and every client that logs in is getting that one table.
So if you add rows to it for one client, then send the table to another client, both clients will see the same table (with the rows added).
The problem is that emptying out a table is logic that has nothing to do with your back-end server. There's no reason for your server to be handling emptying a table, your server should only handle page navigations and AJAX calls pretty much, it shouldn't be changing how the webpage looks, because the server can only respond to each client one time.
What's the point in responding to a client with GeneratorTable and then updating GeneratorTable on the server? The client will never see the updates made to the table unless they're resent from the server.
You stated that you are new to this and need to learn about JS and client-side, this exercise should serve as an example of why you need to put certain code on the front-end and some code on the back-end, as there isn't really an elegeant way to do what you're looking to do with just the server.
I'm using Ext.net MessageBox confirm to get confirmation from user,
whenever they want to update a record they click yes on message box. But the message box only show one time when the for loop is done. Here is the code:
foreach (DataRow row in dataTable.Rows)
{
foreach (DataColumn col in dataTable.Columns)
{
var item = row[col];
// some code....
RM.RegisterClientScriptBlock("confirm", "showResult();");
if (hdfIsAgree.Text == "1")
{
hr_TimeSheetCodeServices.Update(item);
}
}
}
Here the client-side code:
<script>
var showResult = function () {
Ext.Msg.confirm('Update', 'Do you want to update?', function (btn) {
if (btn == "yes") {
hdfIsAgree.setValue('1');
} else {
hdfIsAgree.setValue('0');
}
});
}
</script>
<ext:Hidden runat="server" ID="hdfIsAgree" />
First parameter in RegisterClientScriptBlock method is key, which means, that if you invoke this method second time with the same key, your first script will be updated; you will not create two records. Something like this:
for (int i = 0; i < 3; i++)
RM.RegisterClientScriptBlock("confirm", "showResult();");
int count = RM.ClientScriptBlockBag.Count; // 1
will give you only one registered script. Change the key to something unique, to register multiple scripts:
for (int i = 0; i < 3; i++)
RM.RegisterClientScriptBlock($"confirm-{i}", "showResult();");
int count = RM.ClientScriptBlockBag.Count; // 3
Using RM.RegisterClientScriptBlock("confirm", "showResult();"); in for loop will override the previous request with current(latest) request.
Hence this works only for the last value of the for loop, and hence it shows the window only once, as all the previous requests were overridden.
Try running the loop on client side, save the confirmation value in any parameter and then send all values simultaneously(the parameter) on server side with particular id to recognize each value and then apply your logic.
I need to add asp image button inside a javascript function. Actually I need to create image buttons inside a html table dynamically. Here is my code. I need to add the image button where I have mentioned as "--- Here I need to add the image button ---"
function tableSeats() {
document.write("<table align=center>");
for (var i = 1; i <= 5; i++) {
document.write("<tr height=30px>");
for (var r = 1; r <= 10; r++) {
document.write("<td width=30px>");
document.write(--- Here I need to add the image button ---);
document.write("</td>");
}
document.write("</tr>");
}
document.write("</table>");
}
If this is not possible, please tell me a way to do it using c#.net
Thanks in advance...
I believe by using javaScript you are not able to create any kind of server controls like image button and all. But you can do some kind of things like <img /> And using jQuery you can invoke the client click and using ajax you can easily communicate with server.
function tableSeats() {
document.write("<table align=center>");
for (var i = 1; i <= 5; i++) {
document.write("<tr height=30px>");
for (var r = 1; r <= 10; r++) {
document.write("<td width=30px>");
document.write("<img src='Your picture path' alt='' />");
document.write("</td>");
}
document.write("</tr>");
}
document.write("</table>");
}
Updated the code
Check this for jQuery click
http://api.jquery.com/click/
Say my WebBrowser1 downloaded a page that has a following line:
<span customattr="hamolulu">hamolulu</span>
It is inside of a td tag, inside of big table, inside of iFrame, inside of div etc.
How to I click this thing using c# ?
I need to do something following:
int i = 0;
for (i = 0; i <= 500; i++)
{
if (webBrowser1.Document.GetElementsByTagName("span")[i].GetAttribute("customattr") == "hamolulu")
{
webBrowser1.Document.GetElementsByTagName("span")[i].InvokeMember("click");
break;
}//end if
}// end for
but for some reason it does not work this way, so I'm thinking if it's possible to check the innerHTML of the span, or innerText?
I tried both:
webBrowser1.Document.GetElementsByTagName("span").InnerHTML == "hamolulu"
webBrowser1.Document.GetElementsByTagName("span").InnerText == "hamolulu"
And I failed both times.
Update:
I just noticed that the line is actually like this:
<span customattr="hamolulu"><a>hamolulu</a></span>
So I wrote a simple function:
int i = 0;
for (i = 0; i <= webBrowser1.Document.GetElementsByTagName("a").Count - 1; i++)
{
log(i.ToString()+ " : " +webBrowser1.Document.GetElementsByTagName("a")[i].InnerHtml);
} //log(string) is a custom function that saves all strings to a file log.txt
And what I've seen is that this link (and span) does not show up in my log.
In other words, getElementsByTagName("span") and getElementsByTagName("a") doesn't see the item. My guess is that it is because of iFrame. Do you have any thoughts about this?
another solution using no js (because you don't own the "page")
since it is inside an iframe then you should search within that iframe
HtmlElementCollection iframes = WebBrowser1.Document.GetElementsByTagName("iframe");
HtmlElement iframe = iframes(0 /* iframe index */); //
HtmlElementCollection spans = iframe.Document.GetElementsByTagName("span");
for (i = 0; i < spans.Count; i++) {
HtmlElement span = spans(i);
if (span.GetAttribute("customAttr") == "customAttrValue") {
string onclick = span.Children(0).GetAttribute("onclick"); //span.Children(0) should return the <a>
WebBrowser1.Document.InvokeScript(onclick);
}
}
Unless I am missing something...
<span id="hamolulu">hamolulu</span>
Then when you want to change it...
document.getElementById('hamolulu').innerHTML="<h1>Test!</h1>";
If you set up your span as an HTML server control:
<span runat="server" id="myspan" customattribute="customvalue">hello world</span>
Then you can register an event handler on page load:
protected void Page_Load(object sender, EventArgs e)
{
myspan.Attributes["onclick"] = "this.innerText='hamolulu'";
}
Another way to do it is using Page Methods which would call a page C# method using AJAX.
you can make it simpler by using JavaScript and invoking it from c# whenever you need to.
WebBrowser1 .Document .InvokeScript ("functionName")
javascript:
function functionName(){
var spans = document.getElementsByTagName('SPAN');
for (i = 0; i < spans.length; i++)
{
var span = spans[i];
if (span.getAttribute("customattr") == "hamolulu")
{
eval(span.getAttribute('onclick')); // .. be careful "span" has no "click()" method. you should use the onlick attribute if available
break;
}//end if
}// end for
}
On one web user control
public void displayFindingSection(int sectionsid,string text,string head)
{
SectionHeading.Text = head;
DataSet totImgs;
totImgs = objGetBaseCase.GetFindingsNewerImages(sectionsid);
FindingViewerlist.DataSource = totImgs;
DataBind();
SectionText.Text = text;
}
On other web user control
public void DisplayFindingsViewer(CipCaseWorkflowItem2 item)
{
FindingViewerDisplay.Visible = true;
ImageAndSimpleViewer.Visible = false;
objGetBaseCase.GetFindingsImages((Convert.ToInt32(Session["CaseId"])), item.ItemId);
FindingsViewerNew = objGetBaseCase.GetFindingViewerNewElementDetails(item.ItemId);
for (int i = 0; i < FindingsViewerNew.Count; i++)
{
FindingViwerDisplay uc = (FindingViwerDisplay)LoadControl("FindingViwerDisplay.ascx");
FindingPlaceholder.Controls.Add(uc);
uc.displayFindingSection(Convert.ToInt32(FindingsViewerNew[i].Index), FindingsViewerNew[i].Text, FindingsViewerNew[i].Title);
}
}
I am adding the all the image in user control and displaying the image, but when i am using the above code, web user control is also adding every time and one image is showing in in control what i want is all images should show in only one user control.. sectionsid is getting the image id from the database. I think prob with for loop but i am unable to solve it.. help me it that
Might be it is happening u have defined it inside the loop
FindingViwerDisplay uc = (FindingViwerDisplay)LoadControl("FindingViwerDisplay.ascx");
FindingPlaceholder.Controls.Add(uc);
On Each loop you are adding uc and calling displayFindingSection whcich ofcouse add 1 image than loop go back add a new control again and than add one image it will go on till your loop completion so add control once before loop and call just displayFindingSection in loop..
Do this,
FindingViwerDisplay uc = (FindingViwerDisplay)LoadControl("FindingViwerDisplay.ascx");
FindingPlaceholder.Controls.Add(uc);
//define here a dataTabel with three columns let say u have datatable dt
for (int i = 0; i < FindingsViewerNew.Count; i++)
{
dt.Rows.Add(Convert.ToInt32(FindingsViewerNew[i].Index), FindingsViewerNew[i].Text, FindingsViewerNew[i].Title);
}
uc.displayFindingSection(dt);
Then work out on that dt in displayFindingSection
Sorry if i am wrong...