Show Window from another method and thread [duplicate] - c#

This question already has answers here:
How do I create and show WPF windows on separate threads?
(4 answers)
The calling thread cannot access this object because a different thread owns it.WPF [duplicate]
(6 answers)
Closed 2 years ago.
I have an init method in which I create a thread and a window is displayed
This is my application code
public static SplashWindow _splashWindow;
private static ManualResetEvent ResetSplashCreated;
private static Thread SplashThread;
public static void Init(SplashWindow splashWindow)
{
_splashWindow = splashWindow;
ResetSplashCreated = new ManualResetEvent(false);
SplashThread = new Thread(() => ShowSplash(_splashWindow));
SplashThread.SetApartmentState(ApartmentState.STA);
SplashThread.IsBackground = true;
SplashThread.Name = "Splash Screen";
SplashThread.Start();
ResetSplashCreated.WaitOne();
}
public static void ShowSplash(SplashWindow splash)
{
splash.Show();
ResetSplashCreated.Set();
System.Windows.Threading.Dispatcher.Run();
}
now when i call this code:
SplashHelper.Init(new anim());
I get an error in the splash.show(); line
System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'

You cannot create the window on one thread and Show() it on another.
You will have to both create the thread and call Show() in your ShowSplash method (or on the calling thread).
If you look at the source code of Show, you'll see that the first thing it does is to call a VerifyContextAndObjectState() method.
This method checks whether you are on the thread on which the window was originally created. If you are on another thread, it throws the InvalidOperationException that you are currently getting.

Related

Progress bar update in child thread not updating, Work done in main thread [duplicate]

This question already has answers here:
The calling thread cannot access this object because a different thread owns it
(15 answers)
The calling thread cannot access this object because a different thread owns it.WPF [duplicate]
(6 answers)
Closed 2 years ago.
I have an application which has a C# front end (GUI client), and a C++ back end (business logic). The back end is responsible for requesting progress bar functionality and does this by issuing events which the front-end has observer delegates to respond to and act upon. so events are dispatched and responded to in the main thread.
I would like to spawn a second thread to display and update the progress bar, as the main thread is occupied by the back-end doing its thing. While I can show the progress bar window when supposed to, and correctly hide when finished, it will not respond to updates/increments.
ProgressBarWindow holds the progress bar control (pBar).
ProgressBarWindow is owned by progressBarThread.
public static EventObserver.ProgressBeginEvent pbe = null;
public static EventObserver.ProgressFinishEvent pfe = null;
public static EventObserver.ProgressIncrementEvent pie = null;
public static Thread progressBarThread = null;
static private void InitialiseProgressBarManager()
{
// Setup the progress bar callbacks...
pbe = new EventObserver.ProgressBeginEvent(delegate
(int currentIncrements, int totalIncrements, string message)
{
// Create the thread and progress bar window...
progressBarThread = new Thread(() =>
{
ProgressBarWindow sw = new ProgressBarWindow();
sw.pBar.IsIndeterminate = false;
sw.pBar.Minimum = currentIncrements.Value;
sw.pBar.Maximum = totalIncrements.Value;
sw.pBar.Value = 0;
sw.Show();
pie = new EventObserver.ProgressIncrementEvent(delegate ()
{
sw.pBar.Value++; // The calling thread cannot access this object... see below
});
pfe = new EventObserver.ProgressFinishEvent(delegate ()
{
progressBarThread.Abort();
progressBarThread = null;
});
});
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.IsBackground = true;
progressBarThread.Start();
});
}
The window shows when expected, and the ProgressIncrementEvent gets raised correctly (it is owned by the thread), however I get an exception when it tries to access the value.
The calling thread cannot access this object because a different thread owns it.
Do I need a mutex or some other lock? I was hoping the scope of the observer delegate would allow the delegate to have mutable access to the thread local ProgressBarWindow even though it is being invoked from the main thread?
I'm not a great C# developer and even less so C# threading so I am a bit stuck about what I should be doing here or even if I can achieve this behaviour. Any help or direction to get this working would be appreciated.
P.S I am using WPF with ProgressBarWindow being defined in XAML.
Try to access the control using the window´s dispatcher:
sw.Dispatcher.BeginInvoke(new Action(() => sw.pBar.Value++));

