Entity Framework confusions - wpf

This is WPF + MVVM + EF. I have a table of Sales Orders in my DB. In the UI, I show a ComboBox with all SalesOrderNumbers and a Grid with Labels and TextBoxes that shows the details of selected order in the ComboBox. Consider the following ViewModel:
Class SalesOrderViewModel
Public Property AllSalesOrderNumbers As List(Of Integer)
Public Sub New()
AllSalesOrderNumbers = context.SalesOrders.Select(Function(x) x.orderNumber).ToList()
If AllSalesOrderNumbers.Count > 0 Then SelectedOrderNumber = AllSalesOrderNumbers(0)
End Sub
Private Property mSelectedOrderNumber As Integer
Public Property SelectedOrderNumber As Nullable(Of Integer)
Get
Return mSelectedOrderNumber
End Get
Set(value As Nullable(Of Integer))
mSelectedOrderNumber = value
End Set
End Property
Public ReadOnly Property SelectedOrder As SalesOrder
Get
Return context.SalesOrders.FirstOrDefault(Function(x) x.orderNumber = SelectedOrderNumber)
End Get
End Property
End Class
In the UI, ComboBox's ItemsSource is bound to AllSalesOrderNumbers and SelectedValue is bound to SelectedOrderNumber (with Mode=OneWayToSource). On the other hand, the Grid's DataContext is bound to SelectedOrder. Whole of it is working fine.
My question is about the New Sales Order button. For adding new record, have added an ICommand to my ViewModel that basically does the following:
Dim NewOrder = context.SalesOrders.CreateObject()
context.SalesOrders.AddObject(NewOrder)
mSelectedOrderNumber = NewOrder.orderNumber
AllSalesOrderNumbers.Add(mSelectedOrderNumber)
I'm confused about the following:
SelectedOrder queries the model for the current value of SelectedOrderNumber property. Since the DB doesn't yet have this new record, it returns null. How do I ask it to look into local context object before going to the DB?
Unlike DataSets, it doesn't assign a negatively incrementing identity value to orderNumber, so I'm wondering what will happen if I add another order.
I do not see the newly added order object in context.SalesOrders collection after the 2nd line above (context.SalesOrders.AddObject(NewOrder)) executes.

The technical answers:
1.SelectedOrder queries the model for the current value of SelectedOrderNumber property. Since the DB doesn't yet have this new record, it returns null. How do I ask it to look into local context object before going to the DB?
You can query the local collection of a DbSet like so:
context.SalesOrders.Local.FirstOrDefault(...)
2.Unlike DataSets, it doesn't assign a negatively incrementing identity value to orderNumber, so I'm wondering what will happen if I add another order.
When you add new Orders, EF will keep track of each of them as a separate instances.
3.I do not see the newly added order object in context.SalesOrders collection after the 2nd line above (context.SalesOrders.AddObject(NewOrder)) executes.
See 1. It's in the Local collection.
Architectural answer: having a context in your view model is not the best choice. A view model should not worry about its data, it should receive data and after that be self contained. Its responsibility is to respond to user interaction and notify these to the controller or service layer. So the model should be populated with a list of Orders, after which the context is disposed. When new orders are added the model should tell the controller, either instantly or after a save command, and maybe receive a command to (re)display the Order. As such the view model is relieved from knowing the technical details of a data layer.

Related

Working with ObservableCollections in WPF and MVVM

