Handle exceptions with WPF and MVVM - wpf

I am attempting to build an application using WPF and the MVVM pattern. I have my Views being populated from my ViewModel purely through databinding. I want to have a central place to handle all exceptions which occur in my application so I can notify the user and log the error appropriately.
I know about Dispatcher.UnhandledException but this does not do the job as exception that occur during databinding are logged to the output windows. Because my View is databound to my ViewModel the entire application is pretty much controlled via databinding so I have no way to log my errors.
Is there a way to generically handle the exceptions raised during databinding, without having to put try blocks around all my ViewModel public's?
Example View:
<Window x:Class="Test.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestView" Height="600" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<StackPanel VerticalAlignment="Center">
<Label Visibility="{Binding DisplayLabel, Converter={StaticResource BooleanToVisibilityConverter}}">My Label</Label>
</StackPanel>
</Window>
The ViewModel:
public class TestViewModel
{
public bool DisplayLabel
{
get { throw new NotImplementedException(); }
}
}
It is an internal application so I do not want to use Wer as I have seen previously recommended.

The Binding implementation is designed to be fault tolerant and so it catches all the exceptions. What you could do is to activate the following properties in your bindings:
ValidatesOnExceptions = true
NotifyOnValidationError = true
See also the MSDN.
This causes to raise the attached Error property on the bound control.
However, this infrastructure is designed for validating the user input and show validation messages. I’m not sure if this is what you are doing.

Related

Xaml: design a layout with dynamically visible components

In a mvvm application some areas inside a window (in reality it is a UserControl inside MainWindow) are dynamically displayed according to the user selections.
The changing blocks are inside Stackpanels, I have 4 of them and only one at a time is displayed. This is accomplished binding Visibility to a bool property and using the BooleanToVisibilityConverter.
I put all the alternate StackPanel inside parent control. It works correctly, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
How can I easily create the layout having more controls which share the same window area and are displayed one at a time ?
Setting A Design Time Only Data Context
Developing XAML in the Studio Designer can be greatly simplified by setting the Design-Time Data Context.
One implementation is based on setting a duplicate DataContext which will be ignored during the final compilation.
To implement the switching, add to the ViewModel, a property that will inform the designer whether it can be used in Development Mode or not.
I use an MVVMLight situation for this example, but for this declared instance property IsInDesignMode and static property ViewModelBase.IsInDesignModeStatic.
Example:
using System.ComponentModel;
namespace DataContextDesignTime.Example
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _flag;
public bool Flag
{
get => _flag;
set
{
if (!Equals(_flag, value))
{
_flag = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Flag)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotFlag)));
}
}
}
public bool NotFlag => !Flag;
}
}
<Window x:Class="DataContextDesignTime.Example.ExamleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataContextDesignTime.Example"
mc:Ignorable="d"
Title="ExamleWindow" Height="450" Width="800">
<d:Window.DataContext>
<local:MyViewModel Flag="True" NotFlag="True"/>
</d:Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Border Background="LightBlue" Height="200"
Visibility="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Border Background="LightGreen" Height="400"
Visibility="{Binding NotFlag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In this example, you can change property values in XAML or in the Property Browser.
And you will immediately see the work of your bindings, triggers, how the display for certain data changes.
Note
This may fail on more complex VMs/packages, but in general by setting the DataContext at design time is not difficult.
I need to recompile the project to see the changes in the properties.
The XAML Designer panel has an «Enable/Disable Project Code» button.
, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
This problem is easily resolved by bringing up the Document Outline tab in visual studio. Once open, navigate to the visible tree and toggle the eyeball to visibly hide/unhide the control[s] one is not interested in; during design time only.

Calburn Micro - IoC is not initialized in WPF designer

I have a WPF application, using Caliburn Micro, and StructureMap for DI.
On my window, I have a ContentControl, the name of which is a property on my view model - at runtime Caliburn successfully locates the correct view based on the type of this property and displays it in that area.
At design-time, though, an exception is thrown: "InvalidOperationException: IoC is not initialized.". Looking at the stack trace, its obvious that Caliburns ViewLocator is attempting to use IoC to create an instance of the view, but the IoC container is not initialized at design time.
So, the question is: How do you initialize Caliburn's IoC at design time?
EDIT:
Here is my UserControl declaration:
<UserControl x:Class="MyNamespace.Views.Checklist.ChecklistQuestionEditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:vm="clr-namespace:MyNamespace.ViewModels.Checklist"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro.Platform"
d:DataContext="{d:DesignInstance Type=vm:ChecklistQuestionEditDesignerViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True"
d:DesignHeight="700" d:DesignWidth="1000">
And here is the ContentControl that causes the issue:
<ContentControl IsTabStop="False" Grid.Row="2" Grid.Column="0" Margin="12" Name="TranslationView"/>
This control is populated by Caliburn's Name conventions via this property in the view model:
private ChecklistQuestionTranslationViewModel _TranslationView;
public ChecklistQuestionTranslationViewModel TranslationView
{
get { return _TranslationView; }
set
{
if (_TranslationView != value)
{
_TranslationView = value;
NotifyOfPropertyChange(() => TranslationView);
}
}
}
If I remove the ContentControl line above from the XAML, all other designer functionality works as expected.
For some reason, you can't copy the exception message from the designer, so I am putting a screen shot here. You can see that Caliburn is attempting to use IoC to create an instance of the View. but I don't have 10 reputation, so I can't post my screenshot.
You might want to have a look at this SO thread I recently asked (and answered): up to now is working great for us.
The main points of that approach are:
Prepare a fake design-time version for each of your view-models, both the container/parent as well as the one mapped to the ContentControl of your view (the child).
Each design-time version has the empty constructor, no external dependencies to be set. It must not rely on DI/IoC container at all.
For every public property data-bound between view and original view-model, the design-time view-model should have a property with the same name ... not necessarily the same type.
This is the case of your child view-model mapped to the ContentControl: the real parent view-model has e.g. a property of type MyCommandBarViewModel, while the fake parent view-model has one of type MyDesignTimeCommandBarViewModel.
This might be applied to a whole visual tree of view-models, the leaves being view-models that just show simple property types (strings, numbers, etc.)
In order to have the Designer and Caliburn.Micro work together to generate those design-time versions, you need to add the following on the root UI element (Window, UserControl, ...):
<Window
d:DataContext="{d:DesignInstance Type=fake:YourDesignTimeViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True">

