When passing an ObservableCollection using MVVM Light's messaging, is a copy passed or is a reference passed? - mvvm-light

We're working on a new WPF app, using MVVM Light. We've got a customized ObservableCollection which starts as being bound to a datagrid. According to the project's specification we have to start on a form showing the datagrid and then when a user selects a row we show the user a detail view in another form. At this point we're getting the selected row and assigning that to an object which we assign to a MVVM Light message so that the detail view will be able to display the record's details.
Now that we're getting into this we've encountered a complication. The specs require that the detail view be able to navigate through the collection, even though at this point it doesn't have the collection. We had through we could accomplish this through an interface we defined that we called IRecordService, implementing it for each type of record we work with. However the problem is that the record has no idea if its the first record in the collection, the last one, etc. And that's necessary because of buttons on the detail form where people can navigate through the collection. We've been trying to do this with, for example CustomerRecordService, but so far that hasn't worked out. Perhaps it will if we keep at it.
But I've been wondering, what if instead of creating an object that has the selected record in it which gets passed into a message, we instead pass the whole collection and the key to the selected record into the message which then is caught by the detail viewmodel? My co-workers primary concern is how is the ObservableCollection passed, under these circumstances? Is a copy of the ObservableCollection passed or a reference to theObservableCollection that's in the listing viewmodel? I would think its a reference, but wanted to ask to make sure I'm right, or not.

It has to be just a reference. Otherwise messenger would have to know how to clone every single object. But you can easilly check it. After you get an object in your details viewmodel, change it. Add something, remove something and change some parameter of some objects in the collection. Then check if it has been changed in the main form with the grid.

Related

How to programmatically link newly created records to a record from another table

