DetailsView: How to set a HiddenField value using the CommandArgument on a New command? - asp.net

I have inherited some code that has a GridView and a DetailsView in a webpart control.
The DetailsView is able to create two different kinds of an object e.g. TypeA and TypeB.
There's a dropdown list that filters the GridView by object type and the DetailsView has an automatically generated Insert button.
<asp:DetailsView ID="myDetailsView"
AutoGenerateInsertButton="True"
AutoGenerateEditButton="True"
AutoGenerateRows="false"
OnItemUpdating="OnItemUpdating"
DefaultMode="ReadOnly"
OnDataBound="OnDetailsViewBound"
OnItemInserting="OnItemInserting"
OnModeChanging="OnDetailsViewModeChanging"
runat="server">
I have been asked to:
remove the filter on the GridView; and
split the New buttons/links into two so there's a separate button for creating each type of object.
Removing the filter means that I need some other way to track what kind of object we're creating.
I have split the New links by changing the above to:
<asp:DetailsView ID="myDetailsView"
AutoGenerateInsertButton="False"
AutoGenerateEditButton="True"
AutoGenerateRows="false"
OnItemUpdating="OnItemUpdating"
DefaultMode="ReadOnly"
OnDataBound="OnDetailsViewBound"
OnItemInserting="OnItemInserting"
OnModeChanging="OnDetailsViewModeChanging"
runat="server">
and adding
<FooterTemplate>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton runat="server" ID="lnkCreateNewTypeA" CommandName="New" CommandArgument="TypeA" CssClass="buttonlink">New Type A</asp:LinkButton>
<asp:LinkButton runat="server" ID="lnkCreateNewTypeB" CommandName="New" CommandArgument="TypeB" CssClass="buttonlink">New Type B</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</FooterTemplate>
I haven't yet removed the filter so the changes currently function the same as before, as I'm using the New command.
What I was hoping to be able to do is somehow capture the New event so I can put the CommandArgument value into a hidden field, that the DetailsView would then use to determine which type of object it's creating and also to show/hide fields.
When I put breakpoints in all of the event handlers in my code, the first one to break is OnDetailsViewModeChanging, which doesn't have access to the CommandArgument.
OnItemCommand (if it's hooked up) is triggered when any button within the DetailsView is pressed and does give me access to the CommandArgument but I'm not sure what exactly needs to be done within this method to mimic the chain of events that occurs when you use automatically generated buttons.
Is my only option for retrieving the CommandArgument to capture it in the OnItemCommand event handler or is there some other event that is triggered on the New command?
Can anyone explain to me the sequence of events that occurs when the New command is triggered?
I read somewhere that it changes the mode to Insert but I don't know what else it does. I believe the OnItemInserting method isn't called until the "Insert" link is clicked.
Any help would be gratefully received!!
Edit:
I have found this link on DetailsView events but it hasn't answered my question.
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.detailsview_events.aspx
Edit:
I have tried adding the following:
in ascx:
<asp:DetailsView ID="myDetailsView"
...
OnItemCommand="OnItemCommand"
...
runat="server">
...
<asp:TemplateField HeaderText="Object Type" HeaderStyle-CssClass="columnHeader">
<ItemTemplate>
<asp:HiddenField runat="server" ID="hidObjectType" Value=""/>
<asp:Label runat="server" ID="lblObjectType"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
in code behind:
protected void OnItemCommand(object sender, DetailsViewCommandEventArgs e)
{
if (e.CommandName.Equals("New"))
{
var objectType = e.CommandArgument.ToString();
HiddenField typeHidden = this.myDetailsView.FindControl("hidObjectType") as HiddenField;
if (typeHidden != null)
{
typeHidden.Value = objectType;
}
Label typeLabel = this.myDetailsView.FindControl("lblObjectType") as Label;
if (typeLabel != null)
{
typeLabel.Text = objectType;
}
}
}
I found that I didn't need to set the mode (this.myDetailsView.ChangeMode(DetailsViewMode.Insert);) in this method, as the OnDetailsViewModeChanging event handler still triggered.
This finds the controls and sets the values on them correctly. If I check the values again in OnDetailsViewModeChanging, their values are still set but as part of the logic in this method, there is a call to
this.myDetailsView.DataBind()
which causes a postback and at this point, the values are lost. I tried adding
EnableViewState="True"
but this made no difference. I've reviewed the page lifecycle (http://spazzarama.files.wordpress.com/2009/02/aspnet_page-control-life-cycle.pdf) and thought that maybe this.EnsureChildControls() would help but it's also made no difference.
An alternative would be to store the value in the session but I'd rather not.

As far as I can tell there is no event for capturing the "New" command aside from OnItemCommand, which captures all commands. (NOTE: You will need to make sure that CausesValidation="False" is set on your LinkButton or the code won't break into OnItemCommand).
When stepping through the code, the following occurred:
After the linkbutton was pressed, OnItemCommand is triggered. CommandName = "New" and here I could retrieve the CommandArgument
Next OnModeChanging is triggered. e.NewMode = "Insert". From all examples I've seen, here you call ChangeMode on the DetailsView and then call Databind() on it
Next OnDataBound is triggered as a result of calling Databind()
I didn't find a way to retain the value of the hidden variable between the various events so I ended up using a session variable. Code is below in case anyone wants it.
DetailsView declaration in the ASCX:
<asp:DetailsView ID="myDetailsView"
AutoGenerateInsertButton="False"
AutoGenerateEditButton="True"
AutoGenerateRows="false"
OnItemInserting="OnItemInserting"
OnItemUpdating="OnItemUpdating"
OnItemCommand="OnItemCommand"
DefaultMode="ReadOnly"
OnDataBound="OnDetailsViewBound"
OnModeChanging="OnDetailsViewModeChanging"
runat="server">
In the code-behind:
constant declarations...
private const string SESSIONKEY_MYVALUE = "MyValue";
private const string DEFAULT_OBJECTTYPE = "TypeA";
OnItemCommand event handler...
protected void OnItemCommand(object sender, DetailsViewCommandEventArgs e)
{
if (e.CommandName.Equals("New", StringComparison.InvariantCultureIgnoreCase))
{
var objectType = e.CommandArgument.ToString();
HiddenField typeHidden = this.myDetailsView.FindControl("hidObjectType") as HiddenField;
if (typeHidden != null)
{
typeHidden.Value = objectType;
}
HttpContext.Current.Session[SESSIONKEY_MYVALUE] = objectType;
}
}
OnModeChanging event handler....
protected void OnDetailsViewModeChanging(Object sender, DetailsViewModeEventArgs e)
{
if (e.NewMode == DetailsViewMode.Insert)
{
this.myDetailsView.ChangeMode(DetailsViewMode.Insert);
this.myDetailsView.DataBind();
}
}
OnDataBound event handler...
protected void OnDetailsViewBound(object sender, EventArgs e)
{
if (this.myDetailsView.CurrentMode == DetailsViewMode.Insert)
{
var sessionVar = HttpContext.Current.Session[SESSIONKEY_MYVALUE];
var objectType = sessionVar == null ?
DEFAULT_OBJECTTYPE :
sessionVar.ToString();
var typeHidden = this.myDetailsView.FindControl("hidObjectType") as HiddenField;
if (typeHidden != null)
{
typeHidden.Value = objectType;
}
}
}

Related

select data from gridview field and populate textbox

I'm trying to populate a single text box (or parameter) with data from a gridview column when I click on a button in that row.
Gridview gets it data from a sqlconnection
the gridview is
| Drawing |
| 12345 | VIEW
| 12346 | VIEW
the VIEW is a template button with an onclick event, when the user clicks the button the data from the Drawing column (12345) should be passed to ether a textbox or a paremeter. (this is the part I dont know how to do) once the Iv got the number in a textbox I can use it as pareameter and then a pdf is opened of that drawing, I have code for this and is working.
thanks for any help
If you are using C#, the simplest thing to do would be to add an in-built select command button to the gridview rows at runtime. Then on the selectedindexchanged event of the gridview simply access the cell of the selected row that you want the value from. You can then assign that string to anything you want. Like so:
protected void myGridView_SelectedIndexChanged(object sender, EventArgs e)
{
string myString = myGridView.SelectedRow.Cells[4].Text.ToString();
TextBox1.Text = myString;
}
Remember that the cell index collection is zero based, so [0] is actually the first cell in the row.
Use TemplateFields and the grid view's OnRowCommand event, like this:
Markup:
<asp:gridview id="GridView1"
OnRowCommand="GridView1_RowCommand"
runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:TextBox ID="TextBoxDrawing" runat="server"
Text="<%# Eval("Drawing")) %>" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="selc" runat="server" Text="View"
CommandName="View"
CommandArgument="<%# ((GridViewRow)Container).RowIndex %> />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code-behind:
protected void GridView1_RowCommand(Object sender, GridViewCommandEventArgs e)
{
// If multiple buttons are used in a GridView control, use the
// CommandName property to determine which button was clicked
if(e.CommandName == "View")
{
// Convert the row index stored in the CommandArgument
// property to an integer
var index = Convert.ToInt32(e.CommandArgument);
// Retrieve the row that contains the button clicked
// by the user from the Rows collection
var row = GridView1.Rows[index];
// Find the drawing value
var theDrawingTextBox = row.FindControl("TextBoxDrawing") as TextBox;
// Verify the text box exists before we try to use it
if(theDrawingTextBox != null)
{
var theDrawingValue = theDrawingTextBox.Text;
// Do something here with drawing value
}
}
}

How to hide link button based on a result returned by a class ?

I am bit new to C# and got a question.
I have a class as below that simply return false ( this is just to test)
public class SetAuthority
{
public SetAuthority()
{
//
// TODO: Add constructor logic here
//
}
public static Boolean AuthorizedToAddEdit()
{
return false;
}
}
I have a DetailsView with two link buttons to Edit and add New record. I want to hide the link buttons based on the above class method returning value.
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="Edit" Text="Edit"></asp:LinkButton>
<asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="New" Text="New"></asp:LinkButton>
</ItemTemplate>
Above works file and Edit and New link buttons are hidden when I run the program.
But the question is, I have a separate link button outside of the DetailsView. It is just a link to navigate to another page. I want to hide this in similar way using the same logic. I have the below code in my webform.
<asp:LinkButton ID="LinkButton5" runat="server" CausesValidation="False" visible='<%# SetAuthority.AuthorizedToAddEdit() %>'
CommandName="OpenAdminPage" Text="Open Admin Page"></asp:LinkButton>
But the link button is always visible and seems it is not calling the class and not getting the value back. It appeared to be the class not return any value and can someone help me to identify what is the different between having this and working in DetailsView and not working for a simple link button.
Note: have a workaround where I can call the same method in Page Load event that works fine without any issue. Code is below
protected void Page_Load(object sender, EventArgs e)
{
Boolean myAllowAdd;
myAllowAdd = SetAuthority.AuthorizedToAddEdit();
if (myAllowAdd == false)
{
LinkButton1.Visible = false;
}
}
The reason is that this is for databinding expressions only: <%# Since the DetailsView is databound it works there.
If you would DataBind the page it worked also for the LinkButton outside of the DetailsView:
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
inline asp.net tags... sorting them all out (<%$, <%=, <%, <%#, etc.)
Side-note: be careful with static in ASP.NET. The static method does not yet hurt. But if you'd also use static fields you'd enter a minefield since it would be shared across all requests. Your current code-behind "work-around" is the better approach anyway.

LinkButton - bind field to ToolTip or CSSClass

I have a legacy asp.net 3.5 application. I need to bind a filed to CssClass so that i can utilize it via jquery.
Basically, in the datagrid, there are 2 buttons. Button one is visible and button two is not visible. On click of button one, i want to perform action and then make button two visible and hide button one. How can i do this? I just need a kick in the right direction...
<asp:LinkButton ID="lnkDelete" runat="server"
ToolTip="Delete Order <%# DataBinder.Eval(Container.DataItem, "TransID")%>"
OnClientClick="return DeleteOrder();"
OnClick="OrderDelete" CommandArgument='<%# Eval("TransID")'
CssClass="">
<asp:Image ID="Image1" runat="server" ImageUrl="~/images/icons/delete.gif"
BorderStyle="None" />
</asp:LinkButton>
My current binding inside the tooltip results in an error, "the server tag is not well formed".
On the code behind OrderDelete, i can disable the delete link, but how can i make the other button visible?
//delete indivisual order
protected void OrderDelete(object sender, EventArgs e)
{
string transactionID = String.Empty;
LinkButton lnkDelete = (LinkButton)sender;
if (lnkDelete != null)
transactionID = lnkDelete.CommandArgument;
if (!String.IsNullOrEmpty(transactionID))
{
//do delete
}
//refresh results
}
For the server tag not well-formed error, try something like this:
ToolTip='<%# String.Format("Delete Order {0}", DataBinder.Eval(Container.DataItem, "TransID")%>'
For the second part of your question, a little more of your code might help to give you a more specific answer, but in lieu of that, if you know which row of the DataGrid you're in, you should be able to do a FindControl in that row for the second button and make it visible.
Update
You might try setting the tooltip in the codebehind, using the RowDataBound event. Something like this:
protected void gv1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton btn = e.Row.FindContorl("lnkDelete") as LinkButton;
// You'll need to retrieve the values you want to dynamically populate
// the ToolTip with from other controls in the row;
// I don't know if you'd be able to use the DataSource or not, but you might.
btn.ToolTip = "Delete Order ";
}
}

how to access templatefield from code behind

the reason why i am looking to update dynamic is because i am using objectdatasource and my objectdatasource have a collection of object and within that object i have another object that i wanted to access so for an example:
+Student
......
......
......
-Courses
.........
.........
Name
Update end
how do i bind templatefield from code-behind?
<asp:Gridview ID="gridview1" runat="Server">
<columns>
<asp:TemplateField HeaderText="Name" SortExpression="Name">
<ItemTemplate>
</ItemTemplate>
</asp:TemplateField>
</columns>
</asp:Gridview>
First of all define your key field in GridView control, just add net attribute to GridView markup: datakeynames="StudentID".
You can use both event handler for GridView: RowDataBound or RowCreated. Just add one of this event handler and find there control that is placed in your ItemTemplate. Like here, for instance:
void ProductsGridView_RowCreated(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
// Retrieve the LinkButton control from the first column.
Label someLabel = (Label)e.Row.FindControl("someLabel");
if (someLabel != null)
{
// Get Student index
int StudentId = (int)GridView.DataKeys[e.Row.RowIndex].Values[0];
// Set the Label Text
// Define here all the courses regarding to current student id
someLabel.Text = //
}
}
}
This example was gotten from MSDN
Here are some code samples from MSDN:
http://msdn.microsoft.com/en-us/library/aa479353.aspx
These are in VB but you should be able to locate C# also :-)
If you follow this link and scroll down you will find a code sample:
http://bytes.com/topic/asp-net/answers/624380-gridview-generated-programmatically

