Using Mode=TwoWay and AutogenerateColumns=True in Silverlight 3 DataGrid - silverlight-3.0

My List<BusinessObject> has some public properties that I want to bind to columns in a DataGrid. Unfortunately, the names of the public properties are not good and I may not even know what they are until runtime. For this reason, I set AutoGenerateColumns=True and interecept each DataGridAutoGeneratingColumnEvent so I can inspect what it is and either cancel it, hide it, or name the header something else.
It works great but I cannot figure out how to set the Mode=TwoWay so that my INotifyPropertyChanged events get fired once all the columns are generated and somebody edits a cell.
Bonus question:
On navigating up and down the rows of the grid, does the grid's datacontext automatically get set with that row's BusinessObject?

Thanks to this post, I learned that the binding happens on DataGridTextColumn. So the way to set the Mode at runtime is:
1 private void DataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
2 {
3 DataGridTextColumn tc = (DataGridTextColumn)e.Column;
4 tc.Header = "Custom Header";
5 tc.Binding.Mode = BindingMode.TwoWay;
6 }
Now that I have TwoWay binding, I have to figure out how changes make it back to my BusinessObject.

If the binding is correct your business objects will automatically receive the required updates. To do you binding programmatically you might need a little more code, something like:
...
Binding binding = new Binding("Propertyname");
tc.binding.Mode = BindingMode.TwoWay;
...

Related

Entity Framework Database First Property Validation

I'm using EF database first and with MVC.
I'm wanting to add some validation on a property to compare its old value to its new one and report a validation error to the MVC ModelState if there is a problem.
This would be easy enough using code first and validating using 'set' on the property. However I can't do this using database first because its auto generated.
I've looked at using IValidatableObject and the validate() method however by then the value has already been changed on the property so I can't see the old one anymore to compare to.
Short of creating a method to pass the new value into first to check it, I can't think of another way.
Any suggestions?
Thanks
If you want to compare a new value to an old value then you are going to have to grab the values from the database first (before updating) and compare them:
[HttpPost]
public ActionResult Update(MyObject myObject)
{
var oldObject = db.Objects.FirstOrDefault(o => o.Id == myObject.Id);
//Compare oldObject.Value to myObject.Value
}
You could still use IValidatebleObject and pass in the objects that you need to keep that logic outside the controller.
Its not the ideal and this has started illustrating some of the weaknesses in Model and DB first but here's how I ended up doing it.
I decided to change the property in my model so that set was private and then create a separate method in the partial class to set the value. The validation is then all done in that method.
Thanks for your help anyway

Dealing with complex models in ASP.NET MVC

