Silverlight Concurrent Data Sources for a Grid's Control - c#

I have the following data model:
Camp -> CampEvent <- Event.
Camp has CampId and Name associated with it.
Event has EventId, Name, Start/End (Dates).
CampEvent has (CampId,EventId)PK, CampId FK, EventId FK.
The tables are used to create a Domain Model and a Domain Service which is consumed from the client side on Silverlight.
I am successfully able to display the Event's table in Silverlight using a grid.
The Grid has two template columns - one to display a checkbox, and another to display the name of the event.
So now the problem is somehow I need to check the checkboxes when this control goes in edit mode.
I've noticed that the Grid doesn't have OnDataBound event, or it doesn't have a way of setting the state of each checkbox to checked other than through binding.

Well, apparently in Silverlight you don't have the luxury of messing around with the contents of a GridViewRow. You can, however, achieve this by changing the underlying data source.
In the above scenario we have a control which is used for creating an instance of Camp and associating it with one or many events. In a sense the control can either Create or Update a "Camp" object and its relationships with Events. The control state is controlled by an Enumeration public enum Mode { Create, Update }; and depending on which value this property has, the control will do either one, or both of the following binding binding operations:
Get all event data and display it in a grid composed of rows having a checkbox & label.
Check the boxes denoting the events in which a specific camp participates.
This was all nice and dandy in theory but in principle I realized Silverlight needs a discrete data source to bind to. I created a collection of a CampEvent custom object in which every element has a boolean prperty IsChecked, along with the event name and event Id. The CampEvent object is not a Domain Entity object and is only used for binding.
So to achieve my goal, these are the steps I took.
Declare ObservableCollection where T is used for only binding. In this case the underlying data source for T is our Event, and a Linq to Entity query was used to grab the Id and the Name of the Event and transforms it into a CampEvent object with its IsChecked property set to false by default.
If the Control is in Create mode I am done. The checkboxes in the Grid's template column are two-way bound to the IsChecked property of the underlying data source. Step one is enough to create the default UI with all check boxes unchecked. Otherwise go to 3
Well, number 2 was wrong, so therefore the control was in "Update" mode. If the SelectedCamp property of the control is set (and this property is of type Camp). At this point we create a Linq to Entitities query where we ask the domain service to Include the Events associated with the specified camp.
Once the response from the query arrives, we iterate through every Event object that is associated with the camp. For every event that was received, we check to see if it exists in our ObservableCollection data source. If it does, we set the IsChecked property to true for that item. Once we data bind the grid, all events that are associated with a specific camp will be "Checked".
Mission accomplished.
Few words on Database Structure, Domain Models generated by the Entity Framework, and WCF RIA.
Well, as it turns out EF will get you maybe 80% there out of the box. The tool is not intelligent enough to know what a many-to-many relationship is. In the case with camp and events we have the following structure:
camp -> participates in many -> events
(many) events -> have many -> camps (as participants)
So to make this happen we need a "joiner" table between camps and events. To do this properly, the joiner table should in theory have at the very least two columns:
CampId -> Foreign Key
EventId -> Foreign Key
Now to create a primary key for the Table, we should have:
CampId + EventId -> Composite Primary Key.
Making our table have only 2 fields. Now this is super important because this relationship creates the Navigation property possible in EF.
When the domain model is generated, the EF will not create the joiner table in the model. However to enable the navigation property between Camp and Event and vise versa there are a couple of things that have to happen on the underlying Domain Service meta data object.
**1. Locate the Camp metadata info. Decorate the IEnumerble<Event>Events property with:
[Include]
[Association("CampEvent", "CampId", "EventId", IsForeignKey=True)]
And to explain what those mean: The Include says whenever you query the domain model, please include every Event for the specified camp(s). The Association says there is an association table between camp and event for the navigation property to work. In the lookup table the camp has CampId identifier and Event has EventId. Use those to find all Events for the specified camp(s)**.
2. Do the same for whatever other navigational properties you have.

Related

WPF - NotMapped annotation in EF6 not storing / saving property value(s)

