My view model class has a method (not sure if that is good practice or if view models are supposed to be strictly property and property changing mechanisms) that connects to a service. Of course I want to handle any possible WCF exceptions when connecting or disconnecting.
Let's use endpoint not found as an example considering that is an exception that I would want to bring to the user's attention. Consider the rough code example:
public void Connect()
{
ServiceClient proxy = null;
try
{
proxy = new ServiceClient();
proxy.Subscribe();
// ...
}
catch(EndpointNotFoundException)
{
// should I do something here?
}
// .. other WCF related exception catches and a finally
}
Is it considered good practice to maybe invoke System.Windows.MessageBox.Show() directly within the catch or should I maybe rethrow the exception so another layer of my WPF application catches it? If so, where is the ideal place to catch such an exception?
I've been handling exceptions in my MVVM client by catching them and wrapping them in an ErrorViewModel property of whatever ViewModel caught the exception.
Let's say a ViewModel A catches the EndpointNotFoundException. To present this error, I wrap the Exception in an ErrorViewModel and assign that to A's Error property.
The View associated with A contains a ContentControl bound to A's Error property. Meanwhile, I use a DataTemplate to associate an Error View to the ErrorViewModel. In that View, Visibility is determined by whether or not A's Error property contains an exception.
So A's View contains an error-message View that will only appear when an exception is caught, and can be dismissed by the user (an OK button on the error-message View invokes a command on A that clears A's Error property, thereby changing the error-message View's visibility to Collapsed).
Thus far, this seems to be a good approach that preserves proper MVVM decoupling.
Hope that helps. One way or another, honestly, I'd consider System.Windows.MessageBox.Show() in a WPF app as purely a last resort. Why give up rich control over the UI in favor of that old thing? Speaking of which, here's another popup-implementation approach.
Related
Trying to figure out how to capture view-only validation errors such as entering non-numeric characters in a text box bound to an integer property. I would like the Catel DataWindow to behave consistently.
Description:
I have a Catel MVVM window (implemented using DataWindow and a view model with a model property.)
The model property is an integer:
public Foo { get { GetValue .......... } }
The view model property is also an integer, and bound to the model:
[ViewModelToModel(...)]
public Foo { get { GetValue .......... } }
In the view, there is a text box that is bound to Foo. When the user enters a non-integer value in the text box, there is naturally an error in the binding process, and because the text box has ValidatesOnExceptions set to true, the following appears in the Catel info message bar:
Two problems that I must fix:
I need a custom error message ("Value 117.228 could not be converted" is not going to fly here.)
The WarningAndErrorValidator does pick up the error, but the DataWindow OK button is still enabled, and I am able to "save" the view model. I need OK to be disabled when there are any errors, even if they don't make it to the view model.
A web search has provided a couple possible solutions:
Bind to a view model property that's a string, and handle mapping/conversion between the view model and the model
Build support in the MVVM framework to trap UI validation errors and communicate them to the view model
Solution #1 is definitely the "workaround" solution because it means I need something like this in the view model (excuse the pseudo-code...):
[ViewToViewModel(...)]
public int Foo { ...... }
// Also a Catel property
public string Foo_Raw { ...... }
// Property changed handlers for both the above properties, keeping them in sync with one another when possible...
protected override void ValidateBusinessRules(List<.......> validationResults)
{
if (this.Foo_Raw != this.Foo.ToString())
{
validationResults.AddError("Foo must be an integer.");
}
}
I am not pleased at the prospect of creating this kind of rickety structure.
I'd much rather go with something like #2, but I didn't see anything in Catel's documentation that suggests that approach is supported. Did I miss an easy solution?
UPDATE: I just learned about the numeric text box behavior which might be another way to solve my specific problem, but I am really looking for a more general solution for capturing binding/UI errors in the view model validation.
The issue is that the exceptions you are trying to receive are not yet bound (since binding them goes wrong). There is no way for the vm to be aware of this issue. Since this is a view-related issue, you can only handle this error in the view.
One solution might be to forward the messages caught by the WarningAndErrorValidator onto the view model. You can define your own WarningAndErrorValidator on the view and subscribe to the Validated event. Then you can pass this onto your vm. This will require you to write a custom base class for your views if you want this shared among all controls in your app.
Geert van Horrik's answer was not quite correct (unless I missed something, Geert). The WarningAndErrorValidator only traps view model errors, not errors from the visual tree itself, or binding errors. It turns out that is something the InfoBarMessageControl does without help from the WarningAndErrorValidator.
What I did was, in my subclass of DataWindow, I duplicated the logic from InfoBarMessageControl that traps and analyzes visual tree validation errors, and I maintained a similar error message data structure.
I then overrode DataWindow::ValidateData like so:
protected override bool ValidateData()
{
// In addition to base class logic, make sure there are no errors of any kind including view-only errors (like binding exceptions or ValidationRule errors).
return base.ValidateData() && this.ViewErrorCount == 0;
}
ViewErrorCount is a simple int that I update when I trap errors as described above.
What i did
I have HomeViewModel and SellsViewModel.
In the HomeViewModel, I have property "SellID"
In the constructor of SellViewModel, i am able to Resolve reference of HomeViewModel and stored it in m_objHomeViewModel variable in SellViewModel
In the XAML of SellViewModel, i have a textbox which shows "SellID", this textbox is bound to "m_objHomeViewModel.SellID"
What i am getting doing this
Doing this, whenever user selects difference "Sell" on HomeViewModel, automatically my SellViewModel picks it up and shows changes in SellView.
Question
As XAML textbox in SellView is bound to a property in HomeViewModel, changes are getting reflected on UI immediately
But i am not able catch any event (Such as property change) in SellViewModel, catching such event i want to load other values for the selected "SellID" from database.
I am not using Event Agreegator. If used, i can easily subscribed to event in SellViewModel published by HomeViewModel
Q1: How to do it without using Event Agreegator?
Q2: If in XAML, TextBox is bound to property m_objHomeViewModel.SellID, will it create memory leakage?
Q3: If in the HomeViewModel, i get reference to SellViewModel (Using container.resolve) and call a public property or method of SellViewModel whenever "SellID" property in HomeViewModel is modified. Is it a good programming practice? Here i think it will create tight coupling between HomeViewModel and SellViewModel
Please suggest on this...
Regards
A1: If I understand your design, your SellVM will need to manually subscribe to the PropertyChanged event of your HomeVM. If the SellId property of your HomeVM raises PropertyChanged, then your SellVM will see that and respond accordingly.
A2: Without seeing the entire application, simply databinding to a property won't cause a memory leak. As long as the UI is displayed, the HomeVM will be in memory, but .NET does a pretty good job of recognizing when it is no longer needed and cleaning up the memory. The answer to this is highly dependent on your overall design, but the simple act of binding the SellID from the HomeVM through the SellVM won't, on its own, cause a memory leak.
A3: This sounds a little strange - Without understanding the full architecture, it seems that the SellID should belong to the SellVM, and when the users switches SellID, the HomeVM loads the SellVM with the appropriate SellID. This seems more OO and allows you to separate concerns. This way everything about the "Sell" (sale?) is encapsulated in the SellVM and the HomeVM is strictly responsible for coordination (loading the correct child VMs). But this is based on what little I can gather about your overall design.
My model has a couple properties one is a string and the other is an observablecolletion. When the model is created it fires off a backgroundworker thread to basically poll a .dll for data. Based on the data it receives it will either set the string and/or add and item to observable collection. My string property seems to fire its Onproperty change just fine and the view updates. However, my observable Collection throws a cross thread exception. I have tried moving code where i set the ObesrvableCollection to the worker.ReportProgress and get the same error. I have moved the logic into the view model and still get the same threading error. I'm unsure why my string property works for one. I have read about Dispatcher.Invoke, but i'm pretty sure that my model should not be aware of this. Can anyone explain the correct way to go about this please.
Just fyi - my view is not tied directly to my model. I have a property for my model in my viewModel and the model gets passed through constructor injection. Just want to put that out there before anyone thinks my model is talking directly to the the view.
Hard to give specifics without code. However, WPF automatically marshals property change notifications for scalar properties but not collections. Hence, you must be modifying the collection from a non-UI thread.
There is no reason why your VM can't use Dispatcher, or perhaps the more generic SynchronizationContext if you prefer. It can make things more tricky to test, however.
If you post code there may be a way you can simplify things.
As Kent said, if you're not on the UI you need to use the Dispatcher to update your collection:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
// update your ObservableCollection here
}));
A common MVVM/WPF approach is to data-bind the UI's controls directly to the underlying model object. The model object may contain its own validation logic (perhaps exposed via IDataErrorInfo) or may be validated by a helper class checks a model object instance for errors. In either case, at times the model have invalid data in it and so be in an invalid state.
However, in the DDD world, the model is never to be in an invalid state. How do you suggest performing validation when using WPF and DDD?
Thanks,
Ben
I don't think the View in MVVM should be binding directly to the domain model, it really should be binding to a View Model instead. Then the View Model can be in an "invalid" state which can be reflected through IDataErrorInfo. Only later when a user operation (e.g. Save, OK, Apply) applies this to the underlying domain model should the domain model enforce validity, you can also prevent the apply by not allowing the operation in the UI.
Although I must say I've found that it's not always easy to do this without duplicating the validation logic to some extent.
I'm tending to think that a facade or similar layer should be used as the MVVM "model." This facade can be in an invalid state (unlike the DDD model). For validation, it can either contain its own logic or a tool like FluentValidation can be used. Once it is in a valid state, its "do action" function can be called. This will pass the data in the facade to the underlying DDD model. With this approach, at no point does the DDD model encounter invalid data.
With this approach, the facade and its validation logic could be used by multiple view/view model pairs, eliminating the validation logic duplication present when each view model does its own validation.
Validation belongs in your business logic (domain model). I suggest taking a look at FluentValidation for .NET.
I've actually had good luck having the ViewModel setter call the underlying Model object, and letting FluentValidation throw an exception. If you're using a WPF TextBox, the binding will keep working, but the TextBox will show a red outline (assuming you've used the syntax where the TextBox updates the ViewModel on each and every keystroke). Just don't throw an exception in the getter or you'll break the binding.
It's better to route all your communication from the ViewModel to the Model through some intermediary (I introduced a Presenter, but this can be as simple as passing the Model operation as a lambda to a callback on some mediator). When an exception happens while operating on the Model, the Presenter catches the exception and displays a friendly message to the user with the details from the exception. FluentValidation produces really nice default error messages for this purpose.
I have a databound WPF CheckBox control that appears to be eating exceptions thrown by the corresponding property setter when the value is toggled in the UI. I know this can happen if I provide a ExceptionValidationRule on the Binding instance, but I double checked that the ValidationRules for the Binding instance has count zero. I also checked the call stack for intervening exception handlers and none exist. Nonetheless, the thrown exception does not bubble to the top and produce a crash in the app as I would expect.
If I throw an exception from a button click handler in the same UI, the exception does bubble up and cause an application crash, ruling out some sort of global exception handler.
Any ideas?
Thanks!
To add to itowlson's answer, the Binding class provides the UpdateSourceExceptionFilter property, which allows you to provide logic that runs when an exception occurs updating the source. It is used in conjunction with the ExceptionValidationRule class, and allows you to do something other than adding a ValidationError when the update fails.
No, this is expected behaviour: the WPF data binding infrastructure catches exceptions caused by saving the value from a binding target back to the source. I suspect this is because there is no way for the app to set up an exception handler around the save operation (because it is called from WPF code rather than from app code), so if WPF did not do this, the app would crash without the chance to handle the exception.
(By contrast, in a button click handler, you are writing the code so you do have the opportunity to handle exceptions. Therefore WPF thinks it's okay to let the exception propagate if you decide not to handle it.)