I have 2 Windows in my WPF application. I'm running the SecondWindow from MainWindow using a background thread like as follows.
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
SecondWindow sw = new SecondWindow(this);
sw.Show();
Dispatcher.Run();
}));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
and I also have Public Function in the SecondWindow Like this...
public async void hide_animate()
{
//Some Stuff//
}
I want to call hide_animate from a method from my MainWindow
I tried some options I found by googling, but nothing seems to work. any help?
Update:
I've tried this to call the function but still nothing happening...
Window mywindow = FindMyWindow("SecondWindow");
if (mywindow != null)
{
SeocondWinodw.hide_animate();
}
private Window FindMyWindow(string windowName)
{
foreach (Window wnd in System.Windows.Application.Current.Windows)
{
if (wnd.Title.ToUpper().Equals(windowName.ToUpper()))
{
return wnd;
}
}
return null;
}
Related
What I want to achieve is to make a modaless window modal without hiding it first.
This does the job for the start:
form.Show();
// do preparation stuff and display in form
form.Visible = false;
form.ShowDialog();
// Cleanup application logic and exit gracefully
There are tasks running that do the actual job in the background. The form is used to display the event log.
What I would like is to avoid hiding the form and showing it again as modal dialog. What I have tried (linqpad snippets):
void Main()
{
var form = new MyForm();
form.Show();
// do stuff
form.WaitForClose();
}
class MyForm : Form
{
private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
public MyForm()
{
this.Closed += (s, e) => tcs.TrySetResult(true);
}
public void WaitForClose()
{
var r = tcs.Task.Result;
}
}
But this code is blocking.
This one is working, but I would like to have it more simple:
void Main()
{
var form = new MyForm();
Task.Factory.StartNew(() =>
{
form.WaitToLoad();
// do preparation stuff and display in form
Task.Factory.StartNew(() => {
// do bakcground stuff
});
form.WaitToClose();
// Cleanup application logic and exit gracefully
});
form.ShowDialog();
}
class MyForm : Form
{
private EventWaitHandle _loadWaitHandle = new AutoResetEvent (false);
private EventWaitHandle _closeWaitHandle = new AutoResetEvent (false);
public MyForm()
{
this.Load += (s,e) => _loadWaitHandle.Set();
this.Closed += (s, e) => _closeWaitHandle.Set();
}
public void WaitToClose()
{
_closeWaitHandle.WaitOne();
}
public void WaitToLoad()
{
_loadWaitHandle.WaitOne();
}
}
I feel I am missing something obvious here.
Calling .Result on a task will block until the result can be resolved. Try changing the method to something like this
public async Task WaitForClose()
{
var r = await tcs.Task;
}
The you await the call to this and it will yield to other operations while waiting for this to complete.
Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}
I am developing a WPF application. I am using two monitors. I am using splash screen to pop up on window when some function is executing.
The problem I have is when I move the application to secondary monitor and start processing functions splash screen is still being displayed in primary monitor instead on secondary monitor.
Any help on this?
you can try for BusyIndicator if suitable for you or else you can try below code to show Splash Window.
void TemplateSelector_Loaded(object sender, RoutedEventArgs e)
{
showWin();
Thread.Sleep(10000);
CloseWin();
}
private void CloseWin()
{
WindowManager.Close();
}
Window tempWindow = new Window();
public void showWin()
{
WindowManager.LaunchWindowNewThread<SplashWindow>();
}
}
public class WindowManager
{
private static Window win;
public static void LaunchWindowNewThread<T>() where T : Window, new()
{
Thread newWindowThread = new Thread(ThreadStartingPoint);
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
Func<Window> f = delegate
{
T win = new T();
return win;
};
newWindowThread.Start(f);
}
private static void ThreadStartingPoint(object t)
{
Func<Window> f = (Func<Window>)t;
win = f();
win.WindowStartupLocation = WindowStartupLocation.CenterScreen;
win.Topmost = true;
win.Show();
Dispatcher.Run();
}
public static void Close()
{
if (win != null)
win.Dispatcher.BeginInvokeShutdown(DispatcherPriority.Send);
}
}
The problem is that i have some kind of splash screen which shows loading animation.
I have special manager that show and hide it.
class Manager
{
private Form CurForm;
Thread curt;
private Manager()
{
curt= new Thread(start);
curt.ApartmentState = ApartmentState.STA;
curt.IsBackground = true;
curt.Start();
}
void start()
{
CurForm = new Animation();
Application.Run(CurForm);
}
public static readonly Manager Active = new Manager();
public static void Show()
{
if (Active.CurForm != null)
{
Active.CurForm.Invoke(new Action(() => { Active.CurForm.Show(); }));
}
}
public static void Hide()
{
if (Active.CurForm != null)
{
Active.CurForm.Invoke(new Action(() => { Active.CurForm.Hide(); }));
}
}
}
I open some modal form (ShowDialog). This modal form doesn't show in taskbar.
I easily can minimise it and after clicking on main form on task bar it show that modal form on top.
But when I show this loading animation while it's loading all necessary data.
some kind like that (of course it is just a sample to test it work, and in real app it tooks much time to load all data and form with lots of controls)
public modal()
{
Manager.Show();
InitializeComponent();
Thread.Sleep(5000);
Manager.Hide();
}
And when i'm trying to minimise and restore it like i said above it doesn't restore my modal form and just show my main not available form. And more than that it works in some cases but in some not.
Does anybody know why it is happens or how to fix it?
it is strange but when i modify like this, everything seems to work normal.
I just kill separate ui thread.
public class MyApplicationContext:ApplicationContext
{
public Form CurForm;
ManualResetEvent ready = new ManualResetEvent(false);
public MyApplicationContext()
{
CurForm=new Animation();
CurForm.Show();
}
}
class Manager
{
private MyApplicationContext CurContext;
Thread curt;
void start()
{
try
{
CurContext = new MyApplicationContext();
Application.Run(CurContext);
}
catch
{
CurContext.CurForm.Close();
}
}
private void Init()
{
curt = new Thread(start);
curt.SetApartmentState(ApartmentState.STA);
curt.IsBackground = true;
curt.Start();
}
public static Manager Active
{
get
{
if (active == null)
{
active = new Manager();
}
return active;
}
}
private static Manager active;
public static void Show()
{
Active.Init();
}
public static void Hide()
{
Active.curt.Abort();
}
I have two forms, the main form and one that pops up as a modal dialog. From a process spawned in the main form, I want to dynamically update the text on the modal dialog. Here's what I have:
In the main form, I do this:
// show the wait modal
var modal = new WaitDialog { Owner = this };
// thread the packaging
var thread = new Thread(() => Packager.PackageUpdates(clients, version, modal));
thread.Start();
// hopefully it worked ...
if (modal.ShowDialog() != DialogResult.OK)
{
throw new Exception("Something failed, miserably.");
}
The PackageUpdates method takes the modal dialog, and does this:
// quick update and sleep for a sec ...
modal.SetWaitLabelText("Downloading update package...");
Thread.Sleep(2000);
modal.SetWaitLabelText("Re-packaging update...");
To be thread safe, I do this in the modal dialog:
public void SetWaitLabelText(string text)
{
if (lblWaitMessage.InvokeRequired)
{
Invoke(new Action<string>(SetWaitLabelText), text);
}
else
{
lblWaitMessage.Text = text;
}
}
Everything works great ... most of the time. Every three or four times that the modal pops up, I get an exception on the lblWaitMessage.Text = text; and it's not invoking the command.
Am I missing something in this setup?
Like #Hans Passant pointed out, you should wait for the modal.Load-event. One good option is to use the ManualResetEvent to inform your thread to wait until that happens.
The WaitOne method will block the thread until the Set method is called. Here's a very simple setup which should do the trick.
public partial class Form1 : Form
{
ManualResetEvent m_ResetEvent;
public Form1()
{
InitializeComponent();
m_ResetEvent = new ManualResetEvent(false);
}
private void button1_Click(object sender, EventArgs e)
{
Dialog d = new Dialog { Owner = this, ResetEvent = m_ResetEvent };
var thread = new Thread(new ParameterizedThreadStart(DoSomething));
thread.Start(d);
if (d.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
throw new Exception("Something terrible happened");
}
}
private void DoSomething(object modal)
{
Dialog d = (Dialog)modal;
// Block the thread!
m_ResetEvent.WaitOne();
for (int i = 0; i < 1000; i++)
{
d.SetWaitLabelText(i.ToString());
Thread.Sleep(1000);
}
}
}
And here is the modal form
public partial class Dialog : Form
{
public Form Owner { get; set; }
public ManualResetEvent ResetEvent { get; set; }
public Dialog()
{
InitializeComponent();
}
public void SetWaitLabelText(string text)
{
if (label1.InvokeRequired)
{
Invoke(new Action<string>(SetWaitLabelText), text);
}
else
{
label1.Text = text;
}
}
private void Dialog_Load(object sender, EventArgs e)
{
// Set the event, thus unblocking the other thread
ResetEvent.Set();
}
}
I think you should rewrite the code to let thread.Start() isn't called before modal.ShowDialog().
As a workaround, you can try this:
public void SetWaitLabelText(string text) {
Invoke(new Action<string>(SetWaitLabelText2), text);
}
void SetWaitLabelText2(string text) {
lblWaitMessage.Text = text;
}
The first method always uses Invoke, regardless the value of InvokeRequired. The second method actually does the thing. This pattern is usable when you always call the function from another thread.