Dynamically generated columns in radgrid disappear after postback - telerik

Am using radgrid and creating it in aspx but on certain action i add more GridTemplateColumns to the grid.
private void CreateDateColumns(List<DateTime> occurenceList)
{
if (occurenceList != null && occurenceList.Count > 0)
{
int index = 1;
foreach (DateTime occurence in occurenceList)
{
string templateColumnName = occurence.Date.ToShortDateString();
GridTemplateColumn templateColumn = new GridTemplateColumn();
templateColumn.ItemTemplate = new MyTemplate(templateColumnName, index);
grdStudentAttendanceList.MasterTableView.Columns.Add(templateColumn);
templateColumn.HeaderText = templateColumnName;
templateColumn.UniqueName = templateColumnName;
index++;
}
}
}
private class MyTemplate : ITemplate
{
protected RadComboBox rcbAttendance;
private string colname;
private int _index;
public MyTemplate(string cName, int index)
{
colname = cName;
_index = index;
}
public void InstantiateIn(System.Web.UI.Control container)
{
rcbAttendance = new RadComboBox();
rcbAttendance.Items.Add(new RadComboBoxItem("---Select---", "-1"));
rcbAttendance.Items.Add(new RadComboBoxItem("Present", "1"));
rcbAttendance.Items.Add(new RadComboBoxItem("Absent", "2"));
rcbAttendance.Items.Add(new RadComboBoxItem("Leave", "3"));
rcbAttendance.ID = "rcbAttendance" + _index;
container.Controls.Add(rcbAttendance);
}
}
All are fine with creation but when i press save button or any combobox make postback the only dynamically generated columns content disappear and the other columns stay.
What i noticed that columns still in place with headertext but only content are disappeared (in my case content are comboboxes)
After enabling viewstate for grid only header text appear.
What should i do to keep columns contents after postback and get their values ?

When creating template columns programmatically, the grid must be generated completely in the code-behind using the Page_Init event. Then, you must create the templates dynamically in the code-behind and assign them to the ItemTemplate and EditItemTemplate properties of the column. To create a template dynamically, you must define a custom class that implements the ITemplate interface. Then you can assign an instance of this class to the ItemTemplate or EditTemplateTemplate property of the GridTemplateColumn object.
Blockquote
Column templates must be added in the Page_Init event handler, so that the template controls can be added to the ViewState.
Blockquote
Source: Telerik
Basicly, you need to create all GridTemplateColumns in the Page_Init. We had the same problem and this approach fixed it.

Related

Vaadin Select - fields changed inside binder's apply do not write changes to bean from item

Using Vaadin 14.7.0.
Inside a CRUD editor (Enhanced CRUD Editor) I'm building various fields, amongst which I have a Select.
The Select is initialized with a list of options but I'm also trying to change the items from CRUD form edit to CRUD form edit depending on changes from my underlying database so that the user can select new values.
BindingBuilder<Item, SelectOption> bindingBuilder = binder.forField(s);
if (prop.isMandatory()) {
bindingBuilder.asRequired(requiredI18n);
}
bindingBuilder.bind(new ValueProvider<Item, SelectOption>() {
private static final long serialVersionUID = 1L;
#Override
public SelectOption apply(final Item item) {
ListPropertyDefinition lp = ((ListPropertyDefinition)prop);
Serializable currentValue = item.get(lp.getName());
Collection<SelectOption> sOptions = null;
if (lp.getSelectOptions() != null) {
ListDataProvider<SelectOption> ldp = (ListDataProvider)s.getDataProvider();
sOptions = ldp.getItems();
} else {
sOptions = getNewOptions(item, prop.getName());
s.setItems(sOptions);
}
return new SelectOption("N/A", currentValue);
}
}, new Setter<Item, SelectOption>() {
private static final long serialVersionUID = 1L;
#Override
public void accept(final Item bean, final SelectOption fieldvalue) {
bean.set(prop.getName(), fieldvalue != null ? fieldvalue.getValue() : null);
}
});
Now, if the s.setItems(sOptions) branch is being called then the Select field gets populated with the new values sent by the backend but when I'm saving the item the value that I get is null, regardless of what I select in the select field.
This does not happen when I do not change the items in the select field (i.e. if branch).
I did some debugging for comparing 2 select fields - one that changes its values on the fly and one that has values that don't change... from what I could see the field that has values changing on the fly has a null buffered value as seen in the attached image:
vs the field that does not have its values modified in the binder's apply method:
Not sure if what I'm doing is the right way of "refreshing" a select field's values and / or what should I do so that I get the selected value back in the bean on CRUD form save.
I think you are doing things in overly complicated manner. Based on your code I think your principal challenge is how to set empty selection to be "N/A"
For that you simply need to enable empty selection to be allowed. You need to have one placeholder item for empty selection, for which you generate "N/A" as caption. Then you can just do:
Binder<Item> binder = new Binder<>();
Select<SelectOption> select = new Select<>();
...
select.setEmptySelectionAllowed(true);
select.setEmptySelectionCaption("N/A");
binder.forField(s).bind(Item::getProperty,Item::setProperty);
public class SelectOption {
...
}
// Make the item bean also to follow POJO convention
public class Item {
private SelectOption property;
public SelectOption getProperty() {
return property;
}
public void setProperty(SelectOption property) {
this.property = property;
}
}

