ASP Net - Casting a Request.Form control from Code Behind - asp.net

This seems really simple, but for some reason Im stumped..
Im dynamically generating an HTML Select Box, lets call it myselect. Im creating this select box based on some database values Im generating an HTML Select Box.. almost like a string that Im just spitting out to the page. So it's never a control in the codebehind, just part of a string thats rendered as HTML by the browser. If I submit my form, and in my codebehind I perform:
Dim myVal as String = Request.Form("myselect")
That code will give me the VALUE of the myselect select box. How can I refer to this control to cast it as a System.Web.UI.HtmlControls.HtmlSelect control? Request.Form seems to give me the value, but I want to reference the object itself..

If this select box isn't declared statically on your page (that is, you're adding it to the Control Collection), you'll have to do something like this: Dim MySelect as HtmlSelect = Page.FindControl("MySelect") as HtmlSelect
You'll have to forgive me if my casting syntax is a bit off -- I'm used to casting in C#.

If your dynamically generating the control in your code file, then it will not be available to you on post back. As long as you generate it again before the viewstate is processed (you can do it in your oninit), then you can access it as you would anything else.
MySelect.SelectedValue

In response to the comments above (thanks for your help), I found what Gabriel McAdams and jwiscarson had to say were true. In browsing the Request object, I found that its nothing more than a collection of key\value pairs. Performing Request.Form("myformobj") will return a value, because thats all thats available to the application. If necessary, I suppose I can whip up some nifty javascript to track form object types, but it's certainly not necessary in my case.
Thanks for the help

Related

How to get the generated Unique ID prefix for a page's controls

