How To Use ObjectDataSource With Complex Objects and FormView Control - asp.net

I have a complex object. For example a SCHOOL object that contains a collection of PERSON object. How can I use the ObjectDataSource control with a FormView and flatten the complex object? An example display would be to display the school name and comma separate the students on the page. Is this possible?
I.E.
public string Id
{
get { return m_id; }
set { m_id = value; }
}
public string SchoolName
{
get { return m_schoolName; }
set { m_schoolName = value; }
}
public List(Person> Students
{
get { return m_students; }
set { m_cast = students; }
}

Found the solution here:
Displaying an IGrouping<> with nested ListViews.
I nested a DataList control inside my FormView control to get it working.

Related

Binding labels textProperty to object's property held by another final ObjectProperty

In app I'm bulding I used data model presented by James_D here:
Applying MVC With JavaFx
I just can find a way to bind labels text to property of object held in DataModel
Data is structured like this:
model class Student
//partial class
public class Student {
private final StringProperty displayName = new SimpleStringProperty();
public final StringProperty displayNameProperty(){
return this.displayName;
}
public Student(){
}
public final String getDisplayName() {
return this.displayNameProperty().get();
}
public final void setDisplayName(String displayName) {
this.displayNameProperty().set(displayName);
}
}
Student instaces are held by StudentDataModel class
public class StudentDataModel {
// complete student list
private final ObservableList<Student> studentList = FXCollections.observableArrayList();
private final ObjectProperty<Student> selectedStudent = new SimpleObjectProperty<>(new Student());
public final Student getSelectedStudent() {
return selectedStudent.get();
}
public final ObjectProperty<Student> selectedStudentProperty() {
return selectedStudent;
}
public final void setSelectedStudent(Student student) {
selectedStudent.set(student);
}
}
StudentList is displayed by Table View, there is change listener that sets selectedStudent like this:
public class TableViewController {
public void initModel(StudentDataModel studentDM) {
// ensure model is set once
if (this.studentDM != null) {
throw new IllegalStateException("StudentDataModel can only be initialized once");
}
this.studentDM = studentDM;
tableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
studentDM.setSelectedStudent(newSelection);
}
});
}}
There is another controller ActionsBarController that has label to display selected student (this seems redundant, but there is option for selecting multiple objects to perform bulk operations).
StudentDataModel is initialized properly (I can see it in debuger) but below doesn't do anything:
chosenStudentLabel.textProperty().bind(studentDM.getSelectedStudent().displayNameProperty());
//this shows class name with instance number changing correctly
chosenStudentLabel.textProperty().bind(studentDM.selectedStudentProperty().asString());
I could inject ActionsBarController to TableViewController and change label text from change Listener there, but this seems counter productive with data model.
What am I doing wrong?
Your code doesn't work, because you call (and evaluate) getSelectedStudent() at the time the binding is created (i.e. when you initialize the model). As a consequence, you only bind to the displayName property of the student that is selected at that time. (If nothing is selected, you'll get a NullPointerException.) The binding will only change if that initially-selected student's display name changes; it won't change if the selection changes.
You need a binding that unbinds from the old selected student's display name, and binds to the new selected student's display name, when the selected student changes. One way to do this is:
chosenStudentLabel.textProperty().bind(new StringBinding() {
{
studentDM.selectedStudentProperty().addListener((obs, oldStudent, newStudent) -> {
if (oldStudent != null) {
unbind(oldStudent.displayNameProperty());
}
if (newStudent != null) {
bind(newStudent.displayNameProperty());
}
invalidate();
});
}
#Override
protected String computeValue() {
if (studentDM.getSelectedStudent() == null) {
return "" ;
}
return studentDM.getSelectedStudent().getDisplayName();
}
});
Note that there is also a "built-in" way to do this, but it's a bit unsatisfactory (in my opinion) for a couple of reasons. Firstly, it relies on specifying the name of the "nested property" as a String, using reflection to access it. This is undesirable because it has no way to check the property exists at compile time, it requires opening the module for access, and it is less good performance-wise. Secondly, it gives spurious warnings if one of the properties in the "chain" is null (e.g. in this case if the selected student is null, which is will be initially), even though this is a supported case according to the documentation. However, it is significantly less code:
chosenStudentLabel.textProperty().bind(
Bindings.selectString(studentDM.selectedStudentProperty(), "displayName")
);

