I have a user control that has a text box and a button. I have repeated this user control n times using for loop in my main.aspx page
for (int i = 0; i < 5; i++)
{
testcontrol newControl = (testcontrol)LoadControl("~/testcontrol.ascx");
newControl.ID = "myControl-" + i.ToString();
HtmlButton btnGenerateReport =newControl.FindControl("btnGenerateReport") as HtmlButton;
btnGenerateReport.ID = "button_generate-" + i.ToString();
btnGenerateReport.ServerClick += BtnGenerateReport_Click;
}
my testcontrol.ascx looks like this
<div class="row">
<div class="col-md-8 blockMe">
<input runat="server" class="form-control" />
</div>
<div class="col-md-4">
<button type = "button" id="btnGenerateReport" class="btn btn-default btnGenerateReport" aria-label="Left Align" runat="server">
<span class="fa fa-cog" runat="server" id="gennerateReportSpan" aria-hidden="true"></span>
</button>
<br />
</div>
On ButtonClick I am trying to show spinning icon so that i can do some background process. I have subscribed to the HtmlButton OnServerClick method in mainpage.aspx
private async void BtnGenerateReport_Click(object sender, EventArgs e)
{
System.Web.UI.HtmlControls.HtmlControl tt = ((System.Web.UI.Control)sender).Parent.FindControl("gennerateReportSpan") as System.Web.UI.HtmlControls.HtmlControl;
tt.Attributes.Clear();
tt.Attributes.Add("class", "fas fa-spinner fa-spin");
Task taskA = Task.Factory.StartNew(() =>
{
Task.Delay(5000).Wait();
}).ContinueWith((x) =>
{
tt = ((System.Web.UI.Control)sender).Parent.FindControl("gennerateReportSpan") as System.Web.UI.HtmlControls.HtmlControl;
tt.Attributes.Add("class", "fa fa-cog");
});
}
I am trying to achieve the spinning effect by changing the attributes of the htmlelement. I am able to set new class i.e spinning but I am not able to reset it once the task is done. Is there any better way to do it ?
This way you can never make it back to non-spinning because when your thread is finish you don't have no way to inform the client the way you make it.
I can think and suggest two ways
first way - the ajax call
Ok, this is the most common way, to make an ajax call to a aspx file and wait for the return. You can find many example for this...
second way - your way
You make your calculations call inside the page with a flush just before the long running function - when the calculations finish the page will continue to render the rest of it and change back the icon.
Here is a working example.
On aspx page
<form id="form1" runat="server">
<div>
<h1 id="changeme" style="color:blue">Header Test</h1>
</div>
</form>
<%Response.Flush();%>
<%GenerateReport();%>
<script>
document.getElementById("changeme").style.color = "red";
</script>
on page behind
public void GenerateReport()
{
// simulate the delay
Thread.Sleep(5000);
}
Last words
avoid the async on this web pages, is not desktop programming that you don't want to hold the render of the user interface. Here the user is on another computer and the call to you its a new thread by default, a thread to another computer. So all that is done with out any extra new thread creation.
Related
So I've been creating different explicit waits after doing tasks like updating contact info, submitting forms, etc because every time I do this, a loading overlay will pop up for a few seconds. I just realized it's the same exact loading code on each page. Instead of waiting for results to show on the page or something else to appear, I just want to wait for this loading overlay to hide. Is it possible to write some reusable code to wait for this overlay to hide before proceeding?
The overlay code:
<div id="progress" style="display: none;" role="status" aria-hidden="true">
<div id="divOverlay"></div>
<div id="divLoading">
<img id="loading" src="/loading.gif" />
</div>
</div>
When the loading display is present, the div attributes will become:
<div id="progress" style="display: block;" role="status" aria-hidden="false">
<div id="divOverlay"></div>
<div id="divLoading">
<img id="loading" src="/loading.gif" />
</div>
</div>
If your version of .net supports ExpectedConditions, you can straight away use the below solution
var wait = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(10));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.XPath("//div[#id='progress']")));
If it does not support then the below solution may work.
//returns as soon as element is not visible, or throws WebDriverTimeoutException
protected void WaitUntilElementNotVisible(By searchElementBy, int timeoutInSeconds)
{
new WebDriverWait(_driver, TimeSpan.FromSeconds(timeoutInSeconds))
.Until(drv => !IsElementVisible(searchElementBy));
}
private bool IsElementVisible(By searchElementBy)
{
try
{
return _driver.FindElement(searchElementBy).Displayed;
}
catch (NoSuchElementException)
{
return false;
}
}
and use it like this :
WaitUntilElementNotVisible(By.Id("progress"), 10);
I am having difficulty adding a Button control to a specific spot in an .aspx page. I think I can create the button, but I don't know how to add it to the page.
Code is as follows:
<%
var cpus = productItems.FindAll(t => t.Type == "cpu");
foreach (var cpu in cpus)
{ %>
<div class="row product cpu">
<div class="col-md-3">
<img class="center-block" src="Content/images/processor.jpg" />
<span class="price"><%= cpu.Price %></span>
<span class="addtocart">
<% Button b = new Button();
b.ID = "Button" + cpu.ID;
b.CommandArgument = cpu.ID.ToString();
b.Text = "Add to Cart";
b.OnClientClick = "Addtocart_Click";
%>
</span>
<br />
</div>
</div>
<% } %>
I can also create the Button as part of the productItems collection, but still presents the problem of how to render the button on the page.
I'm sure there's a better way to do this, just not sure where to look.
Thanks in advance.
In WebForms, you can make use of listing controls that have a concept of a DataSource (Some listing of objects) and a template which renders how each of those objects appear. In general, you should use these whenever you have a list of items that you want to render on the site.
In this particular case, you will probably want to make use of the ListView control. This allows you to define a layout template and an item template.
Your aspx markup would look like the following:
<asp:ListView ID="lvCpus" OnItemDataBound="lvCpus_ItemDataBound" runat="server">
<LayoutTemplate>
<div class="row product cpu">
<div runat="server" id="itemPlaceholder"></div>
</div>
</LayoutTemplate>
<ItemTemplate>
<div runat="server" class="col-md-3">
<img class="center-block" src="Content/images/processor.jpg" />
<span class="price"><%# Eval("Price") %></span>
<span class="addtocart">
<asp:Button ID="addToCart" Text="Add To Cart" runat="server" />
</span>
</div>
</ItemTemplate>
</asp:ListView>
This defines a ListView control and creates a LayoutTemplate that matches your container. Internally it has a div that must have the id itemPlaceholder which is used to populate the various items that are bound to this control.
The ItemTemplate portion defines what you expect each individual item to look like. In this case, it's a column that contains a CPU for purchase.
Notice that the button is defined as a regular ASP Web Control, but none of the dynamic data is set. That's because if you try to assign a property like CommandArgument with an evaluated item, the server tag will not be well-formed and you'll get the YSOD. To work around this, you need to specify an OnItemDataBound function for the ListView that is called when you bind data to this Web Control. In my case, it's called lvCpus_ItemDataBound.
The ItemDataBound method in this case will look like the following:
protected void lvCpus_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var cpu = e.Item.DataItem as Cpu;
if (cpu == null)
{
return;
}
var btn = e.Item.FindControl("addToCart") as Button;
if (btn == null)
{
return;
}
btn.CommandArgument = cpu.Id.ToString();
// Set other server-side properties required from code.
}
}
When you bind a data source, it has 0 or more items in it. For every item in the data source, this method is called and will let you specify server-side appropriate values that can't be expressed directly in the template.
In our case, we specify the CommandArgument from the Cpu class, but other values could be specified as well.
Finally, we need to make sure we can fill the list view with data. So in Page_Load perhaps, we can bind data to this ListView like the following:
protected void Page_Load(object sender, EventArgs e)
{
lvCpus.DataSource = GetCpus();
lvCpus.DataBind();
}
private IEnumerable<Cpu> GetCpus()
{
yield return new Cpu { Id = 1, Price = 5 };
yield return new Cpu { Id = 2, Price = 10 };
yield return new Cpu { Id = 3, Price = 15 };
yield return new Cpu { Id = 4, Price = 15 };
yield return new Cpu { Id = 5, Price = 20 };
}
We first set the List View's data source to the CPU list that you have and then call the DataBind() method on the ListView. This triggers the OnItemDataBound function to begin filling in the data, and at the end you are left with, in this case, 5 CPUs displayed on the site.
I have added span as runatserver and add control(i.e. button) into it
Try below code,
<div class="row product cpu">
<div class="col-md-3">
<img class="center-block" src="Content/images/processor.jpg" />
<span class="price"><%= cpu.Price %></span>
<span class="addtocart" id="buttonContainer" runat="server">
<% Button b = new Button();
b.ID = "Button" + cpu.ID;
b.CommandArgument = cpu.ID.ToString();
b.Text = "Add to Cart";
b.OnClientClick = "Addtocart_Click";
buttonContainer.Controls.Add(b);
%>
</span>
<br />
</div>
</div>
In your situation, since you are using inline code, the below approach is the most optimal.
I have place a placeholder control to your original markup, that has an id of placeHolder1 at the location where the button needs to appear. This is what gives you total control over where the button will appear in the rendered page.
You need to use a placeholder control, which is a standard ASP.Net
control for adding controls dynamically at run-time.
You place the placeholder control at the location in your page markup where you would like the dynamically created button to appear.
Then, just use the line of code placeHolder1.Controls.Add(b); to add your button control.
Dynamically add a button at a certain location in your page
<%
var cpus = productItems.FindAll(t => t.Type == "cpu");
foreach (var cpu in cpus)
{ %>
<div class="row product cpu">
<div class="col-md-3">
<img class="center-block" src="Content/images/processor.jpg" />
<span class="price"><%= cpu.Price %></span>
<span class="addtocart">
<% Button b = new Button();
b.ID = "Button" + cpu.ID;
b.CommandArgument = cpu.ID.ToString();
b.Text = "Add to Cart";
b.OnClientClick = "Addtocart_Click";
placeHolder1.Controls.Add(b);
%>
<asp:PlaceHolder ID="placeHolder1" runat="server"></asp:PlaceHolder>
</span>
<br />
</div>
</div>
<% } %>
I know its a rudimentary questions, but I am out of practice on webforms. I am using Stripe.js for the first time, and want to use it in conjunction with stripe.net to process the client side. Here is the client code:
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="StripePage.aspx.cs" Inherits="StripePage.StripePage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
// This identifies your website in the createToken call below
// You need to put your real publish key here.
Stripe.setPublishableKey('pk_test_1nDJ3hA1Mv2Sy9bUoYcBMXmm');
// ...
// I am using jquery to process the payment. It knows what form to
// process it on based on the name 'payment-form'
jQuery(function ($) {
//payment submission
$('#payment-form').submit(function (event) {
var $form = $(this);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
Stripe.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
//if there is a error, it is displayed on the page if there was
//no error this is where it gets sent to the server.
var stripeResponseHandler = function (status, response) {
var $form = $('#payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// token contains id, last4, and card type
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
});
</script>
<form method="POST" id="paymentForm" runat="server">
<span class="payment-errors" runat="server"></span>
<div class="form-row">
<label>
<span>Card Number</span>
<br />
<input id="number" type="text" data-stripe="number" clientidmode="Static" />
<input type="text" size="20" data-stripe="number" runat="server" />
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<br />
<input type="text" size="4" data-stripe="cvc" runat="server" />
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<br />
<input type="text" size="2" data-stripe="exp-month" runat="server" />
</label>
<br />
<input type="text" size="4" data-stripe="exp-year" runat="server" />
</div>
<asp:Button ID="submit" ClientIDMode="Static" runat="server" Text="SubmitPayment" OnClick="submit_Click" />
</form>
</asp:Content>
The last call in JS creates a JSON object that I want to know how to get to on the C# page on the button click:
protected void submit_Click(object sender, EventArgs e)
{
....
}
I am wanting to do the javascript implementation to avoid having to do PCI compliance. Am I approaching this incorrectly? Should it be all Stripe.net to process everything, and skip the js entirely? Or if this is right, how can I get the form post data in the button click event?
Thanks for the tips in the comments. After much perusal of the internet and hair pulling, I walked away for a bit and came back with this solution.
Made the button just a standard html input (not the asp:Button)
Got the posted back information that was being sent via the JavaScript in the Page_Load event like so
Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
StripeConfiguration.SetApiKey("[API Secret Key");
NameValueCollection nvc = Request.Form;
string amount = nvc["amount"];
var centsArray = amount.Split('.');
int dollarsInCents = Convert.ToInt32(centsArray[0]) * 100;
int remainingCents = Convert.ToInt32(centsArray[1]);
string tokenId = nvc["stripeToken"];
var tokenService = new StripeTokenService();
StripeToken stripeToken = tokenService.Get(tokenId);
var myCharge = new StripeChargeCreateOptions
{
TokenId = tokenId,
AmountInCents = dollarsInCents + remainingCents,
Currency = "usd"
};
var chargeService = new StripeChargeService();
StripeCharge stripeCharge = chargeService.Create(myCharge);
}
}
Seems like using the NameValueCollection (which lives in System.Collections.Specialized namespace) gave me the ability to grab what I needed from the Request.Form by pulling it out via variable name. Since I new the variable names, it was simply a matter of grabbing them and then following the Stripe .NET library documentation to get the token and process the payment.
I want to just post a comment to the answer, but I'm not allowed to yet. So, this isn't really an answer, more of a response to the OP's own findings.
I'm doing the exact same thing, using Stripe.js. I can simply grab stripeToken using Request.Form, and get all other non-c/c-related fields the usual way in the code-behind (e.g. int Total = PaymentTotal.Value;); but what I'm noting isn't so much about grabbing the data, it's that you have to handle it on Page_Load (or during some later point in the page life cycle), because the Submit button's onclick event is never actually fired, since the form is actually submitted using js and not the button.
So, being all literal and kinda answering the original question, you can't actually get the data in the button click event, w/o firing that event manually on postback (I'm surmising).
Hopefully save someone else some time figuring that out if they come here looking to make Stripe and .NET work together.
I have a < div> with < asp:ListView>- with results of searching. I want to hide this div, and show it when ListView will be full (or better - when this part of code will be completed)
lvSearchResult.DataSource = getSearchResult();
lvSearchResult.DataBind();
How can I do this? Meanwhile when this < div> with listview will be not visible, I want to show another div with information "Loading". When ListView will be ready, < div> with results will show up, and < div> with "loading" will be hidden.
If you are using an Update panel you can acheive with code similar to below. This will show a modal panel over the page while it is updating.
You could modify the start and end request methods to also hide / show the div containing the list view
note this uses jQuery.
<div id="workingDialog" style="display: none" title="Please wait">
<p>
Loading Data
</p>
</div>
<div id="listViewDiv" style="display:none">
//List View
</div>
<script>
var _workingDialog;
//Page Load event
function pageLoad(sender, args) {
//Register events
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequest);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
_workingDialog = $('#workingDialog');
}
function beginRequest(sender, args) {
$(_workingDialog).dialog({ modal: true });
$('#listViewdiv').hide();
}
function endRequest(sender, args) {
$(_workingDialog).dialog('close');
$('#listViewdiv').show();
}
</script>
http://wraithnath.blogspot.co.uk/2011/12/showing-modal-dialog-while-page-is.html
Declare your divs like:
<div id="searchResultDiv" runat="server" visible="false">...</div>
<div id="loadingDiv" runat="server">...</div>
The runat="server" makes them accessible in your asp.net code behind.
Then in your code you can change their properties, in this instance change the Visibility:
lvSearchResult.DataSource = getSearchResult();
lvSearchResult.DataBind();
searchResultDiv.Visible = true;
loadingDiv.Visible = false;
depending on your list you can make a method that ads 2 css classes, one for when the list is full and one for the other case. so in one css you will have display: none; and in the other display: inline-block;
From my understanding you could use css? and set display:none and when your condition is met change display to block/show?
Hope this helps: http://webdesign.about.com/od/dhtml/a/aa101507.htm
add runat="server" to the div, then you can set visible = false/true
Use runat="server" attribute in your div
Then depending on any condition you can show or hide div
<div runat="server" id="myDiv">
var result = getSearchResult();
if(result!= null){
myDiv.Visible = true;
lvSearchResult.DataSource = result;
lvSearchResult.DataBind();
}
Use AJAX with UpdatePanel , no? that will work..
I have a placeholder control within an UpdatePanel, and when the Add Vehicle button is clicked, I need a new "row" of controls to appear (The first "row" is added declaratively). The UserControl however gets added but not displayed. How can I fix this?
protected void AddVehicleButton_Click(object sender, EventArgs e)
{
int count = Convert.ToInt32(VehicleRegistrationCountHiddenField.Value);
var TBId = "VehicleRegistrationEnhancedTextBox" + count;
IList<Panel> oldPanels = (IList<Panel>)Session["VehiclePanels"] ?? new List<Panel>();
//Seperator
Literal hr = new Literal { Text = "<HR/>" };
//Vehicle Registration
UserControl uc = new UserControl(){ID="3"};
uc.LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
Label vehicleRegistration = new Label
{
ID = TBId + "_Label",
AssociatedControlID = TBId,
Text = "Vehicle Registration:"
};
EnhancedTextBox vehicleTypeTextBox = new EnhancedTextBox
{
ID = TBId,
Required = true,
RequiredErrorText = "Vehicle Registration is a required field."
};
//Readd previously added panels
foreach (var addedPanel in oldPanels)
{
AddVehiclePlaceholder.Controls.Add(addedPanel);
}
//Add new controls to the form
Panel newPanel = new Panel();
newPanel.Controls.Add(hr);
newPanel.Controls.Add(uc);
newPanel.Controls.Add(vehicleRegistration);
newPanel.Controls.Add(vehicleTypeTextBox);
AddVehiclePlaceholder.Controls.Add(newPanel);
//Increment the ID count
count++;
VehicleRegistrationCountHiddenField.Value = count.ToString();
//Save the panel to the Session.
oldPanels.Add(newPanel);
Session["VehiclePanels"] = oldPanels;
}
The html is below:
<div id="Step2" style="" data-step="2">
<h2> Step 2 (optional): Capture the offender(s) vehicle Information. </h2>
<hr>
<div id="VehicleTypeFields">
<div>
<label for="">Vehicle Registration</label>
<div id="Body_Body_UpdatePanel1">
<div>
<div>
<script type="text/javascript" src="/Controls/ImageUploadAndCrop/Javascript/jquery.Jcrop.min.js">
<link type="text/css" rel="stylesheet" href="/Controls/ImageUploadAndCrop/CSS/jquery.Jcrop.css">
<script type="text/javascript">
<div>
<div id="Body_Body_VehicleRegistrationImageUploadAndCrop_UploadPanel">
</div>
<input id="Body_Body_VehicleRegistrationEnhancedTextBox" type="text" placeholder="CA123-456" name="ctl00$ctl00$Body$Body$VehicleRegistrationEnhancedTextBox">
</div>
</div>
</div>
</div>
<div id="Body_Body_AddVehiclesUpdatePanel">
<input id="Body_Body_VehicleRegistrationCountHiddenField" type="hidden" value="2" name="ctl00$ctl00$Body$Body$VehicleRegistrationCountHiddenField">
<div>
<hr>
<label id="Body_Body_VehicleRegistrationEnhancedTextBox1_Label" for="Body_Body_VehicleRegistrationEnhancedTextBox1">Vehicle Registration:</label>
<input id="Body_Body_VehicleRegistrationEnhancedTextBox1" type="text" name="ctl00$ctl00$Body$Body$VehicleRegistrationEnhancedTextBox1">
<span id="Body_Body_VehicleRegistrationEnhancedTextBox1_RequiredFieldValidator" style="display:none;">Vehicle Registration is a required field.</span>
</div>
</div>
</div>
The problem is your use of LoadControl on a new instance of UserControl rather than the page's implementation. IE rather than this:
UserControl uc = new UserControl(){ID="3"};
uc.LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
Do this instead:
Control uc = LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
uc.ID = 3;
I think that will fix your immediate problem with nothing displaying but there are still other issues. While it might be possible to store the entire control in session state, my own testing shows there are anomolies in doing so. I would instead make sure you give each instance of your user control a unique ID (not "3" every time), and store those ID's somewhere. Then loop thru those ID's, calling LoadControl for each one, setting the control ID as you loop (this is required in order for the past viewstate to be reapplied to your user control.
I would also move that loop to either Page_Init or Page_Load so that your dynamically created user controls can properly participate in the page's lifecycle. Creating them in that click event is too late for them to catch their own events and they won't be created at all in the case of a postback outside of that click event.