Open New Windows from Background Thread on ViewModel - wpf

So I have a WPF application (MVVM) with a Splash Screen. On the splash screen startup I have a background thread on the ViewModel that does some start up related activities. In certain instances I want to open a couple of additional windows (user input needed etc...). I was getting a number of issues/errors/exceptions while trying to do this (mostly around that new window - also MVVM - trying to populate its UI items, such as combo boxes). So I've pulled back the issue to a simpler form - the "tempWindow" doesn't have anything so it doesn't throw UI population errors, but basically it does just open them and once the background thread is done, closes them all. If someone could point me in the right direction on what I am doing incorrectly here it would be appreciated.
The constructor for the ViewModel kicks off a background thread
public SplashScreenViewModel()
{
this.LoadingStatusText = "Starting Startup Processing ... ";
this.VersionNumber = "version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
var threadBackgroundStartUpProcesses = new Thread(new ThreadStart(this.BackgroundStartUpProcesses));
threadBackgroundStartUpProcesses.SetApartmentState(ApartmentState.STA);
threadBackgroundStartUpProcesses.IsBackground = true;
threadBackgroundStartUpProcesses.Start();
}
The background thread, should just open three windows (which it does) but those windows should stay open (they disappear once the thread completes).
private void BackgroundStartUpProcesses()
{
for (int i = 0; i < 3; i++)
{
var objTempWindow = new tempWindow();
objTempWindow.Show();
}
}
// EDIT: updated with responses, now get an error when the TempWindow has a comboBox that is being populated from the TempWindowViewModel.
private void BackgroundStartUpProcesses()
{
for (int i = 0; i < 3; i++)
{
var objTempWindow = new tempWindow();
objTempWindow.Show();
}
System.Windows.Threading.Dispatcher.Run();
}
When it goes to open the TempWindow the exception: "The calling thread cannot access this object because a different thread owns it." is thrown and appears to be when a ComboBox on that Window is trying to be populated.

I suspect that windows need an owner and when the owner dies, so does the window. Opening the windows on the main thread makes them stick around. Something like this...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var threadBackgroundStartUpProcesses = new Thread(new ParameterizedThreadStart(this.BackgroundStartUpProcesses));
threadBackgroundStartUpProcesses.SetApartmentState(ApartmentState.STA);
threadBackgroundStartUpProcesses.IsBackground = true;
threadBackgroundStartUpProcesses.Start(System.Windows.Threading.Dispatcher.CurrentDispatcher);
}
private void BackgroundStartUpProcesses(object d)
{
System.Windows.Threading.Dispatcher dispatcher = (System.Windows.Threading.Dispatcher) d;
for (int i = 0; i < 3; i++)
{
dispatcher.BeginInvoke((Action)(() =>
{
var objTempWindow = new tempWindow();
objTempWindow.Show();
}));
}
}
}
edit
I've just done some digging on WPF threading and it looks like you can open windows on multiple threads, but you need to start the new dispatcher. See near the end of this page:
http://msdn.microsoft.com/en-us/library/ms741870.aspx
In your BackgroundStartupProcesses, under objTempWindow.Show() add this line
System.Windows.Threading.Dispatcher.Run();

Are you setting your MainWindow before closing the splash screen? WPF sets the first opened window of an application as the MainWindow and unless you've changed the ShutdownMode of your App.xaml then once you close the splash screen the application shuts down.

Related

An exception is getting thrown when trying to show progressbar

I have a requirement to show a progress bar in my WPF application when I navigate from one view to another view. I've a service to show and close the progress bar. My code to show the progress bar goes like below. And it's showing perfectly fine.
public void ShowProgressBar<T>() where T : Window
{
var thread = new Thread(
new ThreadStart(
delegate ()
{
_progressWindow = Activator.CreateInstance<T>();
_progressWindow .Show();
_currentDispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
}
));
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Highest;
thread.IsBackground = true;
thread.Start();
}
After the progress bar is being shown, I'm trying to navigate to another view using viewManagementService wherein it removed existing view from the region if any and adds the new view and Since I know the last method that gets hit, I'm trying to close my progress bar there withe same service class being injected into its constructor and my closing logic goes like this.
public void CloseProgressBar()
{
if (_progressWindow != null && _currentDispatcher != null)
{
//Close the window and shutdown the dispatcher.
_currentDispatcher.Invoke(() => CloseWindow());
}
else if(_progressWindow != null && _currentDispatcher == null)
{
_progressWindow.Close();
}
}
/// <summary>
/// CloseWindow
/// </summary>
private void CloseWindow()
{
_progressWindow.Close();
_currentDispatcher.InvokeShutdown();
}
But, while trying to do this, it's sometimes saying that the task is already cancelled or it's saying that the task is being owned by a different thread. I couldn't figure out whether the problem is with my code or the way I'm doing?
Any suggestion is highly appreciated. Thank you.

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.