As we know, ASP.NET WebForms will generate a Unique ID (as well as name) to a control to prevent collisions in the control heirarchy. Let's say we have a TextBox control with an assigned ID of "MyTextBox" in the markup. If this textbox is on a page with a Master Page then the TextBox control will be given a Unique ID of "ctl00$MainContent$MyTextBox" or something similar.
What I want to know is, for a given page, is it possible to know what the prefix WILL BE? In my above example I would like to know all controls I create on that page will be assigned with a prefix of "ctl00$MainContent$". I have examined the Page object and I cannot find an easy way to extract this information. Note: inspecting already existing controls on the page (like the TextBox) isn't an option. I simply need to know, at run time, what the prefix would be.
-- EDIT: Why do I need to do this? --
Ultimately I am trying to solve the problem that this post illustrates:
ASP.NET 4.5 TryUpdateModel not picking Form values in WebForm using Master-Page
I'm using the ModelBinding features introduced in ASP.NET 4.5. Problem is, as the above post points out, is that the name value collection found in the form will NOT match up with your model's properties. The built-in FormValueProvider expects a one-to-one match with the form key (name) and the model's properties. Unfortuantely, the form's keys will have the typical "ctl00$MainContent$" prefix to the names.
I have a semi-working solution where I created a custom IValueProvider that compares the end of the form key with the model's property. This works 95% of the time, but there's always a chance of multiple hits.
Ideally, and this is what I'm trying to figure out, if I could determine WHAT the prefix is I can then prefix that the IValueProvider's passed in key, look for that in the form and find the exact match.
So that is why I'm wondering if there's any way to know what the prefix should be for a given page.
The real answer is to simply code in such a way that you never have to know this information. that might not always be easy - but that's quite much what we do. You can certainly in code behind get/grab the "id" of the given button.
so for example, I become VERY tired of having to wire up a little toast message all over the place. So, I put in a little js routine (in the master page).
But I did need the client ID of a given control.
Well, the code behind needed (wants) to place the toast message next to whatever I clicked on.
So my server side "toast" message caller of course will after the server side code is done does the common "script" inject that will run when the page finally makes it final trip back down to the browser, displays the page, and then of course runs that script I injected.
So, my server side code does this:
MyToast2(Me, btnUpdate.ClientID.ToString, "Update ok!", "Settings changed")
So note how I get/grab/pass the "ID" of the control that the server is going to render. You can use ClientID to get the the final "ID" used for that control in code behind.
So, that btnUpdate is just a simple button placed on the web form. But who cares what super ugly "ID" the server assigns. i just need the "id" of the control so the JavaScript client side can pick up that control - and thus know/get/have the position of the control, and thus I get this result:
Or if I am some place else - again I can call that js routine - and that routine needs the current control. so might have this:
So, I can now just call a routine and pop up a message - not have to write any new js code for the gallzion notices and little pops I have all over the place.
so the little javaScript routine of course does this:
function toastcallm(cntrol, h, t, d) {
var cmd = $('#' + cntrol);
var mypos = cmd.position();
bla bla bla
But only important was that I get/determine and pass the used server "client" id to that routine - I don't really care what it is , or how to see, or how to list them out. I suppose a better jQuery selector or using wild card might work - but I really don't want to know the control ahead of time - but only that I can get the clientID used when I need it.
I simply write code that assumes somewhere along the way when I need such a client id, I simply get it and use it.
So, on the server side? Well, we always build and write code based on the control ID, but you want to get your hands on the actual id? Then you can use in the server code behind:
btnUpdate.ClientID.ToString
(ie: somecontrol.ClientID).

detailsview, how null value came?

I am having a problem dealing with Detailsview InsertItemTemplate. I am following ASP.Net 4 for Beginners by WROX.
I have a dbase with table Employee(EmpId, Name, DoB, Sex, DeptId, Position)
I am trying to insert values into this table using Detailsview.
Using the regular detailsview I could insert all the values. It works fine.But when I converted the detailsview to template, there is a problem.
What I did?
-On Smart tag of Detailsview, I clicked Edit Fields. Delete all the avaible fields except EmpID and added Template Field from Available Fields.
-Again on Smart Tag of Detailsview, clicked Edit Template and selected InsertItemTemplate in DropDownList.
-Using Standard components from Toolbox, I added text box for Name and dropdownlist for other fields. (I have not deleted EmpId, so I do not need one for it).
-Here is the rest of Markup view:
Enter following information to register:
Name:
DoB:
Sex:
DeptId:
Position:
-And here is the code behind CS file for Insert event for detailsview
protected void DetailsView1_ItemInserting1(object sender, DetailsViewInsertEventArgs e)
{
string id = System.Guid.NewGuid().ToString();
TextBox txt_name = (TextBox)DetailsView1.FindControl("EmpId");
e.Values["EmpId"] = id;
DropDownList ddl_month = (DropDownList)DetailsView1.FindControl("DDL_Month");
string month = ddl_month.SelectedValue;
e.Values["DDL_Month"] = month;
DropDownList ddl_day = (DropDownList)DetailsView1.FindControl("DDL_Day");
string day = ddl_day.SelectedValue;
e.Values["DDL_Day"] = day;
DropDownList ddl_year = (DropDownList)DetailsView1.FindControl("DDL_Year");
string year = ddl_year.SelectedValue;
e.Values["DDL_Year"] = year;
DropDownList ddl_sex = (DropDownList)DetailsView1.FindControl("DDL_Sex");
string sex = ddl_sex.SelectedValue;
e.Values["DDL_Sex"] = sex;
DropDownList ddl_pos = (DropDownList)DetailsView1.FindControl("DDL_Pos");
string pos = ddl_pos.SelectedValue;
e.Values["DDL_Pos"] = pos;
DropDownList ddl_deptid = (DropDownList)DetailsView1.FindControl("DDL_DeptId");
string deptid = ddl_deptid.SelectedValue;
e.Values["DDL_DeptId"] = deptid;
}
-Could you also suggest me how to combine the three selected value from dropdownlist for Date of Birth and add it to database.
Thank you!!!
To elaborate:
Yes that would be the first step. The connection string should be added to your web.config under the '' element:
http://msdn.microsoft.com/en-us/library/bf7sd233.aspx
You can then read this out in your code:
Read connection string from web.config
The following shows a reasonably good general form for doing a database update into a SQL database based on values you're taking from a form - done in C# (which I'd recommend getting started with instead of VB.NET, for various good reasons)
Update database with values from textbox
This shows how to add the connection string directly inside the code instead of from the web.config, which may be simpler for you in the first instance. This shows how to do the database update by using 'parameters' in the query (the 'variables' if you like that are denoted with # symbols). Using parameters is basic good practise that it's worth getting used to (don't worry too much about why, but you may want to research the 'SQL injection' technique that hackers use to attack websites). This is a good starting point for you. It may seem complex at first, but ask any developer with a few years of experience and they'll tell you it's generally the way to go. As you progress in development the tempting 'out of the box' stuff provided by .NET becomes increasingly limiting, and you lose the ability to finely tune performance and other characteristics of good applications.
The example I've given is actually still relative simple. As you develop, there are other aspects you should start bringing into your code, especially creating methods that do the bit that accesses the database (by using a SQL query) in a separate class that gets called a 'Data Access Layer' (often an intermediate layer gets used called the 'Business Logic Layer' or similar, but don't worry about that yet).
Also the example I've given should really be packaging up some of the code in methods that give a clear indication of what the code is doing e.g. at a simpler level a method 'UpdateMachineDetails' could at least be used to encapsulate the code that updates the database.
There are lots of tutorials if you Google for it that show you how to update a datbase 'manually' in .NET, but the form I'm giving you puts you on a good track towards best practice that is used industry-wide by software developers.
With the built in .NET controls, usually you'll have problems if you try deleting fields that have been generated. Your best bet to get things working this way would be to completely delete and re-add the controls you're using, and just try changing the field that you're trying to use a template with. You may still have some issues getting this to work, but shouldn't be too difficult.
Using controls in this way tends not to be flexible, and it's difficult to fix issues. That's why most experienced developers abandon this 'easy' way of doing things, and do database updating manually by creating a form, reading the values and passing this through to a method that will carry out the relevant SQL update. It takes a while to learn but once you get the hang of it it becomes easy and solves so much difficulty trying to grapple with the very .NET specific way you need to work with controls.