ASP.NET MVC Conditional ViewModel Abstraction

I am new to ASP.NET MVC and I am stuck on a point. I am working on a classified site. My situation is, I have a lot of categories in which a user can post their ads and each ad category have different View. I have created a Controller Action like
public ActionResult PostAd(string CategoryName, string SubCategoryName)
{
if(categoryName == "Vehicle" && SubCategoryName == "Cars")
{
var model = new CarAdViewModel();
// set CarAdViewModel properties...
return View("CarAdCreateView", model);
}
else if(categoryName == "Vehicle" && SubCategoryName == "Bikes")
{
var model = new BikeAdViewModel();
// set BikeAdViewModel properties...
return View("BikeAdViewModel", model);
}
else if(categoryName == "Property" && SubCategoryName == "RentHouse")
{
var model = new RentHouseAdViewModel();
// set RentHouseAdViewModel properties...
return View("RentHouseAdViewModel", model);
}
else................... so on and so on
}
My problem is I have huge number of Categories and Sub Categories almost 60+. And if I keep on coding like above for 60+ categories and subcategories, my PostAd method is going to blast and become unmanageable.
Please tell me some best practice or pattern which can bring me out of this problem.
Unfortunately, some of what you are doing cannot be avoided. There needs to be some form of model and view selection based on category.
Use a factory pattern. Create a base class:
public abstract class BaseCategory
{
public abstract string GetViewName();
public abstract Object CreateModelFromFormData();
}
For each category, create a sub-class derived from BaseCategory and implement the abstract functions.
In your action, do the following:
public ActionResult PostAd(string categoryName, string subCategoryName)
{
BaseFactory factory;
if (categoryName == "Vehicle")
{
if (subCategoryName == "Cars")
{
factory = new CarsFactory();
}
else ...
}
else ...
return View(factory.GetViewName(), factory.CreateModelFromFormData());
}
I have a couple reasons for this schema:
I am purposefully using if/else for the factory selection. Your controller is going to be created and re-created for every action call. So pre-populating a list will constantly and needlessly create objects for categories that will not be selected. A simple if/else will be more efficient. If you want to prevent the if/else, you can put your factories in a Dictionary and select based on the categories, but that would be a lot of needless constructor actions.
I made the CreateModelFromFormData a function because I assume you'll need to copy data from the posted form data. This may require passing in data, but I left the function parameterless.
I used base/derived classes because the copying of the form data will probably need to be custom from the model being created and the form data being posted. Also, saving to persistent storage (file or database) may be category-specific as well.
It would be one of some possible solutions
public class PostAdData
{
public string CategoryName;
public string SubCategoryName;
public string ViewName;
public Type Model;
}
public class PostController : Controller
{
private readonly List<PostAdData> _theData;
public HomeController()
{
_theData = InitializeData();
}
public ActionResult PostAd(string categoryName, string subCategoryName)
{
var data = _theData.FirstOrDefault(c => c.CategoryName == categoryName && c.SubCategoryName == subCategoryName);
if (data != null)
{
var model = Activator.CreateInstance(data.Model);
return View(data.ViewName, model);
}
return View("Error");
}
[NonAction]
public List<PostAdData> InitializeData()
{
var result = new List<PostAdData>
{
new PostAdData
{
CategoryName = "Vehicle",
SubCategoryName = "Cars",
ViewName = "CarAdCreateView",
Model = typeof (CarAdViewModel)
}
};
return result;
}
}
You should make this data driven. You create a lookup table that has a compound primary key of category and subcategory. Then it has a table with View in it. Then you simply ad rows for each category/subcategory/view combination.
If you absolutely don't want a database, then you can use a simple hashset or dictionary.
var views = new Dictionary<Tuple<string,string>,string>();
views.Add(new Tuple<string,string>("Vehicle", "Cars"), "CarAdCreateView");
Then in your PostAd you just lookup the correct view.
What a beautiful solution on www.asp.net to my question, here is the link : http://forums.asp.net/t/1923868.aspx/1?ASP+NET+MVC+Conditional+ViewModel+Abstraction
Edit:
My code is :
public class AdsController : Controller
{
private readonly IAdService _adService;
public AdsController(IAdService adService)
{
_adService = adService;
}
public ActionResult PostAd(string Category, string SubCategory)
{
//Here I will call
var strategy = GetStrategy(CategoryName, SubCategoryName);
strategy.FillModel(_adService );
return View(strategy.ViewName, strategy.Model);
}
}