I have a model that looks like this:
Business
- Branch
- Phone(*)
- Phone Type
- Number
- Opening hours (*)
- Days in week
- Working period (*)
- From time
- To time
- Custom field (*)
- Name
- Value
- Address
- Address line
- City
- State
- Zip
- Yada yada
I created Editor Templates for each of the class types above.
I want to have a common Business editor template with a submit form that posts the entire structure to a single action and saves it, both for an existing or new entity.
Is Editor Templates the right approach? How do I submit the form along its entire downline?
How do I make Add and Remove buttons to add/remove phone numbers within the form?
How do I order items in the collection (i.e. I want to have arrows near each phone number so the user can move it up or down in the client list, then handle the saving on server, for that I already have the solution).
Bottom line, my issue is how to get the right values posted back to the server, and how to modify the inner collections on the client. Once the proper data is on the server in this way or another I'll know how to deal with it. My problem is the client side and the correct way of data submission.
Update
I saw this answer, which basically answers the 1st part of my question, tho the latter two still remain (add-remove-order buttons - manage collections on client).
My problem is not how to add/remove/reorder rows at the client's DOM, but how to modify the client data and then receive it in the server in the action the looks like this:
[HttpPost]
public ActionResult Save(Business business)
{
/// blah blah
}
Update
Here is how I try to shove in the new data:
View:
#Ajax.ActionLink("Add", "AddCustomField", new AjaxOptions { UpdateTargetId = "customFields", InsertionMode = InsertionMode.InsertAfter })
Action:
public PartialViewResult AddOpeningTimes()
{
var ot = new OpeningTimes();
ot.WorkingPeriods.Add(new WorkingPeriod());
var e = EditorFor(ot);
//just here for debugging, the values are both empty strings
e.ViewData.TemplateInfo.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix;
return e;
}
//this method is on the base controller:
protected PartialViewResult EditorFor<TModel>(TModel model)
{
return PartialView("EditorTemplates/" + typeof(TModel).Name, model);
}
The thing is the name for the appropriate fields are not enumerated as needed (Branches[0].CustomField[0].Key), instead, it's just Key.
As far as i know, there is no 'simple' way to do this.
Add button - you have to wire javascript that creates a part of form (eg. phone type select and phone text box) and set its id/name. Basically you find the last item in the form, which will have name of Phone[x].PhoneType, and set the values on new part of form to appropriate values with x + 1.
An option to avoid generating the part the form by yourself is to create a hidden 'template' and copy that. Then change id and name.
Remove button - if you simply deleted items from DOM, you would create gaps in the sequence and MVC doesn't know how to deal with that. One possible approach is to mark items in the form as deleted using a hidden field, then handling that on the server.
Reordering - I would add a property called Order to whatever needs this feature, then render it as hidden and change using javascript when reordering. You also have to set it appropriately when adding an item.
Useful properties in these situations are also: IsNew, IsUpdated - along with IsDeleted allow for relatively easy processing on the server.
Of course, if you have nested collections each needing add/remove/reorder functionality it will be kind of difficult to do and debug.
UPDATE
The action rendering the partial view can't know what the html prefix should be, because it doesn't have the context (that the parent is Branch object etc.).
If you want to use AJAX, i would recommend sending the html field prefix as a parameter (public PartialViewResult AddOpeningTimes(string htmlPrefix)). htmlPrefix could be Branches[0].CustomField[last_custom_field + 1].. It's probably the cleanest way to achieve what you want, even if it's in fact not very clean.

Caliburn Micro Update RowDetails of a Datagrid

Let me explain my problem. I am working with Caliburn Micro and have a datagrid where a ObservableCollection is being binded as an Itemsource:
private static ObservableCollection<Models.GamesProperties> _dgGames;
public ObservableCollection<Models.GamesProperties> DgGames
{
get { return _dgGames; }
set
{
_dgGames = value;
NotifyOfPropertyChange(() => DgGames);
}
}
So, when I load my data from my database, the collection will be filled and binded to my datagrid (called DgGames)
Everything is fine so far. I have defined for each DataRow "DataRowDetails". Contains an ImageSource, some TextBlocks and so on. The mentioned collection above contains also those DataRowDetails.
I have now created another View and ViewModel for a dialog window in order to change the DataRowDetails. It does work, but the datagrid (DgGames) won't apply these changes. It does not show any update i have done to the collection. What am I missing ?
Thanks in advance to those who can help me.
If you need more information, feel free to ask.
In order to see modification on the fly in the grid too, Models.GamesProperties has to implemen properly INotifyPropertyChanged. Since you are using Caliburn, you probably want to derive it from PropertyChangedBase.

ASP.Net MVC DefaultModelBinder not binding properties on POST

