How can I turn binding errors into runtime exceptions? - wpf

Just as there is "treat warning as errors" set in our projects to catch early possible problems, I would love to have a runtime exception to catch them early.
I have recently been bit by this problem and I would have been glad to have this.
Can it be done? And if yes, how?

You could hook into the PresentationTraceSources collection with your own listener:
public class BindingErrorListener : TraceListener
{
private Action<string> logAction;
public static void Listen(Action<string> logAction)
{
PresentationTraceSources.DataBindingSource.Listeners
.Add(new BindingErrorListener() { logAction = logAction });
}
public override void Write(string message) { }
public override void WriteLine(string message)
{
logAction(message);
}
}
and then hook it up in code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
BindingErrorListener.Listen(m => MessageBox.Show(m));
InitializeComponent();
DataContext = new string[] { "hello" };
}
}
Here is the XAML with a binding error
<Grid>
<TextBlock Text="{Binding BadBinding}" />
</Grid>

I implemented a solution very similar to the one proposed by Dean Chalk:
Derived a TraceListener that throws instead of logging
Added that listener to PresentationTraceSources.DataBindingSource
Please see the complete solution on GitHub, it includes a demo application and a unit test project.

First add this class to your project:
using System.Diagnostics;
namespace WpfTestApp
{
public class BindingErrorListener : TraceListener
{
public static void Register()
{
PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorListener());
}
public override void Write(string message)
{
}
public override void WriteLine(string message)
{
#if DEBUG
throw new System.Exception(message);
#endif
}
}
}
Then call the Register method in your App.xaml.cs class:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
BindingErrorListener.Register();
// ...
}
}
This way, (by throwing an exception) if you have any binding errors then you will be aware of those errors in the first place, that is, as soon as you start (F5) your application. If you wish, you can log those by injecting your logger object in the BindingErrorListener constructor.

Related

RequestNavigate in MainWindowViewModel ctor

hi i want when application luanched, application navigate to a view automatically so i used like this:
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainContent>();
containerRegistry.RegisterForNavigation<Subscene>();
}
and in MainWindowViewModel
public MainWindowViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
_regionManager.RequestNavigate("ContentRegion", "Subscene", myparameter);
}
this code not work, No errors and no navigation
my region exist in another usercontrol called MainContent
This works for "normal" view models only, not for the view model of the shell, because that one is created too early. You should do the initial navigation in OnInitialized.
internal class MyApp : PrismApplication
{
// ...
protected override void OnInitialized()
{
base.OnInitialized();
Container.Resolve<IRegionManager>().RequestNavigate("ContentRegion", "Subscene", myparameter);
}
// ...
}
Unrelated sidenote: if you use the parameter instead of the field in the constructor, Resharper will tell you when the field is no longer needed in future refactorings.

Open new window on click in WPF, Ninject and Caliburn.Micro

I'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".

Getting an Instance of IModuleManager in the Shell

