Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Here is my thinking about your questions:</p> <p><strong>Question:</strong> <em>Would the recommended approach be to convert these DTOs to a model which implemented INotifyPropertyChanged using Automapper or similar? Or should the DTO be used as the model directly in the viewmodel?</em></p> <p><strong>Answer:</strong> My approach I like the most is containment. I agree with you DTO shouldn't have anything but getters and setters. Keep it as clean as possible, hence it shouldn't fire INotifyPropertyChanged. I also don't think the View should have direct access to object model (if for no other reason, you don't have the benefit of property changed). The disadvantage of my approach is some extra code in the ViewModel, but I think it worth it.</p> <pre><code>public class VmBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void raise( string propName ) { if( PropertyChanged ) { PropertyChanged( this, new PropertyChangedEventArgs(propName) ); } } } public class OrderLineVm : VmBase { private OrderLineDTO orderLine; public OrderLineVm( OrderLineDTO ol ) { orderLine = ol; } public OrderLineVm( ) { orderLine = new OrderLineDTO(); } int customerId { get { return orderLine.customerId; } set { orderLine.customerId=value; raise("customerId"); } } int productId { get { return orderLine.productId; } set { orderLine.productId=value; raise("productId"); } } double quantity { ... } } </code></pre> <p>Through the miracle of garbage collection, the OrderLineDTO will be created only once (when it comes from the server) and live as long as it is needed. There are two public constructors: one with the DTO (typically, when objects coming from the server), and one created on the client.</p> <p>For the OrderVm this is a little bit more complex, since you would like to have a ObservableCollection (vs. List) of OrderLineVm (vs. OrderLineDTO), so containment won't work. Also note that orderLines only has a getter (you add and remove order lines from it, but you don't change the entire list. Allocate it once during construction).</p> <pre><code>public class OrderVm : VmBase { private string _orderDetails; public string orderDetails { get { return _orderDetails; set { _orderDetails=value; raise("orderDetails"); } } private ObservableCollection&lt;OrderLineVm&gt; _orderLines; public ObservableCollection&lt;OrderLineVm&gt; orderLines { get { return _orderLines; } } } </code></pre> <p><strong>Question:</strong> <em>How would the OrderViewModel communicate to the OrderLineViewModel in this scenario?</em></p> <p><strong>Answer:</strong> If a communication is required, indeed you should do it the simplest way. Both View Model classes are at the same layers. The OrderVm references a list of OrderLineVm, and if you need a communication from OrderLineVm class to order, just keep a reference.</p> <p>However, I would strongly argue that a communication isn't needed. Once the View is bound appropriately, I see no reason for such communication. The Mode property of the binding should be "two way", so everything changed in the UI will be changed in the View Model. Addition, Deletion to the list of order lines will reflect automatically on the View thanks to the notifications sent from the ObservableCollection.</p> <p><strong>Questions:</strong> Do you allow the user to add an invalid line send the request to the server let the server apply the relevant rules and return the response? Or do you somehow apply the logic in the smart client app before sending the request to the server?</p> <p><strong>Answer:</strong> There is nothing wrong having data validation on the client, in addition to the server. Avoid duplicate code - have a single assembly (probably the assembly that defines the DTO) that performs the validation, and deploy this assembly in the client. This way your application will be more responsive, and you will reduce workload on the server.</p> <p>Obviously you need to do data validation on the server (for security reason, and for race conflicts). You must handle situation when the server returns errors even though validation on the client passed.</p> <p><strong>EDIT:</strong> (follow up on comment from Alex):</p> <p>Showing a dropdown list: I think the source of your confusion is that there are actually two independent ItemsSource (and hence two separate data context): There is one list of order lines, and embedded within each order line is the list of ProductIDs, which are the items populated the combobox. It is only the SelectedItem that is a property of the ProductLine. Typically, the list of possible ProductID should be global to the application (or the order). You'll have the ProductIDs a property of the entire form, and give it a name (e.g. x:Key or x:Name). Then, in the ComboBox element just reference this list:</p> <pre><code>&lt;ComboBox ItemsSource="{Binding Source={StaticResource ProductIDs}}" SelectedItem="{Binding Path=productId}" /&gt; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload