Can I Use Braintree.js With A .NET Web Application? - c#

So I've been looking into Braintree Payments for a couple of days now. I love the architecture, concept, etc. After looking through the documentation and the .NET walk-throughs I've noticed that all of the examples for .NET are in MVC3. I am trying to integrate Braintree into my current .NET Web Application using regular web forms.
My goal is to have a normal web form post back to the payment page with both customer data and card data. The card data should be encrypted using their Braintree.js. That way I can send everything over to Braintree for processing including the encrypted card data.
The form would look something like this:
<p>
<label>Card Number</label>
<asp:TextBox ID="number" AutoCompleteType="Disabled" MaxLength="20" Width="150" data-encrypted-name="number" runat="server" />
</p>
<p>
<label>CVV</label>
<asp:TextBox ID="cvv" AutoCompleteType="Disabled" MaxLength="4" Width="50" data-encrypted-name="cvv" runat="server" />
</p>
<p>
<label>Expiration (MM/YYYY)</label>
<asp:TextBox ID="month" AutoCompleteType="Disabled" MaxLength="2" data-encrypted-name="month" runat="server" />
/
<asp:TextBox ID="year" AutoCompleteType="Disabled" MaxLength="4" data-encrypted-name="year" runat="server" />
</p>
<asp:Button ID="btnSubmit" Text="SUBMIT" runat="server" />
<script type="text/javascript" src="https://js.braintreegateway.com/v1/braintree.js"></script>
<script type="text/javascript">
var braintree = Braintree.create("MyClientSideKey");
braintree.onSubmitEncryptForm('braintree-payment-form');
</script>
Then in the code-behind I would set the Form.Action, Form.Method and Form.ID as follows:
protected void Page_Load(object sender, EventArgs e)
{
Form.Action = "CreateTransaction()";
Form.Method = "POST";
Form.ID = "braintree-payment-form";
}
So then hopefully when the user submits the form it hits the "CreateTransaction()" member along with the encrypted card data in the "collection" parameter like this:
[HttpPost]
public ActionResult CreateTransaction(FormCollection collection)
{
TransactionRequest request = new TransactionRequest
{
Amount = 1000.0M,
CreditCard = new TransactionCreditCardRequest
{
Number = collection["number"],
CVV = collection["cvv"],
ExpirationMonth = collection["month"],
ExpirationYear = collection["year"]
},
Options = new TransactionOptionsRequest
{
SubmitForSettlement = true
}
};
Result<Transaction> result = Constants.Gateway.Transaction.Sale(request);
return null;
}
When I take this approach the form never posts back to the "CreateTransaction()" member. What am I missing? Can this be done using regular old web forms?

