WPF User Control children not updating c# - c#

I am trying to add a child control (UserControl) to a Grid and the changes are not reflecting. But if another child control (UserControl) is added to the same grid then layout gets updated and both child are visible. This operation perform on button click.
/*this Operation is perform in Backgroud Worker */
void func()
{
/*adding first User Control*/
addRemoveChild(true,FirstChild);//even tried to run this fuc with Dispatcher
FixButton();
addRemoveChild(false,FirstChild);
}
void addRemoveChild(bool isAdd,UserControl uc)
{
if (isAdd)
{
parentGrid.Children.Add(uc); /*parentGrid is Parent Grid*/
parentGrid.UpdateLayout();
return;
}
else
{
parentGrid.Children.Remove(uc);
parentGrid.UpdateLayout();
}
}
void FixButton()
{
/* here some operation is perform which takes 5 min to complete till then FirstChild is not visible*/
addRemoveChild(true,secondChild); /*When this Func run the first Child is visible*/
}

Your function is performed in a background worker: It's not done in the Dispatcher Thread. Every time you use a Dispatcher object (an object that has been created by the Dispatcher thread such as Controls) you should be in the Dispatcher thread.
The background worker is useful to perform task and update UI relative to the task's status in "real time".
You aren't using the background worker correctly. The code in the DoWork is executed in a separate thread whereas the ProgressChanged callback is executed in the Dispatcher thread.
What your code should look like this:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, args) => {
bw.ReportProgress(0);
FixButton();
bw.ReportProgress(100);
};
bw.ProgressChanged += (sender, args) => {
if (args.ProgressPercentage == 0) {
parentGrid.Children.Add(uc);
} else if(args.ProgressPercentage == 100) {
parentGrid.Children.Remove(uc);
}
};
bw.RunWorkerAsync();
By the way you don't need to call UpdateLayout() and your DoWork callback function should never use Dispatcher objects (remove addRemoveChild from FixButton function)

Related

How to access the UI thread?

I have a Winform APP that does a simple task.
There is an event listener for a button that create a new thread:
ThreadStart work = (addToList);
Thread thread = new Thread(work);
thread.Start();
Now I need the second part of the calculation ot be done with the UI thread (so my instruction ask)
"
Note that you need to access the list box within the UI thread (hint: you can use Control.Invoke for WinForm
"
Any idea how to do so?
The Invoke method needs a delegate (reference to a function) and will schedule that on UI thread. Considering that all the elements on the form share the same thread as UI thread, you can use the Invoke method from either the form or any of its controls:
class MyForm
{
private void Button_Click(object sender, EventArgs e)
{
var thread = new Thread(new ThreadStart(FullCalculation));
thread.Start();
}
private void FullCalculation()
{
OffUIThreadCalculation();
this.Invoke(OnUIThreadCalculation); // Schedules OnUIThreadCalculation to run on the UI thread of `this`, aka the form
}
private void OffUIThreadCalculation()
{
// UI elements should not be used here
}
private void OnUIThreadCalculation()
{
// UI elements can be used here
}
}

WPF NotifyPropertyChange from different thread

I have my VM implemented INotifyPropertyChanged interface. I created another thread T for populating a list that I bind to Xaml. After list is populated, I call PropertyChanged in thread T, and my UI got refreshed correctly.
My question is in what case I would need to use Dispatcher? Why I don't need to use Dispatcher in my case? I thought Dispatcher is used when the code in other thread want to notify the changes to the UI thread by enqueuing the changes to the UI refresh queue, such as adding items to ObservableCollection from another thread, and UI thread will then pull data from the queue.
private List<string> _ListData;
public List<String> ListData
{
get
{
if (_ListData == null)
Initialise( () => ListData = ReturnSlow());
return _ListData;
}
set { _ListData = value; }
}
private List<string> ReturnSlow()
{
List<string> Test = new List<string>();
Test.Add("1");
Test.Add("2");
Thread.Sleep(2000);
return Test;
}
public void Initialise(Action initialiser)
{
Task t = new Task(() =>
{
initialiser();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ListData"));
});
t.Start();
}
Your app has a main UI thread (usually ManagedThreadId==1). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
If you're in the main window you can use:
Dispatcher.BeginInvoke(...
If you're in someother context eg a view model then use:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.
MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.
Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.
CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.
Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
Which can be called as:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});

The calling thread cannot access this object error

I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..

Update controls created in another thread?

I have two threads.
Thread 1: WPF thread. Shows a Window with all the information.
Thread 2: Loops constantly, receiving information & updates the Window in thread 1.
I have the following interfaces.
IModuleWindow
{
void AddModule(IModule module);
void RemoveModule(IModule module);
}
IModule
{
UserControl GetSmallScreen();
UserControl GetBigScreen();
}
IModuleWindow is implemented by the WPF window in Thread 1
IModule is implemented by an object, is instantiated in Thread 2, and then sent to thread 1.
I want to Add the UserControls in IModule to the Window object in thread 1, and show them. IModule objects get updated constantly in thread 2 and they have to change their text.
Basically the idea is that this program is supposed to show the state of objects in thread 2 , which gets updated constantly.
What is the best way to accomplish this in WPF?
IMO the best idea is to use BackgroundWorker, with the very handy ReportProgress method and ProgressChanged event.
The ProgressChanged event is raised on the GUI thread, so you can perform your updates to the GUI directly. Here's how you code should look like:
// initialize the worker
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
// thread 2 (BackgroundWorker)
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// main loop
while(true)
{
// time-consuming work
// raise the event; use the state object to pass any information you need
ReportProgress(0, state);
}
}
// this code will run on the GUI thread
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// get your state back
object state = e.UserState;
// update GUI with state
}
It helped me lot to understand what i must do.
The scenario must be like that:
ObservableCollection images = new ObservableCollection();
TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
for (int i = 0; i < 50; i++)
{
//GET IMAGE Path FROM SERVER
System.Windows.Application.Current.Dispatcher
.BeginInvoke((Action)delegate()
{
// UPDATE PROGRESS BAR IN UI
});
images.Add(("");
}
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
// EXCEPTION IF THREAD IS FAULT
throw t.Exception;
}
System.Windows.Application.Current.Dispatcher
.BeginInvoke((Action)delegate()
{
//PROCESS IMAGES AND DISPLAY
});
});
You must use System.Windows.Application.Current.Dispatcher.BeginInvoke() for updating UI in WPF.
It would be nice to be able to use controls created at another thread,
thats what I want ideally
The short answer: forget it.
A UI control belongs to a single UI thread only. The best you can do here, is to create controls in main thread, prepare data in background thread, and update controls' properties in main (UI) thread again.
For data preparation I recommend use TPL.

Making a Controls.Add method call thread-safe

Let's say I have the following code:
public void Inject(Form subform)
{
this.tabControl1.TabPages[1].Controls.Add(subform);
this.Refresh();
}
How can I convert the Controls.Add() call to a thread-safe call, using Control.Invoke?
The only way to make Control.Add thread safe is to make sure it's called from the UI thread. This also implies that the Control being added is usable from the UI thread.
Here is a function though which produces a delegate which can add to a Control from any thread (presuming the added Control is OK on the UI thread).
public Action<Control> GetAddControl(this Control c)
{
var context = SynchronizationContext.Current;
return (control) =>
{
context.Send(_ => c.Controls.Add(control), null);
};
}
Then for a given Control you can pass the resulting delegate to any thread.
// From UI thread
Action<Control> addControl = c.GetAddControl();
// From background thread
addControl(subForm);

Categories

Resources