Advice for Building a dynamic "Advanced Search" Control in ASP.NET

alt text http://img3.imageshack.us/img3/1488/advancedsearch.png
I'm building an "Advanced Search" interface in an ASP.NET application. I don't need SO to write this thing for me, but I'm stuck on a specific problem regarding dynamic controls and ViewState. I would like some direction for how to approach this. Here's my situation:
Ingredients:
A serviceable set of API objects representing entities, fields, and searches, which handles constructing a search, generating SQL, and returning the results. So that's all taken care of.
ASP.NET 3.5
Desired Interface Functionality:
(1) On initial page load, the interface gets a preconfigured Search object with a set of SearchCriterion objects. It binds them into a set of controls (see image above.)
Some search items are simpler, like:
Field (DropDownList) | Operator (DropDownList) | Value (TextBox)
Search Criterion controls for some field types have important information stored in viewstate, like:
Field (DropDownList) | Operator (DropDownList) | Value (DropDownList) where the "Value" dropdownlist is populated by a database query.
Some fields are lookups to other Entities, which causes a chain of field selectors, like:
Field (DropDownList) Field (DropDownList) | Operator (DropDownList) | Value
(2) The user modifies the search by:
Adding and Removing search criteria by clicking respective buttons
Configuring existing criteria by changing the Field, Operator, or Value. Changes to Field or Operator will require the control to reconfigure itself by changing the available operators, changing the "Value" input control to a different type, or adding/removing DropDownLists from the "Fields" section if Lookup-type fields are selected/unselected.
(3) Finally, the user hits "Search" to see their results.
The Problem:
As you probably already know if you're answering this question, controls added dynamically to the page disappear on postback. I've created a UserControl that manipulates the control collection and neatly accomplishes step (1) above as you can see in the attached image. (I'm not concerned about style at this point, obviously.)
However on Postback, the controls are all gone, and my Search API object is gone. If I could get the dynamically generated control collection to just play nice and stick in ViewState, I could examine the controls on postback, rebuild the Search object, then handle control events neatly.
Possible Solutions
I could make the Search object serializable and store it in viewstate. Then on page load I could grab it and reconstruct the control collection at page load time. However I'm not sure if this would play nicely with controls raising events, and what happens to the viewstate of Drop-down lists that contain data from the database - could I get it back? It's highly undesirable for me to have to re-query the database on every postback.
I could develop a custom server control (see this link) for this kind of thing... but that is a new topic for me and would involve some learning, plus I'm not totally sure if a custom server control would work any more nicely with non-fixed control collections. Anybody know about that?
I was thinking that I might be able to accomplish this using databound controls - for example I could bind my criterion collection to a repeater which has a fixed control collection (maybe hide the non-used "value" controls, use an inner repeater for the "Field" drop-down lists). Then all the information would stay in ViewState... right?
Any new ideas would be greatly appreciated.
thanks for your help.
b.Fandango
I've been coding for about a day and I got this working beautifully using the third option I suggested in my question - old-school databound controls. Actually I only thought of the idea when I was forced to write out the question in detail - doesn't that just happen to you all the time?
I put my SearchCriterionControl into an asp:Repeater and bound it to my object collection. For the Field Chooser I put an asp:DropDownList inside a nested asp:Repeater and bound the Field array to that. Everything works beautifully, keeps state, actually required very little code. So I never had to dynamically add controls to the page, thank goodness.
Thanks for your suggestions, Ender, Matt and andrewWinn.
Since no one else has taken a stab at this for 2 hours, I'll throw my hat in the ring with a solution that does not rely on viewstate at all (or the ASP.NET model of postbacks).
What if you grabbed all the input values with jQuery and instead of doing a post-back did a post against the page (or a new results.aspx page)? Or, you could make the entire thing asyncrhonous and do an Ajax request against a web method, get fed the results, and populate on the client side as needed?
The unfortunate thing here is you have to reconstruct which type of controls were used to figure construct your search query since that data wont be passed with the viewstate. But I imagine you were already going to have to do some kind of translation of your input data into a query form anyway.
Read here for more information about using jQuery to hit an ASP.NET page method. Remember - page methods must be static (it's an easy oversight).
I'm not sure what you're doing server side to construct your query - but I would highly recommend LINQ. I did a similar "advanced search" function previously, and after a few different attempts found that LINQ was a wonderful tool for this problem, regardless of whether I was hitting SQL with LINQtoSQL or just hitting an in-memory collection of objects.
This worked so well because 1) LINQ is deferred execution and 2) A LINQ query returns another queryable object. The implication here is that you can chain your LINQ queries together as you construct them from your input, instead of having to do a single massive clause translation to SQL or whatever backstore you are using (one of my attempts was constructing SQL clauses with strings, but still passing input data via SQLParameters for SQL injection protection - it was messy and complicated when hand crafted LINQ was orders of magnitude easier to understand and implement).
For example:
List<string> data; // or perhaps your a DB Context for LINQtoSQL?
var query = data.Where(item => item.contains("foo"));
if( {user supplies length search option} )
query = query.Where(item => item.Length < 5);
// etc, etc.
// LINQ doesn't do anything until the query is iterated, at which point
// it will construct the SQL statement without you worrying about details or parameter binding
foreach(string value in query)
; // do something with the results
Because of deferred execution and the queryable return type, you can concatenate LINQ queries to this expression all day long and let it worry about the implementation details (such as converting to a SQL query) at execution time.
I can't provide you with the exact steps that you will need to do, but I HIGHLY suggest looking into asp.net page life cycle. I created a user control as a DLL one time. I had to capture postback data at specific steps in the lifecycle and recreate and rebind the data at other steps. Additionally thinkgs like viewstate are only available at certain points also. I know that I had to override On_init, On_prerender and some other methods.
Sorry I couldn't be more help, but I don't have the code with me (its with an old employer). I hope this helps.
If you are adding controls to the controls tree dynamically, you need to add them on postpack as well. Just call the method that builds the control on Page_Load or Page_Init and the controls should stay on the page on postback.

