CaliburnMicro IEventAggregator won't work with ChildViews - wpf

and sorry if this is a somewhat easy question, but I'm still new to CaliburnMicro and EventAggregator is proving to be the worst thing to learn by a long mile. Anyway, let's go to it. I have an app with a main ShellView and a bunch of ChildViews that display various information, and I need certain parameters to be shared between the Views. It did not take very long to find I need to use EventAggregator, but with Net6 I could not make it work, no chance. I found this app someone else did in Net3.1 and CaliburnMicro where a new window is created, on this second window there is a TextBox and a send button. Whatever you type here gets sended to the Main window. I studied the code and replicated the app succesfully, also with the latest version of Caliburn and Net6.
BUT then I decided to modify the app and instead of having a new window, now I have exactly the same but with a ChildView inside the ShellView, and here is where nothing works.
1st on the Bootstrapper
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.PerRequest<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
As seen, I've created a SimpleContainer, then initialised it as a Singleton.
Also _container.Singleton<IWindowManager, WindowManager>(); is no longer needed as I am not opening a new window anymore, so that could be commented but as I had so many issues, I let it be just in case for this last try before posting this question.
2nd I've created a class for the message IHandle to manage.
public class EventMessage
{
public string Text { get; set; }
}
3rd I created and edited the ChildViewModel (still called SecondWindowViewModel as this experiment is directly derivated from the original worknig one) and ChildView (well SecondWindowView still). Note that SecondWindowView is a WPF User Control, not a Window.
class SecondWindowViewModel : Screen
{
private readonly IEventAggregator _eventAggregator;
private string _secondTextBox;
public string SecondTextBox
{
get { return _secondTextBox; }
set { _secondTextBox = value; NotifyOfPropertyChange(() => SecondTextBox); }
}
public SecondWindowViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator= eventAggregator;
}
public void SendBack()
{
EventMessage ToSend = new EventMessage();
ToSend.Text = SecondTextBox;
_eventAggregator.PublishOnUIThreadAsync(ToSend);
}
As seen, I have an IEventAggregator _eventAggregator, then on the constructor of the class I added this._eventAggregator= eventAggregator; and then on the method SendBack that is called upon pressing the button I send the message with SubscribeOnUIThread.
And lastly the ShellViewModel:
public class ShellViewModel : Conductor<Object>, IHandle<EventMessage>
{
private readonly IEventAggregator _eventAggregator;
private string _parentText;
public string ParentText
{
get { return _parentText; }
set { _parentText = value; NotifyOfPropertyChange(() => ParentText); }
}
public ShellViewModel(IEventAggregator eventAggregator)
{
ActivateItemAsync(new SecondWindowViewModel(_eventAggregator));
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public Task HandleAsync(EventMessage message, CancellationToken cancellationToken)
{
ParentText = message.Text;
return Task.CompletedTask;
}
//public void NewWindow()
//{
//WindowManager wm = new WindowManager();
//SecondWindowViewModel swm = new SecondWindowViewModel(_eventAggregator);
//wm.ShowWindowAsync(swm);
//}
}
}
Now here instead of inheriting from Screen and IScreen, I inherit from Conductor because I want to have that ChildView on my form. NewWindow is how it worked before but now that button no longer works as I don't need to launch a new window anymore, that's why it is commented out.
As seen, on the contructor I subscribe _eventAggregator, and then HandleAsync does the job of receiving the message and assign it to a variable. Now on the Caliburn Documentatin the method to use is public void Handle() but that no longer works, that's the only way I managed to make it work.
Now when I run this the app does load and seems to work just fine, but as soon as the SendBack() method gets called (in SecondWindowViewModel) the line _eventAggregator.PublishOnUIThreadAsync(ToSend); launches an exception System.NullReferenceException: 'Object reference not set to an instance of an object.'
From my understanding EventAggregator should not care if I'm sending the message to a Window or a user panel or anything. Only thnig I changed is commenting out NewWindow and deleting the old SecondWindowWiew WPF Window and replacing it with a new SecondWindowView WPF User Control with the exact same XAML, then in ShellWiev added bellow a <ContentControl x:Name="ActiveItem"/>.
I'm a bit of a loss here, I've been trying everything, coping the code from the documentation, looking for tutorials online, other StackOverflow questions, and while I could make the UI load and make HandleAsync work, for 3 days straight I could not make it work with ChildViews. That code I wrote does work with a new window. I even run into problems of ShellView straight up not loading unless putting a new constructor that takes no parameters and empty inside, but that's for another day.
Sorry for the extra long post, but I think it's important to put all the information out there. Thank you for your time and again, sorry if this is somewhat of a dumb question, but we all have to start somewhere no?

