STA Thread is not loading the Main Window in WPF - c#

I am loading MainWindow in App_Startup (). I wanted to show the progress bar while loading the window. But it is not working :
void App_Startup(object sender, StartupEventArgs e)
{
Thread bootStrapThread = new Thread(new ThreadStart(runBootStrapProcess));
bootStrapThread.SetApartmentState(ApartmentState.STA);
bootStrapThread.IsBackground = true;
bootStrapThread.Start();
_loadingProgressBar = new loadingProgressBar();
_loadingProgressBar.ShowDialog();
}
I want to load the window from thread :
void runBootStrapProcess()
{
MetadataReader mr = new MetadataReader();
if (currentVersionNo.Equals(remoteVersionNo))
{
Application.Current.Shutdown();
}
else
{
MainWindow mw = new MainWindow();
mw.Show();
}
_loadingProgressBar.ShouldCloseNow = true;
}

You can try this:
void runBootStrapProcess() {
MetadataReader mr = new MetadataReader();
if (currentVersionNo.Equals(remoteVersionNo)) {
Application.Current.Shutdown();
} else {
System.Windows.Application.Current.Dispatcher.BeginInvoke(
new Action(
() => {
MainWindow mw = new MainWindow();
mw.Show();
}));
}
_loadingProgressBar.ShouldCloseNow = true;
}
You basically from the thread when you want to show the window send it to the main application thread. This thus stops the application from closing down when the thread exits since the MainWindow is Shown from the main thread.

I suspect the window is missing the message pump since the WPF Application class with its Dispatcher is running on a different STA Thread

Related

Create a child window from a FileSystemWatcher

I have a FileSystemWatcher watching for newly created files.
When it sees one, I would like it to open a child window.
Using this:
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}
The error I'm getting is:
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on
After some googling. I ended up with this
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Invoke((MethodInvoker)delegate
{
win.Show();
});
which gives me a different error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Here's the scenario. on a game, each time a new table is opened, a new file is created. When that file is created, I want to open a child window to display statistics on that table.
Is this even possible?
What I've done in the past to work with InvokeRequired is to place it within an if statement that will call the method on the UI thread if it hasn't been called from the UI thread.
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
ShowWindow();
}
private void ShowWindow()
{
if (this.InvokeRequired)
{
var del = new MethodInvoker(ShowWindow);
this.BeginInvoke(del);
return;
}
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}

Threaded ticker control on top of Main thread control in wpf