Startup displaying a modal window followed by a standard window

I am launching an MVVM application with code in the App.xaml.cs like so:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Set data directory
string baseDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + #"\BlowTrial";
if (!Directory.Exists(baseDir))
{
Directory.CreateDirectory(baseDir);
}
AppDomain.CurrentDomain.SetData("DataDirectory", baseDir);
//Application initialisation
AutoMapperConfiguration.Configure();
//Security
CustomPrincipal customPrincipal = new CustomPrincipal();
AppDomain.CurrentDomain.SetThreadPrincipal(customPrincipal);
// Create the ViewModel to which
// the main window binds.
var mainWindowVm = new MainWindowViewModel();
MainWindow window = new MainWindow(mainWindowVm);
// When the ViewModel asks to be closed,
// close the window.
EventHandler handler = null;
handler = delegate
{
window.Close();
if (!window.IsLoaded) //in case user cancelled close event
{
mainWindowVm.RequestClose -= handler;
}
};
mainWindowVm.RequestClose += handler;
window.Show();
}
I would like to test for the existence of entities containing important data for running the application, and if these do not exist, run a wizard (as a dialog) which obtains these settings:
if (BlowTrialDataService.GetBackupDetails().BackupData == null
|| !_repository.LocalStudyCentres.Any())
{
DisplayAppSettingsWizard();
}
static void DisplayAppSettingsWizard()
{
//testfor and display starup wizard
var wizard = new GetAppSettingsWizard();
GetAppSettingsViewModel appSettings = new GetAppSettingsViewModel();
wizard.DataContext = appSettings;
EventHandler wizardHandler = null;
wizardHandler = delegate
{
wizard.Close();
wizard = null;
appSettings.RequestClose -= wizardHandler;
};
appSettings.RequestClose += wizardHandler;
wizard.ShowDialog();
}
When I place this code in the MainWindow.xaml.cs, the application runs correctly. When it is placed in either the App.xaml.cs (before the code to instantiate the instance of MainWindow), or in the constructor for MainWindowViewModel, the wizard displays correctly, but the application ends without displaying the MainWindow on completion of the wizard. If there is no cause to display the wizard, MainWindow displays correctly in all cases.
Examining the debug output, there are no errors of note (a few first chance exceptions related to sql commands).
Is there a reason for this - having the code in the code behind MainWindow.xaml does not seem the most logical place (which to my mind would be the app.xaml.cs).
Thank you for your expertise.
The default value of ShutdownMode is System.Windows.ShutdownMode.OnLastWindowClose which means if the last window was closed App will shutdown.
You didn't put all in code in here, I suppose that setting wizard window didn't show before main window closed, this lead app exit.
I suggest you set shutdownmode to OnExplicitShutdown which you can decide when to close your app by your own.

What is the Alternative to show "Please wait" window in wpf while the call is non threaded