I'm facing a really strange problem that has me smoked.
I have a fairly simple scenario where I have a strongly typed view that is correctly populated from the controller on the GET, but then when it POSTS the form to the controller, the Reqeust is full of all the right values and right key names for the default model binder to correctly populate one of my model objects, and the DMB creates the correct opject, but it never populates any of the properties, they're all in their default state.
This was working before, the only changes I can think of were that I tried a custom modelbinder (then removed it; double checked to make sure I'm not still using that), and I refactored the model to have a base class with some of the props.
Any thoughts?
A very similar scenario - that the DefaultModelBinder is - essentially - not binding to your model, arrise if you would give your bound model object the same name as one of its properties:
Model
Public Property ArbitraryName1 As Integer
Public Property Answer As String
Public Property ArbitraryName2 As Boolean
View
<p/> ... #Model.ArbitraryName1
<p/> Answer: #Html.TextBoxFor(Function(model) model.Answer)
<p/> ... #Html.CheckBoxFor(Function(model) model.ArbitraryName2)
Controller
<HttpPost()>
Function Index(answer As Model) As ActionResult
' answer is Nothing
End Function
(Using ASP.NET MVC 3)
Got it. The model had been refactored in a way which naturally affected the ability of the mdoel binder to populate it.
The name of your input param do not have to be equal to some property name of the object. Remember that all data coming as an array of name -> value and the default binding use the names for make the relation work.
I had this behaviour arise by moving two properties from the top of the class to further down. I still can't work out why this stopped the binding of a third property from working (so this isn't a solution so much as a 'watch out for') but I repeated the change multiple times and each time the binding went from working to not working.
I also found that after making this change I sometimes had to 'Clean' the solution for the binding to start working again.

ASP.net MVC - How to persist model over various views

Situation: In some project management software written in asp.net I have a create project page (working fine). I need to add to this the ability to add tasks from a list of templates to this project pre-creation BUT the list of available tasks is dependent on some values sitting in the create form.
My abstract solution is this:
I have a "Create" view and an "Add Tasks" View - both strongly typed to a composite viewModel defined in the controller
My Create method checks which button was used to call it - if the
button was "Add Tasks" it then renders the AddTasks view, passing the model in from the create view, again all in the same controller.
The AddTasks View posts to the Create view with one of two buttons, one loads the view and the other causes an actually DB save.
My Problem is this:
The different views use different properties of the same model, but in passing this model between them, the data is reset (in any case reload or save).
I am guessing this is happening from auto binding of data - though I thought fields not present on the form would not overwrite existing model data passed down.
There is hardly any code in the controller manipulating the model at present - It is only passed from view to view in these cases.
This is the controller code:
// POST: /Project/Create/<viewModel>
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id,id")] ProjectViewModel model)
{
if (model.SubmitValue == "Create")
{
try
{
model.Project.Id = Guid.NewGuid();
model.Save(this.User.Identity.Name);
return this.RedirectToAction("Details", new {id = model.Project.Id});
}
catch (Exception e)
{
this.ModelState.AddModelError(e.ToString(), e.ToString());
}
return View(model);
}
if(model.SubmitValue == "AddTasks")
{
return this.View("AddTasks",model);
}
return this.View(model);
}
//POST: /Project/AddTasks/ + model
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddTasks([Bind(Include = SelectedCarrierTasks")]ProjectViewModel model)
{
return View(model);
}
The Question is: How do I maintain the state of the model across these views until it finally save it?
I would prefer to avoid any hackish (TempData) or JS dependant solutions, but I am not closed to these if they are really the best solution.
Thanks,
Adam Tolley
One simple solution is to persist the ViewModel object in a Session variable and bind the View from this source.I ts certainly not the most elegant solution. Another option, and probably less elegant one is persist this model data in the database, with some temporary/unsaved flag.
The problem is that when you display the add tasks view you're not providing fields for your "Project" object therefore the ModelState loses the data related to the project, you will need to provide this fields to ensure you're not loosing that data.
You don't need to display this fields they can be of type hidden and they will preserve the value. Just make sure that if you will be binding to a view model you will need to name this fields correctly like this Model.Project.Property.
Perhaps I am trying to solve the wrong problem (ala Bruce Eckel). I am going to try to move to a structure that needs this sort of fuzzy boundary less. I don't want to adopt a REST paradigm only to shoe-horn it into a stateful application.
Possibly these controls belong on the same page, and I can use some JQuery goodness to put in a tab pane for easiness on the eyes.
Thanks to those who answered, I found each useful and will try to remember to up-vote them as soon as I have some more rep.
I can't comment on other peoples questions at the moment, but the only real option is the session if you want to persist an objects state during web requests, or serializing it and placing it in a hidden field.
Or a final option would be to change the way your pages work so you can save the object after each request...
If your using nHibernate then you might want look into the Conversations pattern, but this just essentially saves the nHibernate session into the asp.net session anyway...

Resources