I am beginner to C# .net. I have simple app in wpf which access a listbox from user thread. in winforms i can use invokerequired, a equivalent for wpf using dispatcher did not help. My system also hangs for the buttons so debugging is though. Please provide solution for the below code. thanks in advance
private void Monitor_mtd()
{
while (AppStatus != 0)
{
if (flag2 == 1)
{
listBox1.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new list1MtdDelegate(list1Mtd), "Best practice");
}
}
}
private delegate void list1MtdDelegate(string ls1);
private void list1Mtd(string ls1)
{
listBox1.Items.Add(ls1);
}
private void button1_Click_1(object sender, RoutedEventArgs e)
{
Monitor = new Thread(new ThreadStart(Monitor_mtd));
Monitor.Start();
flag1 = 1;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
flag2 = 1;
}
There are a couple of issues that arise in your approach. Firstly, the way that you bind your data to the ListBox and secondly trying to update the ListBox from a user thread.
You can solve the binding of the ListBox by using an ObservableCollection so that the UI is updated with the necessary values (have a look at this post for more information on this). However, this also raises another problem and that is that the ObservableCollection cannot be called from another thread other than the one it is dispatching (see more on this here also). This means that you need another implementation for the ObservableCollection. Thomas Levesque made an AsyncObservableCollection that can be modified from any thread and still notify the UI when its modified.
I made a sample implementation that you can download here showing the full solution.
Related
I have a question about using external c++ library (irrKlang.dll) which is an audio playback engine. Now, the problem is that when I get a SoundStopped event out of it, and do an action in the main form, all kinds of stack related errors arise. Let me show the code:
namespace WindowsFormsApplication4
{
public class IsoundFinished : ISoundStopEventReceiver
{
public delegate void OnSoundStoppedEventHandler(object source, EventArgs e);
public event OnSoundStoppedEventHandler IStopped;
public void OnSoundStopped(ISound iSound, StopEventCause reason, object userData)
{
if (reason.ToString() == "SoundFinishedPlaying")
IStopped?.Invoke(this, EventArgs.Empty);
}
}
}
That is an extended class for me to do custom actions (for example - if sound finished, raise the event...) I am creating an instance of it, for the event action to get exposed in my main Form1 class:
IsoundFinished iStopReceiver = new IsoundFinished();
Now in my main form, I have this line in my Form1() method, just under my InitializeComponent():
iStopReceiver.IStopped += new soundFinished.OnSoundStoppedEventHandler(OnStopped);
It's for subscribing to the event handler. And finally - my OnStopped() method which is supposed to do stuff when the song ends it's playback - it's on the same Form1:
private void OnStopped(object sender, EventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(OnStopped), sender, e);
return;
}
btnStop1.PerformClick();
}
My Stop1 button method is (for those who work with the IrrKlang) ISound.Stop(); and few more lines of code, dealing with the display of playlist and so on. Although I have invoked it from the main UI thread - which should provide me with some degree of thread misalignment protection, all kinds of errors appear, mostly
Cannot evaluate expression because a native frame is on the top of the call stack.
Of course, if I do it without event handler, ISound.Stop(); drops the sound from the engine, like it should. I know something wrong is happening with the threads, but I can't figure out what's going on. If someone would give me few tips, I'd appreciate that a lot.
Well it seems I've solved it myself ! It's all about understanding how the threads are working in Visual C#. The problem was this : I was actually PAUSING the background thread where my audioengine was triggering the event - so 'till I performed an action after INVOKE in the main UI thread, background thread was paused along with the whole irrKlang engine. It was unable to purge itself properly, so it's call stack got clogged!
Using BEGININVOKE solved the problem, as it doesn't PAUSE the background task. It lets it run instead. Diagram on this answer gave me much needed piece of info I was looking for.
Maybe someone will need this answer too, glad I helped myself :P
private void OnStopped(object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new Action<object, EventArgs>(OnStopped), sender, e);
return;
}
btnStop1.PerformClick();
}
For some event, the subscription by hooking the event directly object is failing but working when you use a local variable.
I haven't figured out why yet, who does?
I came across this mystery as I start working with CommandBarsEvents.OnUpdate, in order to detect interaction with a shape, what can comes with certain problems as described here.
There are other events with that problem?
to keep clear here comes an (incomplete) overview of events which are affected and which not
events what DO need a local variable
this.Application.CommandBars.OnUpdate
.
Their code has to be like this:
using Office = Microsoft.Office.Core;
namespace Project
{
public partial class AddIn
{
private Office.CommandBars commandBars; //declaration of local variable
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
commandBars = this.Application.CommandBars; //initialization of local variable
commandBars.OnUpdate += new Office._CommandBarsEvents_OnUpdateEventHandler(commandBars_OnUpdate); //"indirect" subscription to event`
}
}
}
events what NOT need a local variable
((InteropExcel.AppEvents_Event)Application).NewWorkbook
this.Application.WorkbookNewSheet
this.Application.WorkbookOpen
.
Their code can be like this:
using Office = Microsoft.Office.Core;
namespace Project
{
public partial class AddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
((InteropExcel.AppEvents_Event)Application).NewWorkbook += new InteropExcel.AppEvents_NewWorkbookEventHandler(Excel_NewWorkbook_EventHandler); //"direct" subscription to event`
}
}
}
I need to pass an object with some information to consume in my Background Task. I try to search in web but not found any solution. It is possible?
One workarround is save information what I need to pass in isolated storage in my MainProjet and in my BackgroundTask project consume information saved before. But this solution is not beautiful to use.
Someone help me?
Thanks in advance
You can use SendMessageToBackground method
var message = new ValueSet();
message.Add("key",value);
BackgroundMediaPlayer.SendMessageToBackground(message);
In background task listen to this method
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
}
private void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e)
{
foreach (string key in e.Data.Keys)
{
switch (key.ToLower())
{
}
}
}
I spend a lot of time working with Windows Forms controls but from a background worker thread - I suppose this is good practice really since you don't want your form to be locking up when people click buttons. To be honest, with just about everything GUI related action I normally do in a background worker thread, so the interface is nice an responsive to the user (Wish more people would do that!).
So my question is... every time I have to interact with controls I have to "Invoke" them, with something like:
if (control.InvokeRequired)
{
//
}
Standard practice right? However, this leads me to some terribly messy code, because just about every control type I have, I need a MethodInvoker delegate or something. It's adding thousands of lines of code to my protects, and its terribly time consuming.
I currently have hundreds of "property setting" methods like:
private void Safe_SetLableText(Label control, string text)
{
if (control.InvokeRequired)
{
control.Invoke((MethodInvoker)delegate
{
control.Text = text;
});
}
else
{
control.Text = text;
}
}
So, is there some other technique, or way to do this, or some way to being able to always alter a property of a control, no matter what the control is and no matter what thread im in?
something like: (pseudocode)
BackgroundWorker.RunWorkerAsync();
private void thing_to_do()
{
// We are in a background thread now
DoSomeDatabaseWorkThatTakesALongTime();
InvokeAnyControls();
// Do some stuff...
controlX.Text = "123"
controlY.Height = 300;
controlZ.text = ControlA.text;
RestoreAnyControls();
}
You could wrap your InvokeRequired code with a delegate, like so:
public static void Invoke2<TControl>(this TControl c, Action<TControl> code) where TControl : Control {
if( c.InvokeRequired ) c.Invoke( delegate() { code(c); } );
else code(c);
}
Then use it like so:
private void Safe_SetLableText(Label control, string text) {
control.Invoke2( c => c.Text = text );
}
Of course you might want better names than Invoke2, but I hope the idea sits will with you. Note that the lambda-expression syntax is a C# 3.0 feature, but the Action<T> delegate is part of .NET 2.0, so this will compile against the .NET Framework 2.0 so long as you're VS2008 or later.
I'm posting an answer to my own question because I think it will add value to the community.
1) I wanted to "simplify" my code, and one if the most important finds was that that the:
control.InvokeRequired
really isnt needed... its pretty much a given. Importantly, you CAN rely on the fact that the control will need to be invoked if you are in a background (or non-UI) thread.
2) The invocation travels "UP" the control tree, so if you have:
Form > Control > Control inside Control > etc > etc
You only need to invoke "Form" (top most), and then you can alter the properties of the child elements.
So here is my clean and simple solution to working with background workers (or non-UI threads). I have just tested this now and it works great.
public partial class Form1: Form
{
public Form1()
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(this.bgDoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.bgComplete);
bgw.RunWorkerAsync();
}
private void bgComplete(object sender, EventArgs e)
{
// You are not in the UI thread now, so you can Invoke without error
this.Invoke((MethodInvoker)delegate
{
// Now you can change any property an any control within this form.
// Remember "this" refers to Form1.
this.label1.Text = "test123";
this.label2.Text = "test456";
this.label3.Text = this.label4.Text;
// You can set progress bars too, not just label text
}
}
private void bgDoWork(object sender, DoWorkEventArgs e)
{
// Do something that takes a long time
}
}
As you are already using the Background worker why don't you 'misuse' OnProgressChanged?
private void thing_to_do()
{
// We are in a background thread now
DoSomeDatabaseWorkThatTakesALongTime();
BackgroundWorker.ReportProgress(1, "state");
DoSomeMoreDatabaseWorkThatTakesALongTime();
BackgroundWorker.ReportProgress(2, YourObjectHere);
}
void OnProgressChanged(ProgressChangedEventArgs progressArgs)
{
switch(progressArgs.ProgressPercentage)
{
case 1:
// Do some stuff...
controlX.Text = "123"
controlY.Height = 300;
controlZ.text = ControlA.text;
break;
case 2:
// other stuff
YourObject obj = (YourObject) progressArgs.UserState;
// wahtever...
break;
default:
break;
}
}
In a multi form .NetCF 3.5 application I'm trying create the forms in the background while the user is occupied with the previous form.
We're using Orientation Aware Control in the project
We use a wrapper class (FormController) (please let me know if I'm using the wrong terminology) to keep static references to the different forms in our application. Since we only want to create them once.
At the moment the Forms are created the first time they are used. But since this is a time consuming operation we'd like to do this in the background while the user
Application.Run(new FormController.StartUI());
class FormController{
private static object lockObj = new object();
private static bool secIsLoaded = false;
private static StartForm startForm = new StartForm();
private static SecForm m_SecForm;
static SecForm FormWorkOrderList
{
get
{
CreateSecForm();
return m_SecForm;
}
}
private static void StartUI(){
startForm.Show();
ThreadStart tsSecForm = CreateSecForm;
Thread trSecForm = new Thread(tsSecForm);
trSecForm.Priority = ThreadPriority.BelowNormal;
trSecForm.IsBackground = true;
trSecForm.Start();
return startForm;
}
private static void CreateSecForm()
{
Monitor.Enter(lockObj);
if(!secIsLoaded){
m_SecForm = new SecForm();
secIsLoaded = true;
}
Monitor.Exit(lockObj);
}
private static void GotoSecForm()
{
SecForm.Show();
StartForm.Hide();
}
When I call GotoSecForm() the program throws an excepton on SecForm.Show() with an exection with hResult: 2146233067 and no other valuable information.
The stacktrace of the exception is:
on Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)
on System.Windows.Forms.Control.SuspendLayout()
on b..ctor(OrientationAwareControl control)
on Clarius.UI.OrientationAwareControl.ApplyResources(CultureInfo cultureInfo, Boolean skipThis)
on Clarius.UI.OrientationAwareControl.ApplyResources()
on Clarius.UI.OrientationAwareControl.OnLoad(EventArgs e)
on Clarius.UI.OrientationAwareControl.c(Object , EventArgs )
on System.Windows.Forms.Form.OnLoad(EventArgs e)
on System.Windows.Forms.Form._SetVisibleNotify(Boolean fVis)
on System.Windows.Forms.Control.set_Visible(Boolean value)
on System.Windows.Forms.Control.Show()
I'm quite qlueless about what's going wrong here. Can anyone help me out?
Or are there some better ways to load the forms in the background?
Let me know if any more information is needed.
You can't create forms (or safely do any manipulation of controls or forms) in background threads. They need to be created on the same thread that the message pump is running on - its just the way that Windows Forms work.
Creating the form itself shouldn't be in itself an expensive task. My advice would be to perform any expensive computations needed to display the form in a background thread, and then pass the result of those computations back to the main message pump in order to create and display the form itself.
(Half way through writing this I realised that this question is about windows mobile, however I'm 99% sure that the above still applies in this situation)