LINQ query to WebControl.Controls - asp.net

I have three TextBox controls on the page
<asp:TextBox ID="TextBox1" runat="server" AutoPostBack="True"
OnTextChanged="TextBox_TextChanged" TabIndex="1">
<asp:TextBox ID="TextBox2" runat="server" AutoPostBack="True"
OnTextChanged="TextBox_TextChanged" TabIndex="2">
<asp:TextBox ID="TextBox3" runat="server" AutoPostBack="True"
OnTextChanged="TextBox_TextChanged" TabIndex="3">
and an event handler
protected void TextBox_TextChanged(object sender, EventArgs e)
{
WebControl changed_control = (WebControl)sender;
var next_controls = from WebControl control in changed_control.Parent.Controls
where control.TabIndex > changed_control.TabIndex
orderby control.TabIndex
select control;
next_controls.DefaultIfEmpty(changed_control).First().Focus();
}
The meaning of this code is to automatically select TextBox with next TabIndex after page post back (see Little JB's problem). In reality I receive InvalidCastException because it's impossible to cast from System.Web.UI.LiteralControl (WebControl.Controls contains actually LiteralControls) to System.Web.UI.WebControls.WebControl.
I am interested is it possible to modify this aproach somehow to receive working solution? Thank you!

OfType
from control in changed_control
.Parent
.Controls
.OfType<WebControl>()

You should be able to use the OfType method, to only return controls of a given type.
e.g.
var nextcontrols = from WebControl control in
Changed_control.Parent.Controls.OfType<TextBox>()... etc

The problem is that LiteralControl does not inherit from WebControl. It can't have the focus though, so it's OK to not select them. In your LINQ statement, add another condition checking for a WebControl. So your where line should be where control.TabIndex > changed_control.TabIndex && control is WebControl.

Related

Is it possible to use DataBinding to evaluate Controls on .aspx page?

I'm not sure if I am asking this question correctly. I know that I can accomplish what I need in code behind, but I'm wondering if this is possible. I want to hide a control if there is a value in another control. I know I can use databinder.eval in a repeater, but can I use it just for a normal asp control on the page?
In other words, I want to do something like this:
<asp:TextBox runat="server" ID="ConditionalText" Text="Show if other value is empty" Visible ='<%# testValue.Text != "" ? false : true %>'></asp:TextBox>
<asp:TextBox runat="server" ID="testValue"></asp:TextBox>
I tried just the way I have it above, and <%# testValue. exposed available properties of "testValue" TextBox so I thought it might work. It didn't throw any errors but it did not show/hide the textbox. I'm just wondering if this is possible and what I would have to do to accomplish this.
Any assistance is greatly appreciated.
It can work, but since you are using a databinding expression outside a GridView, Repeater etc. you have to call it manually.
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
//rest of the code
}
//call databind manually
DataBind();
}
PS better to use IsNullOrEmpty instead of = ""
<asp:TextBox runat="server" ID="ConditionalText" Text="Show if other value is empty"
Visible='<%# !string.IsNullOrEmpty(testValue.Text) ? false : true %>'></asp:TextBox>

Simple HTTP Post returning empty Request.Form

I am trying a simply HTTP post to post data from one form to another in asp.net.
sender page code
<form id="form1" runat="server" method="post" action="CILandingPage.aspx">
<asp:TextBox name="txtUname" runat="server" Width="180px"></asp:TextBox>
<asp:TextBox name="txtPassword" runat="server" TextMode="Password" Width="180px"></asp:TextBox>
<asp:TextBox name="txtTransaction" runat="server" Width="180px"></asp:TextBox>
and the receiver page has code
lblUserName.Text = Request.Form["txtUname"].ToString();
lblPassword.Text = Request.Form["txtPassword"].ToString();
lblTransactionID.Text = Request.Form["txtPassword"].ToString();
it throws NullReferenceException because Request.Form object is empty.
what am i missing?
Set the PostBackUrl property for the control to the URL of the page to which you want to post the ASP.NET Web Forms page.
Remove action and add PostBackUrl into Button.Instead name use ID property value.
In Default.aspx
<form id="form1" runat="server" method="post">
<div>
<asp:TextBox ID="TextBox1" name="txtUname" runat="server" Width="180px"></asp:TextBox>
<asp:TextBox ID="TextBox2" name="txtPassword" runat="server" TextMode="Password" Width="180px"></asp:TextBox>
<asp:TextBox ID="TextBox3" name="txtTransaction" runat="server" Width="180px"></asp:TextBox>
<asp:Button ID="button" PostBackUrl="~/CILandingPage.aspx" runat="server" />
</div>
</form>
In CILAndinaPage.aspx.cs
using System;
public partial class CILandingPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Response.Write(Request.Form["TextBox1"].ToString() +Environment.NewLine);
Response.Write(Request.Form["TextBox2"].ToString() + Environment.NewLine);
Response.Write(Request.Form["TextBox3"].ToString());
}
}
}
You may use PreviousPage reference as below:
protected void Page_Load(object sender, EventArgs e)
{
// first check if we had a cross page postback
if ( (PreviousPage != null) && (PreviousPage.IsCrossPagePostBack))
{
Page previousPage = PreviousPage;
TextBox UserName= (TextBox)previousPage.FindControl("txtUname");
TextBox Password= (TextBox)previousPage.FindControl("txtPassword");
// we can now use the values from TextBoxes and display them in two Label controls..
lblUserName.Text = UserName.Text;
blPassword.Text = Password.Text;
}
}
This code in Page_Load will get referenced to previous page that has posted the data & helps you get the same on the destination page.
Hope this helps!!
Since you are posting cross-page, it is likely that collection item (txtPassword) doesn't exist. You can try setting the ClientIdMode of each control to static so that the id used in the HTTP post matches what you are looking for in the .Form collection on the target page.
Check out this article for more info on cross-page posting: https://msdn.microsoft.com/en-us/library/ms178139%28v=vs.140%29.aspx
Use your browser debugging tool (F12) to see what is transmitted in the HTTP Post body.