I'm fairly new to WPF and still try to get the feeling on how to do something with built-in functions rather than inventing the wheel on my own again.
Today I stumbled upon a problem, that I couldn't solve with built-in functions and the possible ways I could think of I didn't like very much. So hopefully you can point me in the right direction or even can name a clever way with built-in functions.
So, for the sake of simplicity let's say I'd like to write a ViewModel for the MailMessage class that can be found in the System.Net.Mail namespace.
Imports System.Collections.ObjectModel
Imports System.Net.Mail
Public Class MailMessageViewModel
Private _message As MailMessage
...
End Class
A MailMessage object has (among others) a property To of type MailAddressCollection containing all the recipients for my e-mail as MailAddress objects.
In my ViewModel I wrap this collection of MailAddress objects into an ObservableCollection.
And here's my first question, how do I do that. Do I use:
Public ReadOnly Property Recipients As ObservableCollection(Of MailAddress)
Get
Return New ObservableCollection(Of MailAddress)(_message.To)
End Get
End Property
or do I use:
Private _recipients As ObservableCollection(Of MailAddress)
Public ReadOnly Property Recipients As ObservableCollection(Of MailAddress)
Get
If _recipients Is Nothing Then
_recipients = New ObservableCollection(Of MailAddress)(_message.To)
End If
Return _recipients
End Get
End Property
My view model now has a bindable property Recipients.
Now I'd like to be able to delete an e-mail address from the To collection of my MailMessage.
But when I delete an address from the ObservableCollection, my UI gets updated properly, but the To collection stays untouched. If I delete directly from the To collection of my MailMessage, the ObservableCollection and therefore my UI don't reflect the changes.
Do I really have to wire the ObservableCollection and the corresponding source collection manually by using the CollectionChanged event or by doing all changes twice (in the ObservableCollection and in the real collection)? Or is there any clever WPF way I don't know of?
Don't "wrap" anything.
Simply create a View Model containing properties needed to send your mail message.
At some point in future, you'll actually be sending the message. For example, the user clicks a Send button that fires an ICommand somewhere. At this time, convert your ViewModel into a MailMessage.
You cannot "wrap" one collection within another without lots of code. It only takes a few minutes to copy property values from an instance of one type to an instance of another type.
If the changes always go from the ObservableCollection to the original List, i think that you could add a handler to 'CollectionChanged' event of the ObservableCollection. I think that doing it this way won't be so onerous.
AddHandler Recipients.CollectionChanged, AddressOf RecipientsCollChanged
....
Private Sub RecipientsCollChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
If e.OldItems IsNot Nothing Then
For Each elem In e.OldItems
_message.To.Remove(elem)
Next
End If
End Sub
Obviously, if you want, you can also handle the modify and the adding of elements into the ObservableCollection using the informations into the NotifyCollectionChangedEventArgs parameter.

WPF: EF to ObservableCollection and DataGrid auto-update: a definitive solution

I am relatively new to WPF and the MVVM model, but after hours of searching and reading articles regarding the matter, I have not come up with a definitive solution to this.
1) Model: I have an EF model relying on an SQL Express database. For this example, I will use a property named Visits, a DbSet(Of Visit).
2) ViewModel: an ObservableCollection(Of Visit). It is declared as such:
Private _Visits As ObservableCollection(Of Visit)
Public Property Visits As ObservableCollection(Of Visit)
Get
Return _Visits
End Get
Set(value As ObservableCollection(Of Visit))
_Visits = value
RaisePropertyChanged("Visits")
End Set
End Property
...
Visits = New ObservableCollection(Of Visit)(
From V In Application.context.Visits
Where V.IsRobotGenerated AndAlso
Not V.IsSynced
)
3) View: a WPF datagrid.
<DataGrid
x:Name="grdVisits"
ItemsSource="{
Binding Path=Visits,
RelativeSource={RelativeSource FindAncestor, AncestorType=Window}
}"
/>
Now I have a background thread which modifies the EF data directly (does all CRUD operations).
What I want is the datagrid to reflect these changes as they happen.
What I am doing right now is replace the Visits ObservableCollection by re-calling the constructor above, which results to 2 drawbacks:
1) Performance wise I suppose it is not the best choice, especially when we are talking about quite a few thousands of rows (each with its RowDetails).
2) The user has in the meantime scrolled down the datagrid or opened a RowDetailsTemplate. His position and everything is lost if the entire collection is reset.
I have read somewhere about DependencyProperties but I do not have time to go through it thoroughly since I have no clue if it is a pointer to the right direction. If it is, I will be happy to do so.
Any help will be greatly appreciated.
When you add or remove elements from an ObservableCollection bound to an ItemsSource, those changes are instantly reflected so what you need to do is simply to modify the ObservableCollection accordingly as your CRUD operations take place.
Bear in mind that if you are using a version of .NET prior to 4.5 you'll need to dispatch the ObservableCollection changes to the UI thread.
UPDATE: answering to the comment
What about the following scenario: two datagrids in the view, which are bound to two different ObservableCollections. However, these ObservableCollections are created with a different constructor so as to apply filtering to the underlying Visits. Can you provide a bare bones example as to how I can accomplish the aforementioned?
Filtering is a different story. It is supported by an intermediate layer set between your collection and the items control (ObservableCollection and DataGrid respectively in this case): collection views. Actually, you always bind to collection views, not to the collections directly.
So, for your scenario you'd bind both DataGrids to the same underlying collection but using two different collection views, one for each DataGrid. This way, the data stays the same and synchronized with whatever you need it to be (your CRUD operations for instance). However, each DataGrid can display a subset of the data based on filtering, sorted and/or grouped in a different way.
I'd suggest you take a look into the MSDN article regarding data binding to collections.

MVP (Model-View-Presenter) in VBA and returning a value

