problem in silverlight 4 async how to wait till result come - silverlight

Here is what i have problem
i have following code :
//Get All master record
entryE_QuestMaster = new ObservableCollection<E_QuestMaster>();
QuestVM.getExamsMasterbyExamID(eUtility.ConvertInt32(this.txtID.Text), ref entryE_QuestMaster);
//
//Loop to show questions
int iNumber=1;
foreach (var oIn in entryE_QuestMaster)
{
Node subNode = new Node();
subNode.Content = oIn.e_Question;
subNode.Name = "Quest_" + iNumber.ToString().Trim();
subNode.Tag = oIn.e_QID.ToString();
subNode.Icon = "/Images/Number/" + iNumber.ToString().Trim() + ".gif";
iNumber++;
this.tvMainNode.Nodes.Add(subNode);
}
here is async method calling wcf service
/// <summary>
///
/// </summary>
/// <param name="ID"></param>
public void getExamsMasterbyExamID(int ID, ref ObservableCollection<E_QuestMaster> iCollectionData)
{
ObservableCollection<E_QuestMaster> iCollectionDataResult = iCollectionData;
eLearningDataServiceClient client = new eLearningDataServiceClient();
client.getExamsMasterCompleted+=(s,e)=>
{
iCollectionDataResult = e.Result;
};
client.getExamsMasterAsync(ID);
}
problem : when ever system run --> QuestVM.getExamsMasterbyExamID(eUtility.ConvertInt32(this.txtID.Text), ref entryE_QuestMaster);
its does not wait till i get e.result its just move to next line of code which is
foreach loop.
plssss help any one or give idea with sample code what should i do to wait till e.result
i wanted to some how wait till i get e.result
any idea ?

one simple solution. Move the code that you want to execute after getting e.result in this loop.
client.getExamsMasterCompleted+=(s,e)=>
{
iCollectionDataResult = e.Result;
};

Add an Action to the getExamsMasterbyExamID method, then execute the callback after is completed.
public void getExamsMasterbyExamID(int ID, ref ObservableCollection<E_QuestMaster> iCollectionData,Action<object> callback)
{
ObservableCollection<E_QuestMaster> iCollectionDataResult = iCollectionData;
eLearningDataServiceClient client = new eLearningDataServiceClient();
client.getExamsMasterCompleted+=(s,e)=>
{
iCollectionDataResult = e.Result;
//the callback will be executed on the calling method
callback(e.Result);
};
client.getExamsMasterAsync(ID);
}
now when you call the Async method add the loop inside the callback like this:
entryE_QuestMaster = new ObservableCollection<E_QuestMaster>();
QuestVM.getExamsMasterbyExamID(eUtility.ConvertInt32(this.txtID.Text), ref entryE_QuestMaster,r=>
{
int iNumber=1;
foreach (var oIn in entryE_QuestMaster)
{
Node subNode = new Node();
subNode.Content = oIn.e_Question;
subNode.Name = "Quest_" + iNumber.ToString().Trim();
subNode.Tag = oIn.e_QID.ToString();
subNode.Icon = "/Images/Number/" + iNumber.ToString().Trim() + ".gif";
iNumber++;
this.tvMainNode.Nodes.Add(subNode);
}
});
after 2 months prob this wont be useful for the asker but maybe for someone else...

Related

ObservableCollection AddRange in async Task

Hi I want to use the ObservableCollection (AddRange) in async Task but i get NotSupportedException
private ObservableCollection<CoronavirusCountry> _data = new ObservableCollection<CoronavirusCountry>();
public ObservableCollection<CoronavirusCountry> data
{
get => _data;
set => SetProperty(ref _data, value);
}
Task.Run(async()=>{
APIService service = new APIService();
data.AddRange(await service.GetTopCases());
Status = "Updated " + DateTime.Now;
});
Not sure which AddRange method you are referring to, because ObservableCollection doesn't have that out of the box.
Anyway - assuming you wrote an extension method - it has to be called in the UI thread, so running a Task doesn't make sense.
The awaitable method shown below should be sufficient. It would await the asynchronous service call, and update the collection in the main thread.
public async Task UpdateData()
{
var service = new APIService();
var newData = await service.GetTopCases();
Data.AddRange(newData); // use proper naming!
Status = "Updated " + DateTime.Now;
}
In order to call and await the above method, you could have an async Loaded event handler like this:
public MainWindow()
{
InitializeComponent();
viewModel = new ViewModel();
Loaded += async (s, e) => await viewModel.UpdateData();
}

How to parallelize 3d model creation?