Thanks in advance for your advice!
Background
I’m creating a database to track orders placed by customers.
An ‘Orders’ table stores general details about an order like the customer’s name, order date, and delivery-required date.
A separate ‘Order_Items’ table stores the specific items that the customer has ordered.
The is a one-to-many relationship between the ‘Orders’ table and ‘Order_Items’ table, i.e. one ‘Order’ can have many ‘Order_Items’, but each ‘Order_Item’ must be associated with only one ‘Order’.
Current State
Currently, I have a page where the user creates a new ‘Order’ record. The user is then taken to another page where they can create as many ‘Order_Item’ records as are needed for the order.
Desired State
What I would like to achieve is: When a user creates new ‘Order_Item’ records, it automatically allocates the current ‘Order’ record as the foreign key for the new ‘Order_Item’ record.
What I've Tried So Far
Manual Action By The User: One way of establishing the link between an 'Order' and all of its 'Order_Items' would be to add a drop-down widget which which effectively asks the user something like "Which order number do all of these items belong to"? The user's action would then establish the link between the two tables and associate one 'Order' with many 'Order_Items'. However, my goal is for this step to be handled programatically instead.
Official Documentation: I’ve referred to the offical documentation which was useful, but as I'm still learning I don’t really know exactly what to search for. The prefetch feature appeared promising but does not actually establish a link; it just loads associated records more efficiently.
App Maker Tutorials: I found an App Maker tutorial which creates an HR App where a user can create a list of ‘Departments’, then create a list of ‘Employees’, and then link an ‘Employee’ to a ‘Department’. However, in the example app this connection is established manually by the user. In my desired state I would like the link to be established programatically.
Manual Save Mode:
I’ve also tried switching to manual save mode so that the user has to create a draft ‘Orders’ record and then several draft ‘Order Items’ records and then save them all at once. However, I haven’t managed to make this work. I’m not sure whether the failure of this approach is because 1) I’m try to create draft records on more than one table, 2) I’m just not doing it correctly, or 3) I thought I read somewhere that draft records are deprecated.
Other Ideas
I'm very new to this field and am may be wrong, but I have a feeling I may need to use some scripting to establish the link. For example, maybe I could use a global variable to remember which 'Order' the user creates. Then, for each 'Order_Item' I could use the onBeforeCreate event to trigger a script that establishes the link between the 'Order_Item' and the 'Order' that was remembered from the previously established global variable.
Updated Question
Thanks Markus and Morfinismo for your answers. I have been using both answers with some success.
Morfinismo: I've successfully used the code you directed me to on existing records but cannot seem to get it to work for newly created records.
For example:
widget.datasource.createItem(); // This creates a new record
var managerRecord = app.datasources.Manager.item; // This sets the Manager of the currently selected parent record as a variable successfully.
var teamRecord = app.datasources.Teams.item; // This attempts to set the Manager of the currently selected record as a variable. However, the record that was created in line 1 is not selected. Therefore, App Maker does not seem to know which record this line of code relates to and returns the error Cannot set property ‘Manager’ of null.
// Assign the manager to the team.
teamRecord.Manager = managerRecord; // This successfully assigns the manager but only in cases where the previous line of code was successful (i.e. existing records and not newly created ones).
Do you have any suggestions or comments on how to apply this code to records that are created by the initial line of code in line 1?
I have found the easiest way to create related items for situations such as yours is to actually import a form with the datasource set to Parent: Child (relation) or Parent: Child (relation) (create). So in your case the datasource would need to be set to Order: Order_Items (relation).
You can get this accomplished in two different ways using the form widget wizard:
Option 1:
If your page datasource is set to Order_Items, drag your form on your page.
In the datasource selection section, your datasource in the form widget should default to `Inherited: Order_Items'. Click the 'Advanced' button in the bottom left corner, then from the datasources category find Order as your datasource, then select relations in the next field, and then Order_Items in the next field, choose 'Insert only' or 'Edit' form and then the appropriate fields you want in the form.
Now every item that gets created in that form will automatically be a child record of the currently selected record in your Order datasource.
Option 2:
If your page datasource is set to Order, drag your form on your page.
In the datasource selection section, your datasource in the form widget should default to Inherited: Order. Scroll down in your datasource selection section until you find Order: Order_Items (relation), then choose 'Insert only' or 'Edit' form and then the appropriate fields you want in the form.
Now every item that gets created in that form will automatically be a child record of the currently selected record in your Order datasource.
In your Order model, make sure that the security setting is set appropriately that a user is allowed to create relations of Order_Items in Order. That is the simplest approach in my opinion since you don't have to hard code the parent into your form or client/server scripts. It is automatically based on the currently selected parent, and is essentially doing the same thing that #Morfinismo explained in the client script section.
The comment I placed under your question included a link to the official documentation that explains what you need. Anyways, your question is not clear enough to determine whether you are creating the records via client script or server script, hence this is a very general answer.
To manage relations via client script:
var managerRecord = app.datasources.Manager.item;
var teamRecord = app.datasources.Teams.item;
// Assign the manager to the team.
teamRecord.Manager = managerRecord;
// Changes are saved automatically if the datasource in auto-save mode
// Add a team member to a Manager's team.
// Note: Retrieve Members on the client before proceeding, such as by using prefetch option in datasource - datasources Team -> Members)
var engineerRecord = app.datasources.TeamMember.item;
teamRecord.Members.push(engineerRecord);
To manage relations via server script:
// Get the record for the Team to modify.
var teamRecord = app.models.Teams.getRecord("team1");
// Assign a manager to the Team.
var managerRecord = app.models.EmployeeDB.getRecord("manager1");
teamRecord.Manager = managerRecord;
// Note: The new association is not saved yet
// Assign a team member to the Team.
var engineerRecord = app.models.EmployeeDB.getRecord("engineer1");
teamRecord.Members.push(engineerRecord);
// Save both changes to the database.
app.saveRecords([teamRecord]);
The above information is taken directly from the official documentation, which like I said, I referred to in the comment I placed under your question.

Can you create a joined view from task list and related content?

I'm trying to create a view of approval tasks that also includes a column from the related form library. I have tried creating a linked data source between the tasks list and the form library, but have trouble finding much information on creating linked views with the task list.
I have tried:
http://deannaschneider.wordpress.com/2012/07/25/joining-the-task-list-with-related-content-in-a-dvwp/
without luck - it just tells me "there are no items to show in this view." which I assume means it couldn't be joined correctly with the specified table.
I am using the standard approval workflow.
Here is the closest solution I've found so far
1.) Create task form fields in SharePoint designer.
2.) Go into Approval(1) to add the task form fields.
3.) Click 'Change the behavior of a single task'. Add 'Set task field' action in the Before a task is assigned section to set the task form fields to get the value of Current Item:ID.
4.) Use the new task field to create your subview on your linked datasource
While not optimal - and it created me many different problems - I was able to create the view desired.
Hopefully someone will come up with a better solution.

Razor MVC - Object Edit Field Change Log

Users modify a DB object in an edit form that I have, pretty straight forward.
I need to implement a 'change log' on this object. I need to record which fields where changed and what they were before and after. I'm using Razor MVC.
I've done this by writing triggers for the table on update/delete. On update/delete of a record, the trigger pushes the record to a History table, in a History database. This creates the change log. Then you would just need to display it; to identify the change would require evaluating each and every field.
There's nothing already built that wold do this for you that I know of.

Make Gridview interact with something other than properties

We're planning to create a web application where users can build custom "forms," choosing which fields they would like, and how the data in those fields should be represented. Users can then fill out these forms in a DetailsView-like control, thereby creating "documents." The documents can be shown in a DetailsView, or certain fields of several of them can be shown in a GridView. At least, that's the idea.
The problem is that GridView and DetailsView seem to be specifically designed to access Properties on objects that come out of a DataSource. Since we want to have completely arbitrary forms, we can't restrict ourselves to building a class with Properties to represent each field. We have to be able to have any number of dynamically-specified fields on a form.
Is there any way to leverage the existing controls so we don't have to re-implement paging, sorting, and all the other things that GridViews are already set up to do, or will I just have to create my own GridView-like control from scratch?
Edit:
More specifically, the difficulty I am having is in getting inline editing to work on the GridView. For example, let's say that one of the "fields" that is added to a "form" is a calendar field, which should display a date as text in read-only mode, and display a calendar control in edit mode. When the "save" button is clicked, the date selected by the calendar control needs to be saved to the database as the new value for the given field of the given document (i.e. instance of the form). My initial idea was to create a special DataControlField class which, given a form field key, would know how to databind thusly:
FormDocument doc = DataBinder.GetDataItem(cell) as FormDocument;
FormFieldValue fieldValue = doc.FieldValues[FieldKey];
fieldValue.AddReadOnlyControls(cell);
... instead of:
Object dataObject = DataBinder.GetDataItem(cell);
cell.Text = DataBinder.GetPropertyValue(dataItem, FieldKey);
This would probably work for displaying the field values, but if the user tries to edit and save one of the FormDocuments I don't know how I would convince the GridView to do something like this:
doc.FieldValues[FieldKey] = newValue;
Currently, the API for DataControlField uses the ExtractValuesFromCell method to put the property name and value into an IOrderedDictionary. Those values are then applied to the given properties of the objects in the GridView's databound IEnumerable. The problem is, I can't work with properties of an object because in this case the object needs to have a completely arbitrary number of fields.
A GridView can be bound to any object that implements IEnumerable. The advantage of using one of the xDataSource controls is that it can implement paging and sorting for you without any additional code, but you certainly aren't tied to them.
If I understand your question correctly, you do not know the number of columns to display in the GridView until runtime. In that case, I would recommend building an array from your form data and binding the grid to that. You will have to implement paging and sorting yourself.
The DetailsView is not very customizable so you should take a look at the FormView. However, I think you are going to end up dynamically adding controls to whatever container you use.
What you need is totally dynamic GridView. I quess you would have to extend it with the controls ( functionalities ) in your description
Here's what I ended up doing:
I created a new data type that contained a Dictionary of answers, indexed by Field ID.
I created a new type of DataControlField with a FieldId property, which retrieves the proper answer value for that FieldId from the Dictionary mentioned above.
I added data type and data keys properties to this custom DataControlField and overrode the ExtractValuesFromCell method so that it could create a new instance of the answer class and add those values to a Dictionary, which was stored under the property name by which that dictionary would be found in the new data type mentioned in step 1.
I used my own GridView class, used the .NET Reflector to see how the normal GridView calls the ExtractValuesFromCell method, and then changed that so that it would pass the same Dictionary object in to each DataControlField. This way, each field could add to the same Dictionary, rather than replacing the Dictionary that the last one had added under the same property name.
I used a DataFieldGenerator to generate the one of my custom DataControlFields for every field associated with a given form, and I told the GridView to use that DataFieldGenerator to auto-generate its fields.
I set up my ObjectDataSource so that it would know how to save all the answer values from an object of the type mentioned in step 1.
It was tricky, but worthwhile.

ASP.NET and objects

Let's say I have a class Person, with a string[] nickNames, where Person can have 0 or more nicknames stored. I want to create an asp.net page where a user can go and add/edit/delete nicknames.
Question is- how to i persist the Person object between postbacks? I query the DB and create the object then display it on a form, but then the user has the option to edit/delete fields of that object.. once the page is displayed with the fields of Person, how do I update that object with the changes the user made, to store to db?
Thanks!
Well if your Person Object is serializable you could store it in ViewState and if not, you could stick it in Session, but it sounds like you might have a general lack of understanding about Data Persistance in general
Depending on your implementation, and whether you're coding this all by hand or using the built in DataSource/DataAdapter controls, theres a bunch of ways to do it.
You could have a look at some basic ASP.NET/ADO.NET Tutorials to point you in the right direction
http://aspnet101.com/aspnet101/tutorials.aspx?id=17
Query the object it again (you could store it in a session variable but that doesn't scale), gather and apply changes from user upon postback.

Resources