TableView change all the rows when I try to edit one column value in javafx

I have a TableView that has its items property bound to a class Group's applications property which is declared as :
ObjectProperty<ObservableList<Application>> applications = new SimpleObjectProperty();
it is initialised as :
applications.set(new FXCollections.observableArrayList<Application>());
applications.get().add(new Application());
applications.get().add(new Application());
The class Application class contains some properties and another class Customer declared and initialised as :
ObjectProperty<Customer> customer = SimpleObjectProperty<>(new Customer());
Now there are four columns in the tableView that are editable they are 'ApplicationNo', 'ApplicationDate', 'CustomerId' and 'CustomerName'. The first 2 column's cell value factory is set to Application class's applicationNo and applicationDate properties. and the last 2 column's cell value factory are set to the Application class's customer property's customerId and customerName properties. All the four columns are editable with combobox control that are populated with values from the database.
In addition to the above stated table view, I have a form in the FXML consisting of fields that are there in the Application class and the Customer class. This form is disabled when no table row is selected and is enabled with the values of the Application instance, when any of the table view row is selected.
Now my problem is when I select any row and edit the values in the form or in the table, all the row attain the new value that I enter.
I am not uploading the code because it is very lengthy.
the code for updateItem method of table cell editor
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (control != null) {
updateControl();
}
setGraphic(control);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getItemText());
setGraphic(null);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
Code for cell factory and cell value factory of column applicationNo
applicationNo.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<GroupRow, ApplicationModel>, ObservableValue<ApplicationModel>>() {
//GroupRow is the object that contains the Application object
#Override
public ObservableValue<ApplicationModel> call(CellDataFeatures<GroupRow, ApplicationModel> param) {
return param.getValue().getApplicationProperty();
}
});
applicationNo.setCellFactory(
new Callback<TableColumn<GroupRow, ApplicationModel>, TableCell<GroupRow, ApplicationModel>>() {
#Override
public TableCell<GroupRow, ApplicationModel> call(TableColumn<GroupRow, ApplicationModel> param) {
return new ApplicationTableCellEditor();
//ApplicationTableCellEditor is custom TableCell
}
});
applicationNo.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<GroupRow, ApplicationModel>>() {
#Override
public void handle(CellEditEvent<GroupRow, ApplicationModel> event) {
event.getRowValue().setApplication(event.getNewValue());
tableChanged();
applicationChanged(event.getNewValue());
}
});
Actually I have figured out the actual problem and its solution. For changing Row selection I was using :
tableView.getSelectionModel().selectedItemProperty().addListener(.....);
This actually when I selected the 2nd row after selecting the first row as both the rows contained and equal instance of Application resulting in row1.equals(row2) which in turn did not allow the above listener to fire. So, The unbinding of values never happened and result that the controls remained bound to both the rows.
So as a solution I changed the above to:
tableView.getSelectionModel().selectedIndexProperty().addListener(......);
Here this fired every time I selected a different row thereby unbinding the controls and the properties of the previous row and binding the controls to the properties of the new row. This solved my problem. I would request If any one has any other suggestions please post the answer.

ObjectStateEntry GetModifiedProperties() incorrectly detects change to byte[] property

