WPF STA thread error - wpf

I am following the code given on this thread C# Async WebRequests: Perform Action When All Requests Are Completed
In my WPF app I need to asynchronously download images from the server. However I get the following error
The calling thread must be STA, because many UI components require this.
Could it be because I am doing UI updates on the main thread? I have also declared the calling thread's state to STA, my code follows:
private void FixedDocument_Loaded(object sender, RoutedEventArgs e)
{
Thread t = new Thread(new ThreadStart(AsyncLoadImages));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
private void AsyncLoadImages()
{
foreach (string resFile in resFiles)
{
string imageuri = #"http://www.example.com/image.jpg";
WebRequest request = HttpWebRequest.Create(imageuri);
request.Method = "GET";
object data = new object();
RequestState state = new RequestState(request, data);
IAsyncResult result = request.BeginGetResponse(
new AsyncCallback(UpdateItem), state);
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(ScanTimeoutCallback), state, (30 * 1000), true);
}
}
private static void ScanTimeoutCallback(object state, bool timedOut)
{
if (timedOut)
{
RequestState reqState = (RequestState)state;
if (reqState != null)
{
reqState.Request.Abort();
}
Console.WriteLine("aborted- timeout");
}
}
private void UpdateItem(IAsyncResult result)
{
RequestState state = (RequestState)result.AsyncState;
WebRequest request = (WebRequest)state.Request;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = response.GetResponseStream();
bi.EndInit();
Image i = new Image(); //hitting the error at this line
i.Source = bi;
}
Please can someone help?
Many Thanks

You can try wrapping your code in below, this however is dirty solution.
MyUIElement.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
//your code here
}));
best if MyUIElement was your top window.

You need to call every UI operation in the MainThread i guess your UpdateItem method will not be called in the UI Thread thus you get this exception.
i would change 2 things:
First, use the BackgroundWorker class, which makes this kind of async operations in WPF alot simpler.
Second, if you have another thread (Backgroundworker or custom Thread) you always must Dispatch every UI operation into the main thread.

Related

Due to the use of Dispatcher,t he window still locks

I have a program similar to chatbot in Wpf.
I have a stack where I create the user controls I have and enter them.
I have to use Net3.5 .
The response from the server is delayed.
The problem I have is when I type and send the textbox the server does not answer,
I can not type another question and the window is locked.
Did I use Dispatcher correctly?
private void send_Click(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
DispatchFit();
}
private void DispatchFit()
{
Dispatcher.BeginInvoke(new Action(ResponsServer), DispatcherPriority.Background);
}
public void ResponsServer()
{
Thread.Sleep(3000);
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}
When that ResponsServer() callback is being processed on your UI thread, then that Sleep is elongating the amount of time that callback is taking to process (Sleep does not pump the UI's dispatcher message queue).
If you want your callback to be done after 3 seconds, then you need to use a timer, or you can use "async" to cause a delayed processing of your callback.
Look at this question: Delayed Dispatch Invoke?
Or use this to have a BackgroundWorker do the delay and then call your ResponsServer on the UI thread (not the best code as it creates a new BackgroundWorker each time).
https://www.codeproject.com/Tips/240274/Execute-later-for-delayed-action
You are a little confused about the methods.
If I understand correctly, then Sleep is an emulation of the delay in the execution of sending a message to the server.
Then you need something like:
private async void send_ClickAsync(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
await ResponsServerAsync();
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}
public async Task ResponsServerAsync()
{
Thread.Sleep(3000);
}
For .Net Framework 3.5
private void send_Click(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
Thread thread = new Thread(ResponsServer);
thread.Start();
}
public void ResponsServer()
{
Thread.Sleep(3000);
if (Dispatcher.CheckAccess())
{
StackChildrenAdd();
}
else
{
Dispatcher.InvokeAsync(StackChildrenAdd);
}
}
private void StackChildrenAdd()
{
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}

CefSharp Offscreen example as a dynamic library called by WinForm application

I wrote a shared library which consumes CefSharp.Offscreen to do the html retrieving work. It works fine when a Console Application calls it. But when a WinForm app connects it, after tcs.TrySetResult(true) is executed, it does not jump into await browser.GetSourceAsync() as what it did in Console App.
In WinForm App, it could be successful if any UI element is not created and not in the UI constructor, but if I create a UI element before calling the shared library, it fails always.
In another way, I force calling "var source = await browser.GetSourceAsync();" to get current html source, but it still does not response in WinForm connection.
[STAThread]
static void Main()
{
// I can put the init code here, but it does not help
//CefSimpleLib.CefTest.Initialize();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TextBox tb = new TextBox(); // this blocks below call
CefSimpleLib.CefTest cf = new CefSimpleLib.CefTest();
Application.Run(new FormMain());
//CefSimpleLib.CefTest.UnInitialize();
}
namespace CefSimpleLib
{
public class CefTest
{
public CefTest()
{
// You need to replace this with your own call to Cef.Initialize();
// Default is to use an InMemory cache, set CachePath to persist cache
Cef.Initialize(new CefSettings { CachePath = "cache" });
MainAsync();
System.Threading.Thread.Sleep(1000 * 1000);
Cef.Shutdown();
}
private async void MainAsync()
{
var browserSettings = new BrowserSettings();
//Reduce rendering speed to one frame per second, tweak this to whatever suites you best
browserSettings.WindowlessFrameRate = 1;
using (var browser = new ChromiumWebBrowser("https://www.baidu.com", browserSettings))
{
await LoadPageAsync(browser);
var source = await browser.GetSourceAsync();
await Task.Delay(10);
}
}
public Task LoadPageAsync(IWebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
//Wait for while page to finish loading not just the first frame
if (!args.IsLoading)
{
browser.LoadingStateChanged -= handler;
tcs.TrySetResult(true);
}
};
browser.LoadingStateChanged += handler;
return tcs.Task;
}
}
}