Bit of a special case here, because I'm using a library called helix-toolkit but bear with me.
The thing is I would like to parallelize the creation of model objects in my code using a backgroundworker.
I know that there is a big issue with mutlithreading and working on UI elements but maybe somehow there is a workaround.
Here is the rough structure of my code:
First file in which I create the Backgroundwoker, splitting the workload for creating Geometry3D objects and finally calling SetModelGeometry to bind the geometries to the viewport. The second file shows how the binding is done.
MainWindow.xaml.cs
private void Draw_Building()
{
_worker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
_worker.DoWork += Draw_Building_DoWork;
_worker.ProgressChanged += DrawBuilding_ProgressChanged;
_worker.RunWorkerCompleted += DrawBuilding_RunWorkerCompleted;
_worker.RunWorkerAsync(10000);
}
private void Draw_Building_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// returns a list containing Geometry3D objects ( created by meshBuilder.ToMeshGeometry3D() )
Geometryhandler.Draw_Building(sender, e);
}
private void DrawBuilding_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
StatusProgressBar.Value = e.ProgressPercentage;
var i = (int)e.UserState;
var actualComponent = MyBuildingComponents.First(c => c.Id == i);
LblStatusbarInfo.Text = "Currently meshing element #" + actualComponent.Globalid + " (" +
actualComponent.Objectname + ")";
StatusProgressbarMsg.Text = "Meshing (" + e.ProgressPercentage + " %)";
}
private void DrawBuilding_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StatusProgressbarMsg.Text = "-";
StatusProgressBar.Value = 0;
LblStatusbarInfo.Text = "Meshing completed.";
Geometry = e.Result as List<MeshIdandGeometry>;
// creates MeshGeometryModel3D objects and binds them to the viewport using the List of Geometries
MainViewModel.SetModelGeometry(Geometry);
}
MainViewModel.cs
public void SetModelGeometry(List<MeshIdandGeometry> geometry)
{
MyModelGeometry = new Element3DCollection();
if (geometry != null)
{
foreach (var mygeometry in geometry)
{
var s = new MeshGeometryModel3D
{
Geometry = mygeometry.Geometry,
Material = mygeometry.Material,
};
this.MyModelGeometry.Add(s);
s.Attach(MyModelViewport.RenderHost);
}
}
this.OnPropertyChanged("MyModelGeometry");
}
My problem at the moment is the following error message:
The calling thread cannot access this object because a different
thread owns it.
which is thrown in the SetModelGeometry function when trying to attach the ModelGeometry to the viewport.
I guess the compiler is complaining about the fact that the geometries were created in different threads, to which he has no access now.
Is there any workaround/solution without destroying the parallel execution of the DrawBuilding function?
EDIT:
EDIT 2: posted the wrong version of the Draw_Building method
The Draw_Building method in the Geometryhandler:
public void Draw_Building(object sender, DoWorkEventArgs e)
{
var geometry = new List<MeshIdandGeometry>();
var standardMaterial = new PhongMaterial()
{
AmbientColor = SharpDX.Color.LightGray,
//DiffuseColor = new Color4(0.35f, 0.35f, 0.35f, 1.0f),
//DiffuseMap = new BitmapImage(new System.Uri(#"Con_Diffuse_2.jpg", System.UriKind.RelativeOrAbsolute)),
//NormalMap = new BitmapImage(new System.Uri(#"Con_Normal_2.jpg", System.UriKind.RelativeOrAbsolute)),
};
var max = _mainWindow.MyBuildingComponents.Count;
var i = 1;
// Loop over building components
foreach (var component in _mainWindow.MyBuildingComponents)
{
//if (i == 5) break;
var component1 = component;
var componentTriangles = _mainWindow.MyTriangles.Where(triangle => triangle.ComponentId == component1.Id);
var meshBuilder = new MeshBuilder(true, true, true);
// Loop over triangles in building element
foreach (var triangle in componentTriangles)
{
var triangle1 = triangle;
var p1 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId1);
var p2 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId2);
var p3 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId3);
if (p1 != null && p2 != null && p3 != null)
{
//meshBuilder.AddTriangle(new Vector3((float)p1.X, (float)p1.Y, (float)p1.Z),
// new Vector3((float)p2.X, (float)p2.Y, (float)p2.Z),
// new Vector3((float)p3.X, (float)p3.Y, (float)p3.Z));
// coordination are switched to match the coordinate system in SharpDX viewport
meshBuilder.AddTriangle(new Vector3(-(float)p1.Y, (float)p1.Z, -(float)p1.X),
new Vector3(-(float)p2.Y, (float)p2.Z, -(float)p2.X),
new Vector3(-(float)p3.Y, (float)p3.Z, -(float)p3.X));
}
}
var mesh = meshBuilder.ToMeshGeometry3D();
var meshandtriangle = new MeshIdandGeometry
{
Id = component1.Id,
Geometry = mesh,
Material = standardMaterial,
};
geometry.Add(meshandtriangle);
i++;
var progressPercentage = Convert.ToInt32(((double)i / max) * 100);
var backgroundWorker = sender as BackgroundWorker;
backgroundWorker?.ReportProgress(progressPercentage, component1.Id);
}
e.Result = geometry;
}
Big thanks to #egse for finding a solution.
Part of the code that causes the problem:
var standardMaterial = new PhongMaterial()
{
AmbientColor = SharpDX.Color.LightGray,
//DiffuseColor = new Color4(0.35f, 0.35f, 0.35f, 1.0f),
//DiffuseMap = new BitmapImage(new System.Uri(#"Con_Diffuse_2.jpg", System.UriKind.RelativeOrAbsolute)),
//NormalMap = new BitmapImage(new System.Uri(#"Con_Normal_2.jpg", System.UriKind.RelativeOrAbsolute)),
};
Basically the problem with the above code is that the material is created as a local variable inside the scope of the backgroundworker. This causes problems with ownership when the UI thread tries to enter the material objects.
The solution to this problem is to make sure that the material is created by the UI thread (e.g in this case, the constructor of the Geometryhandler)
TL;DR: Do not create instances of classes which inherit from DependencyObject in another thread than the UI.