I have requirement in which i need to show smooth scrolling text with some GIF Images and jpeg or mediaelement on a ticker. However, since this involves lot of CPU cycles for the main UI thread, i planned to create the ticker control on another thread with a dispatcher and then host this ticker on the form. However, i am getting a cross-thread exception that thread cannot access the control as another thread owns it.
I have done similar thing in Delphi, wherein i have set the ticker parent with SetWindowParent();
my code is as below
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TickerControlContainer loclContainer = new TickerControlContainer(this);
}
}
public class TickerControlContainer
{
private MainWindow f_Window;
private void CreateControl()
{
TickerControl loclControl = new TickerControl();
loclControl.InitializeComponent();
f_Window.Dispatcher.Invoke((MethodInvoker)delegate { AddControl(loclControl); });
}
private void AddControl(TickerControl piclControl)
{
f_Window.Content = piclControl;
**// exception occurs**
}
public TickerControlContainer(MainWindow piclWindow)
{
f_Window = piclWindow;
ManualResetEvent loclResetEvent = new ManualResetEvent(false);
Dispatcher loclDispatcher = null;
Thread th1 = new Thread(new ThreadStart(() =>
{
loclDispatcher = Dispatcher.CurrentDispatcher;
loclResetEvent.Set();
Dispatcher.Run();
}));
th1.SetApartmentState(ApartmentState.STA);
th1.Start();
loclResetEvent.WaitOne();
loclDispatcher.BeginInvoke((MethodInvoker)delegate { CreateControl(); });
}
}
Do i need to put a contentcontrol or something on my form, instead of setting as the content of the form.
This is just a sample that i am trying to do. Please help.
There's only one UI thread in WPF/.NET (though I think different windows can run on separate threads), so I don't really think there's an easy way to do what you're trying to do here.
Is it the animation that's taking up a lot of CPU, or are you doing a lot of processing in addition to the animation? If so, I would offload the calculations to a background thread and then invoke it to the UI thread when complete.
I was able to host control created on another thread on my main window, but before creating the control, the Window has to be shown atleast once.
using...
namespace WpfMultiDispatcherUpdates
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnCreateControl_Click(object sender, RoutedEventArgs e)
{
TickerControlContainer loclContainer = new TickerControlContainer(this);
}
}
public class TickerControlContainer
{
private MainWindow f_Window;
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
private static extern IntPtr SetParent(IntPtr hwndChild, IntPtr hwndParent);
private void CreateControl(HwndSource piclSource)
{
TickerControl loclControl = new TickerControl();
loclControl.InitializeComponent();
Window loclHostWindow = new Window();
loclHostWindow.WindowStyle = WindowStyle.None;
loclHostWindow.WindowState = WindowState.Normal;
loclHostWindow.Left = 0;
loclHostWindow.Top = 0;
loclHostWindow.ShowInTaskbar = false;
loclHostWindow.Content = loclControl;
loclHostWindow.ShowActivated = true;
loclControl.Height = 200;
loclControl.Width = (double)f_Window.Dispatcher.Invoke(new Func<double>(() => { return f_Window.Width; }));
piclSource.SizeToContent = SizeToContent.WidthAndHeight;
loclHostWindow.SizeToContent = SizeToContent.WidthAndHeight;
loclHostWindow.Show();
SetParent(new WindowInteropHelper(loclHostWindow).Handle, piclSource.Handle);
}
private void AddControl(TickerControl piclControl)
{
f_Window.Content = new ContentControl() { Content = piclControl };
}
public TickerControlContainer(MainWindow piclWindow)
{
f_Window = piclWindow;
ManualResetEvent loclResetEvent = new ManualResetEvent(false);
Dispatcher loclDispatcher = null;
Thread th1 = new Thread(new ThreadStart(() =>
{
loclDispatcher = Dispatcher.CurrentDispatcher;
loclResetEvent.Set();
try
{
Dispatcher.Run();
}
catch (Exception E)
{
System.Windows.MessageBox.Show(E.Message);
}
}));
th1.SetApartmentState(ApartmentState.STA);
th1.Start();
loclResetEvent.WaitOne();
try
{
HwndSourceParameters loclSourceParams = new HwndSourceParameters();
loclSourceParams.WindowStyle = 0x10000000 | 0x40000000;
loclSourceParams.SetSize((int)piclWindow.Width, 200);
loclSourceParams.SetPosition(0, 20);
loclSourceParams.UsesPerPixelOpacity = true;
loclSourceParams.ParentWindow = new WindowInteropHelper(piclWindow).Handle;
HwndSource loclSource = new HwndSource(loclSourceParams);
loclSource.CompositionTarget.BackgroundColor = Colors.Transparent;
loclDispatcher.BeginInvoke((MethodInvoker)delegate { CreateControl(loclSource); });
}
catch (Exception E)
{
System.Windows.MessageBox.Show(E.Message);
}
}
}
}
However, i need to add the Resize events and resize my control when the MainWindow height and width changes. The values are hardcoded for testing purposes. Now, the drawing on the child control does not affect my Main Window, but the complexity of controlling my child control is more..
The host should not have any other child controls in the area where we host this threaded control.

How to close object of form created by Application.run() method in C#?

I am working on C# win form application.My problem is when i click on menu i created one separate thread which is showing the progress(splash progress form). When i abort thread, progress form still display..! but when i move mouse pointer over the form it disappear
immediately..!
Following is my code
Thread progressThread = new Thread(() => Application.Run(new frmOperationProgress()));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
progressThread.Abort();
How to close this progress form object in c#
The problem is using Abort - it's not generally recommended because there is no guarantee that it's going to do what you expect (in your case hide the form).
Best to add proper cancellation support into your thread and handle hiding the splash screen directly.
Please never ever use Abort(). This kind of work is best done trough BackgroundWorker; if you insist on Thread
Try:
var form = new frmOperationProgress();
Thread progressThread = new Thread(() => Application.Run(form));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
form.ExternalClose();
Where ExternalClose is method of form like this:
public void ExternalClose() {
if (InvokeRequired) {
Invoke(new MethodInvoker(() => { ExternalClose(); }));
} else {
Close();
}
}
Solution using BackgroundWorker:
In backround worker you have to do UI stuff in ProgressChanged event (which is running in UI thread) and do the dirty work in DoWork event (background thread).
FormMain.cs: (Form with single BackgroundWorker control, named "backgroundWorker1", with wired up events backgroundWorker1_DoWork, backgroundWorker1_ProgressChanged and WorkerReportsProgress set to true)
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApplication1 {
public partial class FormMain : Form {
private FormProgress m_Form;
public FormMain() {
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
backgroundWorker1.ReportProgress(0, "hello");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(20, "world");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(40, "this");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(60, "is");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(80, "simple");
backgroundWorker1.ReportProgress(100, "end");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.ProgressPercentage == 0 && m_Form == null) {
m_Form = new FormProgress();
m_Form.Show();
}
if (e.ProgressPercentage == 100 && m_Form != null) {
m_Form.Close();
m_Form = null;
return;
}
var message = (string)e.UserState;
m_Form.UpdateProgress(e.ProgressPercentage, message);
}
}
}
Where FormProgress is simple form with ProgressBar progressBar1 and Label label1 and one extra method:
public void UpdateProgress(int percentage, string message) {
this.progressBar1.Value = percentage;
this.label1.Text = message;
}
You can just close your form and the thread (which is blocked by the message loop of that form) will be ended naturally:
var yourForm = new frmOperationProgress();
//Start it
Thread progressThread = new Thread(() => Application.Run(yourForm));
progressThread.IsBackground = true;
progressThread.Start();
//....
//close it
yourForm.Invoke((Action)(() => yourForm.Close()));

