A 508 Scan flagged the fact that we have identical id's for buttons in gridviews, and I'm wondering how to get around this. For some of these buttons we can remove the id's entirely and they don't seem to affect functionality, but others actually do have back-end code that enables/disables them based on other factors. Here's the front-end code:
<asp:TemplateField HeaderText="Accept" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button
ID="btnAccept"
runat="server"
Text="Accept Data"
CssClass="inherited"
CommandName="AcceptData"
CommandArgument="<%# ((GridViewRow) Container).RowIndex %>" />
</ItemTemplate>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:TemplateField>
And here is some of the backend code that pertains to this button:
protected void gvDashboard_RowDataBound(object sender, GridViewRowEventArgs e)
{
try
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
int rowIndex = e.Row.RowIndex;
Button btnAccept = e.Row.FindControl(#"btnAccept") as Button;
Button btnRemove = e.Row.FindControl(#"btnRemove") as Button;
bool errors = (bool)gvDashboard.DataKeys[e.Row.RowIndex][#"Errors"];
string fileType = (string)gvDashboard.DataKeys[e.Row.RowIndex][#"FileType"];
string processingStatus = (string)gvDashboard.DataKeys[e.Row.RowIndex][#"ProcessingStatus"];
bool accepted = (bool)gvDashboard.DataKeys[e.Row.RowIndex][#"Accepted"];
int records = 0;
if (gvDashboard.DataKeys[e.Row.RowIndex][#"Records"] is System.DBNull)
{
int recordsColumnIndex = Utilities.GetColumnIndexByHeaderText(gvDashboard, #"Records");
e.Row.Cells[recordsColumnIndex].Text = #"0";
}
else
{
records = (int)gvDashboard.DataKeys[e.Row.RowIndex][#"Records"];
}
if (fileType == #"CN" || fileType == #"CP")
{
if ((DBNull.Value.Equals(gvDashboard.DataKeys[e.Row.RowIndex][#"ZipId"]))
|| ((int)gvDashboard.DataKeys[e.Row.RowIndex][#"ZipId"] == 0))
{
// CN or CP did not come from a zip file
if ((accepted))
{
btnAccept.Enabled = false;
btnRemove.Enabled = false;
}
}
else
{
btnAccept.Enabled = false;
btnRemove.Enabled = false;
}
}
if (accepted || processingStatus == #"I" || processingStatus == #"N")
{
btnAccept.Enabled = false;
}
}
}
catch (Exception ex)
{
DAL.ErrorLog(#"FilteredDashboard.gvDashboard_RowDataBound: Row: " + e.Row.RowIndex.ToString() + " " + ex.Message);
}
}
Ideally I would like to be able to add on an autogenerated number onto the front-end ID, but then account for ID's with the same initial string (like 'btnAccept' or 'btnRemove') regarless of their additional numeric suffixes. Is that possible at all?
ASP.NET Webforms auto generates button IDs for each control, unless you set it to
ClientIDMode="Static"
If you'd like to make sure the IDs are unique, set
ClientIDMode="AutoID" //this is the default setting for all webform controls, and autogenerates an ID
So the issue is probably coming from somewhere else, you can check it yourselves in the inspector of your browser.
as noted in answer by Zee - you can use find control with the one name for each row - however, each row control - including buttons DOES get its own id.
This problem should not exist - and as a general rule never has been a issue. Each row of the GV will auto generate new id for each row of controls. However, while new "id" are generated, you can on the row data bind events use find control with the control name without issue.
Also, you really don't need to save/put in the row index for hte button command args. In fact, you can also get the database primary key - and NOT even display or have the PK displayed in each row.
Assuming you have datakeys set (say to PK "id" column), then you can then use a simple button row click event for a button on the gv row.
You can't double click on the button to add the event (since it inside the GV), but you can use intel-sense like this:
Just type in onclick= , and when you hit "=", the you get intel sense like this:
So, you can choose create new event.
Now, in this plane jane button event you can
Get any value or control of that row.
Get the row index - no need to put in markup
Get the row primary key database row ID - again no need to have in markup.
So, the code behind the button can look like this:
protected void Button1_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
Debug.Print("Row index click = " + gRow.RowIndex);
Debug.Print("Data base PK ID = " + GridView1.DataKeys[gRow.RowIndex]["ID"]);
// And you are free to using gRow.FindControl, or even the cells collection
// for non templated values.
// eg: gRow.Cells[0].Text
Output:
Row index click = 3
Data base PK ID = 11
Note how I used datakeys setting. This is REALLY nice for security, since I don't have to expose client side the database row PK id used. And note how I also picked up the current GV row index - again all without having to put or mess with that stuff in the actual gv row. So, you don't even need to use command Argument either. And you don't need to use commandName either. You ONLY need to set CommandName if you want the Gv selected index event to fire. (so, you might need it), but as a general rule, just drop in a plane jane button - wire up the event, and get the current row with "naming container" as I did. And then you get the GV row, and with that you have everything you need - including rowindex, and even use of datakeys as I pointed out.
In fact, this means for the most part I don't bother with the built in GV events - they are not worth your time in most cases.
Related
Summary: When trying to hide a custom command button in ASPxGridView command column, it hides the buttons strangely, one of the buton texts appears in the filter row entry, and the button handlers stop working. The error message like "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) appeares.
I am using DevExpress 14.2.3.0, the grid view is nested in another grid views and in ASPxRoundPanels.
Details: The command column contains the following custom buttons...
<dx:GridViewCommandColumn VisibleIndex="9">
<CustomButtons>
<dx:GridViewCommandColumnCustomButton ID="btnClose" Text="Close as done">
</dx:GridViewCommandColumnCustomButton>
<dx:GridViewCommandColumnCustomButton ID="btnReopen" Text="Reopen">
</dx:GridViewCommandColumnCustomButton>
</CustomButtons>
</dx:GridViewCommandColumn>
The buttons are displayed fine (as links) and the following code handles them nicely:
protected void gvMilestoneTasks_CustomButtonCallback(object sender, ASPxGridViewCustomButtonCallbackEventArgs e)
{
ASPxGridView grid = sender as ASPxGridView;
if (e.ButtonID == "btnClose")
{
int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 0, N'{1}'",
milestoneID, Page.User.Identity.Name));
grid.DataBind();
} else if (e.ButtonID == "btnReopen")
{
int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 1, N'{1}'",
milestoneID, Page.User.Identity.Name));
grid.DataBind();
}
}
(That is, dedicated SQL stored procedures with different arguments are called [notice the second argument if curious], and the grid.DataBind(); is used to refresh the content of the status column.)
I want to show only one of the buttons. When the row shows the open one, only the Close button should be displayed and active. When the row shows it was closed earlier, only the Reopen button should be visible and active.
I tried to handle visibility in the CustomButtonInitialize event handler (based on the status info -- when the closed is NULL in the database, then it is open; otherwise, the closed contains the datetime of when it was closed):
protected void gvMilestoneTasks_CustomButtonInitialize(object sender, ASPxGridViewCustomButtonEventArgs e)
{
if (e.VisibleIndex == -1)
return;
ASPxGridView grid = sender as ASPxGridView;
if (e.ButtonID == "btnClose")
{
object o = grid.GetRowValues(e.VisibleIndex, "closed");
bool flagVisible = Convert.IsDBNull(o);
e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
}
else if (e.ButtonID == "btnReopen")
{
object o = grid.GetRowValues(e.VisibleIndex, "closed");
bool flagVisible = !Convert.IsDBNull(o);
e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
}
}
I can observe also an error message in the browser in the sense "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) -- this is hidden somewhere deep in the controls; I am not using the FindControl.
Where is the bug hidden?
Thanks for your help.
I think this is a DevExpress bug fixed in version 2014 2.5:
Fix
The reason is that FilterRow behaves as another row of the displayed grid. It is considered to be another visible row. This way the handler should return early also in the case when the cell type is detected as filter.
if (e.VisibleIndex == -1 || e.CellType == GridViewTableCommandCellType.Filter)
return;
Alternatively, that part can be changed to...
if (e.CommandCellType != GridViewTableCommandCellType.Data)
return;
I want to create dynamic text boxes during run time.
Suppose im gettng a text from a database as "# is the capital of India" now i want to replace that "#" by text box while it is rendered to user as below
<asp:TextBox runat="server" id = "someid"></asp:TextBox> is the capital of India
Im able to get the textbox and text as combination. However I cannot access the textboxes with the given id and when any event occurs on the page the textboxes are lost as they logically does not exist untill their state is stored.
I also went through the concepts of place holder and Viewstate to store the dynamically created controls and make their id's available for the methods, but as far as I tried I could not meet the textbox and text combination requirement.
Im looping over the entire text recieved from database and checking if there is any"#". Is yes then i want to replace it with a textbox on which i can call methods to take back the value entered in the text box to database and store it.
eg: text is "#" is the capital of India
for (int i = 0; i < que.Length; j++) //que holds the text
{
if (que[i] == '#')
{
//Textbox should be created
}
else
{
//the texts should be appended before or after the textbox/textboxes as text demands
}
}
On button click I'm passing request to database, which will send me necessary details i.e. question text, options and also saves the current checked value etc.
protected void BtnLast_Click(object sender, EventArgs e)
{
CheckBox1.Checked = false;
CheckBox2.Checked = false;
CheckBox3.Checked = false;
CheckBox4.Checked = false;
QuestionSet q = new QuestionSet();
StudentB b = new StudentB();
q = b.GetQuestion(1, 1, qid, 'L', 0, Checked, date);
qid = Convert.ToInt32(q.Question_Id);
Checked = q.Checked;
if (q.Question_Type == 11) //indicates its objective Question
{
//ill bind data to checkboxes
}
else if (q.Question_Type == 12) // indicate its fill in the blanks question
{
for (int j = 0; j < que.Length; j++)
{
if (que[j] == '#')
{
count++;
string res = "<input type = 'text' runat = 'server' id ='TxtBoxFillUp" + count + "'/>";
htm = htm.Append(res);
}
else
{
htm = htm.Append(que[j]);
}
}
}
}
Any help will be greatly appreciated, thanks in advance.
Adding control in the way you do it won't create control as asp.net creates it. You do have to create controls as usual .net object.
TextBox myNewTextBox = new TextBox() {};
// Set all initial values to it
And add this cotrol to placeholder or panel, whatever you use. Keep in mind that asp.net page events fire even if you use update panel, so in order to maintain state and events of newly created controls you have take care of creating such controls long before page's Load event fires. Here is my answer to another simialiar question.
Seeing the requirements you have:
1.) You need to use JavaScript. Since the ASP.NET will not recreate controls which are dynamically added. Dynamically added controls need to be recreated after every postback. This is the reason why your TextBoxes are Lost after every postback.
2.) You can write JavaScript code to Hide and show the textboxes for blank texts since at every button click you can call Client side functions using: OnClientClick() property of buttons.
3.) Also to Get the TextBoxes using ID property, add them in Markup( .aspx ) portion itself.
I have 2 columns I'm working with in an XtraGrid. When Column1's value changes, I'd like to perform some logic and possibly change the value of Column2 and disable Column2 as well. You can see my code below, but I have 3 problems:
My CustomRowCellEdit function seems to run non-stop in the background.
The SetRowValue on Column2 doesn't seem to really happen unless I click away from the row; I need the change to happen as soon as Column1 is changed.
How can I disable within my IF block?
I've added the following Event to a Grid:
this._myGridView.CustomRowCellEdit +=
new DevExpress.XtraGrid.Views.Grid.CustomRowCellEditEventHandler(
this.myGridView_CustomRowCellEdit);
Here is the Event:
private void myGridView_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e)
{
if (e.Column.FieldName == "Column1" && e.RowHandle >= 0)
{
GridView gv = sender as GridView;
string value1 = gv.GetRowCellValue(e.RowHandle, gv.Columns["Column1"]).ToString();
if (value1 == "something")
{
gv.SetRowCellValue(e.RowHandle, gv.Columns["Column2"], someOtherValue);
// I'd like to disable Column2 in this IF logic.
}
}
}
In the DevX docs, there is a note about the CustomRowCellEdit event that says
Due to the XtraGrid control's infrastructure, the CustomRowCellEdit event fires frequently - each time a cell is refreshed. Therefore, do not implement complex logic for your CustomRowCellEdit event handler...
Given your stated requirements, my approach would be to use the CellValueChanged event instead of CustomRowCellEdit. Your handler could then say something like
private void myGridView_CellValueChanged(object sender, CellValueChangedEventArgs e) {
if (e.Column.FieldName != "Column1") return;
GridView gv = sender as GridView;
if (e.Value == "something") {
gv.SetRowCellValue(e.RowHandle, gv.Columns["Column2"], someOtherValue);
}
}
To make an individual cell non-editable at runtime, see this topic on the DevExpress support site.
how to set readyonly for rows at runtime using Devxpress Grid Contorl.
Essentially what you need to do is handle the grid view's ShowingEditor event, and using the FocusedRowHandle and FocusedColumn properties, decide whether or not to allow editing for the current cell. To disable editing, set the Cancel property of the CancelEventArgs to true.
Hope this helps.
i have a gridview control in which paging is enabled, i have a datakey value,i want to find a row index with respect to datakey value ,i have this code,
protected int GetRowIndex(object userID)
{
for(int i = 0l i <= GridView1.DataKey.Count -1 ; i++)
{
if(GridView1.DataKey[i].Value == userID)
{
return i;
}
}
}
but there is problem with this code, that if that user id is not found in that page it will return 0, my question is that how can i change the page index to find row in all pages.i am using gridview own paging.
private static int IndexOfGridView(GridView gridView, int dataKeyId, int index)
{
foreach (GridViewRow gvRow in gridView.Rows)
{
var dataKey = gridView.DataKeys[gvRow.DataItemIndex];
if (dataKey == null || (int)dataKey.Value != dataKeyId) continue;
index = gvRow.DataItemIndex;
break;
}
return index;
}
There is no direct support for this, because the GridView doesn't have all of the DataKeys stored away somewhere for it to search. It keeps only the set of DataKeys that it uses for the current page. Changing the page index does not automatically "load" the DataKeys for that page so that you can search each one.
You will have to solve this by adding a function which, when given a userID, will return the page to display. It might be named GetPageFromUserID(object userID). This function will need to search your data store, using the same sort order, page size, and filter conditions as your grid.
Even with that page index, which you'll set into the GridView.PageIndex property, you'll still have to call DataBind() on the GridView in order to get it to load the rows (and the DataKeys) from the database so that you can use the code you have above to find the row index.
You may find the row index in the GridView with:
rowIndex = theGridView.DataKeys.IndexOf(theDataKeyValue)
I found this on an old ASP.net VB project we're still maintaining, hope it helps.
string user_id = GridView1.SelectedDataKey["userID"].ToString();
Here you get the Userid of the Selected Data in a gridview
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="userID" OnRowDataBound="GridView1_RowDataBound"
OnSelectedIndexChanged="GridView1_SelectedIndexChanged" >
<asp:CommandField ShowSelectButton="True"> </asp:CommandField>
// write code
</asp:GridView>
U r doing Pagination concept in Gridview, its better do in backend itself, then it will easy for u to display Data i.e u take 10 data at a time while click on first page like wise.. the same concept i have done in repeater which is easy when compare to gridview...
I have a telerik:RadGrid that is bound to a SQL Data Source. One of the columns is for "Location" which is really a look up value in another table.
<telerik:GridDropDownColumn
DataField="d_location_id"
DataSourceID="dsLocation"
UniqueName="d_location_id"
DataType="System.Int32"
ListValueField="d_location_id"
ListTextField="Abbreviation"
HeaderText="Location">
</telerik:GridDropDownColumn>
My list of locations is stored in an ObjectDataSource, which is bound to a static DataTable and sorted alphabetically for me already. What I would like to do is be able to set the default option for this dropdown.
For example, suppose I have the following locations:
1 Home
2 Work
3 Parents
4 Car
I would like to have Parents be my default value.
This sample on Telerik shows something similar to what I'm trying to do. If you click "Add New Record", you'll notice the default city is Kirkland and I'm trying to figure out how to use London as the default when adding a new record.
Not sure if it is the best or most straightforward way or not, but it does work.
protected void gridMyInfo_ItemDataBound(object sender, Telerik.Web.UI.GridItemEventArgs e)
{
if (e.Item.IsInEditMode && e.Item.ItemIndex < 0)
{
GridEditableItem editedItem = e.Item as GridEditableItem;
GridEditManager editMan = editedItem.EditManager;
GridDropDownListColumnEditor editor = editMan.GetColumnEditor("d_location_id") as GridDropDownListColumnEditor;
editor.ComboBoxControl.SelectedIndex = editor.ComboBoxControl.Items.FindItemIndexByText("Parents");
}
}