User control that opens dialog, where dialog result updates properties in viewmodel of the page that uses it

I have been struggling with this question for some time now. It seems relatively simple, but I am unsure of how to solve it. Would love your aid on this.
I have a page that's bound to viewmodel, that has some string properties that represent file paths.
I've built a user control that has a textbox and a button. The control's purpose: show a dialog when you click on the button(file dialog), and update the textbox according to the result.
Here is the xaml:
<UserControl x:Class="MyProject.Controls.FilePickerLauncherControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="me"
d:DesignHeight="300" d:DesignWidth="300">
<DockPanel>
<Button Content="..." DockPanel.Dock="Right" Command="{Binding OpenFileDialogCommand,ElementName=me}"></Button>
<TextBox Text="{Binding Path=PathChosen,ElementName=me}" DockPanel.Dock="Right" VerticalAlignment="Center" Height="30"></TextBox>
</DockPanel>
This is my the command in page's view model(the action):
private void OpenFileExplorer()
{
SimpleIoc.Default.GetInstance<IInputOutputService>().OpenFileDialog(modelsDirectory);
}
Now what I want to do, for every instance of my control, to take the result of the dialog and put it in the textbox that's in the control. I've been thinking of:
maybe a command parameter that would be sent to this OpenFileExplorer, telling which property should be updated somehow, but I am not sure how.
Write this command multiple times for each property. I don't think this is correct.
Create a new viewmodel for my control with this command and make instances of it in my current view model.
What would you guys say the best way to do this? it is relatively simple, although it got me kind of confused.

MVVM way to use different controls for editing different objects

I need to design a form with a treeview in the left and a container for some other control in the remaining area. Whenever user selects an item in the treeview some custom control appears on the right. For example, let's say that the treeview contains values "Audio settings" and "Video settings", and I have two controls that can be bound to these settings and I want to display them on the form when needed.
Now, from what I've read about MVVM, I shouldn't have properties that will return UserControls or DataTemplates, am I right? It will be messing with "VM shouldn't know implementation details of the view" as I see it. So, how do I handle this situation properly in terms of MVVM? Should I maybe use converters for this and if so, how would it look?
I can't provide any code at the moment (mostly because there isn't any), but I will try to clarify the problem if needed.
Thanks in advance.
This is where the WPF templating system helps out.
The main idea is to have a ContentControl display the appropriate view depending on the selected value in the TreeView.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ListBox DockPanel.Dock="Left" ItemsSource="{Binding Editors}" SelectedItem="{Binding SelectedEditor}" />
<ContentControl Content="{Binding SelectedEditor}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type l:VideoViewModel}">
<l:VideoView />
</DataTemplate>
<DataTemplate DataType="{x:Type l:AudioViewModel}">
<l:AudioView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>
AudioView and VideoView are UserControls.
As you can see, the ContentControl's content is bound to the SelectedEditor property in the ViewModel, which is also bound to the SelectedItem of the Listbox.
So the ViewModel for the main view looks like this.
public class MainWindowViewModel : INotifyPropertyChanged
{
public IEnumerable<object> Editors
{
get
{
yield return new VideoViewModel();
yield return new AudioViewModel();
}
}
private object selectedEditor;
public object SelectedEditor
{
get { return selectedEditor; }
set
{
if (selectedEditor == value)
return;
selectedEditor = value;
OnPropertyChanged("SelectedEditor");
}
}
}
So you can see that the main ViewModel has no GUI data in it.
To handle hooking up a TreeView to a SelectedItem property in a ViewModel see Data binding to SelectedItem in a WPF Treeview
You can implement it throw two properties: ShowAudioSettings and ShowVideoSettings in ViewModel and bind it on Visibility of your controls.
Also, you can make it with VisualStateManager.

WPF Databinding to Custom Collection Objects

Public Sub New(ByVal log As Entities.LogSystem)
InitializeComponent()
Me.DataContext = log
End Sub
This is the the initializer for my custom control It passes in a single entity that has several property fields. This control is added to a parent control so that it appears in a stackpanel.
Anyway I am trying to get the specific data from this control into several different text boxes:
<UserControl x:Class="LogSystemPickerItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WavelengthIS.WISRED.UserControls"
Width="579" Height="122">
<UserControl.Resources>
<local:LogSystemPickerItem x:Key="Log"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource Log}}">
<Label Height="30" Name="Label1" VerticalAlignment="Top" Content="{Binding deptDescription}"/>
</Grid>
</UserControl>
As you can see i havent really gotten too far. I have tried many different ways to do this including using dependency properties...I just really cant find a tutorial that shows this specific circumstance...can anyone point me in the right direction?
If you're setting the DataContext in the code behind as per your first code snippet, there's no need to also do it in the XAML, so you can remove the "Log" resource and the corresponding DataContext assignment on the Grid.
Once you've done that, it should work assuming there is a deptDescription property on your log class.
... and in XAML you may do it this way...
<UserControl.DataContext>
<local:LogSystemPickerItem/>
</UserControl.DataContext>

Resources