My dashboard needs too much time to get data from database so I have to use async approach to handle this problem here is my code :
public async Task < Stream > LoadDashboard() {
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
return s;
}
private async void frmMaterialDashboard_Load(object sender, EventArgs e) {
Stream dashboardData = await LoadDashboard();
dashboardViewer1.LoadDashboard(dashboardData);
//show UI components for user interact
}
My code doesn't work and I have to wait for data to come from the database. Should I add anything else ?
This part of code takes long time to load data
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
I want to execute this part async. When my form is loaded I want to call LoadDashboard as a background task to get the data from database ,and the main thread show my user interface form .
The component link that I am using :
https://documentation.devexpress.com/#Dashboard/CustomDocument113927
From what it looks like you have no actual async work you can do, you are reading a resource in to a memory stream. Putting the async keyword on somthing does nothing by itself, the function still runs just like it used to. If you want the work to happen in the background you have to tell it to work in the background by using a new thread.
//Get rid of this async stuff here.
public Stream LoadDashboard()
{
Stream s = new MemoryStream(Encoding.Default.GetBytes(Resource.Dashboard));
s.Position = 0;
return s;
}
private async void frmMaterialDashboard_Load(object sender, EventArgs e)
{
//Start LoadDashboad in a background thread and await it.
Stream dashboardData = await Task.Run(() => LoadDashboard());
dashboardViewer1.LoadDashboard(dashboardData);
//show UI components for user interact
}
Another option is to not copy the string to a memory stream and instead get the stream directly
private void frmMaterialDashboard_Load(object sender, EventArgs e)
{
using (var dashboardStream = Resources.ResourceManager.GetStream("Dashboard"))
{
dashboardViewer1.LoadDashboard(dashboardStream);
}
//show UI components for user interact
}
I got rid of the async because DashboadViewer does not provide a way to call LoadDashboard from the background to the best of my knowledge. You will have to wait till it finishes loading or figure out how to get smaller data.
Related
I new to WPF, and have to put a basic application together
It consists of one main window with a frame, and one page
the page has a basic status text -
the requirement is that when the page loads up, the application has to do a bunch of REST call to fetch some data from remote source, and update the status text as it fetches
problem is, as I update the text, it doesn't seem to be reflected on the page, or maybe it's being blocked - even though I've used Task
so far, I have the following code for testing:
private void Page_Loaded(object sender, RoutedEventArgs e) {
var wnd = Window.GetWindow(this);
wnd.ContentRendered += Wnd_ContentRendered;
}
private void Wnd_ContentRendered(object sender, EventArgs e) {
DisplayMessages();
}
private void DisplayMessages() {
authenticationText.Text = "text one";
var t = Task.Delay(5000);
t.Wait();
authenticationText.Text = "text two";
t = Task.Delay(5000);
t.Wait();
authenticationText.Text = "text three";
t = Task.Delay(5000);
t.Wait();
}
even though I'm waiting after each task, the UI doesn't get updated - rather it just displays text three directly after method is finished - suggestions ?
P.S: there's also a WPF loader on that page, I've noticed that it doesn't get animated as well - it seems the delay is working but everything on the UI isn't updated
I would suggest for getting the data from REST implementation , you should use the background worker and on the basis of completion of thread or progress changed you need to update the UI thread accordingly.
for getting the better insights on background worker.. kindly use this link
How to use WPF Background Worker
In your case you can use progresschanged event of the backgroundworker..
Please Create some property lets say StatusText with InotifyPropertyChanged Interface implemented and bind (use TwoWay Binding) it with the Text property of the authenticationText control .... and in the progress changed event of the backgroundworker set the value of the StatusText property,., which will automatically updates the UI.
You could try to invoke these results on the UI Thread...
Run your task normally with Task.Run or whatever. Each time you are ready to set some property on UI Thread you should invoke it through the dispatcher..
Task.Run(() =>
{
var _Temp = getSomePropTask();
Thread.Sleep(1000);
App.Current.Dispatcher.Invoke(()=>{
authenticationText.Text = _Temp;
});
});
Thanks to suggestion by Ashok, I did some background reading and have come up with the following solution using Task, async and await - which is simpler to manage than background worker threads:
private void Page_Loaded(object sender, RoutedEventArgs e) {
var wnd = Window.GetWindow(this);
wnd.ContentRendered += Wnd_ContentRendered;
}
private void Wnd_ContentRendered(object sender, EventArgs e) {
GetDataAsync();
}
private async void GetDataAsync() {
authenticationText.Text = "Connecting...";
await Task.Delay(5000);
authenticationText.Text = "Getting Member Details...";
List<MemberServiceModel> memberList = await GetMembersAsync();
// more code for handling response
}
private List<MemberServiceModel> GetMembers() {
//get all members synchronous
var request = new RestRequest("Members/Admin", Method.GET);
var response = _client.Execute<List<MemberServiceModel>>(request);
if (response.ResponseStatus != ResponseStatus.Completed) {
//TODO
_restErrorStatus = response.ResponseStatus.ToString();
_restErrorMessage = response.StatusDescription;
_logger.Error("Error in GetMembers");
_logger.Error("Status:" + _restErrorStatus);
_logger.Error("Description:" + _restErrorMessage);
}
return response.Data; ;
}
private Task<List<MemberServiceModel>> GetMembersAsync() {
//get all members asynchronous
return Task.Run(new Func<List<MemberServiceModel>>(GetMembers));
}
I am rather new to multithreading and the likes and still trying to wrap my head around the whole thing.
I have the following scenario (simplified):
public partial class Form1 : Form
{
private AppLogic logic = new AppLogic();
private void GetData()
{
var dataSetNames = logic.GetDataSetNames();
foreach (var dataSetName in dataSetNames)
{
var page = new TabPage();
var dgv = new DataGridView { Dock = DockStyle.Fill, DataSource = logic.GetDataSet(dataSetName) }
page.Controls.Add(dgv);
tabControl1.TabPages.Add(page)
}
}
private void GetMeSomeDataToolStripMenuItem_Click(object sender, EventArgs e)
{
GetData();
}
/* ... */
}
That's fancy and all but since the part where logic gathers data takes a few seconds, the Form will always freeze. I don't want that so I am currently trying to get rid of that. What I tried was changing the GetData() call a bit:
Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData);
tabControl1 = t.Result;
Of course I adjusted GetData() accordingly so it now returns a new TabControl instead of accessing the Form directly.
This didn't improve my situation at all, though, which is probably because accessing the Result Property of a Task forces the accessing Task to wait for completion.
So I am currently looking for a different way to do this but I can't come up with anything.
I guess the best way would be to use ContinueWith:
t.ContinueWith(formerTask => {
if (formerTask.IsFaulted) return;
var x = formerTask.Result;
// do whatever but use Invoke if necessary
})
This starts a task to process the result after your task has completed.
Another way to deal with this is to make the eventhandler async:
private async void GetMeSomeDataToolStripMenuItem_Click(object sender, EventArgs e)
{
TabControl t = await Task<TabControl>.Run(GetData);
tabControl1 = t;
}
Note that the async void pattern should generaly be avoided but is Ok for eventhandlers.
This approach is much easier when you need to update your GUI with the returned results. Creating a TabControl on another thread is highly suspect though. You should separate the data-getting and the control-creating. The latter action should be done on the main Thread.
This will unfreeze your GUI but to make this really efficient the GetData method should be made async, and the task.Run replaced by awaiting an I/O method.
Alright, after some work I found out dryman's answer wasn't optimal for me. This is what I did:
private async void GetData()
{
tabControl1.TabPages.Clear();
Task<List<string>> dataSetNames = Task<List<string>>.Factory.StartNew(logic.GetDataSetNames);
await dataSetNames;
foreach (var dataSetName in dataSetNames.Result)
{
Task<DataTable> sourceTable = Task<DataTable>.Factory.StartNew(() => logic.GetDataSet(dataSetName));
TabPage page = new TabPage { Name = dataSetName }
DataGridView dgv = new DataGridView { Dock = DockStyle.Fill }
dgv.DataSource = await sourceTable;
page.Controls.Add(dgv);
tabControl1.TabPages.Add(page);
}
}
This even makes my code more DRY, since I don't have to write
Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData);
everytime I want to call GetData().
TL;DR: use await threadName; before accessing the Result-property of a thread if you don't want to block your accessing thread.
I'm trying to open an OpenFileDialog within C# (codebehind, on an asp.net page). Because the regular references and the system.windows.form ones have some conflicts, I'm having to use the OpenFileDialog box within a thread, as below:
protected void Button1_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(new ThreadStart(BrowseForFile));
newThread.SetApartmentState(ApartmentState.STA);
newThread.Start();
}
static void BrowseForFile()
{
System.Windows.Forms.OpenFileDialog MyFile = new System.Windows.Forms.OpenFileDialog();
if (MyFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
}
}
The way the page works means that this has to be in C# - using asp.net's fileupload won't work.
Now, the OpenFileDialog appears fine, and I can get values from it, but I ideally need to pass values into the thread (the BrowseForFile) and have it work with other controls on the page, in order to set things up. However, I'm very new to using threads.
Can someone show me, basically, how to get an integer from the Button1_Click into the BrowseForFile, and how I would set a label on the page from BrowseForFile?
If you use a modern version of .net you could use async-await for this. This makes it much easier for you to communicate with your dialog and to pass data to the thread that performs your background work.
To be able to use async-await:
Declare your function async
Let your function return Task instead of void and Task<TResult> instead of TResult.
There is one exception: event handlers may return void
In your async function start your other threads using Task.Run
while the other thread is running you can do other things
if you need the result: call await Task.
In your case, you'll have to change your thread class into a procedure that contains the code in your thread class. This procedure may be in any class. It must be declared async and return Task instead of void:
Of course you'll have to change your thread class into an async procedure:
private async Task MyThreadProcedureAsync(string fileName)
{
// do something really slow with fileName and return void
}
protected async void Button1_Click(object sender, EventArgs e)
{
string fileName = this.BrowseForFile();
if (!String.IsNullOrEmpty(fileName))
{
var myTask = Task.Run( () => MyThreadProcedureAsync(fileName))
// if desired do other things.
// before returning make sure the task is ready:
await myTask;
// during this wait the UI keeps responsive
}
}
private string BrowseForFileName()
{
using (var dlg = new System.Windows.Forms.OpenFileDialog())
{
// if needed set some properties; show the dialog:
var dlgResult = dlg.ShowDialog(this);
if (dlgResult == System.Windows.Forms.DialogResult.OK)
{
return dlg.FileName;
}
else
{
return null;
}
}
}
I have a form "DisplayImagesForm" which calls a function loadImages() in the constructor:
public ImageScraperForm(string query, RichTextBox textBox)
{
InitializeComponent();
this.query = query;
loadImages();
}
Then the loadImages() function creates a new thread at the end:
{
...
Thread thread = new Thread(readNextImage);
thread.Start();
}
The problem is that the thread doesn't seem to be running as a different thread than UI thread. The readNextImage() method loads images from a server which takes some times - while loading the image it blocks the whole form. It's not normal, because the "thread" should be running separately from the UI thread. Also the readNextImage() function can modify the UI elements without Invoke((MethodInvoker)delegate - no exception is thrown.
If you're trying to download an image over the internet and display it in a WinForm control; you're going about it completely wrong.
Do not do any lengthy processing in your Form constructor; that is going to make your form unresponsive. If you are going to display something in the UI you should do that in your Form's Paint event handler. If you are trying to download something over the internet you should be using await, not a Thread. Threads use a CPU core and, since the internet is orders of magnitude slower than a CPU, you are going to be blocking it immediately.
The correct way of doing this is using await to load the file when you need it. If you need it when you start the Load event handler is a good choice.
private async void YourForm_Load(object sender, EventArgs e)
{
using (var c = new HttpClient())
using (var resp = await c.GetAsync(#"http://uri/to/image.jpg"))
using (var content = resp.Content)
using (var s = await content.ReadAsStreamAsync())
{
_img = new Bitmap(s);
}
YourControl.Invalidate();
}
private void YourForm_Paint(object sender, PaintEventArgs e)
{
if (_img != null)
DrawToYourControl(_img);
}
I have a question regarding saving/loading data in Windows Universal Apps 8.1.
I cannot seem to save the data when exiting the app. My call to the serialization is done in the OnSuspending method in App.xaml.cs, but I don't think it is called whenever I close the app.
It sometimes saves the data, sometimes not. Here is my code for OnSuspending and Save methods.
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// TODO: Save application state and stop any background activity
Debug.WriteLine("SUSPENDING");
HabitManager.HabitSerializer.Save();
deferral.Complete();
}
public async static void Save()
{
Debug.WriteLine("SAVED");
var json = JsonConvert.SerializeObject(HabitList.Instance.GetHabits());
var habits = HabitList.Instance.GetHabits();
foreach (var h in habits)
{
Debug.WriteLine("S: " + h);
}
StorageFile saveFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(saveFile, json);
}
So my question is, what could be the problem, and if this isn't a good serializing mechanism, can you suggest me a better one? Thank you
Your OnSuspending call returns before the Save call finishes.
Change Save to return a Task so you can await it:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// TODO: Save application state and stop any background activity
Debug.WriteLine("SUSPENDING");
// Wait fir Save to finish its a sync operations
await HabitManager.HabitSerializer.Save();
deferral.Complete();
}
public async static Task Save()
{
// Same save code
}
Also be careful when testing suspension. The app won't suspend normally while debugging, but VS provides a suspend button to simulate the suspension process.