When I try to insert or update data in my WPF usercontrol datagrid, the data is not being saved to the corresponding property. This is being caused by (at least so I believe) my bound property having the [NotMapped] attribute, all the other properties without the annotation are working correctly.
The data which need's to be updated is inside a DataGrid component which is bound to an ObservableCollection with the appropriate model. Inside the model there are several properties with the [NotMapped] annotation, these properties should be excluded in the creation of the table (model) in my database, though I do need them for binding input, hence the use of the [NotMapped] annotation.
Since the data is bound to a ObservableCollection I can't add the [NotMapped] properties to the usercontrol directly (so they wont be a part of the model).
Below an example:
Part of the XAML
In the image below we can see 1 property (pBreedte) which is 1 of the NotMapped properties, as well as the itemsource of the datagrid:
UserControl Code behind (part of it)
Part of the model which is used in the ObservableCollection
The model is being used for EF6 (code first).
Is there any way that the NotMapped property values can be stored / saved?
The easiest would be to just include the NotMapped properties in the database (so removing the annotation completely) but I am trying to avoid this.
More background information
The NotMapped values are added because they function as a placeholder property. Initially I had several decimal properties bound to the datagrid directly, but textboxes can't handle decimal values very well (WPF validation rule preventing decimal entry in textbox?). So I created a string placeholder for those decimal properties, when the form is being saved the decimal properties are being set to their string placeholder counterparts. This way the user can add decimal places without having to use a delay, value converter or anything.
If you don't need that information in your database, then don't store it - meaning your approach is good.
What I think here what might be the problem is that you are using your entity/database model as UI model.
I would suggest that you try to introduce a different model for the UI controls and user input. The models might seem to be duplicate at the beginning but while you are working on your application they will start to differ, but still describing the same items just form different perspectives.
Example:
Entity model has a class CarEntity. It is a pure POCO class, having only the needed properties that will contain the data in the corresponding table.
Ui model has a class CarUi. It has the same properties as the CarEntity. They are loaded and mapped from the database (from the CarEntity) shown to the user. If the user changes something, the diff values are mapped from the CarUi to the CarEntity and then stored to the DB.
With this separation of models approach, you should not face the issue where one constraint (mark column not to be stored in a table) influences other functionality.
Hope this helps,
Cheers and happy coding!

Binding Validation Update View with Errors

From my reading and trying to understand and implement MVVM and validation, I need some help on the following scenario.
View - User interface (expected)
Model - based on a data table, and via OnColumnChanging, tests for validation requirements on a column-by-column basis (this part works no problem)
ViewModel - glue the connects the model to the view, this works too.
For each of the textbox controls, I have them respectively binding two-way to the data table, current row (still no problem) and it shows the data as expected. The flags including: NotifyOnTargetUpdated, ValidatesOnDataErrors, ValidatesOnExceptions and NotifyOnValidationError are all set to true.
If I use the interface and put in invalid value, it properly triggers the validation and puts red border around control showing it failed.
Now the problem. I start my form, and click the "Add" button (new record, blank values), textbox controls now enabled for editing content. Click the "Save" button. I want to have all the controls refreshed that are missing "required" data. Since my view model is bound to columns of the data table, and that from my readings, all the validation should be handled in the viewmodel, how should I adjust my scenario.
I don't know how to force which controls are bound to respective controls when they've never received focus. In addition, from other readings, to allow for unit testing, you should be able to test under the assumption that there is never a user interface. So, in theory, I could automate creating my viewmodel, which loads my data model, adds a record, tries to save and forces whatever to test all "required" fields.
Am I close??? way off??? Not quite positive on this.
Implement: IDataErrorInfo and check Error within your SaveCommand
Further Reading:
WPF Apps With The Model-View-ViewModel Design Pattern
Using IDataErrorInfo for validation in MVVM with Silverlight and WPF
Validation should be done in two places. One in ModelView (User input validation), second on Model model consistency validation, if required for specific scenario.
You have every text box (TextProperty) is bound to the ModelView's property. TextBox (I presume) has a dependency property which lets to you specify ether to signal error on UI or not.
What you have to do, IMHO, immidiately inside Add event handler set default values to binded ModelView object. What will happen is: for every control, including required one, will be settuped, thow via DataBinding visualized on UI, default value. For required fields the default value can be just not valid one, so requierd field will be immediately appear with Error signal, signaling a user about presence of mandatory fields.