How to data bind Entity Framework objects to a FormView

I am data binding to many FormView controls using EF entity instances, but I have to resort to this ridiculous kludge in order to achieve what I want without using EntityDataSource controls:
propertyHeaderSection.DataSource = new List<PropertyDetailsModel> { _propertyDetails };
I suspect I will have to derive my own control from FormView and enable it to accept an almost POCO as a data source. Where do I start?
This is my implementation, sort of the same idea as patmortech, but i also found out that the ValidateDataSource method on the BaseDataBoundControl is what throws the exception at run-time if your datasource isn't enumerable.
public class CustomFormView : System.Web.UI.WebControls.FormView
{
public override object DataSource
{
get
{
if (!(base.DataSource is IEnumerable))
return new[] {base.DataSource};
return base.DataSource;
}
set
{
base.DataSource = value;
}
}
// This method complains at run time, if the datasource is not
// IListSource, IDataSource or IEnumerbale
protected override void ValidateDataSource(object dataSource)
{
//base.ValidateDataSource(dataSource);
}
}
EDIT:
Considering the suggestion, i've made some changes to the way i check if the assigned DataSource is enumerable or not. I have also managed to create a sample app (VS 2010 Solution) to demo the changes. The app can be downloaded from http://raghurana.com/blog/wp-content/attachments/FormViewDataProblem.zip
In short this is what i am checking to ensure that the existing datasource can be enumerated already or not:
public static bool CanEnumerate( this object obj )
{
if (obj == null) return false;
Type t = obj.GetType();
return t.IsArray ||
t.Implements(typeof (IEnumerable).FullName) ||
t.Implements(typeof (IListSource).FullName) ||
t.Implements(typeof (IDataSource).FullName);
}
Please feel free to suggest more changes, if this isnt quite the desired functionality. Cheers.
Not sure it's the best idea in the world, but this is how you could derive from FormView to allow single object data source values. It basically does the same check that the ValidateDataSource does internally, and then creates a list wrapper for the item if it's not already a valid type.
public class SingleObjectFormView : System.Web.UI.WebControls.FormView
{
public override object DataSource
{
get
{
return base.DataSource;
}
set
{
//will check if it's an expected list type, and if not,
//will put it into a list
if (! (value == null || value is System.Collections.IEnumerable || value is System.ComponentModel.IListSource || value is System.Web.UI.IDataSource) )
{
value = new List<object> { value };
}
base.DataSource = value;
}
}
}

ASP.Net Custom Control

