DevExpress XtraGrid - Change/disable a cell in column2 when column1 changes - devexpress

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.

Related

Unique ID's for buttons in gridview?

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.

ASPxGridView: Cannot hide custom command button selectively. Is it a bug on my side?

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;

Telerik RadGrid GridDataItem - how to determine if column exists?

I'm using a base class to modify the behavior of any Telerik RadGrid that appears on my ASP.Net pages. In the base class, I want to perform certain operations (set Css, tool tips, etc) on many common columns, but not every common column exists in every grid.
In the ItemDataBound event I'm getting an instance to the GridDataItem and in turn I want to get a reference to one or more of the contained cells of the GridDataItem:
var cell = gridDataItem["ColumnUniqueName"]
Problem is that this throws a GridException if the named column doesn't exist:
Cannot find a cell bound to column name 'ColumnUniqueName'
Is there a way to test for existence of a column by name before referencing it or am I
stuck using try catch?
Will sent me on the right path:
var tableView = gridDataItem.OwnerTableView;
var gridColumn = tableView.Columns.FindByUniqueNameSafe(uniqueName);
if (gridColumn != null)
{
var cell = gridDataItem[gridColumn];
...
Try using the RenderColumns collection:
protected void rgGrid_ItemDataBound(object sender, GridItemEventArgs e)
{
if (e.Item is GridDataItem)
{
bool found = (from d in rgGrid.MasterTableView.RenderColumns select d).Any(d => d.UniqueName == "ColumnUniqueName");
}
}

Two way binding with custom expression?

I am using a gridview in my asp.net project to view and modify some records from the database. The database has two columns: start_date and end_date. When a new record is created these columns contains null, but they can be modified later using the gridview update command.
In gridview I have two template fields (having names start_date and end_date) in which I have placed two calendar controls. Upon clicking an update link of gridview it always returns an error because of the null value binding to the calendar. I have used this helper function to solve it:
protected DateTime ReplaceNull(Object param)
{
if (param.Equals(DBNull.Value))
{
return DateTime.Now;
}
else
{
return Convert.ToDateTime(param);
}
}
and used these two custom expressions in calendar control's SelectedDate:
ReplaceNull(Eval("start_date"))
ReplaceNull(Eval("end_date"))
The problem is that two-way data binding the calendars upon selecting a date does not update the database table. Are there any workarounds? Or alternatively, a better solution would be appreciated.
I don't know why you let them null when insert a new record , but many ways you can solve this problem i think .
one of them : in the RowDataBound event of the Gridview
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.Cells[1] == null) //the index of the start_date
{
e.Row.Cells[1].Text = DateTime.Now.ToString(); // in your case you will make the selected date of the calender(through casting to calender) with the value you need.
}
}
}
Or: you can catch the exception , you meet in your Update button through
try and catch block.

Selectively apply css to a row in a gridview

I'm looking for a way to selectively apply a CSS class to individual rows in a GridView based upon a property of the data bound item.
e.g.:
GridView's data source is a generic list of SummaryItems and SummaryItem has a property ShouldHighlight. When ShouldHighlight == true the CSS for the associated row should be set to highlighted
any ideas?
very easy
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView drv = e.Row.DataItem as DataRowView;
if (drv["ShouldHighlight"].ToString().ToLower() == "true")
e.Row.CssClass = "highlighted";
}
}
the code above works if you use a DataTable as DataSource
change to:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
myClass drv = (myClass)e.Row.DataItem;
if (drv.ShouldHighlight)
e.Row.CssClass = "highlighted";
}
}
just for the example above when using generics:
public class myClass
{
public Boolean ShouldHighlight
{ get; set; }
}
if you are working with Generics (List, Dictionary, etc)
keep in mind:
e.Row.dataItem
always return the entire object that you are populating the row with, so it is easy from here to manipulate the appearance of the data in the webpage.
you should use RowDataBound event that will trigger after the data is attached to the row object but not yet written the HTML code in the page, in this way you can check the ShouldHighlight value (I converted to a String cause I do not know the type, you can change it if you know it's a boolean value).
this code runs much faster than megakemp code cause you're not creating a List object and populated with the entire data source for each row...
P.S. take a look at this website, you can find several tutorials for your project using the GridView object
One thing you want to keep in mind is that setting the Row.CssClass property in the RowCreated or RowDataBound event handlers will override any default styles you may have applied at the grid level. The GridView gives you easy access to row styles via properties such as:
gvGrid.AlternatingRowStyle.CssClass = ALTROW_CSSCLASS
gvGrid.RowStyle.CssClass = ROW_CSSCLASS
However, when you assign a CssClass value to a specific row, as is your need in this case, the assignment overrrules any top-level assignment at the grid level. The assignments will not "cascade" as we might like them to. So if you want to preserve the top-level class assignment and also layer on your own, more specific one, then you would need to check the rowState to see what kind of row you are dealing with and concatenate your class names accordingly
If(item.ShouldHighlight)
{
If(e.Row.RowState == DataControlRowState.Alternate)
{
e.Row.CssClass = String.Format("{0} {1}", "highlight", ALTROW_CSSCLASS)
}
else
{
e.Row.CssClass = String.Format("{0} {1}", "highlight", ROW_CSSCLASS)
}
}

Resources