OK, so after LOTS of experimenting and shooting in the dark for a bit I was able to get this the Braintree.js to encrypt the data before passing it to my server. From there I am able to us the code behind to handle the payment processing.
Here is the ASP.NET web form that I'm using:
Card Number
<p>
<label>CVV</label>
<asp:TextBox ID="txtCVV" AutoCompleteType="Disabled" MaxLength="4" Width="50" data-encrypted-name="cvv" runat="server" />
</p>
<p>
<label>Expiration (MM/YYYY)</label>
<asp:TextBox ID="txtMonth" AutoCompleteType="Disabled" MaxLength="2" data-encrypted-name="month" runat="server" />
/
<asp:TextBox ID="txtYear" AutoCompleteType="Disabled" MaxLength="4" data-encrypted-name="year" runat="server" />
</p>
<asp:Button ID="btnSubmit" Text="SUBMIT" runat="server" />
<script type="text/javascript" src="https://js.braintreegateway.com/v1/braintree.js"></script>
<script type="text/javascript">
var braintree = Braintree.create("YOURKEYHERE");
braintree.onSubmitEncryptForm('braintree-payment-form');
</script>
Please take note of a few key details here:
I am using server controls. Not simple HTML tags.
braintree.js will encrypt any field that has the "data-encrypted-name" attribute.
The "data-encrypted-name" attribute does NOT need to be the same
as the control's ID attribute.
The braintree.js script is posting to the
"braintree-payment-form" form.
So when I click Submit button this form will naturally post back. The particular form that I'm using has a master page, so I need to alter the Form.ID in the Page_Load:
protected void Page_Load(object sender, EventArgs e)
{
//Adjust the properties of the form itself as this
//form has a master page.
Form.ID = "braintree-payment-form";
//Wire up the click event
btnSubmit.Click += btnSubmit_Click;
}
In the click event handler I am then able to extract the encrypted values from the Request.Form object and then submit the transaction request to the Braintree Gateway:
void btnSubmit_Click(object sender, EventArgs e)
{
//--------------------------------------------------------------------------------------------------
//Extract encrypted values from the Request.Form object
//braintree.js has encrypted these values before placing them in
//the Request object.
//--------------------------------------------------------------------------------------------------
string number = Request.Form["number"].ToString();
string cvv = Request.Form["cvv"].ToString();
string month = Request.Form["month"].ToString();
string year = Request.Form["year"].ToString();
//--------------------------------------------------------------------------------------------------
//Gateway
//This is the Braintree Gateway that we will use to post the transaction
//to. This is included here in the example but should be extracted out
//into some static class somewhere. Possibly in another layer.
//--------------------------------------------------------------------------------------------------
BraintreeGateway Gateway = new BraintreeGateway
{
Environment = Braintree.Environment.SANDBOX,
PublicKey = "YOURPUBLICKEYHERE",
PrivateKey = "YOURPRIVATEKEYHERE",
MerchantId = "YOURMERCHANTIDHERE"
};
//--------------------------------------------------------------------------------------------------
//Transaction Request
//This is the actual transaction request that we are posting to the
//Braintree gateway. The values for number, cvv, month and year have
//been encrypted above using the braintree.js. If you were to put a
//watch on the actual server controls their ".Text" property is blank
//at this point in the process.
//--------------------------------------------------------------------------------------------------
TransactionRequest transactionRequest = new TransactionRequest
{
Amount = 100.00M,
CreditCard = new TransactionCreditCardRequest
{
Number = number,
CVV = cvv,
ExpirationMonth = month,
ExpirationYear = year,
}
};
//--------------------------------------------------------------------------------------------------
//Transaction Result
//Here we are going to post our information, including the encrypted
//values to Braintree.
//--------------------------------------------------------------------------------------------------
Result<Transaction> result = Gateway.Transaction.Sale(transactionRequest);
}
OK, so this is a very basic example of how to post a transaction to Braintree. However it solves the first big hurdle that I have with getting the card data to be encrypted before it gets to my server. Hope this helps...

Related

Variables inserted into URL not showing in address bar