I am developing a custom control that needs to display a dropdownlist as a composite control.
The drop down list gets populated from a Rest web service. The problem I am facing is that the dropdownlist only has DataTextField and DataValueField but I need a way of storing more values in the control i.e. I have a couple of other properties I need to access for the selected item.
What is the best way of going about this?
Here is the code I have so far:
[ValidationProperty("SelectedValue")]
public class SelectSurveyControl : Panel
{
private DropDownList ddlSurveys;
public string SelectedSurveyId
{
get
{
return SelectedValue;
}
}
public string SelectedSurveyJavascriptEmbedCode
{
get
{
return this.ddlSurveys.SelectedItem.Attributes[""];
}
}
public string SelectedValue
{
get
{
return ddlSurveys.SelectedValue;
}
set
{
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
ddlSurveys.SelectedValue = value;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnInit(e);
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
IList<Survey> surveys = GetSurveys();
this.ddlSurveys.DataSource = surveys;
this.ddlSurveys.DataTextField = "title";
this.ddlSurveys.DataValueField = "id";
this.ddlSurveys.DataBind();
ddlSurveys.SelectedValue = this.SelectedValue;
ddlSurveys.CssClass = "umbEditorTextFieldMultiple charlimit";
ddlSurveys.Attributes.Add("SurveyId", SelectedSurveyId);
ddlSurveys.Attributes.Add("JavascriptEmbedingCode", SelectedSurveyId);
this.Controls.Add(ddlSurveys);
}
public IList<Survey> GetSurveys()
{
...
}
}
Try using a string join/split to store and retrieve the various values, then you don't have to customize your dropdown list very much.
For Example:
Text: Some Title
Value: 1|testing test|2/12/2010
This will let you store as many values as you want, so long as you choose an appropriate character to join and split on. I usually use the bar, as in my example above.
Side Note: I was looking at your selected value set handler and it needs some tweaking. You shouldn't check for a null drop down list, instead you should call EnsureChildControls() before each get and set instead. Make sure you override the CreateChildControls() method and create your controls there.
You could use a hidden field and iterate thru a copy of the returned Surveys like this:
foreach(Survey s in Surveys){
string val = s.id + ":" + s.<property1> + ":" + s.<property2>;
hiddenField.Value += val +",";
}
When you need to read from the hidden field, you use String.Split to separate the values into arrays using ',' as the separator and in each array, you split again using ':'.
In the first split Array1[0] who be the survey id and Array1[n!=0] would be the properties of the Survey with the id = Array1[0]. Array[n!=0] would then be split into Array2.
I would suggest handling empty property values with an empty string or something or else you might end up with unequal lengths especially if you specify StringSplitOptions.RemoveEmptyEntries.
Agricfowl

Asp.Net Gridview - One Column is List<string> - Want to Show Only The Last Item

I have an Asp.Net GridView. One of the Columns is a List, but I only want to show the Last Item in the list. How can I do this?
List<string> Column1
I am binding the Gridview to a business object:
public Class GridObject
{
List<string> Column1 { get; set; }
}
EDIT
This worked, but is it the best solution:
<%# ((List<string>)Eval("Column1"))[((List<string>)Eval("Column1")).Count - 1] %>
I would add a property to the object you are binding to, and use that property instead of the list property in your binding.
public Class GridObject
{
List<string> Column1 { get; set; }
public string Column1LastValue
{
get
{ // return Column1.Last(); if linq is available
return Column1[Column1.Count-1];
}
}
}
Edit: Adding a presentation wrapper allows you to unit test what will be displayed. You are doing a translation in the view, which is OK, but since you technically have some logic happening to translate your business object to something proper for display, you would likely want to unit test that translation. Then, any formatting you want to apply to any of your business object fields is wrapped in a testable class, rather than hidden on the untestable view. Here is a sample of how this could be done:
public class GridObjectView
{
private GridObject _gridObject;
public GridObjectView(GridObject gridObject)
{
_gridObject = gridObject;
}
public string Column1
{
get
{
return _gridObject.Column1.Last();
}
}
}
Then to do the databinding, you could do this:
List<GridObject> data = GetGridData();
grid.DataSource = data.Select(g => new GridObjectView(g));
grid.DataBind();
Your best bet is to create a template column and use an inline script to retrieve the value from the list:
<%= ((List<string>)DataBinder.Eval("Column1"))[((List<string>)DataBinder.Eval("Column1")).Count] %>
Or you could store the result in the text of a label or a literal.
Hope that helps

Resources