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.
Related
I have a WPF application with a page with some code as shown below
public partial class MyPage : Page
{
public MyPage ()
{
InitializeComponent();
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
this.Cursor = Cursors.Wait;
this.btnClose.Content = "Cancel";
// some long time consuming processing
this.Cursor = Cursors.Arrow;
this.btnClose.Content = "Close";
}
}
I am doing two things here on the Close button click hander which are causing problems. Before long processing I change the button context text to Cancel. I also want to change cursor for whole page to wait. Once long processing is done I set the cursor state and button content back to where it was. However I am facing following two issues.
When application is doing long running operation, I don't get to see the button content as Cancel. It just keep showing me original content CLose.
The cursor changes to Arrow only on the button. However on rest of page,I still keep getting same arrow cursor.
Any ideas how can these issue be solved?
Your code runs on the UI thread by default, so nothing else can be executed on the UI thread (such as re-rendering the UI) until the thread finishes executing.
There are many ways of releasing control of the UI thread before the code finishes executing, but I find the simplest is to use a Task from the Task Parallel Library which can be used to run code on a separate thread.
For example,
// this runs on the main UI thread
this.Cursor = Cursors.Wait;
this.btnClose.Content = "Cancel";
Task.Factory.StartNew(() =>
{
// this code runs on a background thread
// some long time consuming processing
})
.ContinueWith((e) =>
{
// this code runs from the UI thread again
this.Cursor = Cursors.Arrow;
this.btnClose.Content = "Close";
});
It should be noted that UI objects can only be modified on the UI thread, which is why I put the second UI update in the .ContinueWith(...) of the task. An alternative to this would be to use the Dispatcher to ensure code gets executed on the UI thread. If you decide you need this instead and can't find an easy example via Google, let me know and I'll write one here.
This has to be a duplicate some where
public class WaitCursor : IDisposable
{
private Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
#region IDisposable Members
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
#endregion
}
using (new WaitCursor())
{
// long blocking operation
}
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.
To simulate a modal dialog in WPF, I display a Window and call: Mouse.Capture(dialogBoxArea, CaptureMode.SubTree);
The call returns false.
Mouse.Captured is null.
dialogBoxArea.Visibility is Visibility.Visible.
dialogBoxArea.IsEnabled is true.
If the line is called again a second time, it returns true and correctly captures the mouse.
What condition might I be missing that is preventing the capture from working?
Edit
Here's what I've tried so far.
if (Mouse.Captured != null)
{
// Not called, so presumably, nothing has already captured the mouse
MessageBox.Show("already captured");
}
if (dialogBoxArea.Visibility != Visibility.Visible)
{
// Not called
MessageBox.Show("not visible");
}
if (!dialogBoxArea.IsEnabled)
{
// Not called
MessageBox.Show("not enabled");
}
// According to documentation, this should release mouse capture from anything that holds it
Mouse.Capture(null);
// Attempt to capture the mouse
if (!Mouse.Capture(dialogBox, CaptureMode.SubTree))
{
// This is called
Mouse.Capture(null);
Mouse.Capture(dialogBox, CaptureMode.SubTree);
}
As a first iteration i would talk to your client.
The following opens a dialog option window that is always on top of the original window and blocks calls to it, but does not hinder the overall execution at all. If your customer sees the behaviour he may be happy with that.
namespace StackoverflowExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void NewWindowAsDialog(object sender, RoutedEventArgs e)
{
Window myOwnedDialog = new Window();
myOwnedDialog.Owner = this;
myOwnedDialog.ShowDialog();
}
}
}
I will post another option later here that will illustrate how to load a window into a subdivision (grid etc.) of your xaml. You could filter all other calls based on the content that is loaded into that division rather then filtering the mouscall. Your filtering could run into the problem of the logical vs the viewtree - you only ever want to look at the trees if you create your own templates from scratch.
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();
Some context here...I have a System.Windows.Window that is used to display a modal message box. I created a Show() method that initializes the content of the window, and then calls ShowDialog(). The user clicks a button on this window, some information about the clicked button is set in the Tag property, and then the window is closed via Close().
As expected, I get a ShowDialog Exception when attempting to call ShowDialog() on the Window once is has been closed. Is there some way to reuse that same Window instance so that I don't have to new up an instance every time I need a message box?
For example...
MessageBoxWindow mbw = new MessageBoxWindow();
result = mbw.Show("caption", "message 1");
mbw.Show("caption", "message 2");
// The above throws an exception, so I have to do this...
mbw = new MessageBoxWindow();
result = mbw.Show("caption", "message 2");
Any help would be greatly appreciated!
Use .Hide() instead of .Close(). That removes it without destroying it. Then you can call Show() again when needed.
MainWindow test = new MainWindow();
test.Show();
test.Hide();
test.Show();
You can add a FormClosing event that cancels the form close and instead sets the Form.Visible to false. Then you would also need Show method that checks if this Form is null, so you would know whether you need to create a new Form or just show the one you already have.
For example:
private void FormMessageBox_FormClosing(object sender, FormClosingEventArgs e)
{
//This stops the form from being disposed
e.Cancel = true;
this.Visible = false;
}
public static void Show(FormMessageBox formMessageBox, string message)
{
//if formMessageBox is null we need to create a new one otherwise reuse.
if (formMessageBox == null)
{
formMessageBox = new FormMessageBox(message);
formMessageBox.ShowDialog();
}
else
{
formMessageBox.lblMessage.Text = message;
formMessageBox.Visible = true;
}
}