Related

How to load module when using events

I'm very newbie in C#\Prism ecosystem.
I want to connect modules via events, but if just send event:
_eventAggregator.GetEvent<LoginSuccessEvent>().Publish(new LoginSuccessEventArgs(user));
then my event handler is not working.
As I understand it happens, because reciever ViewModel is not created (I checked with break point in debugger).
But if I navigate from event sender:
_regionManager.RequestNavigate(RegionNames.RootRegion, "WorksheetListView");
_regionManager.RequestNavigate(RegionNames.WorksheetDetailsRegion, "WorksheetDetailsView");
_eventAggregator.GetEvent<LoginSuccessEvent>().Publish(new LoginSuccessEventArgs(user));
Then before first RequestNavigate command reciever ViewModel constructor is called.
_regionManager = regionManager;
_model = container.Resolve<WorksheetListModel>();
OpenWorksheetCommand = new DelegateCommand(OpenWorksheet);
Worksheets = _model.WorksheetList;
eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(OnLoginSuccessEvent);
I tried to add ViewModel class registration to reciever module:
_container.RegisterType<WorksheetListViewModel>();
But no luck. I don't want to add this registration to sender, because hard relation is maked.
But I want to have weak relation between modulel and to do navigation from RECIEVER, but not from SENDER. So sender will don't know anything about reciever.
How can I achive this?
Thanks.
It's a bit unclear what you're trying to achieve, but as far as I get it, your problem's that events are there for those instances that are currently alive. It seems you want more of a state, so that view models that are created after the user logged in can check whether a user is logged in and act accordingly.
I suggest you create a service to hold the currently logged in user and keep the event, because they complement each other nicely.
Example:
public interface IUserManager : INotifyPropertyChanged // this is optional if you keep the event
{
// returns null if no user is logged in
string CurrentUserName { get; }
// returns true if user name and password are valid and updates CurrentUserName
bool TryLogin( string userName, string password );
}
Ok. I found a tons of question like ('EventAggregator don't work around modules').
My solution is very easy. I create a instance of my viewmodel, so constructor with event subscribtion is invoked too.
using Microsoft.Practices.Unity;
using Prism.Modularity;
using Prism.Regions;
using Prism.Events;
using Prism.Unity;
using WorksheetListModule.ViewModels;
using WorksheetListModule.Views;
using WorksheetListModule.Models;
namespace WorksheetListModule
{
public class WorksheetListModule : IModule
{
IRegionManager _regionManager;
IUnityContainer _container;
IEventAggregator _eventAggregator;
public WorksheetListModule(RegionManager regionManager, IUnityContainer container, IEventAggregator eventAggregator)
{
_regionManager = regionManager;
_container = container;
_eventAggregator = eventAggregator;
}
public void Initialize()
{
WorksheetListViewModel vm = new WorksheetListViewModel(_regionManager, _container, _eventAggregator);
_container.RegisterInstance<WorksheetListViewModel>(vm);
_container.RegisterType<WorksheetListModel>();
_container.RegisterTypeForNavigation<WorksheetListView>();
_container.RegisterTypeForNavigation<WorksheetDetailsView>();
}
}
}
Key feature here is RegisterInstance function.
So now I can do navigation in event reciever and event sender now don't know any information about reciever internal structure.

WPF NotifyIcon From Background Thread

I know normally one is not supposed to touch UI elements from threads other than the UI thread, but I am new to WPF and I am wondering if my current working implementation can be improved.
I have an application that is comprised solely of a notification tray icon, and I want to update that icon from a background thread.
Here is my Program.cs entry point:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (IconHandler notify = new IconHandler())
{
notify.Display();
Application.Run();
}
}
}
This is my IconHandler.cs notification icon handler class:
class IconHandler : IDisposable
{
NotifyIcon ni;
public IconHandler()
{
ni = new NotifyIcon();
}
public void Display()
{
ni.MouseClick += new MouseEventHandler(ni_MouseClick);
ni.Icon = Resources.icon1;
ni.Visible = true;
new Thread(new ThreadStart(UpdateIcon)).Start();
}
public void UpdateIcon()
{
while (true)
{
// reference ni directly, it updates fine
}
}
public void Dispose()
{
ni.Dispose();
}
void ni_MouseClick(object sender, MouseEventArgs e)
{
// something useful
}
}
Is there anything blatantly incorrect about this? It seems a bit fishy to me - it was just my first attempt. It seems to work for what I want to do, does anyone have any suggestions for a better implementation? Will I run into lifecycle issues with this setup?
Is there anything blatantly incorrect about this? It seems a bit fishy to me - it was just my first attempt. It seems to work for what I want to do, does anyone have any suggestions for a better implementation? Will I run into lifecycle issues with this setup?
To begin with NotifyIcon is not a WPF control, but comes from the Windows Forms namespace. As such it has normal C# properties (e.g. Icon, Visible) meaning you have been able to alter the icon property in the non-UI thread without an exception being raised. If you had used a WPF controls then they have Dependency Properties and direct manipulation of Dependency Properties outside of the UI thread will cause an exception to be raised.
Will I run into lifecycle issues with this setup?
You've currently NOT created a WPF window or WPF controls. If your application develops such that you start using WPF and the UpdateIcon method is expanded to do more than you currently do and access these WPF objects then yes you will need a strategy to deal with the updates from non-UI threads.
You can hide some of this cross-threaded access using some helper methods.
Example 1 If your strategy becomes referencing WPF controls programmatically from the background thread then you can use a helper method such as this.
It first checks if the call is on the UI thread, if so then it updates the control directly, otherwise it will schedule that the method (itself) be called from the UI thread at a later point in time.
I've used BeginInvoke here so that the background thread can continue before the UI thread has actually called the method. If you want to block the background thread then use Invoke instead.
public void UpdateLabel(Label control, string text)
{
if (Application.Current.Dispatcher.CheckAccess())
control.Content = text;
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => UpdateLabel(control, text)), DispatcherPriority.Normal);
}
Example 2
If your strategy uses Events raised on the background thread to update the WPF controls programmatically then you can hide some of the cross-threading calls as part of raising the event, leaving the WPF update routine quite clean and simple to read.
Any event handlers of this event can be coded knowing that the call will be made from the UI thread, so no threading issues.
public void OnRaiseEvent(EventHandler handler, EventArgs args)
{
if (handler != null)
{
if (Application.Current.Dispatcher.CheckAccess())
handler(sender, new PropertyChangedEventArgs(propName));
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => handler(sender, args)), DispatcherPriority.Normal);
}
}
Example 3
If your future strategy fully utilizes the benefits of WPF with Binding (as opposed to programmatically updating your WPF controls), then you can embed the cross-threading code into the data-bound objects.
If for example your XAML databinds to the MyProperty property of an instance of the MyDataClass class and that class implements the INotifyPropertyChanged interface you can put the cross-threading code in the data class making it possible to update the data from any thread. Here is the example of the class:-
public class MyDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _myProperty;
public string MyProperty { get { return _myProperty;} set { PropertyChanged.SetValueAndNotify(this, ref _myProperty, value); } }
}
This class utilizes the SetValueAndNotify extension method on the PropertyChanged event. It is in here we hide the cross-threading code to simplify other parts of the code. Here's the definition of this extension method.
public static class PropertyChangedExtension
{
public static void SetValueAndNotify<T>(this PropertyChangedEventHandler handler, object sender, ref T destination, T source, [CallerMemberName] string propName = "notset")
{
// Is the new value different from the previous value? If there is no difference then there is nothing more to do
if (Equals(destination, source))
return;
// If we got to this point then the new value is different from the old value, so lets make the assignemnt and raise the property changed event
destination = source;
if (handler != null)
{
if (Application.Current.Dispatcher.CheckAccess())
handler(sender, new PropertyChangedEventArgs(propName));
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => handler(sender, new PropertyChangedEventArgs(propName))), DispatcherPriority.Normal);
}
}
}
The above example uses the [CallerMemberName] attribute from C#5 to remove any typing errors in supplying the property name for the INotifyPropertyChanged arguments. If you are not using the latest then you will need to modify the getter and setter as follows:-
public string MyProperty { get { return _myProperty;} set { PropertyChanged.SetValueAndNotify(this, ref _myProperty, value, "MyProperty"); } }
You must always update UI from UI thread only, however, you can schedule some work on UI thread from background thread using dispatcher
public void Display()
{
ni.MouseClick += new MouseEventHandler(ni_MouseClick);
ni.Icon = Resources.icon1;
ni.Visible = true;
new Thread(new ThreadStart(UpdateIcon)).Start();
}
public void UpdateIcon()
{
while (true)
{
//do some long running work
Application.Current.Dispatcher.Invoke(()=>{
//update ui
});
}
}
But if you don't have long running work and you just want to do something periodically, you should use DispatcherTimer instead of loop in background thread.
The while(true) loop in your code will cause heavy CPU/resource usage. maybe add e.g. Thread.Sleep(1000) into the loop to allow for a break between updates.
The best usage of background threads is to perform the long-running work (e.g. communication with server/DB) on the background thread and once the thread completes, have the UI thread update the UI.
With BackgroundWorker:
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
// long running work
};
worker.RunWorkerCompleted += (sender, args) =>
{
// Update UI
};
worker.RunWorkerAsync();
async/await pattern:
public async void DoWork()
{
// Do long running task
var data = await Task.Run(() => new object());
// Update UI here
}
TaskFactory:
Task.Factory.StartNew(() => new Object()).ContinueWith(task => MessageBox.Show(task.Result.ToString()), TaskScheduler.FromCurrentSynchronizationContext());
If the UI needs to update on a constant loop, maybe use a timer to restart the process on a regular basis. This will save your CPU from taking a pounding.