I have a method being used on a button to redirect to a different page. There are several variables that are inserted into the URL to help navigate to what the user wants to see. It works well, except for the fact that the variables do not show up in the address bar.
Upon button click and redirect to the next page the url looks like: /Beta.aspx/?year=&track=&event=&car=27&session
How can I get my variables to show up in the address bar? Below is the code being used for the button click.
protected void btnConfirm_Click(object sender, EventArgs e)
{
string url = string.Format("Beta.aspx/?year={0}&track={1}&event={2}&car=27&session{3}",hidYear.Value, hidTrack.Value, hidEvent.Value, hidSession.Value);
Response.Redirect(url);
}
Button Setup
<telerik:RadImageButton ID="RadImageButton2" runat="server" Skin="Material" Text="27" OnClick="btnConfirm_Click">
</telerik:RadImageButton>
Form Tag
<form id="form1" runat="server" class="SmallFont">
Hidden Fields
<asp:HiddenField runat="server" ID="hidYear" />
<asp:HiddenField runat="server" ID="hidTrack" />
<asp:HiddenField runat="server" ID="hidEvent" />
<asp:HiddenField runat="server" ID="hidSession" />
When RadImageButton2 is clicked, the page is posted back to the server for processing. This process is called ASP.NET postback mechanism and IsPostback is normally used on page_load event to detect if the page is getting generated due to postback requested by a control on the page, or if the page is getting loaded for the first time. This is important for the case when values of the controls are set programmatically and should not be overwritten when page was posted back.
See this snippet:
protected void Page_Load(object sender, EventArgs e)
{
hidYear.Value = "";
hidTrack.Value = "";
hidEvent.Value = "";
hidSession.Value = "";
}
protected void Init_Click(object sender, EventArgs e)
{
hidYear.Value = "2020";
hidTrack.Value = "1";
hidEvent.Value = "2";
hidSession.Value = "0123456789";
}
protected void btnConfirm_Click(object sender, EventArgs e)
{
string url = string.Format("Beta.aspx/?year={0}&track={1}&event={2}&car=27&session{3}",
hidYear.Value, hidTrack.Value, hidEvent.Value, hidSession.Value);
Response.Redirect(url);
}
It all will run well, but values on the redirect will be always "" because Page_Load() is called with every postback. However, with the following little change the values on redirect will be not changed and populated to the state before form submit:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
hidYear.Value = "";
hidTrack.Value = "";
hidEvent.Value = "";
hidSession.Value = "";
}
}
Note, actual values of controls can be found by looking at the source of the page
Bottom line here: hidden fields should have values when looking at the source before clicking on RadImageButton2 and Page_Load() should not call any code that changes those values, or should check for if (!IsPostBack).
P.S.
Simple redirect does not required server code and can be done with client script, example:
<script type="text/javascript">
function PageRedirect() {
window.location.href = "Beta.aspx/?year="
+ document.getElementById('<%=hidYear.ClientID%>').value
+ "&track=" + document.getElementById('<%=hidTrack.ClientID%>').value
+ "&event=" + document.getElementById('<%=hidEvent.ClientID%>').value
+ "&car=27&session" + document.getElementById('<%=hidSession.ClientID%>').value;
}
</script>
<asp:Button ID="Button1" runat="server" Text="Client Redirect"
OnClientClick="PageRedirect();return false;" />
You're saying that it does work. That means that the variables are being passed.
There is a chance that the solution is really, really simple.
Variables don't show up in the address bar until you click on it. Try clicking on the URL in your address bar, it will select the URL and the variables will show. If not, only then it has to do something with the code.
Ahh, there's the mistake Bro. there isn't any value for these hidden controls
<asp:HiddenField runat="server" ID="hidYear" />
<asp:HiddenField runat="server" ID="hidTrack" />
<asp:HiddenField runat="server" ID="hidEvent" />
<asp:HiddenField runat="server" ID="hidSession" />
either you can add value in the ASPX page as
<asp:HiddenField runat="server" ID="hidYear" Value="1234"/>
<asp:HiddenField runat="server" ID="hidTrack" Value="4321" />
<asp:HiddenField runat="server" ID="hidEvent" Value="1234" />
<asp:HiddenField runat="server" ID="hidSession" Value="2405"/>
or as mentioned by user https://stackoverflow.com/users/2316116/user2316116 you can use
hidYear.Value = "2020";
hidTrack.Value = "1";
hidEvent.Value = "2";
hidSession.Value = "0123456789";
the reason the value is not being displayed in the URL is simply that there isn't a value that can be displayed.
make sure to check for empty values if you're taking inputs from the user in future applications. 😀
You should not have '/' between 'beta.aspx' and '?'. Try removing that extra slash and see if that resolves your issue.

Tagging people using Facebook API (Graph API) ASP.NET

