Good day,
After being unsuccessful in my Google & Stackoverflow queries (not sure what I should search for), I'm posting here a public question.
I have a main form (frmMainMenu). Whenever this form is loaded and a button on this form is pressed, I'm trying to update a chart. However, as this could be CPU-demanding, I was considering calling the operation to retrieve the chart from another thread.
I'm constantly getting an error : Cross-thread operation not valid: Control 'labelX2' accessed from a thread other than the thread it was created on. System.Windows.Forms at System.Windows.Forms.Control.get_Handle()
The idea behind is also to display this symbol (referenced as the circularProgress1 control whenever data is being retrieved and to hide it whenever the operation is complete.
Here below is the code of my method in the userform :
[...]
private void feedDashboard()
{
Thread l_Thread;
ClsStartPgm l_Start = null;
try
{
this.Invoke(new MethodInvoker(delegate()
{
l_Thread = new Thread(() =>
{
try
{
l_Start = new ClsStartPgm();
l_Start.getDashboardInformations(
this.labelX2,
this.circularProgress1,
this.tmr_Progress,
this.chart1,
this.expandablePanel_1);
}
finally
{
// onCompleted();
}
});
l_Thread.Start();
}));
}
catch (Exception exc)
{
ClsFinappErrorManager.manageException(exc);
}
finally
{
}
}
[...]
and if it can be helpful, here is my mentioned getDashboardInformations method of my Class ClsStartPgm
public sealed class ClsStartPgm
{
[...]
// (Constructors, accessors and other methods not mentioned here
// to simplify the reading of my question)
[...]
public void getDashboardInformations(
LabelX pInformationText,
Controls.CircularProgress pCircularProgress,
System.Windows.Forms.Timer pTimer,
Chart pChart1,
ExpandablePanel pExpandablePanel1)
{
List<double> l_DoubleList = null;
List<string> l_StringList = null;
try
{
pTimer.Start();
this.m_Busy_Dashboard_Generation = true;
this.m_Busy_Dashboard_Generation = false;
double[] yValues = l_DoubleList.ToArray();
string[] xValues = l_StringList.ToArray();
pChart1.Series["MySerie"].Points.Clear();
// Populate series data
pChart1.Series["MySerie"].Points.DataBindXY(xValues, yValues);
// Set Doughnut chart type
pChart1.Series["MySerie"].ChartType = SeriesChartType.Pie;
// Set title of the expendable panel
pExpandablePanel1.TitleText = "Situation: " + DateTime.Now.ToShortDateString();
[...]
}
catch (Exception exc)
{
ClsFinappErrorManager.manageException(exc);
}
finally
{
l_Tuple = null;
l_Accounts = null;
}
}
}
Please, could anyone guide me on what's wrong in my code? I'm definitely not asking to get the code written for me. However, I would be keen on understanding what I'm doing incorrectly here in my approach.
Many thanks for your appreciated help and best wishes,
Related
I'm trying to build a TAPI based phone call system using JulMar's Atapi x86. One of the functions is to pop a specific form on an inbound call. However, whenever the form pops, it comes up incorrect, as shown below (I have tried several forms as a test and they all do the same thing). There is no error, nothing in the output window to suggest what the issue is.
Code:
private void incomingcall(object sender, NewCallEventArgs e)
{
string phonenumber = e.Call.CallerId; //get the phone number of the call
SqlCommand getincoming = new SqlCommand(Querystrings.getincomingquery(), DB);
getincoming.Parameters.AddWithValue("##TELEPHONE", phonenumber);
DataTable results = new DataTable();
try
{
DB.Open();
using (var results = getincoming.ExecuteReader())
{
results.Load(results);
}
}
catch (Exception ex)
{
Inbound ib = new Inbound(phonenumber, null);
ib.Show();
}
finally
{
DB.Close();
}
if (results.Rows.Count == 1)
{
loadcontactrequest(Convert.ToInt32(results.Rows[0].ItemArray[0]), phonenumber);
}
else
{
loadinbound(phonenumber, results);
}
}
I have loaded these forms outside of this function at other points, meaning it is something to do with this function. Does anybody know where I'm going wrong?
EDIT:
private void loadcontactrequest(int ContactID, string phonenumber)
{
ContactRequest cr = new ContactRequest(ContactID, Global.loginbound("Single customer found", phonenumber));
cr.Show();
}
These functions have been tested elsewhere and work correctly individually, I believe it might be TAPI related.
EDIT 2 - Delegate:
public static void inittapi()
{
if (TestOptions.notapi)
return;
tapi = new TapiManager("Omitted");
tapi.Initialize();
foreach (TapiLine ad in tapi.Lines) //Get all lines available to this PC
{
if (ad.Name.ToUpper().Contains("Omitted"))
{
phoneline = ad;
phoneline.Open(MediaModes.All); //Open the phone line for making and receiving calls
phoneline.NewCall += new EventHandler<NewCallEventArgs>(new TAPI().incomingcall); //Add the incoming call event handler
}
}
}
It's possible that this event is triggered on a different thread than the UI thread of your application.
Modify the method like this to test whether this is the problem:
private void incomingcall(object sender, NewCallEventArgs e)
{
Form form;
if(Application.OpenForms.Count > 0)
{
form = Application.OpenForms[0];
}
if (form != null && form.InvokeRequired)
{
form.BeginInvoke(new Action(() => { incomingcall(sender, e); }));
return;
}
// Your current code goes here
}
This will identify that we are in a different thread than your main form (form) was created on and then execute the function again on the main form's thread.
I have an application that performs a time consuming task when the user selects an item for a listbox.
When a user selects a show the application will retrieve all the shows information form the tvdb and the display it in the Ui.
The problem occurs when a user quickly changes selection while the show is still loading.
I would like to make it so that a user could change their mind and then make another selection while the first was loading and have that information displayed in the Ui.
I have created a simple demonstration application to show the problem : Demo App .
This is what i tried to do
List box selection event handler
private void lb1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string sid = lb1.SelectedItem.ToString();
try
{
LoadSeries(Int32.Parse(sid));
}
catch (FormatException)
{
MessageBox.Show("Please enter a valid series id");
}
}
LoadSeries
private void LoadSeries(int _seriesId)
{
Task<TvdbSeries> series = Task.Factory.StartNew(() =>
{
TvdbSeries seriesloaded = null;
try
{
seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true);
}
catch (TvdbInvalidApiKeyException ex)
{
MessageBox.Show(ex.Message);
}
catch (TvdbNotAvailableException ex)
{
MessageBox.Show(ex.Message);
}
return seriesloaded;
}
);
series.ContinueWith((antecedent) =>
{
UpdateSeries(series.Result);
},
TaskScheduler.FromCurrentSynchronizationContext()
);
}
If a user changes selection quickly the application errors on the line seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true); and shows this message in the debugger "WebClient does not support concurrent I/O operations."
I did find out that it is because I am making a new request before the last one is finished but I have no way of chaining the code in m_tvdbHandler.GetSeries because its functionality comes from library i am using and some one else wrote .
This is the library tvdblib , I am sure the problem is with how I am doing things and not the library .
when a user makes a selection you can disable the UI till the information is loaded completely and display a message at the bottom loading please wait. Once everything is loaded, enable the Ui and hide the message.
You are posting this question as a C#5.0 question, so you should be using async/await as much as you can.
private Task<TvdbSeries> LoadSeriesAsync(int _seriesId)
{
return Task.Run(() =>
{
TvdbSeries seriesloaded = null;
try
{
seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true);
}
catch (TvdbInvalidApiKeyException ex)
{
MessageBox.Show(ex.Message);
}
catch (TvdbNotAvailableException ex)
{
MessageBox.Show(ex.Message);
}
return seriesloaded;
}
);
}
It would be much better if there was a LoadSeriesAsync.
One way to do it would be to disable lb1 while retrieving the series.
private async void lb1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string sid = lb1.SelectedItem.ToString();
try
{
lb1.IsEnabled = false;
var series = await LoadSeriesAsync(Int32.Parse(sid));
UpdateSeries(series);
}
catch (FormatException)
{
MessageBox.Show("Please enter a valid series id");
lb1.IsEnabled = true;
}
}
I have a WPF project (VS2010, .NET4.0) in which I create a rather big ModelVisual3D object (read from custom format STL file, process info, create mesh, etc.) This takes about 3-4 sec. to be created and another 2-3 sec. to do a mainViewport.Children.Add(ModelVisual3D).
I do this all in a custom class and call this method:
class My3DModel
{
...
public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
{
this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
for (int i = 0, j = 0; i < this.Triangles.Length; i++)
{
this.mesh.Positions.Add(this.Triangles[i].Vertex1);
this.mesh.Positions.Add(this.Triangles[i].Vertex2);
this.mesh.Positions.Add(this.Triangles[i].Vertex3);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
}
this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
this.myModelVisual3D.Content = this.model3DGroup;
return this.myModelVisual3D;
}
}
The return value is also a custom class I created:
class ToothModelVisual3D : ModelVisual3D
{
//VARIABLES
private MyTypes myType;
private int number;
//OPERATORS
public MyTypes MyType
{get { return myType; } set { myType = value; }}
public int Number
{get { return number; } set { number = value;}}
public ToothModelVisual3D() { }
public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}
All I want to do is the following once in the beginning of the program:
{
My3DModel myModel;
myModel = new My3DModel();
myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
myModel.loadTriangles();
mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}
If I do it on the main thread the UI hangs. If I do it on a worker thread and invoke mainViewport.Children.Add(...) it says it cannot access the resourses created on that worker thread. Help?!
From what I understand I've reached a point where I have two threads and resources belonging to each of them (mainViewport => UIThread & myModel => WorkerThread). Neither thread can access directly the other's resource but creating and using myModel on the UIThread makes it hang... All I want to do is have enough responsiveness from the UI, so the user may minimize the program while waiting for it to load the models, nothing more. How can I do that? Is there a way to do all the CPU heavy work on the UIThread, so no resource conflicts arise and have a worker thread that only handles UI for that time?
PS: I've tried with Thread, BackgroundWorker & Task<TResult> classes. Results were similar if not to say the same.
PPS: The full version will load massive models which will load more than 30-40 sec...
I recently came across the same issue when porting an XNA application to WPF.
In my case I partially resolved this by using a background thread to load the positions, normals, and indices from file. Then in that same thread, construct a memory stream containing XAML for the Model3DGroup with the GeometryModel3D and MeshGeometry3D.
Then, in the UI thread, once the memory stream is available, load the model...
Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;
There is still a delay, but as file access is done in a background thread, it is not as severe.
Sorry for the late answer, but I actually managed to workaround the problem long time ago the following way:
delegate void myDelegate();
private void fileOpenButton_Click(object sender, RoutedEventArgs e)
{
try
{
Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
ViewportLoaderThread.IsBackground = true;
ViewportLoaderThread.Start();
}
catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
}
private void loadViewportItemsAsync()
{
try
{
//TRY to browse for a file
if (!browseForFile()) return;
Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);
//Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);
//Load the models to viewport async
myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
this.Dispatcher.BeginInvoke(asyncDel, null);
}
catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
}
private void sendModelsToViewportAsync()
{
for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
{
//Add the models to MAIN VIEWPORT
ModelVisual3D modelVisual = new ModelVisual3D();
GeometryModel3D geometryModel = new GeometryModel3D();
Model3DGroup modelGroup = new Model3DGroup();
geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);
modelGroup.Children.Add(geometryModel);
modelVisual.Content = modelGroup;
mainViewport.Children.Add(toothModelVisual);
}
}
The key was to use the this.Dispatcher.BeginInvoke(asyncDel, null); as it works on the main thread, but does not lag it, because it is executed asynchronously.
Using a delegate still appears to introduce a lag on the UI, a better solution is create the model in a worker thread and then freeze it. The model can then be cloned by the UI thread without the annoying exception. This works for me with models which take 25 seconds or more to load. The only issue I've found with this is that it doesn't work if the model contains a texture.
I have designed a progress bar that I'd like to use when I load a grid (I load a datagridview from a stored procedure). However the process that calls the stored procedure has a few different items it calls (see below). I'm early on in getting the progress bar to work (it doesn't in the code below, hence why Im here), but my question is this.
Can the progress bar properly wok when the progress of what I'm tracking is multiple different methods. The "LoadGrid" method is the one I'd really like to track progress of, as that is the processing of the stored procedure and loading of datagridview (i.e. the time consuimng processes). I guess I'm more asking what's the proper technique as opposed to the exact code to use, but I'm limited in knowedge on progress bars. I know I could use a just a random icon that says "busy" but I'd rather have the progress bar if its possible to do legitimately.
public void btnLoadGrid_Click(object sender, EventArgs e)
{
frmProgress progressForm = new frmProgress();
try
{
progressForm.MdiParent = this;
progressForm.Text = "Importing DSC_0";
progressForm.Top = this.Height / 3 - progressForm.Height / 2;
progressForm.Left = this.Width / 2 - progressForm.Width / 2;
//ofd1.Title = "Import legacy DSC balances";
//this.ofd1.ShowDialog(this);
//Need code to empty grid before loading
grd1.Rows.Clear();
grd1.Refresh();
//Load grid based on new selections
GetUserSelections();
GetUserRelatedInfo();
LoadLabelForecastType();
LoadGrid();
}
catch (Exception ex)
{
util.LogError(ex);
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Cursor.Current = Cursors.Default;
progressForm.Close();
}
And the progress bar itself:
namespace AmortClient
{
public partial class frmProgress : Form
{
public frmProgress()
{
InitializeComponent();
}
public ProgressBar Pbar
{
get { return this.pb1; }
}
}
}
We have a progress bar that uses Background thread and event callbacks. It also uses the "params" parameter and delegates so that it can be generalized into any process in the code.
Here are a few little snippits...
private delegate T Method(ProgressBarCallBackInterface callingform, params object[] argsobject);
private void frmProgress_Load(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(FormTitle))
{
lblSampleTitle.Text = FormTitle;
this.Text = FormTitle;
}
else
{
lblSampleTitle.Text = string.Empty;
this.Text = string.Empty;
}
bgWorkerThread.RunWorkerAsync();
}
private void bgWorkerThread_DoWork(object sender, DoWorkEventArgs e)
{
Delegate method = Delegate.CreateDelegate(typeof(Method), instanceOfClassHavingTheFunction, FullFunctionName, true, true);
if (method != null)
{
ReturnValue = ((Method)method)(this, Parameters);
}
}
public void ReportProgress(int percentage, string statusText)
{
lblProgress.SetPropertyThreadSafe(() => lblProgress.Text, statusText);
bgWorkerThread.ReportProgress(percentage);
}
then in whatever code is using it, I can call
if (progressBar != null)
progressBar .ReportProgress(6, "Verifying Journal Integrity");
my code is slightly more complicated because T cannot be void, so I had to add some switches and a secondary method to allow this to run on processes that have a void return. But the basic shape is there.
Here is a sample of an entry point into the progress bar:
//note: FunctionName, Class Having Function, Params....
frmProgress _frmProgress = new frmProgress("UpdateRigX", RigFacade.Instance, rigX, dRigVersion, ModuleVersion);
_frmProgress.FormTitle = "Updating RigX...";
_frmProgress.ShowDialog();
I the following code that creates windows in an mdi form. The idea is to create a window of a certain type if it dosent exist, or bring it to front if there is already an instance.
public static object CreateWindow(Type windowType, params object[] args)
{
try
{
lock (_definitionToWindow)
{
var def = new WindowDefinition {ControlType = windowType, Args = args};
System.Windows.Forms.Form win = null;
if (_definitionToWindow.TryGetValue(def, out win))
{
win.Activate();
return win;
}
System.Windows.Controls.Control uiElement =
(System.Windows.Controls.Control) Activator.CreateInstance(windowType, args);
object result = null;
if (uiElement is Window)
result = WpfMdiHelper.ShowWpfWindowInMdi((Window) uiElement);
else
result = WpfMdiHelper.ShowWpfControlInMdi((System.Windows.Controls.Control) uiElement);
if (result is System.Windows.Forms.Form)
{
_definitionToWindow.Add(def, result as System.Windows.Forms.Form);
lock (_windowslock)
{
_windows.Add((System.Windows.Forms.Form) result, uiElement as IHasViewModel);
}
((System.Windows.Forms.Form) result).Disposed += new EventHandler(WindowsFactory_Disposed);
}
return result;
}
}
catch (Exception ex)
{
Logger.WriteError("Window creation exception", ex.ToString(), LogEntryCodes.UIException);
}
return null;
}
The code more or less works, but when you click a button that opens a window several types in quick succession it opens up several windows.
After running debug traces I found that lock (_definitionToWindow) is being bypassed by all the clicks (it looks like all calls are being made on the same thread) and the method blocks on Activator.CreateInstance. So when the 2nd call arrives to the dictionary check it doesn't find any previous instances and proceeds to recreate the window.
Anyone knows why this happens? and the proper way to handle this situation?
This should give you a thread safe lock that only allows one caller into CreateWindowImpl even if they're on the same thread. It doesn't block any threads though unlike lock().
static long Locked = 0;
static void CreateWindow(...)
{
if(0 == Interlocked.Exchange(ref Locked, 1))
{
try
{
CreateWindowImpl(...);
}
finally
{
Interlocked.Exchange(ref Locked, 0);
}
}
}