Add new row to database table from Datagrid

I am trying to update a database table from my datagrid using an event handler and ItemCommand. I manage to call the routine and everything is working fine except the text that is inserted into my database is empty. I managed to track this back to the text not being passed from my datagrids footer to the sql parameters. I tried using a string first and then passing that to the parameters but they were also empty. I am accessing the control using the following line.
sqlcmd.Parameters.Add("#GoodsDesc", SqlDbType.VarChar).Value = CType(e.Item.FindControl("txtGoodsDesc"), TextBox).Text
The control itself is defined using
<asp:TemplateColumn HeaderText="Goods Descriptions">
<ItemTemplate>
<asp:Label runat="server" ID="lblGoodsDesc" Text='<%# Eval("GoodsDesc") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="txtGoodsDesc" runat="server" TextMode="MultiLine" Rows="3"></asp:TextBox>
</FooterTemplate>
</asp:TemplateColumn>
Am I missing something here? It's like the text in the footer isnt being tied to the control before I call it.
HI Ryan, we will need more code then just that. Where are you .Add +ing this parameter in addition where is e.Item.FindControl what event is it?
You need to check if you are on the footer control:
protected void dg_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Footer)
{
if (dg.EditItemIndex != -1)
{
((TextBox)e.Item.FindControl("txtGoodsDesc")).Text
}
}
}
Or in vb.net
if (e.Item.ItemType = ListItemType.Footer) then
Dim s as String=String.Empty
s=CType(e.Item.FindControl("txtGoodsDesc"), TextBox).Text
end if
So essentially I discovered that the issue that was occurring was that VB.Net was calling Page_Load before the event itself was being called, and in the Page_Load method I was calling the method that filled the datagrid, which cleared the textboxes in the footer before the values in them were being read.
To stop this I placed a condition around the call to the method in Page_Load
If Not IsPostBack Then
FillDataGrid()
End If
Then I called the FillDataGrid() function as the very last step in the handler, meaning that the data was read in and then the datagrid was bound to the new values.

Resources