Update WPF UI from Different Thread VB.net [duplicate]

Here's my problem.
I'm loading a few BitmapImages in a BlockingCollection
public void blockingProducer(BitmapImage imgBSource)
{
if (!collection.IsAddingCompleted)
collection.Add(imgBSource);
}
the loading happens in a backgroungwork thread.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
String filepath; int imgCount = 0;
for (int i = 1; i < 10; i++)
{
imgCount++;
filepath = "Snap";
filepath += imgCount;
filepath += ".bmp";
this.Dispatcher.BeginInvoke(new Action(() =>
{
label1.Content = "Snap" + imgCount + " loaded.";
}), DispatcherPriority.Normal);
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
blockingProducer(imgSource);
}
}
debugging this part of the code everything looks okay, the problem comes now ...
after finishing loading the images I want to show them in UI one by one. I'm using a dispatcher to do so but I always get the message telling me that the called Thread can not access the object because it belongs to a different Thread.
public void display(BlockingCollection<BitmapImage> results)
{
foreach (BitmapImage item in collection.GetConsumingEnumerable())
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
Thread.Sleep(250);
}), DispatcherPriority.Background);
}
}
debug accuses that the error is here
this.dstSource.Source = item;
I'm trying everything but cant find out what’s wrong. Anyone has any idea?
You have to call Freeze after loading the images in order to make them accessible to other threads:
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze(); // here
As far as I have understood the BitmapCacheOption.OnLoad flag, it is only effective when a BitmapImage is loaded from a stream. The Remarks section in BitmapCacheOption says:
Set the CacheOption to BitmapCacheOption.OnLoad if you wish to close a
stream used to create the BitmapImage. The default OnDemand cache
option retains access to the stream until the image is needed, and
cleanup is handled by the garbage collector.
A BitmapImage created from a Uri may be loaded asynchronously (see the IsDownloading property). Consequently, Freeze may not be callable on such a BitmapImage, as downloading may still be in progress after EndInit. I guess it nevertheless works in your case because you are loading BitmapImages from file Uris, which seems to be done immediately.
To avoid this potential problem you may just create the BitmapImage from a FileStream:
var imgSource = new BitmapImage();
using (var stream = new FileStream(filepath, FileMode.Open))
{
imgSource.BeginInit();
imgSource.StreamSource = stream;
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze();
}
For the further future readers, here is the code I used to fix my problem.
public void display(BlockingCollection<BitmapImage> collection)
{
if (collection.IsCompleted || collection.Count != 0)
{
BitmapImage item = collection.Take();
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
}), DispatcherPriority.Normal);
}
else
{
dispatcherTimer.Stop();
}
}
public void dispatcherTimer_Tick(object sender, EventArgs e)
{
display(collection);
}
public void configureDispatcherTimer()
{
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimeSpan interval = TimeSpan.FromMilliseconds(150);
dispatcherTimer.Interval = interval;
}