I've been trying to write a WPF application using PRISM and MEF and I've been able to get the Shell up and running. I want to be able to load Modules on demand so I need an instance of IModuleManager in the Shell. However, when I try to import it, the application breaks. Here is the relevant code:
Bootstrapper:
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
// Add this assembly to export ModuleTracker
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
moduleCatalog.Load();
foreach (ModuleInfo moduleInfo in moduleCatalog.Modules)
{
this.ModuleCatalog.AddModule(moduleInfo);
}
DirectoryCatalog catalog = new DirectoryCatalog(#".\Modules");
this.AggregateCatalog.Catalogs.Add(catalog);
base.ConfigureAggregateCatalog();
}
protected override void ConfigureContainer()
{
//Export the Container so that it can be injected if needed.
this.Container.ComposeExportedValue(Container);
//Export the Module Catalog so that it can be injected if needed.
this.Container.ComposeExportedValue(ModuleCatalog);
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
Shell:
[Export(typeof(Shell))]
public partial class Shell : Window, IPartImportsSatisfiedNotification
{
[Import(AllowRecomposition = false)]
private IModuleManager moduleManager;
public Shell()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
public void OnImportsSatisfied()
{
}
}
The exception I'm getting is:
No exports were found that match the constraint:
ContractName Shell
RequiredTypeIdentity Shell
If I remove the [Import] attribute for the IModuleManager, everything works fine. Is there something I need to do to export IModuleManager?
Resolved this issue by commenting the following line in the Bootstrapper:
this.Container.ComposeExportedValue(ModuleCatalog);
Not sure why it was causing an issue but would welcome any insights into this matter.
One thing that you have to watch out for is having multiple exports for the same type. This can easily happen when you are using ComposeExportedValue as well as using a DirectoryCatalog (which could contain an Export of the same type).
There is a great package on Nuget for diagnosing these problems called MEFX
If you get this library, you can add the following lines to help find out what is going on
var compositionInfo = new CompositionInfo(AggregateCatalog, Container);
CompositionInfoTextFormatter.Write(compositionInfo, Console.Out);
I would be interested to see what this prints to the Output window when you leave this.Container.ComposeExportedValue(ModuleCatalog); in your program, if you wouldn't mind posting any errors from it.

Unity: Problem with resolving RIA DomainContext

I am using PRISM 4 and got my head around almost all features, however as soon as I would like to inject my DomainContext class (RIA) into my view model, the hell breaks loose. :) It would be great if an experienced Unity/Prism developer could give me an advice how to proceed.
Within my bootstrapper, I am registering the required class in Unity Container like this:
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<SCMDomainContext>();
}
Within the NavigationModule, I have the following in the ctor to register the NavigationView with a particular region.
public NavigationModule(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
_regionManager.RegisterViewWithRegion(Constants.NavRegion, () => _container.Resolve<NavigationView>());
}
The View takes the View Model as dependency:
public NavigationView(NavigationViewModel viewModel)
{
InitializeComponent();
Loaded += (s, e) =>
{
DataContext = viewModel;
};
}
The ViewModel has the following:
public NavigationViewModel(SCMDomainContext context)
{
_context = context;
ConstructCommon();
}
As soon as I comment this ctor out and put a en empty ctor, it is all fine, for some reason I can't resolve the SCMDomainContext class. Which is the one you add to have the Domain Context created for you provided by Ria Services.
Since I am using Silverlight, I can't see the stack trace to follow the exception, all I get is this message on a page. What am I missing please?
Microsoft JScript runtime error: Unhandled Error in Silverlight Application An exception occurred while initializing module 'NavigationModule'.
- The exception message was: Activation error occured while trying to get instance of type NavigationModule, key ''
Check the InnerException property of the exception for more information. If the exception occurred
while creating an object in a DI container, you can exception.GetRootException() to help locate the
root cause of the problem. at Microsoft.Practices.Prism.Modularity.ModuleInitializer.HandleModuleInitializationError(ModuleInfo moduleInfo, String assemblyName, Exception exception)
at Microsoft.Practices.Prism.Modularity.ModuleInitializer.Initialize(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesThatAreReadyForLoad()
at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.Modularity.XapModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.Modularity.XapModuleTypeLoader.HandleModuleDownloaded(DownloadCompletedEventArgs e)
at Microsoft.Practices.Prism.Modularity.XapModuleTypeLoader.IFileDownloader_DownloadCompleted(Object sender, DownloadCompletedEventArgs e)
at Microsoft.Practices.Prism.Modularity.FileDownloader.WebClient_OpenReadCompleted(Object sender, OpenReadCompletedEventArgs e)
at System.Net.WebClient.OnOpenReadCompleted(OpenReadCompletedEventArgs e)
at System.Net.WebClient.OpenReadOperationCompleted(Object arg)
Your help on this is highly appreciated,
Kave
I can't see much wrong here. But having said that, I'm using the Initialize method from the interface in the following way to register types and views for regions:
#region properties
[Dependency]
public IUnityContainer Container { get; set; }
[Dependency]
public IRegionManager RegionManager { get; set; }
#endregion
public virtual void Initialize()
{
this.Container.RegisterType<NavigationViewModel>(new ContainerControlledLifetimeManager());
this.Container.RegisterType<NavigationView>(new ContainerControlledLifetimeManager());
this.RegionManager.RegisterViewWithRegion(Constants.NavRegion, () => this.Container.Resolve<NavigationView>());
}
Not sure whether it makes a difference if you don't explicitly register the ViewModel and the View type. Personally I prefer to have control over the way how a type gets resolved by the container.
In fact its best to create a layer for the DomainContext like this. Then its easily resolvable by an IoC:
public class ContactModuleService : IContactModuleService
{
readonly SCMDomainContext _context = new SCMDomainContext();
#region Implementation of IContactModuleService
public EntitySet<Contact> Contacts
{
get { return _context.Contacts; }
}
public EntityQuery<Contact> GetContactsQuery()
{
return _context.GetContactsQuery();
}
public SubmitOperation SubmitChanges(Action<SubmitOperation> callback, object userState)
{
return _context.SubmitChanges(callback, userState);
}
public SubmitOperation SubmitChanges()
{
return _context.SubmitChanges();
}
public LoadOperation<TEntity> Load<TEntity>(EntityQuery<TEntity> query, Action<LoadOperation<TEntity>> callback, object userState) where TEntity : Entity
{
return _context.Load(query, callback, userState);
}
public LoadOperation<TEntity> Load<TEntity>(EntityQuery<TEntity> query) where TEntity : Entity
{
return _context.Load(query);
}
#endregion
}

Global handling exception in WPF app with Caliburn.Micro

Hi I try implement solution from this site im my WPF app for global exception handling.
http://www.codeproject.com/Articles/90866/Unhandled-Exception-Handler-For-WPF-Applications.aspx
I use Caliburn Micro as MVVM framework. Service I have in external assembly and it is injected in view model class with MEF.
Here is my implementation for global exception handling in WPF app.
App.xaml
DispatcherUnhandledException="Application_DispatcherUnhandledException"
Startup="Application_Startup"
App class:
public partial class App : Application
{
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void Application_DispatcherUnhandledException(object sender,
System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = e.ExceptionObject as Exception;
_msgBox.ShowException(ex);
}
}
Service method from external assembly:
public void ServiceLogOn()
{
try
{
}
catch (Exception ex)
{
throw ex;
}
}
This service method is call in view model class for example on button click event:
[Export(typeof(ILogOnViewModel))]
public class LogOnViewModel : Screen, ILogOnViewModel
{
public void LogOn()
{
_service.ServiceLogOn();
}
}
I run WPF app in Visual Studio and produce exception with message "Bad credentials" in ServiceLogOn method.
I expect that I see the message box with the exception.
But Visual Studio stop debuging app and show exception in service method in service project.
So I try run WPF from exe file and produce same exception in ServiceLogOn method.
I get this error:
Exception has been throw by target of an invocation.
Any exception from view model class is not handled in methods:
Application_DispatcherUnhandledException
or CurrentDomain_UnhandledException.
in App class.
What I do bad?
EDITED with Simon Fox’s answer.
I try implement in MEF bootstraper advice of Simon Fox’s, but I still something do wrong.
I move handle logic for exception to OnUnhandledException method in bootstraper class.
Here is my code from bootstraper class:
public class MefBootStrapper : Bootstrapper<IShellViewModel>
{
//...
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
protected override void OnUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
//...
}
I bind some method from view model on button and throw new exception. Something like this:
public void LogOn()
{
throw new ArgumentException("Bad argument");
}
But result is sam, I test app out of Visual Studio and get this exception.
Exception has been throw by target of an invocation.
Caliburn.Micro has built in support for hooking unhandled exceptions. The Bootstrapper class (which every Caliburn project requires) sets this up for you and provides the virtual OnUnhandledException method.
In your custom BootStrapper you must override OnUnhandledException to perform any custom actions for unhandled exceptions in your app. Note that you will most likely have to marshal actions such as displaying a message box to the UI thread (Caliburn enables this easily via Execute.OnUIThread).
You may also have an issue in the way your service moves exceptions to the client, but without any details of how the service is implemented/hosted/etc I cannot help. Are you using WCF to do SOAP? Are you using FaultContracts?

Resources