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.
Related
This question already has answers here:
Error: Must create DependencySource on same Thread as the DependencyObject even by using Dispatcher
(2 answers)
The calling thread cannot access this object because a different thread owns it.How do i edit the image?
(3 answers)
How do you pass a BitmapImage from a background thread to the UI thread in WPF?
(3 answers)
Closed 1 year ago.
Hello I want to create a buffer of BitmapImage built with a Queue from a background worker. The main target is to load some images from a private network and don't block UI. I can create that buffer without blocking UI but when I try to get one image from the queue I get an System.InvalidOperationException, so I'm trying to access an object owned by another thread.
My worker code:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
buffer.Enqueue(new BitmapImage(new Uri(fullPath, UriKind.Absolute)));
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
Calling method:
private async void LoadValidationImages()
{
var imageList = new List<ReadLabel>(CommonData.CurrentFiche.Labels);
var images = imageList.FindAll(f => f.CoscNumber.StartsWith("err"));
if (images.Count > 0)
{
Queue<BitmapImage> result = await BufferLoader(images);
ImageBuffer = new Queue<BitmapImage>(result);
BufferLoadingCompleted();
}
}
UI thread call method:
private void BufferLoadingCompleted()
{
/*Dispatcher.Invoke(() =>
{*/
imgToValidate.Source = ImageBuffer.Peek();
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
rotation = parameters[ValidationParameters.Parameters.ImageRotation.ToString()].ValueToDouble();
scaleX = parameters[ValidationParameters.Parameters.ImageScaleX.ToString()].ValueToDouble();
scaleY = parameters[ValidationParameters.Parameters.ImageScaleY.ToString()].ValueToDouble();
scrlImage.ScrollToHorizontalOffset(parameters[ValidationParameters.Parameters.ScrollerHorizontalFactor.ToString()].ValueToDouble());
scrlImage.ScrollToVerticalOffset(scrollPosVertical = parameters[ValidationParameters.Parameters.ScrollerVerticalFactor.ToString()].ValueToDouble());
ApplyTransformations();
Console.WriteLine("Load finished");
//});
}
I tried to use Dispatcher.Invoke on BufferLoadingCompleted() but it don't work I get the same exception. What I'm doing wrong?
Final code. Solution as suggested by Andy:
In my background worker code I didn't Freeze() the new objects created inside the working thread, so I was getting an exception.
Solution applies only to the background worker method:
private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
{
var parameters = CommonData.CurrentRecipe.Parameters["validation"];
var buffer = new Queue<BitmapImage>();
foreach (var label in labels)
{
var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);
try
{
if (File.Exists(fullPath))
{
var newImage = new BitmapImage(new Uri(fullPath, UriKind.Absolute));
newImage.Freeze();
buffer.Enqueue(newImage);
}
else
{
throw new ValidationImageNotFoundException(fullPath);
}
}
catch { }
}
return buffer;
});
}
You're creating something on a non ui thread that by default has thread affinity.
Luckily though, a bitmapimage inherits from freezable.
See the inheritance chain:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.bitmapimage?view=net-5.0
Inheritance:
Object
DispatcherObject
DependencyObject
Freezable
Animatable
ImageSource
BitmapSource
BitmapImage
If you call .Freeze() on a freezable then you can pass it between threads.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/freezable-objects-overview?view=netframeworkdesktop-4.8
From there:
What Is a Freezable?
A Freezable is a special type of object that has two states: unfrozen and frozen. When unfrozen, a Freezable appears to behave like any other object. When frozen, a Freezable can no longer be modified.
A Freezable provides a Changed event to notify observers of any modifications to the object. Freezing a Freezable can improve its performance, because it no longer needs to spend resources on change notifications. A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot.
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++));
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.
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 9 years ago.
Hi I am first time working on Threads not sure If I am doing correct
I am getting Error saying :
The calling thread cannot access this object because a different thread owns it" exception
private void ImportProductStatsButtonClick(object sender, EventArgs e)
{
// Get the currently selected manufacturer from the combo box
var selected = comboBoxCorporation.SelectedItem;
buttonProductStatsAndRetailerStats.Enabled = false;
buttonSummariseRetailerStats.Enabled = false;
buttonSummariseProductStats.Enabled = false;
// Do we have one?
if (selected != null)
{
// Extract the combo record
var corporation = (ComboBoxCorporrationItem)selected;
// Do we have one?
if (corporation.Corporation != null)
{
// yes
// Make this on a seperate thread so that the UI continues to work
var thread = new Thread(MigrateProductStats);
thread.Start(corporation.Corporation.Id); // This enables me to pick the manufacturer that we are summarizing for
}
}
}
private void MigrateProductStats(object corporationIdObj)
{
// after thread completion I need to Enable my buttons.
buttonProductStatsAndRetailerStats.Enabled = true;
buttonSummariseProductStats.Enabled = true;
}
Try with:
private void MigrateProductStats(object corporationIdObj)
{
Invoke(new Action(() =>
{
// after thread completion I need to Enable my buttons.
buttonProductStatsAndRetailerStats.Enabled = true;
buttonSummariseProductStats.Enabled = true;
});
}
Even better than Control.Invoke would be to use BackgroundWorker to handle threading for you. It generates progress and completion events back on the UI thread to make UI updates easy.
If you're using C# 5, you can also use async to start the background processing and await to cause the UI updates to occur when processing completes.
I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..