Using WPF Extended Toolkit MessageBox from other threads and curious behavior

I am having trouble using the WPF Extended Toolkit (version 2.1.0.0) MessageBox from other threads. The namespace is: Xceed.Wpf.Toolkit.MessageBox
I replaced my regular MessageBoxs (System.Windows.MessageBox) with the Toolkit MessageBox and get errors when I launch one from another thread. The System.Windows.MessageBox has no such problems. I saw this posting that reports the problem, but there seems to be no follow up:
https://wpftoolkit.codeplex.com/workitem/21046
I'm guessing there is a work around. An example is presented there that shows the problem, but here is my simple example:
First, I wrap the Toolkit.MessageBox. I do this primarily because I'm applying style (although I've commented that out to show that's not the problem)
public class CustomMessageBox
{
//static DummyUserControl1 _ctrl = new DummyUserControl1();
public static MessageBoxResult Show(string msgText, Style styleArg = null)
{
Cursor saveCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = null;
//Style style = styleArg != null ? styleArg : _ctrl.FindResource("MessageBoxStyle1") as Style;
// MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK, style);
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK);
Mouse.OverrideCursor = saveCursor;
return result;
}
}
The main window just has two buttons on it, and here's the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnMainThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
CustomMessageBox.Show("Hello on main thread");
}
private void btnAltThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
Thread altThread1 = new Thread(new ThreadStart(AltThread1Proc));
altThread1.SetApartmentState(ApartmentState.STA);
altThread1.Priority = ThreadPriority.AboveNormal;
altThread1.IsBackground = true;
altThread1.Start();
}
public void AltThread1Proc()
{
MessageBox.Show("Hello on Alt Thread");
CustomMessageBox.Show("Hello on alt thread");
}
}
The problems occur in AltThreadProc() with CustomMessageBox.Show(...). The curious behavior I referred to is this: If you hit the main thead button and then the Alt thread button, you get the error:
Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.
However, if you skip the main thread button and just hit the Alt thread button, you get the error:
The calling thread cannot access this object because a different thread owns it.
I'm curious what the "Freezable" error is all about and why you can get different errors based on what would seem to be an innocuous event: clicking/not clicking button that produces message box on main thread.
Ideally, it would be nice to just replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox, but if there is some sort of extra code to write, that might be acceptable. The documentation, and the link I provided hints at using a WindowContainer, but I can't really see any examples of how you do that. I was attracted to the Toolkit MessageBox as it allows one to do some cool stuff with MessageBox (which I don't show here) such as apply styles, change the text of the OK, CANCEL button, etc.
Any ideas would be much appreciated.
Thanks,
Dave
Extra info:
User1341210 suggestion works well if you just have one window. However, if you have a second window in it's own thread it doesn't work so well. Perhaps someone can tell me what I'm doing wrong. I use the suggestion of the TaskScheduler, but the code throws an exception if the TaskScheduler used is the one of the second window. That is, all works fine if I use the TaskScheduler of the first window, but throws an exception if I use the TaskScheduler of the second window. Here is the code behind for my second window:
public partial class AltThreadWindow : Window
{
private TaskScheduler _ui;
public AltThreadWindow()
{
InitializeComponent();
_ui = TaskScheduler.FromCurrentSynchronizationContext();
}
// This constructor is for passing in the TaskScheduler of the mainwindow and works great
public AltThreadWindow(TaskScheduler scheduler)
{
InitializeComponent();
_ui = scheduler;
}
private void btnWindowsMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Standard Windows message box");
}
private void btnCustomMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result;
Task.Factory.StartNew(() => { result = CustomMessageBox.Show("Custom MessageBox on separate window"); }, CancellationToken.None,
TaskCreationOptions.None,
_ui);
}
}
Notice the two constructors. The default one assigns the TaskScheduler of the second window. The other constructor allows one to pas in the TaskScheduler of the main Window.
Here's the code I use to launch the second window from the main window. Again, I'm launching the second window on another thread, and I pass in the TaskScheduler of the main window. It would be nice to use the TaskScheduler of the second window instead.
_altWindowThread = new Thread(new ThreadStart(AltWinThreadProc));
_altWindowThread.SetApartmentState(ApartmentState.STA);
_altWindowThread.Priority = ThreadPriority.AboveNormal;
_altWindowThread.IsBackground = true;
_altWindowThread.Start();
And the actual threadproc:
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
public void AltWinThreadProc()
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
_altWindow = new AltThreadWindow(_ui);
_altWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
Notice here I pass in the TaskScheduler of the MainWindow.
we had the same issue in our application (I created the work item on codeplex).
The error messages are quite confusing and I cant provide you an answer to that.
But:
We didn't used a separated WindowContainer to solve it. Instead came up with calling the separate task/thread with the UI scheduler:
Task.Factory.StartNew(
() => { result = CustomMessageBox.Show(messageText); },
CancellationToken.None,
TaskCreationOptions.None,
_ui);
Where _ui is assigned in a method that is executed from UI context (e.g. Constructor of your Window/Control:
_ui = TaskScheduler.FromCurrentSynchronizationContext();
Hope this helps for solving the "replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox" part of your question.
If you want that the messagebox shows up on another Window you have to set the "Owner" property of the message box to the other window.
Best regards.

WPF Creating a Command Library

New to WPF/XAML and looking to build a Command library for my application. Current version of my application is "flat" meaning all the code behind resides in my MainWindow.cs file. I've decided to separate things out into User Controls and all the UI part so far works great but....
I'm having trouble binding my commands to things now that I've done this. To make it simple I created a new WPF project, added my Menu control (Controls\Menu.xaml) and referenced this in my MainWindow.xaml. Now, I've gone ahead and added a CommandLibrary class and can't seem to get anything working. Here is my File -> New command code:
public static class MyAppCommands
{
private static RoutedUICommand _NewItem;
static MyAppCommands()
{
_NewItem = new RoutedUICommand("Create a new project", "NewItem", typeof(MyAppCommands));
}
public static RoutedUICommand NewItem
{
get { return _NewItem; }
}
private string filePath = null;
private bool dataChanged = false;
public static void NewItem_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (dataChanged)
{
string sf = SaveFirst();
if (sf != "Cancel")
{
ClearState();
}
}
else
{
ClearState();
}
}
public static void NewItem_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static void BindCommandsToWindow(Window window)
{
window.CommandBindings.Add(new CommandBinding(NewItem, NewItem_Executed, NewItem_CanExecute));
}
Where do I put the below in my class as a lot of my command will be using them...?
private string filePath = null;
private bool dataChanged = false;
Many Thanks!
I think you could benefit from setting up some design patterns like MVC or MVVM.
Check out these frameworks:
Caliburn (my favorite)
Prism
You can also follow MVC or MVVM just using a couple small helper classes, check out ariticles around the web, like this one MVVM.
Check this if you want a light-weight framework: MVVM Foundation

how to cleanup view model properly?

I have a view model that is used as the data source for my custom control. In the view model's constructor I set up a WMI ManagementEventWatcher and start it. My view model implements IDisposable, so I stop the watcher in the Dispose method.
When I embed the custom control into a window, and then close the window to exit the application it throws an InvalidComObjectException saying "COM object that has been separated from its underlying RCW cannot be used". This happens because of my watcher, and if I do not create it, there is no exception. there is no additional information about the exception such as stack trace, etc.
My guess is that something keeps the view model until the thread that the watcher uses terminates but before the watcher is stopped, and I do not know how to handle this.
Any advice?
Thanks
Konstantin
public abstract class ViewModelBase : IDisposable, ...
{
...
protected virtual void OnDispose() { }
void IDisposable.Dispose()
{
this.OnDispose();
}
}
public class DirectorySelector : ViewModelBase
{
private ManagementEventWatcher watcher;
private void OnWMIEvent(object sender, EventArrivedEventArgs e)
{
...
}
protected override void OnDispose()
{
if (this.watcher != null)
{
this.watcher.Stop();
this.watcher = null;
}
base.OnDispose();
}
public DirectorySelector()
{
try
{
this.watcher = new ManagementEventWatcher(new WqlEventQuery(...));
this.watcher.EventArrived += new EventArrivedEventHandler(this.OnWMIEvent);
this.watcher.Start();
}
catch (ManagementException)
{
this.watcher = null;
}
}
}
this article has the solution: Disposing WPF User Controls
basically, WPF dos not seem to use IDisposable anywhere, so the app needs to cleanup itself explicitly. so in my case, i subscribe to the Dispatcher.ShutdownStarted event from my control that uses the view model that needs to be disposed, and dispose the control's DataContext from the event handler.

Resources