Timer not getting called when backgroundworker running

I have a WPF window with a button that spawns a BackgroundWorker thread to create and send an email. While this BackgroundWorker is running, I want to display a user control that displays some message followed by an animated "...". That animation is run by a timer inside the user control.
Even though my mail sending code is on a BackgroundWorker, the timer in the user control never gets called (well, it does but only when the Backgroundworker is finished, which kinda defeats the purpose...).
Relevant code in the WPF window:
private void button_Send_Click(object sender, RoutedEventArgs e)
{
busyLabel.Show(); // this should start the animation timer inside the user control
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
string body = textBox_Details.Text;
body += "User-added addtional information:" + textBox_AdditionalInfo.Text;
var smtp = new SmtpClient
{
...
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
}));
}
Relevant code in the user control ("BusyLabel"):
public void Show()
{
tb_Message.Text = Message;
mTimer = new System.Timers.Timer();
mTimer.Interval = Interval;
mTimer.Elapsed += new ElapsedEventHandler(mTimer_Elapsed);
mTimer.Start();
}
void mTimer_Elapsed(object sender, ElapsedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
int numPeriods = tb_Message.Text.Count(f => f == '.');
if (numPeriods >= NumPeriods)
{
tb_Message.Text = Message;
}
else
{
tb_Message.Text += '.';
}
}));
}
public void Hide()
{
mTimer.Stop();
}
Any ideas why it's locking up?
Using Dispatcher.Invoke in your worker_DoWork method is putting execution back on the UI thread, so you are not really doing the work asynchronously.
You should be able to just remove that, based on the code you are showing.
If there are result values that you need to show after the work is complete, put it in the DoWorkEventArgs and you will be able to access it (on the UI thread) in the worker_RunWorkerCompleted handler's event args.
A primary reason for using BackgroundWorker is that the marshalling is handled under the covers, so you shouldn't have to use Dispatcher.Invoke.

BitmapFrame in another thread

I am using a WPF BackgroundWorker to create thumbnails. My worker function looks like:
private void work(object sender, DoWorkEventArgs e)
{
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
if (!string.IsNullOrEmpty(path))
{
FileInfo info = new FileInfo(path);
if (info.Exists && info.Length > 0)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = 200;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(info.FullName);
bi.EndInit();
var item = new BoxItem();
item.FilePath = path;
MemoryStream ms = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bi));
encoder.Save(ms);
item.ThumbNail = ms.ToArray();
ms.Close();
boxList.Add(item);
}
}
}
e.Result = boxList;
}
catch (Exception ex)
{
//nerver comes here
}
}
When this function is finished and before the BackgroundWorker "Completed" function is started, I can see on the output window on Vs2008, that a exception is generated. It looks like:
A first chance exception of type 'System.NotSupportedException' occurred in PresentationCore.dll
The number of exceptions generates equals the number of thumbnails to be generated.
Using "trial and error" method I have isolated the problem to:
BitmapFrame.Create(bi)
Removing that line (makes my function useless) also removes the exception.
I have not found any explanation to this, or a better method to create thumbnails in a background thread.
Lasse, I believe the problem arises because you are performing actions outside of the UI thread that need to be done within the UI thread. Creating UI elements (BitmapImage, BitmapFrame) and adding to UI Containers, I believe, should be done on the UI thread. (Someone correct me if I'm wrong here).
There are a few ways to create those elements on the UI thread without blocking the application for an excessive period of time. The easiest is probably using the BackgroundWorker's ProgressChanged event. ProgressChanged is invoked on the UI thread, which makes it perfect for this situation.
You can use the worker's ProgressChanged event and pass it the path needed to load a thumbnail in the UserState argument.
Thanks for your input. It made start to look for another solutions and I come up with this.
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
using (Image photoImg = Image.FromFile(path))
{
int newWidth = 200;
int width = newWidth;
int height = (photoImg.Height * newWidth) / photoImg.Width;
var thumbnail = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)thumbnail))
{
g.DrawImage(photoImg, 0, 0, width, height);
using (var ms = new System.IO.MemoryStream())
{
var item = new BoxItem();
item.FilePath = path;
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
item.ThumbNail = ms.ToArray();
boxList.Add(item);
}
}
}
}
e.Result = boxList;
}
catch (Exception exp)
{
}
Not using any UI elements .. and works nicely.
Thanks.
//lasse

Resources