How do I subscribe to property changed events raised from child objects? - winforms

I have an Invoice object that exposes a readonly property - Total. This total is just the sum of the total of the invoice items:
public decimal Total { get { return this.InvoiceItems.Sum(s => s.Total); } }
The Total property in the InvoiceItems class looks like this:
public decimal Total { get { return Price * Quantity - Discount; } }
The classes and properties were generated by LINQ (except for Total properties in both classes which I created myself). When a price, for example, changes on an InvoiceItem, I would like the change to be reflected in the Total property of its parent Invoice.

You are going to have to do this manually, i.e.
Iterate over the child InvoiceItems
Add a handler to their PropertyChanged event
When their Price property changes, update your Total property
optionally handle CollectionChanged event on InvoiceItems to add / remove PropertyChanged subscriptions as invoice items are added / removed.

Related

unable to add row to a winforms datagridview that is bound to a SortableBindngList

I have a datagridview that was bound to a generic List<> of my objects. Everything works fine. I then changed the List to a SortableBindingList so that I can sort the columns. This works fine to except now I get an exception when I try to add a row. The exception is:
"Operation is not valid due to the current state of the object."
This occurs in the WinForms runtime in DatagridView.DataGridviewDataConnection.ProcessListChanged method.
Anyone have any ideas what the problem might be?
So you have separated your data from how it is displayed. Good for you! Too often I see that people try to fiddle with cells and rows instead of using the datasource.
However, if your DataSource is a List<...>, the changes that the operator makes to the DatagridView are not reflected in the DataSource.
If you want that items that are added / removed / changed by the operator are also changed in your DataSource, you should use an object that implements IBindingList, like (surprise!) BindingList
You forgot to tell us what you show in your DataGridView, let's assume you show Products
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public decimal Price {get; set;}
public int StockCount {get; set;}
public string LocationCode {get; set;}
...
}
Using visual studio designer you've added the columns that you want to show. You'll have to tell which column shows which property. For instance in the constructor:
public MainForm()
{
InitializeComponents();
// define which columns show which properties
columnId.DataPropertyName = nameof(Product.Id);
columnName.DataPropertyName = nameof(Product.Name);
columnPrice.DataPropertyName = nameof(Product.Price);
...
To make access to the displayed products easy, add a Property:
private BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.dataGridView1.DataSource,
set => this.dataGridView1.DataSource = value;
}
Initialization:
private void ShowProducts()
{
IEnumerable<Product> queryProducts = this.FetchProducts();
this.DisplayedProducts = new BindingList<Product>(queryProducts.ToList());
}
Now whenever the operator adds / removes a row, or edits the cells in a row, the BindingList is automatically updated, even if the columns are rearranged, or the data sorted.
If the operator indicates that he finished editing the data, for instance by pressing a button, you immediately have all updated information:
private void OnButtonOk_Clicked(object sender, ...)
{
ICollection<Product> displayedProducts = this.DisplayedProducts;
// find out what is added / removed / changed
this.ProcessEditedProducts(displayedProducts);
}
If you need to access selected items, consider to add the following properties:
private Product CurrentProduct => (Product)this.dataGridView1.CurrentRow?.DataBoundItem;
private IEnumerable<Product> SelectedProducts => this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
If the operator adds a row, he starts with a row that is constructed using the default constructor. If you don't want this, but for instance initialize the Id, or the LocationCode, consider to subscribe to event BindingList AddingNew
this.DisplayedProducts.AddingNew += OnAddingNewProduct;
void OnAddingNewProduct(object sender, AddingNewEventArgs e)
{
e.NewObject = new Product
{
Id = this.GenerateProductId(),
Name = "Please enter product name>",
LocationCode = "Unknown Location",
...
};
}

Is incorrect to trigger calculations from property setters in a view model?