I am trying to implement the MVP pattern in VBA for a winform as I wish to be able to reuse the same code for the model/presenter but be able to change the view (winform) for another one easily. I think I have the basics sorted out, however, as the forms are more like "settings" forms rather than say, "interactive" ones, I wish to return a collection of values from the form when it closes, but I am not sure which part (M, V or P) to put this logic in.
I was thinking to put it in the presenter and set it up as a property that I could access from elsewhere.
Here's my code so far (please bear in mind I am a beginner with patterns, and this code is simplified it somewhat):
Presenter:
Private model As IPlanningParametersModel
Private view As IPlanningParameterView
Public Sub Initialise(view As IPlanningParameterView, model As IPlanningParametersModel)
Set model = model
Set view = view
End Sub
Public Sub updateViewWthModel()
Set view.PlanningParameters = model
End Sub
Public Sub updateModelWithView()
Set model = view.PlanningParameters
End Sub
Model:
Private m_ParamDictionary As Scripting.Dictionary
Implements IPlanningParametersModel
Private Sub Class_Initialize()
Set m_ParamDictionary = New Scripting.Dictionary
End Sub
Private Sub IPlanningParametersModel_Remove(ByRef Name As String)
Me.Remove Name
End Sub
Private Sub IPlanningParametersModel_Add(ByRef PlanParam As IPlanningParameter)
m_ParamDictionary.Add PlanParam
End Sub
Private Function IPlanningParametersModel_Item(ByRef Name As String) As IPlanningParameter
Set IPlanningParametersModel_Item = m_ParamDictionary.Item(Name)
End Function
View:
Implements IPlanningParameterView
Private Function IPlanningParameterView_Show(Optional ByVal Modal As Boolean = True)
If Modal Then Me.Show (vbModal) Else Me.Show (vbModeless)
End Function
Private Function IPlanningParameterView_Hide()
Me.Hide
End Function
Private Property Let IPlanningParameterView_Caption(ByRef Value As String)
Me.Caption = Value
End Property
Private Property Get IPlanningParameterView_PlanningParameters() As IPlanningParametersModel
'TODO: Cycle through each control in form to obtain configuration
' and add IPlanningParametersModel
End Property
Private Property Set IPlanningParameterView_PlanningParameters() As IPlanningParametersModel
'TODO: Cycle through each item in IPlanningParametersModel and set
' each control in form to reflect the configuration value
End Property
Finally the bit that connects them together:
Dim model As IPlanningParametersModel
Set model = New PlanningParametersModel
Dim view As IPlanningParameterView
Set view = New FPlanningParameterView
Dim pres As Presenter
Set pres = New Presenter
pres.Initialise view, model
So in this case I wish to actually use the values located in the model elsewhere in other code later on.
Should I add a new property to the Presenter part that just returns the model? E.g:
Public Property Get Settings() as IPlanningParametersModel
Set Settings = model
End Property
I've tried to search for a solution to this, but there aren't many examples of MVP in VB6/VBA (in fact, the only decent one I found was here), nearly all are in .NET which sometimes doesn't translate that well back to classic VB since they use features not available.
Edit:
After having more time to think and research on this, I think what I need is a way to obtain and set data in the model directly rather than using the storage object that the model is sat upon. For example, in most examples of MVP, the model is a "facade" for a database or other repository which stores its data somewhere. The other parts of the program (i.e. outside the MVP) then query this database to obtain the information that the model was sat on. In this case, nothing accesses the model directly, the flow goes via the database which is almost "independent " of the rest of the program.
In my particular case, I do not really need an underlying database to store this information as I just need to be able to set and get the values that the model holds.
Edit 2
Perhaps I could implement the "database" for the model as a Singleton class and then pass this to the model's constructor when it's initiated? For example:
Dim model As IPlanningParametersModel
Set model = New PlanningParametersModel
Set model.DataStore = MySingleton
Dim view As IPlanningParameterView
Set view = New FPlanningParameterView
Dim pres As Presenter
Set pres = New Presenter
pres.Initialise view, model
The DataStore property of the model could use an interface and MySingleton would implement the same interface and then I could then use MySingleton outside of the above code. Does that sound reasonable?
Any other suggestions on this structure are welcome as this is my first attempt!
The only problem with exposing the model from the presenter is that whatever is using the presenter becomes tightly coupled to it.
I prefer my presenters to emit events to an Event Aggregator that then publishes to any subscribers. This keeps everything loosely coupled.
Don't know how well this might work in VB6, it's been a long time. However, if you went down this road, your presenter would emit an event when the model changes with the current model attached to the event.

UltraWinGrid nested object properties binding with BindingSource

I am working on a winforms application where I am rendering domain/object data via an ultrawingrid. I am using a bindingsource to bind the object to the grid.For simple objects this works quite well.
The thing I'm trying to get my head around is rendering an object with nested objects for e.g a Person class will have a property of Address class. I would like to display the properties of Address (Street, City, Country) as columns on the grid along with the properties of the Person class.
The grid has to be editable and any user changes need to reflect back on the domain object (which I'm doing via the binding source).
What's the best way to go about this?
Binding
I typically use some sort of code like this:
Dim persons = new BindingList(Of Person)
UltraGrid1.DataSource = persons
The binding list will handle the add/remove of rows for you, but it doesn't know about the properties inside Person. To get that part of the binding to work, you'll need to have Person implement INotifyPropertyChanged. This will notify the ultragrid when the properties have changed. The code will look something like this (yes, unfortunately this makes it so you can't use auto-implemented properties):
Private _phoneNumber As String
Public Property PhoneNumber As String
Get
Return Me._phoneNumber
End Get
Set(ByVal value As String)
If value <> _phoneNumber Then
Me._phoneNumber = value
NotifyPropertyChanged("PhoneNumber")
End If
End Set
End Property
Flattening object hierarchies
It looks like what you're ask for isn't directly possible. There are a few options:
Unbound columns in the UI that you fill in during the InitializeRow event
Modify your Person class to expose the properties of Address with some pass through code to handle the setting of the properties.
(I can provide a code samples if needed)
One-to-many Nested Objects
If you, for example, had multiple addresses per person, you could show them nested in an expandable section under each Person row. To do this, inside your Person you'd have a BindingList(Of Address) which also implements INotifyPropertyChanged. Not exactly what you want, but an option :)
Words of caution
A few notes if you are doing MVP. You'll need to have the same reference to the BindingList in your view and presenter, obviously. Also, if you need to reset the contents, I'd recommend calling list.Clear() instead of creating a new one. If you create a new one in your presenter, you'll have broken the connection with the UltraGrid and you'll have to re-set the DataSource property in the view.

Problem with filtering DataGrid

I have bound my DataGrid to a DataTable and only few of the details are displayed in the grid. When I wanted to filter the DataGrid I created a View with my DataGrid's ItemsSource.
Code:
Dim myView As ICollectionView = CollectionViewSource.GetDefaultView(MyDGrid.ItemsSource)
myView.Filter = New Predicate(Of Object)(AddressOf filterFunc1)
Now When I do the search, the non-displayed fields are also included in the search.
Public Function filterFunc1(ByVal obj As Object) As Boolean
Dim filStr As String = "*" & TextBox1.Text & "*"
For Each item As String In obj.Row.ItemArray
**If item.ToLower Like filStr.ToLower Then**
Return True
End If
Next
Return False
End Function
Also I Have ComboBox fields in the DataGrid which are loaded separately from other DataTable's. Now I cant Include them in the search.
A screenshot from my App:
So how do I make a search that includes only the Text from Displayed part.
EDIT: Also how do I skip searching the null valued fileds? 'cause thats causing an exception in my case.
Well then...
Your question is pretty disjointed and I can't understand all of it - maybe that's why you didn't get an answer so far. Skipping null fields is simply a matter of adding a new condition in filterFunc1 - if Convert.IsDBNull(item) then continue for (assuming item is a field in a DataRow, of course).
However, this programming style is pretty foggy and I'd recommend at the very least being more clear on which columns you filter, and the types of objects in the columns. A much better approach would be to map the data you're getting from the database to actual objects in your application - that allows for more type-safe programming. I think the main problem here is that nobody can really tell what's going on there from a few lines of code because nobody can make any assumptions about what kind of objects are there.
About the items in the ComboBox, no idea what kind of difficulties you're having, you might want to clear that up a bit.
you could maintain, instead of simply strings, structures containing both captions and IDs, like say
public class YourComboItem
public property Id as string [get/set]
public property Title as string [get/set]
end class
Then bind your ComboBox's ItemsSource to a collection of these items retrieved from the database, and set DisplayMemberPath to Title and ValueMemberPath to Id. Then you can use the ComboBox's SelectedValue to get the selected ID. As you can see, having objects instead of raw data structures can have quite some advantages.
Of course, I described getting the SelectedValue directly from the ComboBox, while a much better architecture would be MVVM, with the ViewModel containing an ObservableCollection(Of YourComboItem) and the ComboBox's ItemSource bound to it with an actual binding. Then you can also bind the SelectedItem to a property in your ViewModel, and have the item as a whole, including both Id and Title, to work with without knowing anything about your user interface. Alternatively you could have an ICollectionView generated from the collection of items and bind the ItemsSource to that, then you'd have the selected item in the ICollectionView's CurrentItem property.
I'd really recommend reading up on MVVM (Model-View-ViewModel) to make your work with WPF a whole lot easier.

Resources