I'm currently working on an application that calls data from a WCF service and then loads that data into an ObservableCollection(MyListOfBillsCollection). From the OC, I set my datagrid's itemsource to that collection with the example below.
ucLoading.Visibility = Visibility.Visible;
using (TruckServiceClient service = new TruckServiceClient())
{
bills = await service.GetListOfBillsAsync();
foreach (var item in bills)
{
billItem = MyListOfBillsCollection.FirstOrDefault(x => x.Id == item.Id);
if (billItem == null)
{
billItem = new ListOfBillsView();
isNew = true;
}
billItem.Code = item.StockCode;
billItem.Group = item.GroupName;
...
if (isNew)
MyListOfBillsCollection.Add(billItem);
}
}
dgFloor.ItemsSource = MyListOfBillsCollection; //Blocking UI Thread
ucLoading.Visibility = Visibility.Collapsed;
I've got an issue where, when I load that data from the OC and into my datagrid, my UI Thread gets blocked/application freezes and I need to show a 'spinner/loader' to the user that the application is loading the data.
Is it possible to load data into a datagrid and also showing-and-hiding my spinner with two diffirent UI threads? I know it must be possible, and I have done some research but I cannot get my head around it. So I'm posting my clean code(code without me trying to use Application.Current.Dispatcher) here hoping that someone can give me some headers on what to do.
I have tried Async and Await and and used my code example in a Task method that returns a Task with all the code inside and I then used a dispatcher to release the UI work from within the method to the new Thread, but my 'spinners' still will not work correctly and my window still freezes up. In the Task method, I removed the spinners and called the Show/Collapsed code from where I awaited the Task method.
Related
So I have a few methods that I want to call when my form loads (ideally in the constructor but since async/await doesn't work in the constructor I am using the Form_Load event). Originally I was using a separate thread to do this work which was working great. Everything was getting done and the UI was responsive while the work was being done. However, I have read that using async/await is "better", "less resource intensive" and is just generally preferred over creating separate threads. I guess the reasoning is that using async/await uses fewer threads?
But when I use this method as illustrated below, the UI is frozen/unresponsive while the function that takes a few seconds is running.
In my Form_Load event I am calling a synchronous method:
private void Form_Load(object sender, EventArgs e)
{
CheckForDriver();
}
And then here is my CheckForDriver function:
private void CheckForDriver()
{
System.Management.SelectQuery query = new SelectQuery("Win32_SystemDriver") {
Condition = "Description = 'my driver'" };
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection drivers = searcher.Get();
if (drivers.Count > 0) // valid driver, go to next page
{
wizardControl.SelectedTab = startPage;
task = QueryDeviceAsync(false, new List<Button>());
}
}
where task is a field defined as private Task task;
And here is the QueryDeviceAsync function, the part that takes some time is the switcher.GetDeviceAndSize() function.
private async Task QueryDeviceAsync(bool enableForm, List<Button> buttons)
{
lastBackEnable = backBtn.Enabled;
lastNextEnable = nextBtn.Enabled;
EnableButtons(false, false);
this.Enabled = enableForm;
if (buttons != null)
{
foreach (Button button in buttons)
{
button.Enabled = false;
}
}
await Task.Run(() => switcher.GetDeviceAndSize()); // this function takes a few seconds and this is where the UI becomes unresponsive.
ThreadFinished?.Invoke(buttons);
}
and then in the ThreadFinished event handler, I am doing await task; to wait for the QueryDeviceAsync function to finish, at which time I update some UI stuff based on what the switcher.GetDeviceAndSize function did. I was also confused about whether I can/should update UI stuff in an async method, such as when I am disabling the buttons in the buttons list in the QueryDeviceAsync function. I know this doesn't work in a second thread and has to be done on the thread that they were created in, but this runs without issues.
My main problem is that the form is still unresponsive while I'm using these async functions. It works fine when I use a separate thread so I'm inclined to just go back to that but I thought I would try to figure this method out.
In this case you need to offload the blocking synchronous work to a worker thread. for example:
var search = new ManagementObjectSearcher(Query.ToString());
await Task.Run(() => search.Get());
My small WPF code is giving me this error
The calling thread cannot access this object because a different thread owns it
I know what exactly it is saying but I am unable to understand how can I fix it. I have tried different things but no luck and I admit that I am not good in Task library.
This is what I am trying to achieve.
Load data when WPF form loads - This is ok
On the form user will press the Refresh button to refresh the data from the database.
here is my code
public partial class DocListView : UserControlListBase
{
private ScecoBillDataScope _scecoBillDataScope;
public EntityCollection<ScecoBillEntity> ScecoBills = new EntityCollection<ScecoBillEntity>();
public DocListView()
{
InitializeComponent();
LoadData();
}
private async void LoadData()
{
await Task.Run(() =>
{
_scecoBillDataScope.FetchData();
});
var collectionView = CollectionViewSource.GetDefaultView(_scecoBillDataScope.ScecoBills);
await Dispatcher.BeginInvoke(new ThreadStart(()=> LayoutRoot.DataContext = collectionView));
}
private void BbiRefresh_ItemClick(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
_scecoBillDataScope.Reset();
LoadData();
e.Handled = true;
}}
Actually the error is appearing when I click the Refresh button at this line _scecoBillDataScope.FetchData();
Please advice.
Your problem is this line:
await Dispatcher.BeginInvoke(new ThreadStart(()=> LayoutRoot.DataContext = collectionView));
You're just creating a new thread, you have to actually dispatch on the GUI thread:
Application.Current.Dispatcher.Invoke(() =>
{
LayoutRoot.DataContext = collectionView;
});
Since your collection items are data bound to your view, you have to treat them as part of your UI.
Try loading a new collection from within Task.Run, and then copying them over your existing items while on the UI thread. So, the new collection is built on a thread pool thread (Task.Run), and then the data-bound collection items are modified from the UI thread.
It looks like you want to do something in UI Thread.
As You know You can use Dispatcher class.
But There is another way to ask something to UI Thread.
var uiThread = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
// I assumed that It returns boolean value
_scecoBillDataScope.FetchData();
}).ContinueWith(x =>
{
// Here you can put the code to work on the UI thread.
if (x.Result)
{
var collectionView = CollectionViewSource.GetDefaultView(_scecoBillDataScope.ScecoBills);
LayoutRoot.DataContext = collectionView;
}
}, uiThread);
I hope it helps.
thank you.
I'm not sure if this is possible, but I couldn't find anything when I searched about it.
I have a visual schedule made in WPF that loads and displays appointments. The problem is that it takes a while to load all the visuals and the program becomes unresponsive during that time.
Is it possible to load the appointment visuals and modify the schedule grid in a separate thread while leaving the main thread open for other things? Or possibly keep the schedule grid permanently in a second STA thread so it can do its own thing without interfering with the window?
edit:
Currently what I have:
private static void FillWeek()
{
BindingOperations.EnableCollectionSynchronization(ObservableAppointments, _lockobject);
for (int i = 1; i < 6; i++)
{
FillDay(Date.GetFirstDayOfWeek().AddDays(i).Date);
}
}
private static ObservableCollection<AppointmentUIElement> ObservableAppointments = new ObservableCollection<AppointmentUIElement>();
private static object _lockobject = new object();
public static async Task FillDay(DateTime date)
{
ClearDay(date);
Appointment[] Appointments;
var date2 = date.AddDays(1);
using (var db = new DataBaseEntities())
{
Appointments = (from Appointment a in db.GetDailyAppointments(2, date.Date) select a).ToArray();
}
await Task.Run(()=>
{
foreach (Appointment a in Appointments)
{
var b = new AppointmentUIElement(a, Grid);
ObservableAppointments.Add(b);
}
});
}
private static void ObservableAppointments_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
var a = e.NewItems[0] as AppointmentUIElement;
a.Display();
}
}
private static void ClearDay(DateTime date)
{
var Queue = new Queue<AppointmentUIElement>(Grid.Children.OfType<AppointmentUIElement>().Where(a => a.Appointment.Start.DayOfWeek == date.DayOfWeek));
while (Queue.Count > 0)
{
var x = Queue.Dequeue();
Grid.Children.Remove(x);
ObservableAppointments.Remove(x);
}
var Queue2 = new Queue<GridCell>(Grid.Children.OfType<GridCell>().Where(g => g.Date.Date == date));
while (Queue2.Count > 0)
{
Queue2.Dequeue().AppointmentUIElements.RemoveAll(a => true);
}
}
AppointmentUIElement is derived from Border
Yes
Now the challenge of all this is that visual elements and bound ObservableCollections can only be modified by the UI thread without some additional work. Bound properties that are not collections do not require this.
So lets say you have the "appointment visuals" from the UI bound to an ObservableCollection that has you appointment data in it. What you can do is make your 'search appointments' function async and register your collection for thread synchronization as below. I'm leaving out anything related to INotifyPropertyChange for brevity.
public ObservableCollection<Appointments> Appointments = new ObservableCollection<Appointments>();
private static object _lockobject = new object();
public async Task Load()
{
await Task.Run(() => { /*load stuff into the Appointments collection here */ });
///possibly more code to execute after the task is complete.
}
//in constructor or similar, this is REQUIRED because the collection is bound and must be synchronized for mulththreading operations
BindingOperations.EnableCollectionSynchronization(YourCollection, _lockobject);
There is also a much nastier and not recommended way of modifying UI thread created visual elements.
this.Dispatcher.Invoke(() => {/* do stuff with ui elements or bound things*/});
The gist of what happens is that you call load from the UI thread and when it hits the 'await task.run' it will work the contents of the task in a seperate thread while allowing the UI thread to continue responding to the user. Once the task completes it will then under the hood return to the ui thread to execute whatever else was under it in the load method.
If you forget the EnableCollectionSynchronization part then any attempts to add or remove items inside the task.run will throw an error complainging that you cannot change the contents of a collection in a different thread then the same one it was created with (almost same error as trying to modify a ui element directly).
Comment reply -> the problem with what your doing is here
AppointmentUIElement(a,Grid)
What you really should be doing here is putting the Grid into a custom control that has a bound item template defined that binds to items from the ObservableAppointments which should actually be the appointment data, not UI elements. All of this should be happening through ViewModels on context. The way your doing it will ONLY work if there is just a single thread managing EVERYTHING, as soon as another thread gets involved it will all fall apart on you.
Is it possible to load the appointment visuals and modify the schedule grid in a separate thread while leaving the main thread open for other things? Or possibly keep the schedule grid permanently in a second STA thread so it can do its own thing without interfering with the window?
You could load and display the schedule grid in a separate window that runs on a dedicated dispatcher thread. Please refer to this blog post for an example of how to launch a WPF window in a separate thread.
Keep in mind that an element created on the new thread won't be able to interact with an element created on the main thread though. So you can't simply load the schedule on another thread and then bring it back to the main thread. A visual element can only be accessed from the thread on which it was originally created on.
Turns out there was an issue with the GetUserByID method, then library was updated and the problem seems to have gone away, still learnt how to better access the GUI off thread.
I wrote an application using the TweetInvi library, It retrieves a users Followers and following, Also their Picture, a link to the picture and twitter ID.
It then iterates through the returned lists and displays them (all in different lists)
Now when I first started with this application I had everything run on the _Click event and ofcourse ir froze the UI until it had completed.
I have now moved the code over to a backgroundworker Thread and It's causing some quirky issues.
Sometimes it will 'choose' not to populate certain lists, other times it will.
Sometimes it will load all the lists right except for the Following you list, which filters which of your friends are following you back (with an If statement to filter out Verified accounts)
At first I read that trying to update the UI on the separate thread can cause strange errors, so I have removed any UI control changes except for the Lists it populates.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//user name retrieved from text box, rest of this method will pull various bits of data back
var username = e.Argument.ToString();
var user = User.GetUserFromScreenName(username);
Properties.Settings.Default.LastHandle = boxUsername.Text;
Properties.Settings.Default.Save();
var usersTweets = user.GetUserTimeline(Convert.ToInt32(txtTweetAmount.Text)).ToList();
foreach (var userTweet in usersTweets)
{
lstSearchTweetList.Invoke((MethodInvoker)delegate
{
var searchList = lstSearchTweetList.Items.Add(userTweet.Text);
searchList.SubItems.Add(userTweet.CreatedAt.ToString());
});
}
var show = user.GetFollowers(500).ToList();
foreach (var friend in show)
{
string screenName = "#" + friend.ScreenName;
lstFriend.BeginInvoke((MethodInvoker)delegate
{
lstFriend.Items.Add(screenName); // runs on UI thread
});
}
var friends = user.GetFriends(500);
var followers = user.GetFollowers(500);
var result2 = followers.Where(follower => friends.All(friend => follower.Name != friend.Name));
int i2 = 0;
foreach (var res2 in result2)
{
string screenName = "#" + res2.ScreenName;
lstFollowingChecker.BeginInvoke((MethodInvoker)delegate
{
lstFollowingChecker.Items.Add(screenName);
});
i2++;
// lblFollowBackAmount.Text = Convert.ToString(i2);
}
var result = friends.Where(friend => followers.All(follower => friend.Name != follower.Name));
//lblFriendCount.Text = "(" + result.Count().ToString() + ")";
int i1 = 0;
foreach (var res in result)
{
if (res.Verified != true)
{
string screenName = "#" + res.ScreenName;
lstFollowerChecker.BeginInvoke((MethodInvoker)delegate
{
lstFollowerChecker.Items.Add(screenName);
});
i1++;
// lblCheckerCount.Text = Convert.ToString(i1);
}
}
backgroundWorker1.ReportProgress(1,username);
}
The function calling RunWorkerAsync()
private void btnFind_Click(object sender, EventArgs e)
{
//start backgroundworker and clear friends and search lists
pctProgressBar.Visible = true;
lstFriend.Items.Clear();
lstSearchTweetList.Items.Clear();
lstFollowerChecker.Items.Clear();
lstFollowingChecker.Items.Clear();
lstFriend.Items.Clear();
lstSearchTweetList.Items.Clear();
if (txtTweetAmount.Text == "")
{
txtTweetAmount.Text = "20";
}
backgroundWorker1.RunWorkerAsync();
}
My problem is the strange unexplainable errors are still occurring seemingly randomly.
If this is caused by the lists being updated in the background worker thread, what use is the background worker if I cant use it to do the intensive stuff
I will also include two pictures of a friends account as it better demonstrates the issue's so something handles etc will be blanked out.
First Problem is that it sometimes populates a list multiple times, and the "Not following you back list" should be returning #Theilluminati only once
Again it returns #Theilluminati but lists it twice.
There's also an issue of if I run the below code anywhere, the background worker does not run, that is, It will pull back the picture/name/location but the background worker doesn't run and If I try do it in the actual backgroundworker thread then the lists won't populate.
var username = boxUsername.Text;
var user = User.GetUserFromScreenName(username);
//string ImageURL = user.ProfileImageUrl;
//string biggerImageURL = ImageURL.Replace("_normal", "");
//txtImageURL.Text = biggerImageURL;
//pctDisplaypicture.ImageLocation = biggerImageURL;
//txtTwitterID.Text = user.Id.ToString();
//lblFriendCount.Text = "(" + user.FollowersCount + ")";
Any help at all would be appreciated, I'm now struggling to see the use of Backgroundworker if it can't unload work from the UI thread, Sorry for the long post, Thanks for reading.
Fix Attempts
I have disabled the find button while the task is running and the same issue still occurs.
I have also tried using if(working.Cancellationpending == true) to break out of loops once the task has completed once.
I have changed the list foreach loops to the below respectively, and passed the username as a variable instead of pulling it from the control, the problems seem to have just got worse, no lists at all populate now.
lstSearchTweetList.Invoke((MethodInvoker)delegate
{
lstSearchTweetList.Items.Add(userTweet.Text).SubItems.Add(userTweet.CreatedAt.ToString());
});
backgroundWorker1.RunWorkerAsync(boxUsername.Text);
var username = e.Argument.ToString();
I have tried both answers as solutions and both still lead to same issue's with differing severity, I am still stuck with the problem that uncommenting the code to retrieve name/picture etc still blocks the backgroundworker from running. No matter where it's run from.
You may need to use the Invoke method on the list controls that you are trying to update on the background thread like so:
string screenName = "#" + friend.ScreenName;
lstFriend.Invoke((MethodInvoker)delegate {
lstFriend.Items.Add(screenName); // runs on UI thread
});
One problem you can have with multi-threading is when you try to access shared resources (Collections, Files, etc.) from multiple threads deadlocking can occur as well as race conditions. In order to do this safely a locking object would be created in this case and lock the code that is accessing the shared resource. This way the resource can only be accessed one at a time.
//defined globally
object _MyLockingObject = new object();
and within a certain method locking a list:
lock(_MyLockingObject)
{
myList.Add(item);
}
You are breaking a fundamental rule in Windows GUI programming: never access a control from a thread that is not the same thread that created the control. Bad mojo things happen when you break this rule ;)
Pass the username value via backgroundWorker1.RunWorkerAsync(boxUsername.Text);, and read it via e.Arguments as string.
You then need to use BeginInvoke to interact with the UI controls. Ideally, you should optimize this lambda to suspend the control's layout, replace the entire list of items one call, and resume the control's layout.
// execute on the UI thread
this.BeginInvoke((Action)(() =>
{
lstFriend.Items.Add("#" + friend.ScreenName);
}), null);
I would use the async Control.BeginInvoke over the sync Control.Invoke option. There does not appear to be a reason to wait on the control to render your change.
I want to update my a DataGrid from multiple thread in WPF(c#). I use dataGrid.Dispatcher.BeginInvoke() and dataGrid.Dispatcher.Invoke() but they freeze program (main thread). How can update dataGrid from multiple threads with a timeout ( because I use web service that may be unreachable ).
Use a Task kick off the web service request asynchronously. To do this you will probably need to convert the EAP (event-based asynchronous pattern) style into a TAP (task-based asynchronous pattern) style. Here is how you do that.
private Task<IEnumerable<YourDataItem>> CallWebServiceAsync()
{
var tcs = new TaskCompletionSource();
var service = new YourServiceClient();
service.SomeOperationCompleted +=
(sender, args) =>
{
if (args.Error == null)
{
tcs.SetResult(args.Result);
}
else
{
tcs.SetException(args.Error);
}
};
service.SomeOperationAsync();
return tcs.Task;
}
After you have that in place then you can use the new async and await keywords to make the call and wait for it to return using continuation style semantics. It would look like this.
private async void Page_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
IEnumerable<YourDataItem> data = await CallWebServiceAsync();
YourDataGrid.DataSource = data;
}
That is it! It does not get a whole lot more elegant than that. This will perform the operation asynchronously on a background thread and then bind the results to the DataGrid on the UI thread.
If the WCF service is unreachable then it will throw an exception and will be attached to the Task so that it propagates up to the await call. At that point it will be injected into the execution and can be wrapped with a try-catch if necessary.
If you don't need the DataGrid editing to be done in the threads, you can run them in the main thread like this:
this.Invoke((Action)delegate
{
//Edit the DataGrid however you like in here
});
Make sure to only put things you need to be run in the main thread inside it (otherwise that would defeat the purpose of multithreading).