Does Entity Framework incorrectly detect changes to images?
I have a "Person" entity class defined as follows;
public class Person
{
public int Id { get; set; }
public byte[] Photo { get; set; }
}
I have bound the Photo to a PictureBox control on my form using a datasource.
There form also uses a dynamically created bindingNavigator.
I also have written an audit log to populate captured changes
The procedure calls the ObjectContext DetectChanges() and then
var entries = ObjectContext.ObjectStateManager.GetObjectStateEntries()
Which contains an entry showing that the Photo has been modified.
entry.OriginalValues[name] matches entry.CurrentValues[name]
The Column is varbinary(MAX) in SQL Server and the size of the picture files I have loaded are under 1Mb
If I set all of the columns to null the error no longer occurs
The code I use to get the file is
private void LoadPhotoButton_Click(object sender, EventArgs e)
{
using (var dlg = new OpenFileDialog())
{
dlg.Filter = "JPEG files |*.jpg";
if (dlg.ShowDialog() == DialogResult.OK)
{
PhotoPictureBox.Image = Image.FromFile(dlg.FileName);
}
}
}
I note here that hex zero can be inserted as padding characters when these fields are involved in string conversion.... could the binding be doing that somehow?
If I delete the picturebox from the form, or even bind the control as a text box instead of a picturebox then the behaviour is correct ( not that a picture bound to a text box is any use )
I am able to work around the problem by not binding directly to the picture box.
Instead I just load it on the BindingSource_CurrentChanged event using
private void BindingSource_CurrentChanged(object sender, EventArgs e)
{
var obj = (Person) BindingSource.Current;
this.PictureBox.Image = byteArrayToImage( obj.Photo);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
I am using my work around - documented at the end of the question as a make do answer.

How can I right-align a cell in a Wicket table column?

I'd like to have a PropertyColumn of a DataTable right-aligned. But if I try to add a new SimpleAttributeModifier("align", "right") to the cell item, it is added to a span within the td, rather than the td itself.
public class AssetSizeColumn<T> extends PropertyColumn<T> {
...
#SuppressWarnings("unchecked")
public void populateItem(Item<ICellPopulator<T>> item, String componentId, IModel<T> rowModel) {
IModel<Long> model = (IModel<Long>) createLabelModel(rowModel);
Component label = new Label(componentId, model.getValue().toString());
label.add(new SimpleAttributeModifier("align", "right"));
item.add(label);
}
Can I get at the td to set the alignment?
The trick is that the td is the parent of the item as soon as it is added to the ICellPopulator, so that we can add a modifier to it straight away.
public void populateItem(Item<ICellPopulator<T>> item, String componentId, IModel<T> rowModel) {
IModel<Long> model = (IModel<Long>) createLabelModel(rowModel);
Component label = new Label(componentId, model.getObject().toString());
item.add(label);
label.getParent().add(new SimpleAttributeModifier("align", "right"));
}

trouble in converting Generic List coming from a WCF Service to a DataTable

I am confused on how can I use generic methods to parse generic list into datatable/dataset. My setup:
1. I have a class Customers defined in WCF Service Library.
namespace Wcf.Sample.ServiceLibrary
{
public class Customers
{
public string ID = string.Empty;
public string CompanyName = string.Empty;
public string ContactName = string.Empty;
}
}
2. I use this class to return a generic list from my OperationContract.
namespace Wcf.Sample.ServiceLibrary
{
[ServiceContract]
public interface ICustomerService
{
[OperationContract]
List<Customers> GetAllCustomers();
}
}
3. Consume WCF Service in web client page. On button click I populate the GridView with the list returned from GetAllCustomers(). This works perfectly fine.
GridView1.DataSource = client.GetAllCustomers();
GridView1.DataBind();
4. Now the issue is, for some reason (sort/paging function) I want to actually convert the returned generic list into a datatable. To do so, I have a method that returns me a datatable which I want to bind to a GridView. Here are the methods:
public static DataTable ConvertTo<T>(System.Collections.Generic.List<T> genericList)
{
//create DataTable Structure
DataTable dataTable = CreateTable<T>();
Type entType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
//get the list item and add into the list
foreach (T item in genericList)
{
DataRow row = dataTable.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item);
}
dataTable.Rows.Add(row);
}
return dataTable;
}
public static DataTable CreateTable<T>()
{
//T –> ClassName
Type entType = typeof(T);
//set the datatable name as class name
DataTable dataTable = new DataTable(entType.Name);
//get the property list
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (PropertyDescriptor prop in properties)
{
//add property as column
dataTable.Columns.Add(prop.Name, prop.PropertyType);
}
return dataTable;
}
I am not sure how to call this function? How can I specify the as Customers class which is actually in a webservice? Totally lost. I would appreciate if someone can guide me on the following code, how to make it work.
GridView1.DataSource = ConvertTo<???>(client.GetAllCustomers());
I was able to resolve this issue by modifing the WCF Service itself (although I was reluctant to do so). I modified the GetAllCustomers method to return a datatable instead of generic type. In the service itself, I am converting the generic type into datatable using the same methods:
public static DataTable ConvertTo<T>(System.Collections.Generic.List<T> genericList)
{
//create DataTable Structure
DataTable dataTable = CreateTable<T>();
Type entType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
//get the list item and add into the list
foreach (T item in genericList)
{
DataRow row = dataTable.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item);
}
dataTable.Rows.Add(row);
}
return dataTable;
}
public static DataTable CreateTable<T>()
{
//T –> ClassName
Type entType = typeof(T);
//set the datatable name as class name
DataTable dataTable = new DataTable(entType.Name);
//get the property list
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (PropertyDescriptor prop in properties)
{
//add property as column
dataTable.Columns.Add(prop.Name, prop.PropertyType);
}
return dataTable;
}
Another thing that I noticed is that the following line
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
would always returned null for my type. This was due to the fact that I didn't have any get/set methods in Customers class. I created get/set methods in Customer class and everything worked like a charm.
Thanks to everyone who helped and those who tried to help :)

Resources