wpf threading, thread must be STA

I have a window(say main window) with a frame which has a page in it. A button on the page opens another window(say popup window). Now i am trying to invoke a method in the main window from a button on the popup window. The method has to be multi-threaded, i had a similar solution running in windows forms but i keep getting the calling thread must be STA because many UI components require this in WPF.
The method on the page which opens the popup window modally
Scripts showStocks = new Scripts();
showStocks.ShowInTaskbar = false;
showStocks.ShowDialog();
if (showStocks.DialogResult==true)
{
Window1 wd1 = new Window1();
wd1.doneDeal();
}
Here window1 is our main window. The doneDeal method is
public void doneDeal()
{
// **Some Code**
BackgroundWorker wworks1 = new BackgroundWorker();
wworks1.DoWork += Tickes;
wworks1.RunWorkerCompleted += Tickes2;
wworks1.RunWorkerAsync();
// Page1 pg1 = frame1.Content as Page1;
//NextPrimeDelegate dd=new NextPrimeDelegate(okreport);
// pg1.addScriptBtn.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
// new NextPrimeDelegate(okreport));
//startStopButton.Dispatcher.BeginInvoke(
// DispatcherPriority.Normal,
// new NextPrimeDelegate(CheckNextNumber));
//new Thread(() => Tick(stock, rowID, exchange)) { IsBackground = false }.Start();
}
Finally the method that i am trying to run in the background
public void Tickes(object sender, DoWorkEventArgs e)
{
}
Also i want to populate a gridview from the result of the tickes method, this will be looping and running over and over in the background but periodically returning data to be added to the grid. SHould i do that in the progress update event ? Have tried a lot to wrap my head around the dispatcher and background worker in wpf but am failing to understand the STA apartment state bit. If someone can help me to get my tickes method going or point me in the right direction, i would be very very thankful.
Well i finally was able to solve the STA thread problem am posting the answer just in case somebody comes across a similar problem in the future.
public void doneDeal()
{
if (StockData.flag == 1)
{
row1 = table.NewRow();
row1[col1] = "";
row1[col2] = "";
row1[col3] = "";
row1[col4] = "";
row1[col5] = "";
row1[col6] = "";
row1[col7] = "";
row1[col8] = "";
row1[col9] = "";
row1[col10] = "";
row1[col11] = "";
row1[col12] = "";
table.Rows.Add(row1);
string stock = StockData.stock;
int rowID = (table.Rows.Count - 1);
string exchange = StockData.exchange;
Thread bh = new Thread(delegate()
{
Tick7(stock, rowID, exchange);
});
bh.SetApartmentState(ApartmentState.STA);
bh.IsBackground = true;
bh.Start();
StockData.flag = -1;
}
}
The Tick7 method which is being called is declared like this
[STAThread]
public void Tick7(string stock, int rowID, string exchange)
{
int rowNum = rowID;
int counter = -1;
deletecounter = StockData.deletecounter;
Thread.CurrentThread.Name = StockData.stock;
.
.
.
}