How to ensure all properties have loaded in Silverlight ViewModel pattern (Concurrency Control?)

I am struggling with a seemingly small but quite a painful predicament. I have a an object which acts as a view model to a control.
The underlying View Model is designed to help display a list of Group objects and their related Event (s) OUT OF ALL AVAILABLE EVENTS. In the underlying data model I have the following entities (EF) and their corresponding relationships:
Group -> GroupEvent <- Event
The view model has two observable collections - one for events and one for groups. The events collection represents all events available for any group. All events are loaded only once from the WCF RIA service and upon arrival each Event's entity data is copied into a local object which is pushed into the events collection.
In addition to the properties of the Event entity, the LocalEvent object also defines a "IsSelected" boolean property which by default is initialized to "false". This property is used in a CheckBoxGridColumn with two-way binding so that I can set the check box' state to checked or unchecked.
The Groups collection in the ViewModel is initialized the same way. I created a LocalGroup object which has a collection of LocalGroupEvents. In the constructor once the events have loaded, I load all Group entities (and their related GroupEvents) from the WCF RIA service. I copy all properties of each Group entity into a LocalGroup object, set its collection of LocalGroupEvents and finally push the LocalGroup into the Groups observable collection of the ModelView.
Finally the two collections are bound to their respective grids.
The groups grid only displays the group name, while the Events grid displays a check box next to each event. The groups grid has a SelectedItemChanged event handler and whenever it occurs, I grab the LocalGroup object, I read all of it's LocalGroupEvents and set the "IsSelected" property in the Events grid to true for every event whose Id matches the Eventid in the LocalGroupEvents.
Well, this workflow works fine. 100% of the time. The issue I have is what if the data for the events arrives after the data for the groups? I mean, since any RIA service call is asynchronous, the threads are not paused to ensure that the data will arrive in the right order.
What if there is only one group, and it loads super fast before the events have had a chance to load? In that scenario even if the user clicks on the group, they will see nothing and will not be able to edit the selected group.
How do you ensure that data has arrived in the correct order before the UI binds to model-view?
Thanks,
Bleepzter
You could use IResult and Coroutines from Caliburn Micro. It would allow you to populate those async things in a specific order.
IResult example, docs are here.
public IEnumerable<IResult> GoForward()
{
yield return Loader.Show("Downloading...");
yield return new LoadCatalog("Caliburn.Micro.Coroutines.External.xap");
yield return Loader.Hide();
yield return new ShowScreen("ExternalScreen");
}
You can grab LoadData.cs from here. It's in Samples/GameLibrary/GameLibrary/Framework/Results. It's a Result someone wrote that adds an extension method to DataContext.

Parent-Child relation while using object data source