Recently I needed to implement please wait dialog in wpf application. i found below code. it's really good but it always open an window in saprate thread and hold the position. is there any other alter for below code. while my request of code is non threaded.
private void NewWindowThread<T,P>(Func<P, T> constructor, P param) where T : Window
{
Thread thread = new Thread(() =>
{
T w = constructor(param);
w.Show();
w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
to call above method use below lines. where loading window is you window which you want to show at dialog (please wait. windows)
string t = "Please Wait…";
NewWindowThread<LoadingWindow, string>(c => new LoadingWindow(c), t);
Blocking the ui thread was never a good idea, but it is increasingly more a bad idea.
Windows will tell the user that your app stopped responding. This may incite them to force your appliccations. If you render progress bars, they will lose the animation effects, and they may render incorrect. In WPF the gui animations will stop.
Use background threads for the heavy processing, and if you need to write data back in the objects used by your main thread, marshall them back to the gui thread. BackgroundWorker can be useful there.
this might help you out.
public partial class Splash : Window
{
private static Splash splash = new Splash();
// To refresh the UI immediately
private delegate void RefreshDelegate();
private static void Refresh(DependencyObject obj)
{
obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render,
(RefreshDelegate)delegate { });
}
public Splash()
{
InitializeComponent();
}
public static void BeginDisplay()
{
splash.Show();
}
public static void EndDisplay()
{
splash.Close();
}
public static void Loading(string test)
{
splash.statuslbl.Content = test;
Refresh(splash.statuslbl);
}
}
using above code
Splash.BeginDisplay();
// Setting the status to show the application is still loading data
Splash.Loading("Connecting...");
// Set to sleep to simulate long running process
Thread.Sleep(1500);
Splash.Loading("Retrieving....");
Thread.Sleep(1500);
Splash.Loading("Success....");
Thread.Sleep(1500);
Splash.EndDisplay();

wpf detect open window

In my WPF app (csharp) I have an event handler that when triggered will open a new window (window B) of the application and display some data. However, when the event is triggered again, if the new window (window B) is still open, I don't want to spawn another instance of window B but just update the data being displayed in the current instance. So the question is: How to detect if window B is already and only open if it is not already, otherwise just update the data?
I found the Application.Current.Window collection but somehow that isn't working for me yet. Ideas?
You could create a LoadWindow() method in WindowB that you can call to load (or refresh) the data & that will work regardless of if the window is already open or not. Have it take a delegate to call when this window gets closed:
private Action ParentCallbackOnClose;
public void LoadWindow( Action parentCallbackOnClose ) {
// load the data (set the DataContext or whatever)
ParentCallbackOnClose = parentCallbackOnClose;
// Open the window and activate/bring to the foreground
Show( );
Activate( );
}
and have your window closed event call the close delegate:
private void WindowClosed( object sender, EventArgs e ) {
ParentCallbackOnClose.Invoke( );
}
Now, from your class that opens Window B, have it hold onto that instance it opens, so that if WindowB is already open when someone tries to reload it, it just calls LoadWindow on the existing instance. Something like...
private WindowB WinB;
private void LoadWindowB(Content content)
{
if (WinB == null ){
WinB = new WindowB( );
}
WinB.LoadWindow(content, WindowBClosed);
}
And then you can just have it null out WinB on that close callback so if WinB is closed, then the next time LoadWindowB() is called it will create a new instance of it:
private void WindowBClosed( ){
WinB = null;
}
Since this is the first link Google listed, which posted several years ago, for a solution to check if a Window is already open, I'll post my answer, for others, which I find easier to implement. The ChildWindow is only called from MainWindow so no other Window will need to do any checks.
private void OpenChildWindow()
{
if (this.OwnedWindows.OfType<ChildWindow>().Count() > 0)
{
ChildWindow Win = this.OwnedWindows.OfType<ChildWindow>().First();
Win.Activate();
}
else
{
ChildWindow Win = new ChildWindow();
Win.Owner = this;
Win.Show();
}
}
There is an old school way to do this using an interface. I see this in Java a lot as a way to compensate for not having delegates (correct me if I am wrong). This method will allow you to check if there is a window already open (of any kind). The original response works very well, but you can also do it the following way:
Create the interface
public interface IWindowTracker
{
void WindowIsOpened();
void WindowIsClosed();
}
Implement the interface on the parent (from where you are opening):
public partial class MainWindow : Window, IWindowTracker
In your constructor, accept an object that is of the IwindowTracker interface. Save the instance for future use
IWindowTracker windowTracker;
public ProjectManager(IWindowTracker parentWindowTracker)
{
windowTracker = parentWindowTracker;
InitializeComponent();
}
Setup the calls to the window tracker object
protected override void OnActivated(EventArgs e)
{
windowTracker.WindowIsOpened();
base.OnActivated(e);
}
protected override void OnClosed(EventArgs e)
{
windowTracker.WindowIsClosed();
base.OnClosed(e);
}
and finally implement the IWindowTracker in your parent WPF window
bool windowIsOpen = false;
public void WindowIsOpened()
{
windowIsOpen = true;
}
public void WindowIsClosed()
{
windowIsOpen = false;
}
This will allow you to keep track of if the window is still open and if it is, there is no need to open a new instance of it:
if (!windowIsOpen)
{
remoteProjectManager = new ProjectManager(this);
remoteProjectManager.Show();
}
remoteProjectManager.Focus();
Calling show() on a closed window seems to throw an exception, so my guess is that there is some other way or that if you have closed the window, the window is technically "destroyed"
The nice thing to this is that I can detect if the window is still open and focus on it (so that it comes to the front again).
NOTE: There is a draw back to this, in that in this setup it limits you to opening only one window at a time (assuming that all your windows are implemented like this). In my case, I only ever want to have one window open besides the main window.
You might also want to check if your window is null or not, considering that it probably isn't the only window you will have to open.
edit: oops, my answer is specific to Windows Forms. i just now saw the WPF mention. i'm not sure what the specific code would be for WPF, but i would imagine that it's not all that different conceptually. I think in WPF the property is called IsVisible instead of Visible
You could hold on to the instance of your window (or make it a Singleton) and then when you need to determine if it is visible or not, check it's Visible property.
for example:
if(myWindow.Visible){
myWindow.Hide();
}else{
myWindow.Show();
}
This article it the best I found for passing data between WPF pages. The author used KISS approach to provide a simple solution.

Resources