Load another window in background; when rendered, close the parent window

I have been trying to load another window in background within a window; parent window acts as a splash screen in my case.
InitWindow I = null;
public InitWindow()
{
InitializeComponent();
I = this;
Thread T = new Thread(() =>
{
MainWindow M = new MainWindow();
M.Show();
M.ContentRendered += M_ContentRendered;
System.Windows.Threading.Dispatcher.Run();
M.Closed += (s, e) => M.Dispatcher.InvokeShutdown();
}) { IsBackground = true, Priority = ThreadPriority.Lowest };
T.SetApartmentState(ApartmentState.STA);
T.Start();
}
void M_ContentRendered(object sender, EventArgs e)
{
I.Close();
}
Everything else works fine but it throws an Invalid Operation Exception at:
I.Close();
the calling thread cannot access this object because a different thread owns it.
1) How do I switch/sync thread?
2) Is there a better workaround?
Changed code to:
InitWindow I = null;
Thread C = null;
public InitWindow()
{
InitializeComponent();
I = this;
C = Thread.CurrentThread;
Thread T = new Thread(() =>
{
MainWindow M = new MainWindow();
M.Show();
M.ContentRendered += M_ContentRendered;
System.Windows.Threading.Dispatcher.Run();
M.Closed += (s, e) => M.Dispatcher.InvokeShutdown();
}) { IsBackground = true, Priority = ThreadPriority.Lowest };
T.SetApartmentState(ApartmentState.STA);
T.Start();
}
void M_ContentRendered(object sender, EventArgs e)
{
// Making the parent thread background
C.IsBackground = true;
// foreground the current thread
Thread.CurrentThread.IsBackground = false;
// Abort the parent thread
C.Abort();
}
Works fine as of now, but I don't think it's a reliable solution.

How to set Code execute only one time?.

In my Project I included a splash-screen.
For that i wrote below code in Login Window Contractor. The Splash Screen is working perfectly. But after login, the main page is opened. if it is closed, then login is opened.
In that the Splash Screen is cross the Login page. I don't want this.
How to do this: Splash Screen Shown only once in my project?
Help me...
public LogIn()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(Splash));
th.Start();
Thread.Sleep(3000);
th.Abort();
Thread.Sleep(1000);
}
private void Splash()
{
Welcome sp = new Welcome();
sp.ShowDialog();
}
private void Form1_Load(object sender, EventArgs e)
{
SplashScreen Splash = new SplashScreen();
Splash.Show();
}
if you want the SplashScreen to close after 3 Seconds then use a timer in the SplashScreen and after 3 Seconds user this.close()
You can possibly pass a boolean parameter which will instruct the Login Constructor to display or not to display the Splash Screen.
public LogIn(boolean splashOpened)
{
InitializeComponent();
if(!splashOpened) //If Splash Screen is not opened , Open it
{
Thread th = new Thread(new ThreadStart(Splash));
th.Start();
Thread.Sleep(3000);
th.Abort();
Thread.Sleep(1000);
}
}
private void Splash()
{
Welcome sp = new Welcome();
sp.ShowDialog();
}
private static Welcome sp;
public LogIn()
{
InitializeComponent();
if (sp == null)
{
Thread th = new Thread(new ThreadStart(Splash));
th.Start();
Thread.Sleep(3000);
th.Abort();
Thread.Sleep(1000);
}
}
private void Splash()
{
if (sp == null)
{
sp = new Welcome();
sp.ShowDialog();
}
}

Categories

Resources