Say I have a view model which has price and quantity properties in a view model implementing INotifyPropertyChanged which are bound to text boxes and total cost property which is bound to a text block. When either the price or the quantity is updated the total cost should be recalculated.
I understand that it desirable to minimize the amount of work that is done inside property setters and getters. Is it bad practice to trigger the calculation from setters of the properties? If it is bad practice how else should I do it? I can see two possibilities.
Subscribe to the PropertyChanged event and trigger the calculation from there. I feel this event is more for outside observers than for use in the view model itself, and in any case any the event is triggered from the setter so I don't see how it is much different from triggering the processing from setter directly.
Trigger the calculation from an event on the text box (say LostFocus) the property is bound to either by directly calling a method on the view model from the event or by binding the event to a command on the view model. It seems to me that with MVVM the idea is that we should avoid using control events if possible.
I am afraid this could by a primarily opinion-based question, anyway I would like to share my idea.
I do not see too many problems in triggering the calculation in a property setter. Probably it is important how you do it. IHMO it is not good to replicate OnPropertyChanged("CalculatedPropertyName") everywhere around in your code.
I would prefer something like:
public int Quantity
{
get { return quantity; }
set
{
if (value != quantity)
{
quantity = value;
OnPropertyChanged();
Result = Quantity * Price;
}
}
}
public int Price
{
get { return price; }
set
{
if (value != price)
{
price = value;
OnPropertyChanged();
Result = Quanity * Price;
}
}
}
public int Result
{
get { return result; }
private set
{
if (value != result)
{
result = value;
OnPropertyChanged();
}
}
}
In this way you are just using a private setter and you do not need to remember to raise anything. This is the way I prefer, but if you are looking for more complex ways you can find a lot of ideas here and here.
Last thing - but not least - I do not like alternative ways you suggested, I feel they are too twisted.

Pass a collection to viewmodel and return a selected item

I have a View / ViewModel where a ProductList is loaded. This list is not visible on the screen.
What I need to do is show a new View/ViewModel (e.g. SelectProductView / SelectProductViewModel), pass the ProductList to them, and after a user selects a particular Product, close this view, and make use of selected product.
What is the best way to achieve this?
I am using MVVMLight, but I guess the ideas should not be restricted just to it.
The easiest way is to create a view, and pass collection to it, but that doesn't sound MVVM friendly. I was thinking of creating a SelectProductViewModel from the first ViewModel and pass the collection to it, but I don't know how would I automatically create SelectProductView and bind it to created SelectProductViewModel.
Edit: in my application view structure is a bit complex. I have a main view, which basically needs to host a SelectProductView, since this view must cover whole screen. MainView contains lots of child and grandchild views (through tabs), so there could be 3 different child views or grand childViews that could issue a request for a product to be selected. Also, some view will not have products preloaded, so this task should probably be propagated to a SelectProductViewModel.
Example of Structure:
MainView
/ \
ChildViewA ChildViewB
/ \ / \
GrandChildViewA1 GrandChildViewA2 GrandChildViewB1 GrandChildViewB2
So, GrandChildViewA1, ChildViewB and GrandChildViewB2 could issue a request for a product to be selected. Only the view that issued a request should get the selected product, others should not bother with it. GrandChildViewA1 will have loaded products in it, but GrandChildViewB2 will not have ProductList loaded in it. This means, for performance sake, that GrandChildViewA1 should pass product list to SelectProductViewModel, while GrandCHildViewB2 will not have Product list in it, so SelectProductViewModel should fetch data from database.
I would create a generic viewModel which defines a contract for receiving data.
public abstract class PassDataViewModel<T> : ObservableObject
{
public T Data { get; }
}
I would then create a more general ViewModel for your product list like so:
public class SelectProductViewModel : PassDataViewModel<Product>
{
private Product _selectedProduct;
private ObservableCollection<Product> _products = new ObservableCollection<Product>();
public SelectProductViewModel(IList<Product> products)
{
_selectedProduct = _products.First();
}
public IEnumerable<Product> Products
{
get { return _products; }
}
public Product SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
OnPropertyChanged("SelectedProduct");
OnPropertyChanged("Data");
}
}
public Product Data
{
get { return _selectedProduct; }
}
}
You would use this in the following way:
Your first viewModel can create an instance of the SelectProductViewModel (when a command is invoked, for example)
You pass your products list to the new SelectProductViewModel instance.
Use a DataTemplate to change the view on your screen (this post will show you how to do this).
Have a property in the parent viewModel that returns the product returned from the data property of the SelectProductViewModel (you will need to propagate the PropertyChanged event to your parent viewModel).
the most easy way is to go the viewmodel first approach and use a dialogservice to show the selection view.
your viewmodel with ProductionList simply call the dialogservice and put a ProductSelectionViewmodel with ProductionList as parameter. because this is viewmodel first you have to create a datatemplate so WPF knows how to render your ProductSelectionViewmodel.
here is a link for a simple dialogservice.
btw: in my opinion viewmodel first approach is much easier when doing mvvm.
EDIT:
in your ProductionListViewModel in your SelectProductCommand
var selectProductViewModel = new SelectProductViewModel(this.ProductionList);
var result = this.uiDialogService.ShowDialog("Select Product", selectProductViewModel );
//if result true, simple get the selected product
this.SelectedProduct = selectProductViewModel.MySelectedProduct;
thats all - simple and easy