How to create thread safe GetEventHandler()? [duplicate]

This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 6 years ago.
public partial class JobDataDuplicatorForm : Form
{
public JobDataDuplicatorForm(IJobDataDuplicatorEngine engine)
{
_engine.CopyStartedEvent += GetEventHandler(OnCopyStarted);
_engine.CopyEndedEvent += GetEventHandler(OnCopyEnded);
...
}
private static EventHandler GetEventHandler(Action action)
{
return (sender, args) => action();
}
private void OnCopyStarted()
{
copyStatus.Text = "Copy progress: ";
generateButton.Enabled = false; // Cross-thread operation not valid
}
}
I have the following exception:
Additional information: Cross-thread operation not valid: Control
'generateButton' accessed from a thread other than the thread it was created on.
Can I fix the exception by changing GetEventHandler() instead of wrapping each button in different places like this
Invoke((MethodInvoker)delegate {
generateButton.Enabled = false;
}); ?
How can I do this?
From your comments you said you call JobDataDuplicatorForm(IJobDataDuplicatorEngine engine) from a background thread.
Your class is a Form, any windows controls (Which includes Form) must be initially created on the UI thread or things like Invoke and InvokeRequired break. Whatever code is calling the constructor of JobDataDuplicatorForm must do that call from the UI thread.

Increase progress bar each second -- error [duplicate]

This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 7 years ago.
it's my first experience with C#, I'm trying to make a progress bar that increases each second on a value (a part of program, it receives current value from another object and sets it to the progress bar).
My simplified object source:
public delegate void LoadingProgressChanged(int percents);
public event LoadingProgressChanged loadingProgressChanged;
public void Enable()
{
loadingTimer = new Timer(1000);
loadingTimer.Elapsed += new ElapsedEventHandler(IncreaseLoadingPercentage);
loadingTimer.Start();
}
private void IncreaseLoadingPercentage(object source, EventArgs e)
{
loadedPercents += getLoadingPercentsPerSecond();
loadingProgressChanged(loadedPercents);
}
Form sources:
In constructor
router.loadingProgressChanged += new AbstractRouter.LoadingProgressChanged(percentageChanged);
Body of percentageChanged
public void percentageChanged(int percs)
{
progressBar1.Value = percs;
}
And I get error
An exception of type 'System.InvalidOperationException' occurred in
System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control
'progressBar1' accessed from a thread other than the thread it was
created on.
If there is a handler for this exception, the program may be safely
continued.
I understand why it happens: it seems that percentageChanged form is called in timer thread. But how to implement it correctly? Thanks!
Try with something like this:
public void percentageChanged(int percs)
{
Invoke(new Action(() =>
{
progressBar1.Value = percs;
}));
}

Passing data between threads of a c# winforms application [duplicate]