I have some codes where the user can post picture with message using my Facebook account that has a Graph API and I'm using ASP.NET.
My question is, tagging some name that are not my friends in my ASP.NET using Graph API is possible? If not, is there any other way to tag someone or via using their email? So that when the user want to post it they can even tag their self or other person.
For my code:
//Code behind
protected void Page_Load(object sender, EventArgs e)
{
FaceBookConnect.API_Key = "API KEY";
FaceBookConnect.API_Secret = "API SECRET";
if (!IsPostBack)
{
string code = Request.QueryString["code"];
if (!string.IsNullOrEmpty(code))
{
FaceBookConnect.PostFile(code, "me/photos", (HttpPostedFile)Session["File"], Session["Message"].ToString());
Session["File"] = null;
Session["Message"] = null;
}
}
}
protected void UploadPhoto(object sender, EventArgs e)
{
Session["File"] = FileUpload1.PostedFile;
Session["Message"] = txtMessage.Text;
FaceBookConnect.Authorize("user_photos,publish_actions", Request.Url.AbsoluteUri.Split('?')[0]);
}
//DESIGN
<form id="form1" runat="server">
<asp:FileUpload ID="FileUpload1" runat="server" />
<br />
<br />
<asp:TextBox ID="txtMessage" runat="server" TextMode = "MultiLine"></asp:TextBox>
<hr />
<asp:Button ID="btnUpload" runat="server" Text="Upload" OnClick = "UploadPhoto" />
</form>
No, you can only tag friends, and of course you can't tag people by email. You only get the email of a Facebook user by authorizing that specific user with the email permission.
How to tag friends is explained in the docs - check out the "tags" parameter: https://developers.facebook.com/docs/graph-api/reference/user/photos/#Creating

Jquery calendar does not reload after GridView is populated

I have a simple page with Jquery datepicker, UpdateProgress, and GridView inside of UpdatePanel.
Here is a fragment from the page:
...
Select From Date: <input type="text" id="datepickerfrom" name="datepickerfrom"/>
Select To Date: <input type="text" id="datepickerto" name="datepickerto"/>
<asp:Button ID="btnGetData" runat="server" OnClick="BtnGetData_Click" Text="Get Error List" />
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<asp:Image ID="Image1" runat="server" ImageUrl="~/Images/ajax-loader.gif" />
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
...MyGrid...
</ContentTemplate>
</asp:UpdatePanel>
...
This is the code behind method invoked when clicking on the button:
protected void BtnGetData_Click(object sender, EventArgs e)
{
string dateFrom = HttpUtility.HtmlEncode(Request.Form["datepickerfrom"]);
string dateTo = HttpUtility.HtmlEncode(Request.Form["datepickerto"]);
InputData data = new InputData(dateFrom, dateTo);
Session["inputData"] = data;
gvErrorLog.PageIndex = 0;
LoadLogErrorData(data);
}
When I first load the page and click on one of the Date's text boxes, jQuery datepicker is poped up. When I refresh the page, it pops up as well.
However, after clicking on the button and populating the GridView with the data, it is not displayed anymore.
What can be the reason?
Your tag is
<input type="text" id="datepickerfrom" name="datepickerfrom"/>
This is really the regular html tag. Microsoft ASP.NET does NOT keep the state (ie in ViewState) of regular html tag. After postback, the page life cycle effectively creates a new instance of Page (System.Web.UI.Page) object before sending response back to browser as html.
On the other hand, once you change to
<asp:TextBox ID="datepickerfrom" runat="server" />
You will see it in postback. Also the way you capture those 2 dates in code behind is obsolete (only seen in ASP 1.1).
The namespace for your text tag is
System.Web.UI.HtmlControls.HtmlInputText and the namespace for the server tag is System.Web.UI.WebControls.TextBox. They belong to different namespaces. Any controls in the HtmlControls are for legacy purpose.
You may change to asp:TextBox and access them from code behind as follows:
protected void BtnGetData_Click(object sender, EventArgs e)
{
string dateFrom = datepickerfrom.Text; // -- updated
string dateTo = datepickerto.Text; // -- updated
InputData data = new InputData(dateFrom, dateTo);
Session["inputData"] = data;
gvErrorLog.PageIndex = 0;
LoadLogErrorData(data);
}
If you insist on your tags, you can add a hidden variable and update those hidden variable on change event of your textboxes.
I assume your textboxes are set up like the following
$(function () {
$("#<%=datepickerfrom.ClientID%>").datepicker();
$("#<%=datepickerto.ClientID%>").datepicker();
});
I finally found the answer to my problem here:
http://www.jquerybyexample.net/2010/08/jquery-datepicker-does-not-work-after.html

How to implement a search bar on master page ASP.NET