How can I get a variable into Silverlight 3 control from my ASP.NET code?

I'm playing with Silverlight 3 at the mo. I'm trying to get the current user ID into the Silverlight 3 page. I've done a bit of research and initParams seems to be the right way to go. Trouble is, they seemed to use the asp:Silverlight control and that's gone in SL3. I'm stuck trying to get a variable into the initParams list, assuming this is the right way to go.
I've started with a new Silverlight 3 application called "MyFirstSilverlightApp". I've added a code-behind page to the "MyFirstSilverlightAppTestPage.aspx" to allow me to do any clever bits.
I've managed to hard-code an initParam by adding this to the params of the object defenition:
<param name="initParams" value="userID=id42" />
In App.xaml.cs, I've added the following to Application_Startup:
string userID = e.InitParams["userID"];
and I've passed that into my page in a parameter in the constructor and then used that in a control. That all works.
What I can't work out is how to get the value from the variable I created in the code-behind into the param name value definition. Any help would be gratefully received.
One quick a dirty approach would be to use <% %> in the param:-
<param name="intiParams" value="userID=<%=myUserID%>" />
My prefered solution is to create my own Silverlight Web Control that can render the object tag and its contents in manner taylored to my application.
Add it to your resources collection.
Try something like.
For Each item As KeyValuePair(Of String, String) In e.InitParams
Resources.Add(item.Key, item.Value)
Next

Passing Objects via QueryString