RIA services: Load returning no data

In the BookClub example application from nikhilk Kothary, a combobox is used to display book categories.
It is like this in the viewmodel class (the application is using the MVVM pattern):
private ReferenceDataContext _referenceData;
public BookClubModel() { // Constructor
_referenceData = new ReferenceDataContext();
_referenceData.Load(_referenceData.GetcategoriesQuery(), false);
}
Then there is a property to which the comboxbox is bound:
public IEnumerable Categories {
get {
return _referenceData.Categories;
}
}
Why is this working? Shouldn't we have a "completed" event handler for the load operation?
If I want to fill a IEnumerable property in the constructor, then it is not working:
private ReferenceDataContext _referenceData;
private IEnumerable _categories;
public BookClubModel() { // Constructor
_referenceData = new ReferenceDataContext();
_referenceData.Load(_referenceData.GetcategoriesQuery(), false);
_categories = _referenceData.Categories; _referenceData.Categories was what we were returning in the Categories property above.
}
Why does it work in one case and not for the other?
Daniel
In first case Categories is reference to _referenceData.Categories. And when _referenceData.Categories collection was updated, Categories also updated.
In second case you need event handler for Load operation, then do what you want with loaded entities.
Internally when you call Load, query to database is performing and when result is fetched then called load operation callback. Load operation is async operation and you need to keep in mind that fact

EntityFramework EntityState and databinding along with INotifyPropertyChanged

I have a WPF view that displays a Shipment entity. I have a textblock that contains an asterisk which will alert a user that the record is changed but unsaved. I originally hoped to bind the visibility of this (with converter) to the Shipment.EntityState property.
If value = EntityState.Modified Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
The property gets updated just fine, but the view is ignorant of the change. What I need to know is, how can I get the UI to receive notification of the property change. If this cannot be done, is there a good way of writing my own IsDirty property that handles editing retractions (i.e. if I change the value of a property, then change it back to it's original it does not get counted as an edit, and state remains Unchanged).
Any help, as always, will be greatly appreciated.
Cory
After struggling with the same problem for a little bit, here is a solution that is working for me.
Lets say I have an entity called Trip that was generated by EF, I just needed to extend the class by means of partial class as showed below. The RaiseEntityStateChanged method is useful when you need to force a refresh of the EntytyState property, for example after calling the context's SaveChanges method.
partial class Trip
{
bool _forced = false;
System.Data.EntityState _lastState;
public Trip()
{
_lastState = EntityState;
this.PropertyChanged += (s, e) =>
{
if (_lastState != this.EntityState && e.PropertyName != "EntityState" || _forced)
{
_forced = false;
OnPropertyChanged("EntityState");
}
_lastState = this.EntityState;
};
}
public virtual void RaiseEntityStateChanged()
{
_forced = true;
OnPropertyChanged("EntityState");
}
}
I don't see a way to create a XAML binding on an existing property to do what you are trying to do. But you could write your own IsDirty property, based on the EntityState; you could update this value by subscribing to the PropertyChanged event raised by the base EntityObject. Of course, you'll need to also raise a PropertyChanged event for IsDirty (so that the GUI is notified) and ignore this event in your handler (to prevent infinite recursion).
Edit: added the following after question by OP:
This is how I see it, in order to answer the comment.
In the shipment class, one can add:
public bool IsDirty { get { return EntityState == EntityState.Modified; } }
public Shipment() {
...
PropertyChanged += OnShipmentChanged;
}
private void OnShipmentChanged(object sender, PropertyChangedEventArgs pcea) {
if (pcea.PropertyName != "IsDirty") { // prevent recursion
OnPropertyChanged("IsDirty"); // notifies binding listener that the state has changed
}
}
During the night, I thought of another way, which is to create a multi-binding on each Shipment property (which would replace this whole notion of an IsDirty property and would actually answer the original question). This could make sense if there are just a couple of Shipment properties. I'd say if there are more than 3, we should forget about this idea.

Resources