I am experiencing with a class generator I've written, which generates a class for each table in database with each table field as a property and such.
Before that, I used to add a typed dataset to the project and add some tables to it. It automatically detected the relationship between tables and when I added a parent table as data source of a datagrid, I could add another datagrid and use the foreing key data member of it's bindingsource to fill it, and when someone moved the focus on parent datagrid, the data in child datagrid would change accordingly.
Now that I have my classes, I add an object as data source for my 2 datagrids, but obviously it doesn't detect a parent child relation. But It'd really help if I could have that foreign key relation in my object datasources.
Is there any way to have that relation in object datasource?
If you use LINQ ORM, your foreign key relationships are reflected automatically in your generated model.
Take a look at http://www.hookedonlinq.com/LINQtoSQL5MinuteOverview.ashx for more info.
In case you use ADO.Net, there might be a chance you forgot to tick the choice "Include Foreign Key Columns in the Model." in the ADO wizard but no worries (we've all been there, tick boxes are notoriously easy to overlook ;) ), you'll simply have to re-generate the model (re-run the wizard) but be sure to copy-paste any custom code you've added to a text file or something, so you don't lose it. Good luck!

WinForms databinding and foreign key relationships

I'm developing a WinForms application (.Net 3.5, no WPF) where I want to be able to display foreign key lookups in a databound DataGridView.
An example of the sort of relationship is that I have a table of OrderLines. Orderlines have a foreign key relationship to Products and Products in turn have a foreign key relationship to ProductTypes.
I'd like to have a databound DataGridView where each row represents an orderline, displaying the line's product and producttype.
Users can add or edit orderlines direct to the grid and choose the product for the order line from a comboBoxColumn - this should then update the producttype column, showing the producttype for the selected product, in the same row.
The closest to a good fit that I've found so far is to introduce a domain object representing an orderline then bind the DataGridView to a collection of these orderlines. I then add properties to the orderline object that expose the product and the producttype, and raise relevant notifypropertychanged events to keep everything up to date. In my orderline repository I can then wire up the mappings between this orderline object and the three tables in my database.
This works for the databinding side of things, but having to hand code all that OR-mapping in the repository seems bad. I thought nHibernate would be able to help with this wiring up but am struggling with the mappings through all the foreign keys - they seem to work ok (the foreignkey lookup for an orderline's product creates the correct product object based on the foreign key) until I try to do the databinding, I can't get the databound id columns to update my product or producttype objects.
Is my general approach even in the right ballpark? If it is, what is a good solution to the mapping problem?
Or, is there a better solution to databinding rows including foreign key lookups that I haven't even considered?
I think the problem you're having is that when you are binding to a grid, it is not enough to support INotifyPropertyChanged, but you have to fire the ListChanged events in your IBindingList implementation and make sure that you override and return true for the SupportsChangeNotification property. If you don't return true for this, the grid won't look for it to know if the data has changed.
In .NET 2.0+, you can create a generic collection using the BindingList class, this will take care of most of the nastiness (just don't forget to override and return true for the SupportsChangeNotification property).
If the class you use for data binding has a property that is a collection (such as IBindingList or BindingList), then you can bind the foreign key grid to that property directly. When you configure the bindings in the Forms designer, just select the collection property as the data source for the grid. It should "just work". The only sneaky part is making sure that you handle empty or null collections the right way.
welcome to StackOverflow :)
Normally what you would do is base the information in the drop down on two values ValueMember and DisplayMember.
The ValueMember is the source of the actual controls value (this will be the key value in the order line), the display member is the value that is displayed to the user instead of the value (this will be the FK value).
Is there no particular reason you cannot just return all the data required and set these properties?
Here's a good "How Do I" video that demonstrates data binding:
http://windowsclient.net/learn/video.aspx?v=52579
Well, I don't know whether it's supported by the DataGridView, but when you're doing regular WinForms databinding (say, to a regular TextBox) you can use property paths to navigate through object relationships.
Something like this:
myTextBox.DataBindings.Add("Text", anOrderLine, "OrderedPart.PartNumber");
Would be worth seeing if this works in your situation too.
My original question obviously wasn't clear, sorry about that.
The problem wasn't with databinding to a DataGridView in general, or with the implementation of a DataGridViewComboBoxColumn - as the people who have answered already rightly say, that is well documented on the web.
The problem I've been trying to solve is with the refresh of properties that are drilling down through relationships.
In my orders example, when I change the value of the "Product" column, the "Product Type" column is not being updated - even though in the code I am setting the property and firing the NotifyPropertyChanged event. (In debug I go to all the right places)
After a lot of poking around I realised that this was not even working when I directly set the "Product Type" property of datasource, rather that setting it in the "Product" setter.
The other thing that I believe has me back on the right track is that when I provide a mocked dataccess layer, created in the main form, everything works fine.
Also, when I copy the IList made by nHibernate to a IBindingList - everything again appears fine.
So the problem is I think with threading and the NotifyPropertyChanged events being lost when using certain datasources, in certain ways (wish I could be more definitive than that!)
I'm going to keep researching better ways of resolving this than copying the IList to the IBindingList - maybe I need to learn about thread marshalling.
Edit
I've now developed a solution that solves the issue and think I understand what was confusing me - it appears that anything but basic property databinding doesn't play nicely for lists that aren't derived from BindingList - as soon as I was trying to databind to properties that fired chained NotifyPropertyChanged events, things went haywire and my events got lost.
The data access solution I have now is using a variation of the Rob Conery IRepository pattern, returning my collections to be bound as a custom class I made, a SortableBindingLazyList that derives from BindingList, implements the Sort Core methods and also stores its internal list as a query, delaying the list materialisation.

Categories

Resources