Event not firing as expected for control in formview asp.net

I have a LinkButton in an InsertItemTemplate which when clicked, should display a hidden DropDownList in the InsertItemTemplate. However, it doesn't seem to be working, but it will say, change the text of a label outside the Formview when the LinkButton is clicked. The event is firing, but the part to make the DropDownList visible in the InsertItemTemplate is not doing anything. Code is below:
.aspx:
<asp:FormView ID="formViewNewRecord" runat="server">
<InsertItemTemplate>
<asp:DropDownList ID="ddlAddSelection2" runat="server" DataSourceID="dSource1" DataTextField="Users" DataValueField="Users" AppendDataBoundItems="true" Visible="false">
<asp:ListItem></asp:ListItem>
</asp:DropDownList>
<asp:LinkButton runat="server" ID="lbAddAnother" OnClick="lbAddAnother_Click">+Add Another</asp:LinkButton>
</InsertItemTemplate>
</asp:FormView>
<asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>
C#:
protected void lbAddAnother_Click(object sender, EventArgs e)
{
DropDownList addSelection2 = (DropDownList)formViewNewItem.Row.Cells[0].FindControl("ddlAddSelection2");
addSelection2.Visible = true;
Label2.Text = addSelection2.ID;
}
Your dropdown control is not an immediate child of your formview. So since the FindControl call is not recursive, you have to search for the control in the right location of your form view's child controls. See this for the details but at a high level, you need something along the lines of:
DropDownList ctrl = (DropDownList)FormView1.Row.Cells[0].FindControl("ddlAddSelection2");
After that, you should check it for null for safe measure.

Using hyperlink with querystring for gridview row

Is there someway to turn the row of a gridview into a hyperlink so that when a user opens it in a new tab for example, it goes to that link? Right now I am using a LinkButton and when the user opens it in a new tab, it doesn't know where to go.
I figured the .aspx code would look something like:
<asp:TemplateField>
<ItemTemplate>
<Hyperlink ID="hyperlink" runat="server" ForeColor="red" HtmlEncode="false" navigationURL="testUrl.aspx"
</ItemTemplate>
</asp:TemplateField>
The only thing is, our URLs are set up in the C# code behind as a query string, so I'm not sure how to pass that into the navigationURL section.
I'm guessing there's something I can do on the page_load with the query string to redirect to the page I need, but this is my first time working with query strings so I'm a little confused.
Thanks!
<asp:TemplateField>
<ItemTemplate>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%#String.Format("~/controller.aspx?routeID1={0}&routeID2={1}", Eval("routeid1"), Eval("routeid2"))%>'></asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
routeid1 and routeid2 are passed as query strings to the controller of that page.
What I did recently is modified my class to have a readonly property that constructs the A tag for me. This way I have control over what gets displayed; just text or a link.
<ItemTemplate>
<asp:Label ID="ColumnItem_Title" runat="server" Text='<%# Bind("DownloadATag") %>'> </asp:Label>
</ItemTemplate>
The code behind just binds an instance of the class to the gridview. You can bind the gridview whenever, on load on postback event, etc.
Dim docs As DocViewList = GetViewList()
GridViewDocuments.DataSource = docs
GridViewDocuments.DataBind()
In the above code, the DocViewList, instantiated as docs, is a list of a class that has all the properties that are needed to fill my GridView, which is named GridViewDocuments here. Once you set the DataSource of your GridView, you can bind any of the source's properties to an item.
Something like:
<asp:LinkButton ID="LinkButton_Title" runat="server" target="_blank"
PostBackUrl='<%# Eval(Request.QueryString["title"]) %>'
or binding them from the RowCreated event:
protected void GridView_OnRowCreated(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
(e.Row.FindControl("LinkButton_Title") as LinkButton).PostBackUrl = Request.QueryString["title"]))
}
}