This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 9 years ago.
Short Version :
I need to pass complex data back and forth between threads, 1 thread being a WinForm, the other thread calling an online translator service and changing all the data the Winform uses.
Long Version :
Passing large amounts of data to an online translator service was freezing up my front end for minutes at a time, so I'm trying to move that logic into a thread. The Winform makes extensive use of the data that the online service needs to process and return with new info attached.
I was using this code to kick off the thread :
threadWork tw = new threadWork(); //obj to hold data for thread
tw.settings = frmMain.projectSettings;
tw.CompletedEvent += tw_CompletedEvent; // callback event, returns data
ThreadPool.QueueUserWorkItem(doWork,tw); // kick off thread
receiving callback code :
void tw_CompletedEvent(projectFormat settings)
{
frmMain.projectSettings = settings;
NotifyLoadTransationKeys(frmMain.projectSettings.translationKeys,frmMain.projectSettings.translatedLanguages);
}
which basically created this error :
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on. [on frmMain]
so I found this suggesting an alternative use of [STAThread] [sta = single thread apartment] inside Program.cs (default c# winform entry point)
using System.Threading;
Thread t = new Thread(new ThreadStart(StartNewStaThread));
// Make sure to set the apartment state BEFORE starting the thread.
t.ApartmentState = ApartmentState.STA;
t.Start();
private void StartNewStaThread() {
Application.Run(new Form1());
}
Everything in my C# app starts in 'Program.cs' so I tried the above suggestion like so :
static class Program
{
public static translationUtil TranslationUtil; //location of time intensive webservice
public static projectFormat projectSettings; //data that needs sharing
static void Main() // prog entry point
{
ProgramCode pc = new ProgramCode();
pc.Main();
}
}
class ProgramCode
{
public void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread t = new Thread(new ThreadStart(StartNewStaThread));
t.ApartmentState = ApartmentState.STA;
t.Start();
}
private void StartNewStaThread()
{
Application.Run(new Main());
}
}
in the code above I've tried to move the shared resources to 'Program.cs' and then created a new class and thread to hold my winform,but this still gives me cross threading issues (same error as before)! Has anybody got an suggestions as to how to use threads and share data successfully in my situation?
Update : #HenkHolterman answered my question best thus far, but I've come across this code many times after researching the answer he has given me (using "invoke")
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
Follow on question... the 'if' statement above appears to just set the text without invoke (on one of the branches of the 'if', should I follow this pattern as well, what is it for?
Your basic solution:
void tw_CompletedEvent(projectFormat settings)
{
frmMain.projectSettings = settings;
frmMain.Invoke( () =>
NotifyLoadTransationKeys(
frmMain.projectSettings.translationKeys,
frmMain.projectSettings.translatedLanguages)
);
}
But when NotifyLoadTransationKeys() takes a lot of time you'll need to break it up.
Edit:
Calls to Invoke() are often surrounded with if (control.InvokeRequired) , a small optimization. But when you are sure you are on another (than the UI) thread you can skip that.

thread and event [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
WPF access GUI from other thread
Good day ,
I write class
public class Metric1
{
public event MetricUnitEventHandler OnUnitRead;
public void ReiseEventOnUnitRead(string MetricUnitKey)
{
if (OnUnitRead!=null)
OnUnitRead(this,new MetricUnitEventArgs(MetricUnitKey));
}
.....
}
Metric1 m1 = new Metric1();
m1.OnUnitRead += new MetricUnitEventHandler(m1_OnUnitRead);
void m1_OnUnitRead(object sender, MetricUnitEventArgs e)
{
MetricUnits.Add(((Metric1)sender));
lstMetricUnit.ItemsSource = null;
lstMetricUnit.ItemsSource = MetricUnits;
}
Then i start new thread that every minute calls m1's ReiseEventOnUnitRead method.
In row lstMetricUnit.ItemsSource = null; throws excepition - "The calling thread cannot access this object because a different thread owns it." Why?
You cannot change GUI item from another thread that isn't the GUI thread,
If you are working with WinForms use Invoke and InvokeRequired.
if (lstMetricUnit.InvokeRequired)
{
// Execute the specified delegate on the thread that owns
// 'lstMetricUnit' control's underlying window handle.
lstMetricUnit.Invoke(lstMetricUnit.myDelegate);
}
else
{
lstMetricUnit.ItemsSource = null;
lstMetricUnit.ItemsSource = MetricUnits;
}
If you are working with WPF use Dispatcher.
lstMetricUnit.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
lstMetricUnit.ItemsSource = null;
lstMetricUnit.ItemsSource = MetricUnits;
}
));
You should use Dispatcher.
Example:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => {
lstMetricUnit.ItemsSource = null;
lstMetricUnit.ItemsSource = MetricUnits;
})));
In WPF and Forms -> you cannot modify UI controls from different thread.

Categories

Resources