WPF STA thread error

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.

Silverlight 4 Async wait how to?

I have a problem:
//Get All master record
entryE_QuestMaster = new ObservableCollection<E_QuestMaster>();
//Here I am calling my web service to get data
QuestVM.getExamsMasterbyExamID(eUtility.ConvertInt32(this.txtID.Text), ref entryE_QuestMaster);
//Loop to show questions
int iNumber=1;
foreach (var oIn in entryE_QuestMaster)
{
Node subNode = new Node();
subNode.Content = oIn.e_Question;
subNode.Name = "Quest_" + iNumber.ToString().Trim();
subNode.Tag = oIn.e_QID.ToString();
subNode.Icon = "/Images/Number/" + iNumber.ToString().Trim() + ".gif";
iNumber++;
this.tvMainNode.Nodes.Add(subNode);
}
Whenever I call
QuestVM.getExamsMasterbyExamID(eUtility.ConvertInt32(this.txtID.Text), ref entryE_QuestMaster);
It runs the following code
public void getExamsMasterbyExamID(int ID, ref ObservableCollection<E_QuestMaster> iCollectionData)
{
ObservableCollection<E_QuestMaster> iCollectionDataResult = iCollectionData;
eLearningDataServiceClient client = new eLearningDataServiceClient();
isSync = true;
client.getExamsMasterCompleted+=(s,e)=>
{
iCollectionDataResult = e.Result;
};
client.getExamsMasterAsync(ID);
}
My problem is I wanted to wait until my e.result comes back in iCollectionDataResult.
Currently after calling this service the system continues to the next line of code which is in the foreach loop. In this stage my entryE_QuestMaster does not have any record, I just wanted to wait till my result comes back before the loop continues.
After Answering ChrisF
no dought what chrisf said will work for me, but i wanted to do every thing in my MVVM class rather then in form level , here what i have change in my code , i still need your help guys and whated to do some professional code rather then just writing huge code.
i have added these two lines in my MVVM class
public delegate void ShowQuestionTreeView(ObservableCollection<sp_GetQuestMasterbyExamID_Result> iResultQuestMaster);
public event ShowQuestionTreeView ShowQuestionforTreeview;
then in method i have added this
/// <summary>
///
/// </summary>
/// <param name="ID"></param>
public void getExamsMasterbyExamID(int ID, ref ObservableCollection<sp_GetQuestMasterbyExamID_Result> iCollectionData)
{
ObservableCollection<sp_GetQuestMasterbyExamID_Result> iCollectionDataResult = iCollectionData;
eLearningDataServiceClient client = new eLearningDataServiceClient();
client.getExamsMasterbyExamIDCompleted+= (s, e) =>
{
iCollectionDataResult = e.Result;
**ShowQuestionforTreeview(iCollectionDataResult);**
};
client.getExamsMasterbyExamIDAsync(ID);
}
on client end i have done this
//Generate Treeview for question
QuestVM.ShowQuestionforTreeview += new eQuestCreateVM.ShowQuestionTreeView(QuestVM_ShowQuestionforTreeview);
method :
void QuestVM_ShowQuestionforTreeview(ObservableCollection<sp_GetQuestMasterbyExamID_Result> iResultQuestMaster)
{
//Loop to show questions
int iNumber = 1;
foreach (var oIn in iResultQuestMaster)
{
Node subNode = new Node();
subNode.Content = oIn.e_Question;
subNode.Name = "Quest_" + iNumber.ToString().Trim();
subNode.Tag = oIn.e_QID.ToString();
subNode.Icon = "/Images/Number/" + iNumber.ToString().Trim() + ".gif";
subNode.Title = oIn.e_Question_Text;
iNumber++;
tvCreateQuestion.Nodes[0].Nodes.Add(subNode);
}
}
You need to move this code:
client.getExamsMasterCompleted+=(s,e)=>
{
iCollectionDataResult = e.Result;
};
outside your call to getExamsMasterbyExamID. This will also mean moving where you declare and initialise the eLearningDataServicesClient.
The way you have your code set up at the moment means that you are assuming that iCollectionDataResult will be set when the method returns. However, this is not the case. The method will return before the getExamsMasterCompleted event is fired.
You will need to organise your code along the following lines:
eLearningDataServiceClient client = new eLearningDataServiceClient();
client.getExamsMasterCompleted += (s,e) =>
{
//Loop to show questions
}
client.getExamsMasterAsync(ID);
Or alternatively bind the display of questions to the ObservableCollection so when it gets set the UI updated automatically.

Resources