Set Custom ASP.NET UserControl variables when its in a Repeater

<%# Register Src="~/Controls/PressFileDownload.ascx" TagName="pfd" TagPrefix="uc1" %>
<asp:Repeater id="Repeater1" runat="Server" OnItemDataBound="RPTLayer_OnItemDataBound">
<ItemTemplate>
<asp:Label ID="LBLHeader" Runat="server" Visible="false"></asp:Label>
<asp:Image ID="IMGThumb" Runat="server" Visible="false"></asp:Image>
<asp:Label ID="LBLBody" Runat="server" class="layerBody"></asp:Label>
<uc1:pfd ID="pfd1" runat="server" ShowContainerName="false" ParentContentTypeId="55" />
<asp:Literal ID="litLayerLinks" runat="server"></asp:Literal>
</ItemTemplate>
</asp:Repeater>
System.Web.UI.WebControls.Label lbl;
System.Web.UI.WebControls.Literal lit;
System.Web.UI.WebControls.Image img;
System.Web.UI.WebControls.HyperLink hl;
System.Web.UI.UserControl uc;
I need to set the ParentItemID variable for the uc1:pdf listed inside the repeater.
I thought I should be able to find uc by looking in the e.Item and then setting it somehow. I think this is the part where I'm missing something.
uc = (UserControl)e.Item.FindControl("pfd1");
if (uc != null) { uc.Attributes["ParentItemID"] = i.ItemID.ToString(); }
Any thoughts would be appreciated.
Also tried this with similar results... when I debug inside my usercontrol (pfd1) the parameters I am trying to set have not been set.
uc = (UserControl)e.Item.FindControl("pfd1");
if (uc != null)
{
uc.Attributes.Add("ContainerID", _cid.ToString());
uc.Attributes.Add("ParentItemId", i.ItemID.ToString());
}
UPDATE: It looks like my controls are not connected by a namespace. I've wrapped by the parent control (Layer) and the PressFileDownlad control in a namespace "MyControls". Also updated their Inherits reference on the aspx to read "MyControls.xxxxx". I'm able to type "MyControls.Layer" inside the code on layer.aspx.cs but I'm not able to get "MyControls.PressFileDownload"
If you implement ParentItemID as a public property in your user control, then you should be able to set it declaratively, e.g:
<asp:Repeater id="Repeater1" ...>
<ItemTemplate>
<uc1:pfd ID="pfd1" runat="server" ParentItemId='<%# Eval("ItemID") %>' ... />
Martin is right you should be able to set it in declarative way (in case your property is public) .
But your way should also work (just cast it properly)
((PressFileDownload)e.Item.FindControl("pfd1")).ParentItemId = 0;
The best way is to implement the OnDataBinding event for the user control. I try to stay away from putting code inline in the aspx using webforms if possible.
When the repeater gets bound, for each item that is bound, the OnDataBinding will fire for your user control and your handler can do what it needs. You don't have to go searching for the controls.
Here is an example:
// in your aspx
<uc1:pfd ID="pfd1" runat="server" ShowContainerName="false" ParentContentTypeId="55"
OnDataBinding="pfd1_DataBinding" />
// in your codebehind implement the OnDataBinding event
protected void pfd1_DataBinding(object sender, System.EventArgs e)
{
pfd uc = (pfd)(sender);
uc.ContainerID = _containerID.ToString();
uc.ParentItemID = Eval("ItemID");
// Here you can do more like access other items like hidden fields
// or cached objects or even other controls etc... Skys the limit.
}
EDIT: Notice from your comment you require more data than what is found in the datasource. In this case what I usually do is just make private member variables in the .cs that I store data in. So when you have the container ID just store it in a variable that will be accessible.
Eg in your .cs for your page:
public partial class _TestPage : System.Web.UI.Page
{
private int _containerID { get; set; }
Then when you load the data just set the _containerID property and it will be accessible in the OnDataBinding event. Just make sure you are binding after you have set the _containerID.

Resources