I am trying to create a search bar for my ASP.NET pages that will be included in the master so it will be shown on all pages. Entering search text and hitting search will send you to results.aspx, which then retrieves the value of the text from the master page search box and displays data from the database in a grid view. Searching from my home page works fine, but I want the user to be able to enter a new search text while on the results page and have the page reload with the new data in the grid view.
Here is the code on results.aspx page load
if (PreviousPage != null)
{
TextBox SourceTextBox =
(TextBox)PreviousPage.Master.FindControl("txtSearchMaster");
if (SourceTextBox != null)
{
txtSearch.Text = SourceTextBox.Text;
}
}
Code on master page
<div id="search">
<asp:HyperLink ID="linkAddFile" runat="server" BorderStyle="None" NavigateUrl="~/Default.aspx" Width="150px" >Add File</asp:HyperLink>
<asp:TextBox ID="txtSearchMaster" runat="server"></asp:TextBox>
<asp:Button ID="btnSearch" runat="server" Text="Search" PostBackUrl="~/results.aspx" />
</div>
The issue is once I get to the results page and try to do a new search from there, my conditional (PreviousPage != null) says that it is null.
Don't get the search values from the previous page via PreviousPage property.
Instead, when the user performs a search, grab the value from the search textbox and pass the value to the results page. Example:
master page
<div id="search">
<asp:TextBox ID="txtSearchMaster" runat="server"></asp:TextBox>
<asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
</div>
Master Code behind
protected void btnSearch_Click(object sender, EventArgs e)
{
var searchText = Server.UrlEncode(txtSearchMaster.Text); // URL encode in case of special characters
Response.Redirect("~/Results.aspx?srch="+searchText);
}
Results page code behind
protected void Page_Load(object sender, EventArgs e)
{
if(!String.IsNullOrEmpty(Request.QueryString["srch"])
{
//perform search and display results
}
}
I think you should not get the search values from the previous page via PreviousPage property.
Instead, you can also do this:
TextBox mastertxt = (TextBox)Master.FindControl("txtSearchMaster");
Response.Redirect("~/YourResultPage.aspx?search="+mastertxt.Text.ToString());

ASP.NET AJAX UpdatePanel & Dynamic HTML Insertion Issue (Telerik)

Ok so I have a problem. I am currently working on a project using the Telerik framework for ASP.NET AJAX, although that shouldnt matter much as I am bypassing Telerik completely (almost) for this portion of the work.
I'm putting together a facebook-like chat program into our company's CRM system and, while all has gone well up to this point, I've hit a roadblock. The problem is when I try to "create a new chatbox". I'm using the asp control UpdatePanel in conjunction with a jQuery $.ajax call to pass a JSON method name to my code behind file. Here's the JSON logic:
DoubleClick on user in userlist:
$telerik.$(".UserListEntry").dblclick(function () {
var ToName = $telerik.$(this).children(".UserListEntryName").text();
var FromName = $telerik.$("#lkUserGeneralSettings").text();
$telerik.$.ajax({
type: 'POST',
url: 'DefaultHandler.ashx',
data: { "ToName": ToName, "FromName": FromName },
success: CreateChatBox(),
error: function (response) { alert("error: 001"); }
});
});
CreateChatBox callback:
function CreateChatBox() {
$telerik.$.ajax({
type: 'POST',
url: 'Default.aspx',
data: { MethodName: "CreateChatBox" },
success: ForceAsyncPostback,
error: function (response) { alert("error: 002"); }
});
}
Force asyncpostback (shouldn't be necessary, but this doesnt even work either!):
function ForceAsyncPostback() {
var UpdatePanel1 = '<%=Panel3.ClientID%>';
if (UpdatePanel1 != null) {
__doPostBack(UpdatePanel1, '');
}
alert("Success");
}
The UpdatePanel is created through various literals and some hard-coded html good-ole' fashioned divs. The problem is NOT with the dynamic creation of said elements, this works just fine. In fact my code behind (which I will post below) creates and displays everything perfectly fine if I place it into my PageLoad event.
At any rate, here's the .aspx:
<asp:UpdatePanel ID="Panel3" runat="server" OnLoad="Panel3_Load" UpdateMode="Conditional">
<ContentTemplate>
<asp:Literal ID="ChatBoxesLiteralTop" runat="server" />
<asp:Literal ID="ChatBoxesLiteralMid" runat="server" />
<asp:PlaceHolder ID="ChatBoxesPlaceHolder" runat="server" />
<asp:Literal ID="ChatBoxesLiteralBot" runat="server" />
<div id="UserListCorner">
<img id="UserListBtn" src="images/list.png" />
</div>
<div id="UserList" class="UserListHidden">
<div id="UserListView">
<asp:Literal ID="UserListViewLiteral" runat="server" />
</div>
</div>
</ContentTemplate>
</asp:UpdatePanel>
Code Behind:
protected void Panel3_Load(object sender, EventArgs e)
{
#region Ajax methods
if (Request.Form["MethodName"] == "CreateChatBox")
{
CreateChatBox();
}
#endregion
Engine m_engine = new Engine();
string m_sql = #"SELECT FullName FROM Users WHERE RecordDeleted <> 1";
DataTable dt = m_engine.GetObjectsAsDataTable(m_sql);
for (int i = 0; i < dt.Rows.Count; i++)
{
UserListViewLiteral.Text += "<div class='UserListEntry'><span class='UserListEntryStatus'><img src='images/status-online.png' width='10' /></span> <span class='UserListEntryName'>" + dt.Rows[i]["FullName"].ToString() + "</span></div>";
}
RadAjaxManager.GetCurrent(Page).ResponseScripts.Add("ChatAjax()");
}
private void CreateChatBox()
{
ChatBoxesLiteralTop.Text = #"<div id='ChatBox' class='ChatBoxHidden'>
<div class='ChatBoxHeader'>
<img id='ChatBoxStatusBtn' src='Images/status-online.png' />
<span id='ChatBoxUserLabel'>John Doe</span>
<img id='closeBtn' src='Images/close.png' />
<img id='toggleTab' src='Images/up-arrow.png' />
</div>
<div id='ChatBoxMessageOutput'></div><div class='ChatBoxFooter'>";
TextBox txt = new TextBox();
txt.ID = "ChatBoxMessageInput";
txt.Height = 16;
txt.MaxLength = 270;
txt.Width = 250;
txt.AutoPostBack = false;
ChatBoxesPlaceHolder.Controls.Add(txt);
RadButton btn = new RadButton();
btn.ID = "ChatBoxSendButton";
btn.Text = "Send";
btn.AutoPostBack = true;
btn.Height = 22;
btn.Click += ChatBoxSendButton_Click;
ChatBoxesPlaceHolder.Controls.Add(btn);
ChatBoxesLiteralBot.Text = "</div></div>";
Panel3.Update();
RadAjaxManager.GetCurrent(Page).ResponseScripts.Add("ChatAjax()");
}
I'm certainly overlooking something outrageously stupid, but a fresh set of eyes from a seasoned ASP.Net Ajaxer would be really appreciated! Thanks in advance.
Clarification of Issue
What DOES work:
The code runs properly.
The JSON method is passed to the code behind and read.
The CreateChatBox() method runs through and allegedly populates the literals and controls.
All callbacks in the JSON chain are executed successfully.
What DOES NOT work:
The UpdatePanel, even after the redundant postback, does not have
this new HTML after code executes successfully.
To Whom It May Concern
Well I happened to solve this problem the day after posting it. Of course there are now new hurdles to tackle but I am happy to say that my asynchronous chat module is nearly done. Since nobody decided to take on the problem, I am going to post what I did to properly produce dynamic, asynchronous objects based on hard-coded HTML in ASP.NET since, while Googling the matter, this post was the only relevant result popping up.
I'll start by saying I am well aware that this is a very unconventional approach. That being said, the requirements of the project justified the means. While there may be many other ways to accomplish your goal, this does work. I am not going to go into extraneous details with respect to my particular project, but hopefully this will help somebody in the future.
The Wrongs
The primary problem with my original question was the way I was attacking the rendering sequence. A major flaw in my logic was attempting to separate the ASP controls (literals, placeholders, buttons and such) from my HTML too much. My initial approach (from above) looked like this:
<asp:UpdatePanel ID="Panel3" runat="server" OnLoad="Panel3_Load" UpdateMode="Conditional">
<ContentTemplate>
<asp:Literal ID="ChatBoxesLiteralTop" runat="server" />
<asp:Literal ID="ChatBoxesLiteralMid" runat="server" />
<asp:PlaceHolder ID="ChatBoxesPlaceHolder" runat="server" />
<asp:Literal ID="ChatBoxesLiteralBot" runat="server" />
<div id="UserListCorner">
<img id="UserListBtn" src="images/list.png" />
</div>
<div id="UserList" class="UserListHidden">
<div id="UserListView">
<asp:Literal ID="UserListViewLiteral" runat="server" />
</div>
</div>
</ContentTemplate>
</asp:UpdatePanel>
The final result looks more like this:
<asp:UpdatePanel ID="Panel3" runat="server" OnLoad="Panel3_Load" UpdateMode="Conditional">
<ContentTemplate>
<asp:PlaceHolder ID="ChatBoxesPlaceHolder" runat="server" />
<div id="UserListCorner">
<img id="UserListBtn" src="images/list.png" />
</div>
<div id="UserList" class="UserListHidden">
<div id="UserListView">
<asp:Literal ID="UserListViewLiteral" runat="server" />
</div>
</div>
<asp:Label ID="Label42" Font-Bold="true" runat="server" />
<asp:HiddenField runat="server" ID="LatestDisplayTick" />
</ContentTemplate>
</asp:UpdatePanel>
With the code behind (abridged) similar to this:
Literal top = new Literal();
// ...
UpdatePanel UpdatePanel2 = new UpdatePanel();
// ...
top.Text = #"<div id='ChatBox' class='ChatBoxShown'>
<div class='ChatBoxHeader'>
<img id='ChatBoxStatusBtn' src='Images/status-online.png' />
<span id='ChatBoxUserLabel'>" + UserNames[0] + #"</span>
<img id='closeBtn' src='Images/close.png' />
<img id='toggleTab' src='Images/up-arrow.png' />
</div>";
top.ID = "ChatBoxesLiteralTop";
top.Mode = LiteralMode.PassThrough;
// ...
UpdatePanel2.ID = "UpdatePanel2";
UpdatePanel2.UpdateMode = UpdatePanelUpdateMode.Conditional;
UpdatePanel2.ChildrenAsTriggers = false;
UpdatePanel2.ContentTemplateContainer.Controls.Add(top2);
// ...
Panel3.ContentTemplateContainer.Controls.Add(top);
Panel3.ContentTemplateContainer.Controls.Add(UpdatePanel2);
Panel3.ContentTemplateContainer.Controls.Add(mid);
What It Means
All I've done is wrapped the entirety of my chatbox elements inside a single placeholder, dynamically creating HTML elements that wrap ASP controls all from the server and posting it as a "chunk" of dynamic data. This sidesteps some strange behaviors with ASP control placement otherwise, and allows for a quasi-OO approach for the client-side functionality of my project. After creation, I can now make use of some JSON and AJAX calls to dynamically enter data into said dynamically created controls.
An example would be posting a message received from a database notification to my chat window (ChatBoxesLiteralMid control) when it is received.
After Page/Control Load
if(ChatBoxesLiteralMid != null)
ChatBoxesLiteralMid.Text += #"<div class='ChatBoxEntry'><span class='ChatBoxEntryName ChatBoxSelf'>" + AppParameters.Current.AppUser.FirstName + "</span>: <span class='ChatBoxEntryMessage'>" + dy.Rows[dy.Rows.Count - 1]["Message"].ToString() + "</span></div>";
Why Go To The Trouble?
What I have accomplished is for a particular project with needs to go far above and beyond the original scope, started by another developer years prior. This is an unconventional way to teach the old dog some new tricks. I now have near-full control of an indiscriminate amount of real-time chat boxes/sessions on the CLIENT side. It's really pretty freakin' sweet honestly. Please feel free to post questions (or concerns) as I regularly check SO and am happy to respond.

Categories