I have object A which in turn has a property of type Object B
Class A
property x as Object B
End Class
On my ASP.NET page when I select a gridview item which maps to an object of type A I serialize the object onto the QueryString and pass it to the next page.
However I run into problems if property x actually has some value as it looks like I exceed the QueryString capacity length of 4k (although I didn't think the objects were that large)
I have already considered the following approaches to do this
Session Variables
Approach not used as I have read that this is bad practice.
Using a unique key for the object and retrieving it on the next page.
Approach not used as the objects do not map to a single instance in a table, they arte composed of data from different databases.
So I guess my question is two fold
Is it worth using GKZip to compress the querystring further (is this possible??)
What other methods would people suggest to do this?
If displaying the url of the next page in the browser does not matter, you could use the context.items collection.
context.items.add("keyA", objectA)
server.transfer("nextPage.aspx")
Then on the next page:
public sub page_load(...)
dim objectA as A = ctype(context.items("keyA"), objectA)
dim objectB as B = objectA.B
end sub
One reason to use this is if you want the users to believe that the next page is really a part of the first page. To them, it only appears as if a PostBack has occurred.
Also, you don't really need a unique key using this approach if the only way to use "next page" is if you first came from "first page". The scope for the context items collections is specific to just this particular request.
I agree with the other posters who mentioned that serialized objects on the querystring is a much worse evil than using session state. If you do use session state, just remember to clear the key you use immediately after using it.
I don't understand why you wouldn't use session state but...
Option 1: Viewstate
Option 2: Form parameters instead of querystring
But also be aware that you do not get the same object back when you serialize/deserialize. You get a new object initialized with the values of the original that were serialized out. You're going to end up with two of the object.
EDIT: You can store values in viewstate using the same syntax as Session state
ViewState["key"] = val;
The value has to be serializeable though.
While storing objects in session might be considered bad practice, it's lightyears better than passing them via serialized querystrings.
Back in classic asp, storing objects in session was considered bad practice because you created thread-affinity, and you also limited your ability to scale the site by adding other web servers. This is no longer a problem with asp.net (as long as you use an external stateserver).
There are other reasons to avoid session variables, but in your case I think that's the way to go.
Another option is to combine the 2 pages that need access to this object into one page, using panels to hide and display the needed "sub-pages" and use viewstate to store the object.
I don't think passing it in the query string, or storing it in the session, is a good idea.
You need one of the following:
a) A caching layer. Something like Microsoft Velocity would work, but I doubt you need something on that scale.
b) Put the keys to each object in the databases that you need in the query string and retrieve them the next time around. (E.g. myurl.com/mypage.aspx?db1objectkey=123&db2objectkey=345&db3objectkey=456)
Using session state seems like the most practical way to do this, its exactly what its designed for.
Cache is probably not the answer here either. As Telos mentioned, I'm not sure why you're not considering session.
If you have a page that depends on this data being available, then you just throw a guard clause in the page load...
public void Page_Load()
{
if(!IsPostBack)
{
const string key = "FunkyObject";
if(Session[key] == null)
Response.Redirect("firstStep.aspx");
var obj = (FunkyObject)Session[key];
DoSomething(obj);
}
}
If session is absolutely out of the quesiton, then you'll have to re-materialize this object on the other page. Just send the unique identifier in the querystring so you can pull it back again.
Session isn't always available. For instance when XSS (cross-site-scripting) security settings on IE prevent the storage of third-party cookies. If your site is being called within an IFrame from a site that's not your DNS domain, your cookies are going to be blocked by default. No cookies = no session.
Another example is where you have to pass control to another website that will make the callback to your site as a pure URL, not a post. In this case you have to store your session parameters in a querystring parameter, something that's tough to do given the 4k size constraint and URL encoding, not to mention encryption, etc.
The issue is that most of the built-in serialisation methods are pretty verbose, thus one has to resort to a roll-your-own method, probably using reflection.
Another reason for not using sessions is simply to give a better user experience; sessions get cleared after N minutes and when the server restarts. OK, in this case a viewstate is preferable, but sometimes it's not possible to use a form. OK, one could rely on JavaScript to do a postback, but again, that's not always possible.
These are the problems I'm currently coding around.
Here is what I do:
Page1.aspx - Add a public property of an instance of my object. Add a button (Button1) with the PostBackURL property set to ~/Page2.aspx
Private _RP as ReportParameters
Public ReadOnly Property ReportParams() as ReportParameters
Get
Return _RP
End Get
End Property
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
_RP = New ReportParameters
_RP.Name = "Report 1"
_RP.Param = "42"
End Sub
Now, on the second page, Page2.aspx add the following to the Markup at the top of the page under the first directive:
<%# PreviousPageType VirtualPath="~/Default.aspx" %>
Then for the Page_Load in the code behind for Page2.aspx, add the following
If Not Page.PreviousPage is Nothing Then
Response.write (PreviousPage.ReportParams.Name & " " & PreviousPage.ReportParams.Param)
End If
Faced with a similar situation what I did, is to XML serialize the object and pass it around as query string parameter. The difficulty with this approach was that despite encoding, the receiving form throws exception saying "potentially dangerous request...". The way I got around was to encrypt the serialized object and then encode to pass it around as query string parameter. Which in turn made the query string tamper proof (bonus wandering into the HMAC territory)!
FormA XML serializes an object > encrypts the serialized string > encode > pass as query string to FormB FormB decrypts the query parameter value (as request.querystring decodes also) > deserialize the resulting XML string to object using XmlSerializer.
I can share my VB.NET code upon request to howIdidit-at-applecart-dot-net

Resources