Update UI from different thread.
Posted: 15 Sep 2021, 17:25
Hi,
I'm trying to update the Ui from a different thread.
Thought the simplest way todo this would be through the OnPropertyChanged function like so:
But it seems that CheckAccess() always returns true no matter from which thread its called.
So i implemented my own "Thread-id-watcher" like so:
Now the call from the Non-UI thread correctly goes to the "else" but it is still a hit and miss in that i get a "System.AccessViolationException".
When its a hit and the ui actually gets updated the console puts out:
Error : The calling thread (18080) cannot access this Slidecrew_Noesis.MainWindow because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Border because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this ContentPresenter because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Grid because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this StackPanel because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Grid because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
My 2 button commands:
EDIT:
I believe i fixed it.
The trick was to cache the SynchronizationContext during initializiation. And use that to send functions to the correct thread.
I'm trying to update the Ui from a different thread.
Thought the simplest way todo this would be through the OnPropertyChanged function like so:
Code: Select all
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (Dispatcher.CurrentDispatcher.CheckAccess())
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
else
{
//Dispatcher.CurrentDispatcher.Invoke(() => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); });
Dispatcher.CurrentDispatcher.Invoke(() => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); });
}
}
So i implemented my own "Thread-id-watcher" like so:
Code: Select all
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (_threadId == Thread.CurrentThread.ManagedThreadId)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
else
{
//Dispatcher.CurrentDispatcher.BeginInvoke(() => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }); //no effect at all.
Dispatcher.CurrentDispatcher.Invoke(() => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); });
}
}
public int _threadId;
public ViewModelBase()
{
_threadId = Thread.CurrentThread.ManagedThreadId;
}
When its a hit and the ui actually gets updated the console puts out:
Error : The calling thread (18080) cannot access this Slidecrew_Noesis.MainWindow because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Border because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this ContentPresenter because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Grid because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this StackPanel because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this Grid because a different thread (36032) owns it
Error : The calling thread (18080) cannot access this View because a different thread (36032) owns it
My 2 button commands:
Code: Select all
public void OnCommand(object arg)
{
ButtonText = "Called from UI Thread";
}
public void OnCommandNewThread(object arg)
{
new Thread(new ThreadStart(() => { ButtonText = "Called from NON-UI Thread"; })).Start();
}
EDIT:
I believe i fixed it.
The trick was to cache the SynchronizationContext during initializiation. And use that to send functions to the correct thread.
Code: Select all
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (_dispatcher == Dispatcher.CurrentDispatcher.SynchronizationContext)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
else
{
_dispatcher.Post(new SendOrPostCallback((e) =>
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}), name);
}
}
private SynchronizationContext _dispatcher;
public ViewModelBase()
{
_dispatcher = Dispatcher.CurrentDispatcher.SynchronizationContext;
}
public event PropertyChangedEventHandler PropertyChanged;